flat3 / lodata

The OData v4.01 Producer for Laravel
https://lodata.io/
MIT License
80 stars 26 forks source link

[Laravel 11.x] Unknown database type enum requested, Doctrine\DBAL\Platforms\MySQL80Platform may not support it. #802

Open coderkoala opened 7 months ago

coderkoala commented 7 months ago

Namaskaar.

I have been using LOdata largely relying on its autodiscover feature, with a few overrides here and there(say mapping an SQL view to an endpoint).

On Laravel 10, previously, I used to assign enum as type string as so in my AppServiceProvider(in the boot method:

\DB::connection()->registerDoctrineTypeMapping('enum', 'string');

Then have individual service provider like so:

<?php

namespace App\Providers\Odata;

use App\Providers\Odata\Overrides\UserOwnership as EloquentEntitySet;
use Flat3\Lodata\EntityType;
use Illuminate\Support\ServiceProvider;

/**
 * Lodata Service Provider
 */
class ODataUser extends ServiceProvider
{
    public function boot()
    {
        if (app()->runningInConsole() || app()->environment('testing')) {
            return;
        }
        $entityType = new EntityType('Users');
        $entitySet = (new EloquentEntitySet(\App\Models\User::class, $entityType))
            ->discover();
        \Lodata::add($entitySet);
    }
}

However, I get an error like the linked one: https://flareapp.io/share/LPlpyzQP

I'm guessing this has to do with Laravel removing Doctrine entirely in L11.x.

As of Laravel 11, I have been largely unable to achieve solving this on my own. Any pointers to fixing this or perhaps an alternative approach to implement on migrating from L10.x to L11.x would be great. Thank you! :)

27pchrisl commented 7 months ago

Hi @coderkoala, yep this is indeed related to L11 removing DBAL and the related code. For this change I lifted all the DBAL code Lodata used from L10 and inserted it into Lodata. However, I did not lift out the code relating to installing type mappings for Doctrine.

It might be as simple as making getDoctrineConnection public in https://github.com/flat3/lodata/blob/5.x/src/Drivers/SQL/PDO/Doctrine.php

And then calling it as:

$entitySet->getDoctrineConnection()
                    ->getDatabasePlatform()
                    ->registerDoctrineTypeMapping('enum','string');

Does that work? If so I'd be happy to merge a PR that made that method public :)

coderkoala commented 7 months ago

Namaskaar @27pchrisl, spot on! Looks like it was a good bet bringing implementations into the codebase itself for maintainability.

I turned the method public, then proceeded to refactor all of the service provider. However, I ran into a different issue.

My first registered service provider(Users) ran just fine, however the rest still give off the same error:

namespace App\Providers\Odata;

use App\Providers\Odata\Overrides\UserOwnership as EloquentEntitySet;
use Flat3\Lodata\EntityType;
use Illuminate\Support\ServiceProvider;

class ODataUser extends ServiceProvider
{
    public function boot()
    {
        if (app()->runningInConsole() || app()->environment('testing')) {
            return;
        }
        $entityType = new EntityType('Users');
        $entitySet = (new EloquentEntitySet(\App\Models\User::class, $entityType));
        $entitySet->getDoctrineConnection()
            ->getDatabasePlatform()
            ->registerDoctrineTypeMapping('enum', 'string');

        $entitySet->discover();
        \Lodata::add($entitySet);
    }
}

Please note the UserOwnership merely extends EloquentEntitySet some overrides through composeBuilder for some authorization. I made sure to do it by isolating it to rule out any possibilities, however slim, and I can state with certainty it has nothing to do with it, at least to the extent of my knowledge.

What totally boggles my mind, is aside from the first one, others refuse to bind enum type:

https://flareapp.io/share/Bm0awW9P#/users

Here's my providers.php for reference:

<?php

return [
    ...
    /*
    * OData Services Providers go here.
    */
    App\Providers\Odata\ODataUser::class,
    App\Providers\Odata\ODataOrganizations::class,
    App\Providers\Odata\ODataDomains::class,
    ...
    //-- @Autogenerated.Odata.Marker.Do.Not.Remove@ --//

    /*
    * Custom Blueprint for artisan domain-org ownership...
    */
    ...
];

It looks like DBAL is correctly registering enum as a valid string type for first instance of Service Provider, then it's getting possibly ignored? I couldn't say, although I'm digging into past commits here myself.

Steps I took to see if I lapsed somewhere:

John5 commented 6 months ago

I got the same error with Laravel 10.38, also while running the vendor:publish command.

For now I have worked around it by making the method getDoctrineConnection public like suggested above and registering the type mapping for every model. It is not pretty but workable for now.

chuckmo commented 1 week ago

I'm running into this having just modified several tables to use enum columns in Laravel 11. Are there any plans for this fix to be made without requiring us to modify the source ourselves?

27pchrisl commented 1 week ago

I'm not able to immediately reproduce this by writing unit tests, where I have a migration using $table->enum('something'). Could you provide reproduction instructions for this?

chuckmo commented 1 week ago

I didn't mention that my enum is string-backed, which I see is not supported in the OData spec (saw this issue: https://github.com/flat3/lodata/issues/572)

So makes sense that this doesn't work. I'll withdraw my request for a "fix" this.