kitloong / laravel-migrations-generator

Laravel Migrations Generator: Automatically generate your migrations from an existing database schema.
MIT License
2.44k stars 270 forks source link

[Feature] Support Custom Types DBA #133

Closed gpibarra closed 1 year ago

gpibarra commented 1 year ago

In my Database (MSSQL) i have a custom type, so the execution fails

CREATE TYPE [dbo].[AccountNumber] FROM [nvarchar](15) NULL
 Doctrine\DBAL\Exception 

  Unknown database type name requested, Doctrine\DBAL\Platforms\SQLServer2012Platform may not support it. 

** References https://www.doctrine-project.org/projects/doctrine-orm/en/2.13/cookbook/custom-mapping-types.html https://www.doctrine-project.org/projects/doctrine-orm/en/2.13/cookbook/advanced-field-value-conversion-using-custom-mapping-types.html#advanced-field-value-conversion-using-custom-mapping-types

gpibarra commented 1 year ago

Originally I thought to put the mapping array in the package configuration. But the package configuration is NOT publishable (I did another PR about it).

I then tried to bind to the RegisterColumnType class. This improved the error, but there are other problems that are already difficult for me to solve. app/Providers/AppServiceProvider.php

    public function register()
    {
        //
++      $this->app->bind(\KitLoong\MigrationsGenerator\DBAL\RegisterColumnType::class, function ($app) {
++          return new \App\Support\MigrationsGenerator\MyRegisterColumnType($app->make(\KitLoong\MigrationsGenerator\Repositories\PgSQLRepository::class));
++      });
    }

app/Support/MigrationsGenerator/MyRegisterColumnType.php

<?php

namespace App\Support\MigrationsGenerator;

use KitLoong\MigrationsGenerator\DBAL\RegisterColumnType as RegisterColumnTypeBase;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Doctrine\DBAL\Types\Type;

class MyRegisterColumnType extends RegisterColumnTypeBase
{
    /**
     * @throws \Doctrine\DBAL\Exception
     */
    public function handle(): void
    {
        parent::handle();
        $customTypes = [
            'name' => \App\Support\Doctrine\MSSQL\Types\Name::class
        ];
        foreach ($customTypes as $type => $customType) {
            if (!Type::hasType($type)) {
                Type::getTypeRegistry()->register($type, new $customType);
                $this->registerDoctrineTypeMapping($type, $type);
            }
        }
    }

    /**
     * Registers a doctrine type to be used in conjunction with a column type of this platform.
     *
     * @param  string  $dbType
     * @param  string  $doctrineType
     * @throws \Doctrine\DBAL\Exception
     */
    private function registerDoctrineTypeMapping(string $dbType, string $doctrineType): void
    {
        DB::getDoctrineConnection()
            ->getDatabasePlatform()
            ->registerDoctrineTypeMapping($dbType, $doctrineType);
    }
}

NOTE: method registerDoctrineTypeMapping is private, so I can't call it from this extended class. Should it be a "protected" method?

But this not work:

 UnexpectedValueException 

  Value 'name' is not part of the enum KitLoong\MigrationsGenerator\Enum\Migrations\Method\ColumnType

In file vendor/kitloong/laravel-migrations-generator/src/DBAL/Models/DBALColumn.php:107 no mappings available. I think to change that function, to determine if a field is native or not, I could also create in the customType class a new method to determine with which native type it should be evaluated and then check if that method is present (for example method_exists('customConvertMigrationGenerator') ;)

What do you think would be the best way?

kitloong commented 1 year ago

@gpibarra Thanks for reporting.

I have implemented a similar feature for PgSQL, I will handle this issue.