diff --git a/.gitattributes b/.gitattributes index a34f6d88..3c0e2138 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13,5 +13,4 @@ /phpcs.xml export-ignore /phpstan.neon export-ignore /phpunit.xml.dist export-ignore -/phpunit8.xml export-ignore /testbench.yaml export-ignore diff --git a/.github/workflows/check.yml b/.github/workflows/lint.yml similarity index 69% rename from .github/workflows/check.yml rename to .github/workflows/lint.yml index 9ee06f6d..efc3db46 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/lint.yml @@ -18,19 +18,21 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.1 + php-version: 8.3 extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, gd, redis, memcached tools: composer:v2 coverage: none - name: Install dependencies - run: composer install --prefer-dist --no-interaction + run: | + composer require "laravel/framework:^11.0" --no-interaction --no-update + composer update --prefer-stable --prefer-dist --no-interaction --no-progress - name: phpcs run: composer run phpcs - name: phpstan - run: composer run phpstan + run: ./vendor/bin/phpstan analyse --memory-limit=2G - name: phpmd run: composer run phpmd diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 52d9fdbf..0f12a292 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -77,26 +77,14 @@ jobs: strategy: matrix: - php: [ 8.1, 8.2, 8.3 ] - laravel: [ 9.*, 10.* ] - dbal: [ 3.* ] + php: [ 8.2, 8.3 ] + laravel: [ 11.* ] sqlsrv_extension: [ pdo_sqlsrv ] include: - - php: 7.1 - laravel: 5.7.* - dbal: 2.* - - php: 7.2 - laravel: 5.8.* - dbal: 2.* - - php: 7.3 - laravel: 6.* - dbal: 2.* - - php: 7.4 - laravel: 7.* - dbal: 2.* - - php: 8.0 - laravel: 8.* - dbal: 2.* + - php: 8.1 + laravel: 10.43.* + - php: 8.1 + laravel: 10.* name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} @@ -125,26 +113,15 @@ jobs: - name: Install dependencies run: | composer config --no-plugins allow-plugins.kylekatarnls/update-helper true - composer require "laravel/framework:${{ matrix.laravel }}" "doctrine/dbal:${{ matrix.dbal }}" --no-interaction --no-update + composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update composer update --prefer-stable --prefer-dist --no-interaction --no-progress - - name: Setup PCOV - if: ${{ matrix.laravel == '5.7.*' }} - run: | - composer require pcov/clobber - vendor/bin/pcov clobber - - name: Setup .env run: | mkdir ./database composer run action-env-setup - - name: Execute tests with PHPUnit 8 or below - if: ${{ matrix.laravel == '5.7.*' || matrix.laravel == '5.8.*' }} - run: vendor/bin/phpunit -c ./phpunit8.xml --coverage-clover=coverage.xml - - name: Execute tests - if: ${{ matrix.laravel != '5.7.*' && matrix.laravel != '5.8.*' }} run: vendor/bin/phpunit --coverage-clover=coverage.xml - name: Upload to codecov diff --git a/.phpmd.xml b/.phpmd.xml index a7c4ecde..51b4b282 100644 --- a/.phpmd.xml +++ b/.phpmd.xml @@ -18,12 +18,13 @@ + - + @@ -36,9 +37,14 @@ + + + + + - + diff --git a/README.md b/README.md index 22321f04..a41c9532 100644 --- a/README.md +++ b/README.md @@ -23,17 +23,19 @@ Laravel Migrations Generator supports all five Laravel first-party support datab ## Version Compatibility -| Laravel | Version | -|---------------|-------------------------------------------------| -| 10.x | 6.x | -| 9.x | 6.x | -| 8.x | 6.x | -| 7.x | 6.x | -| 6.x | 6.x | -| 5.8.x | 6.x | -| 5.7.x | 6.x | -| 5.6.x | 6.x | -| 5.5 and below | https://github.com/Xethron/migrations-generator | +| Laravel | Version | +|--------------------|-------------------------------------------------| +| 11.x | 7.x | +| \>= 10.43.x | 7.x | +| 10.x \| <= 10.42.x | 6.x | +| 9.x | 6.x | +| 8.x | 6.x | +| 7.x | 6.x | +| 6.x | 6.x | +| 5.8.x | 6.x | +| 5.7.x | 6.x | +| 5.6.x | 6.x | +| <= 5.5.x | https://github.com/Xethron/migrations-generator | ## Install @@ -148,9 +150,9 @@ The generator first generates all tables and then adds foreign keys to existing However, SQLite only supports foreign keys upon creation of the table and not when tables are altered. *_add_foreign_keys_* migrations will still be generated, however will get omitted if migrate to SQLite type database. -## User Defined Custom Column Type +## User-Defined Type Columns -The generator will register custom data type from the schema, then generate migration as +The generator will recognize user-defined type from the schema, and then generate migration as ```php public function up() @@ -164,7 +166,7 @@ public function up() Note that the new `column` is always added at the end of the created `table` which means the ordering of the column generated in migration will differ from what we have from the schema. -Supported database with custom types: +Supported database with user-defined types: - [x] PostgreSQL - [x] SQL Server diff --git a/composer.json b/composer.json index 60cfee01..e0276021 100644 --- a/composer.json +++ b/composer.json @@ -17,21 +17,19 @@ } ], "require": { - "php": ">=7.1.3", - "illuminate/support": "^5.6|^6.0|^7.0|^8.0|^9.0|^10.0", - "doctrine/dbal": "^2.4|^3.0", - "myclabs/php-enum": "^1.6|^1.7|^1.8", + "php": "^8.1", + "illuminate/support": "^10.43|^11.0", "ext-pdo": "*" }, "require-dev": { - "orchestra/testbench": "^3.6|^4.0|^5.0|^6.0|^7.0|^8.0", + "orchestra/testbench": "^8.0|^9.0", "squizlabs/php_codesniffer": "^3.5", "mockery/mockery": "^1.0", - "friendsofphp/php-cs-fixer": "^2.19.0|^3.1", - "larastan/larastan": "^0.4|^0.5|^0.6|^0.7|^1.0|^2.0", + "friendsofphp/php-cs-fixer": "^3.1", + "larastan/larastan": "^1.0|^2.0", + "slevomat/coding-standard": "^8.0", "phpmd/phpmd": "^2.10", - "barryvdh/laravel-ide-helper": "^2.5", - "slevomat/coding-standard": "^6.0|^7.0|^8.5" + "barryvdh/laravel-ide-helper": "^2.0|^3.0" }, "autoload": { "psr-4": { diff --git a/config/migrations-generator.php b/config/migrations-generator.php index 43dedee0..a84924c3 100644 --- a/config/migrations-generator.php +++ b/config/migrations-generator.php @@ -2,14 +2,13 @@ return [ // Where the templates for the generators are stored. - 'migration_template_path' => __DIR__ . '/../stubs/migration.generate.stub', - 'migration_anonymous_template_path' => __DIR__ . '/../stubs/migration.generate.anonymous.stub', + 'migration_template_path' => __DIR__ . '/../stubs/migration.generate.stub', // Where the generated files will be saved. - 'migration_target_path' => base_path('database/migrations'), + 'migration_target_path' => base_path('database/migrations'), // Migration filename pattern. - 'filename_pattern' => [ + 'filename_pattern' => [ 'table' => '[datetime]_create_[name]_table.php', 'view' => '[datetime]_create_[name]_view.php', 'procedure' => '[datetime]_create_[name]_proc.php', diff --git a/phpcs.xml b/phpcs.xml index 69b85ae5..314de429 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -36,9 +36,9 @@ - + - + @@ -47,17 +47,12 @@ - + - - - - - - + @@ -72,7 +67,7 @@ - + @@ -91,10 +86,10 @@ - + - + @@ -105,12 +100,7 @@ - - - - - - + @@ -120,12 +110,7 @@ - - - - - - + @@ -145,23 +130,13 @@ - + - - - - - - - - - - - - - - + + + + @@ -183,8 +158,8 @@ - - + + @@ -209,39 +184,10 @@ - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/phpstan.neon b/phpstan.neon index 1134e54c..a8bc8484 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -13,10 +13,9 @@ parameters: ignoreErrors: - '#Class Dotenv\\Dotenv constructor invoked with 1 parameter, 4 required.#' - '#Parameter \#1 \$store of class Dotenv\\Dotenv constructor expects Dotenv\\Store\\StoreInterface, string given.#' - - '#Constant (.*)\\Enum\\(.*) is unused#' - - '#Method KitLoong\\MigrationsGenerator\\DBAL\\(.*)Schema::getViews\(\) should return Illuminate\\Support\\Collection but returns Illuminate\\Support\\Collection<(int|\(int\|string\)), KitLoong\\MigrationsGenerator\\DBAL\\Models\\(.*)\\(.*)View>.#' - - '#Method KitLoong\\MigrationsGenerator\\DBAL\\(.*)Schema::getProcedures\(\) should return Illuminate\\Support\\Collection but returns Illuminate\\Support\\Collection<(int|\(int\|string\)), KitLoong\\MigrationsGenerator\\DBAL\\Models\\(.*)\\(.*)Procedure>.#' - - '#Method KitLoong\\MigrationsGenerator\\DBAL\\(.*)Schema::getTableForeignKeys\(\) should return Illuminate\\Support\\Collection but returns Illuminate\\Support\\Collection<(int|\(int\|string\)), KitLoong\\MigrationsGenerator\\DBAL\\Models\\(.*)\\(.*)ForeignKey>.#' + - '#Method KitLoong\\MigrationsGenerator\\Database\\(.*)Schema::getViews\(\) should return Illuminate\\Support\\Collection but returns Illuminate\\Support\\Collection<(int|\(int\|string\)), KitLoong\\MigrationsGenerator\\Database\\Models\\(.*)\\(.*)View>.#' + - '#Method KitLoong\\MigrationsGenerator\\Database\\(.*)Schema::getProcedures\(\) should return Illuminate\\Support\\Collection but returns Illuminate\\Support\\Collection<(int|\(int\|string\)), KitLoong\\MigrationsGenerator\\Database\\Models\\(.*)\\(.*)Procedure>.#' + - '#Method KitLoong\\MigrationsGenerator\\Database\\(.*)Schema::getForeignKeys\(\) should return Illuminate\\Support\\Collection but returns Illuminate\\Support\\Collection<(int|\(int\|string\)), KitLoong\\MigrationsGenerator\\Database\\Models\\(.*)\\(.*)ForeignKey>.#' - '#(.*)expects Illuminate\\Support\\Collection, Illuminate\\Support\\Collection given.#' excludePaths: diff --git a/phpunit.xml.dist b/phpunit.xml.dist index b18123a7..51a734d4 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -17,7 +17,7 @@ src - src/KitLoong/MigrationsGenerator/Types + src/DBAL/Types diff --git a/phpunit8.xml b/phpunit8.xml deleted file mode 100644 index 0bfa86f6..00000000 --- a/phpunit8.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - tests - - - - - src - - src/KitLoong/MigrationsGenerator/Types - - - - diff --git a/src/DBAL/DBALSchema.php b/src/DBAL/DBALSchema.php deleted file mode 100644 index 6d71baa5..00000000 --- a/src/DBAL/DBALSchema.php +++ /dev/null @@ -1,85 +0,0 @@ - - */ - protected $dbalSchema; - - /** - * @throws \Doctrine\DBAL\Exception - */ - public function __construct(RegisterColumnType $registerColumnType) - { - $this->dbalSchema = $this->makeSchemaManager(); - $registerColumnType->handle(); - } - - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - public function getTableNames(): Collection - { - return (new Collection($this->dbalSchema->listTableNames())) - ->map(function ($table) { - // The table name may contain quotes. - // Always trim quotes before set into list. - if ($this->isIdentifierQuoted($table)) { - return $this->trimQuotes($table); - } - - return $table; - }); - } - - /** - * Introspects the table with the given name. - * `listTableDetails` is deprecated since `doctrine/dbal` v3.5 and will be removed from v4. - * This method will try to call `introspectTable` and fallback to `listTableDetails`. - * - * @throws \Doctrine\DBAL\Exception - */ - protected function introspectTable(string $name): Table - { - if (method_exists($this->dbalSchema, 'introspectTable')) { - return $this->dbalSchema->introspectTable($name); - } - - return $this->dbalSchema->listTableDetails($name); - } - - /** - * Make a schema manager. - * - * @return \Doctrine\DBAL\Schema\AbstractSchemaManager - * @throws \Doctrine\DBAL\Exception - */ - private function makeSchemaManager(): AbstractSchemaManager - { - $doctrineConnection = DB::getDoctrineConnection(); - - if (method_exists($doctrineConnection, 'createSchemaManager')) { - return $doctrineConnection->createSchemaManager(); - } - - // @codeCoverageIgnoreStart - return $doctrineConnection->getSchemaManager(); - // @codeCoverageIgnoreEnd - } -} diff --git a/src/DBAL/Models/DBALColumn.php b/src/DBAL/Models/DBALColumn.php deleted file mode 100644 index 0485c501..00000000 --- a/src/DBAL/Models/DBALColumn.php +++ /dev/null @@ -1,445 +0,0 @@ -tableName = $table; - $this->name = $column->getName(); - $this->type = ColumnType::fromDBALType($column->getType()); - $this->length = $column->getLength(); - $this->scale = $column->getScale(); - $this->precision = $column->getPrecision(); - $this->comment = $this->escapeComment($column->getComment()); - $this->fixed = $column->getFixed(); - $this->unsigned = $column->getUnsigned(); - $this->notNull = $column->getNotnull(); - $this->default = $this->escapeDefault($column->getDefault()); - $this->collation = $column->getPlatformOptions()['collation'] ?? null; - $this->charset = $column->getPlatformOptions()['charset'] ?? null; - $this->autoincrement = $column->getAutoincrement(); - $this->presetValues = []; - $this->onUpdateCurrentTimestamp = false; - $this->rawDefault = false; - $this->virtualDefinition = null; - $this->storedDefinition = null; - - $this->setTypeToSoftDeletes(); - $this->setTypeToRememberToken(); - $this->setTypeToChar(); - $this->fixDoubleLength(); - - $this->handle(); - } - - /** - * Instance extend this abstract may run special handling. - */ - abstract protected function handle(): void; - - /** - * @inheritDoc - */ - public function getName(): string - { - return $this->name; - } - - /** - * @inheritDoc - */ - public function getTableName(): string - { - return $this->tableName; - } - - /** - * @inheritDoc - */ - public function getType(): ColumnType - { - return $this->type; - } - - /** - * @inheritDoc - */ - public function getLength(): ?int - { - return $this->length; - } - - /** - * @inheritDoc - */ - public function getScale(): int - { - return $this->scale; - } - - /** - * @inheritDoc - */ - public function getPrecision(): int - { - return $this->precision; - } - - /** - * @inheritDoc - */ - public function getComment(): ?string - { - return $this->comment; - } - - /** - * @inheritDoc - */ - public function isFixed(): bool - { - return $this->fixed; - } - - /** - * @inheritDoc - */ - public function isUnsigned(): bool - { - return $this->unsigned; - } - - /** - * @inheritDoc - */ - public function isNotNull(): bool - { - return $this->notNull; - } - - /** - * @inheritDoc - */ - public function getDefault(): ?string - { - return $this->default; - } - - /** - * @inheritDoc - */ - public function getCollation(): ?string - { - return $this->collation; - } - - /** - * @inheritDoc - */ - public function getCharset(): ?string - { - return $this->charset; - } - - /** - * @inheritDoc - */ - public function isAutoincrement(): bool - { - return $this->autoincrement; - } - - /** - * @inheritDoc - */ - public function getPresetValues(): array - { - return $this->presetValues; - } - - /** - * @inheritDoc - */ - public function isOnUpdateCurrentTimestamp(): bool - { - return $this->onUpdateCurrentTimestamp; - } - - /** - * @inheritDoc - */ - public function isRawDefault(): bool - { - return $this->rawDefault; - } - - /** - * @inheritDoc - */ - public function getVirtualDefinition(): ?string - { - return $this->virtualDefinition; - } - - /** - * @inheritDoc - */ - public function getStoredDefinition(): ?string - { - return $this->storedDefinition; - } - - /** - * Set the column type to "increments" or "*Increments" if the column is auto increment. - * If the DB supports unsigned, should check if the column is unsigned. - * - * @param bool $supportUnsigned DB support unsigned integer. - */ - protected function setTypeToIncrements(bool $supportUnsigned): void - { - if ( - !in_array($this->type, [ - ColumnType::BIG_INTEGER(), - ColumnType::INTEGER(), - ColumnType::MEDIUM_INTEGER(), - ColumnType::SMALL_INTEGER(), - ColumnType::TINY_INTEGER(), - ]) - ) { - return; - } - - if (!$this->autoincrement) { - return; - } - - if ($supportUnsigned && !$this->unsigned) { - return; - } - - if ($this->type->equals(ColumnType::INTEGER())) { - $this->type = ColumnType::INCREMENTS(); - return; - } - - $this->type = ColumnType::fromValue(str_replace('Integer', 'Increments', $this->type)); - } - - /** - * Set the column type to "unsigned*" if the column is unsigned. - */ - protected function setTypeToUnsigned(): void - { - if ( - !in_array($this->type, [ - ColumnType::BIG_INTEGER(), - ColumnType::INTEGER(), - ColumnType::MEDIUM_INTEGER(), - ColumnType::SMALL_INTEGER(), - ColumnType::TINY_INTEGER(), - ColumnType::DECIMAL(), - ]) - || !$this->unsigned - ) { - return; - } - - $this->type = ColumnType::fromValue('unsigned' . ucfirst($this->type)); - } - - /** - * Set the column type to "softDeletes" or "softDeletesTz". - */ - private function setTypeToSoftDeletes(): void - { - if ($this->name !== ColumnName::DELETED_AT()->getValue()) { - return; - } - - switch ($this->type) { - case ColumnType::TIMESTAMP(): - $this->type = ColumnType::SOFT_DELETES(); - return; - - case ColumnType::TIMESTAMP_TZ(): - $this->type = ColumnType::SOFT_DELETES_TZ(); - return; - } - } - - /** - * Set the column type to "rememberToken". - */ - private function setTypeToRememberToken(): void - { - if ( - ColumnName::REMEMBER_TOKEN()->getValue() !== $this->name - || $this->length !== self::REMEMBER_TOKEN_LENGTH - || $this->fixed - ) { - return; - } - - $this->type = ColumnType::REMEMBER_TOKEN(); - } - - /** - * Set the column type to "char". - */ - private function setTypeToChar(): void - { - if (!$this->fixed) { - return; - } - - $this->type = ColumnType::CHAR(); - } - - /** - * When double is created without total and places, $table->double('double'); - * Doctrine DBAL return precisions 10 and scale 0. - * Reset precisions and scale to 0 here. - */ - private function fixDoubleLength(): void - { - if ( - !$this->type->equals(ColumnType::DOUBLE()) - || $this->getPrecision() !== 10 - || $this->getScale() !== 0 - ) { - return; - } - - $this->precision = 0; - $this->scale = 0; - } - - /** - * Escape `'` with `''`. - */ - protected function escapeDefault(?string $default): ?string - { - if ($default === null) { - return null; - } - - $default = str_replace("'", "''", $default); - return addcslashes($default, '\\'); - } - - /** - * Escape `\` with `\\`. - */ - protected function escapeComment(?string $comment): ?string - { - if ($comment === null) { - return null; - } - - return addcslashes($comment, '\\'); - } -} diff --git a/src/DBAL/Models/DBALCustomColumn.php b/src/DBAL/Models/DBALCustomColumn.php deleted file mode 100644 index 3f9d5204..00000000 --- a/src/DBAL/Models/DBALCustomColumn.php +++ /dev/null @@ -1,67 +0,0 @@ -name = $column->getName(); - $this->tableName = $table; - - // COLLATE clause cannot be used on user-defined data types. - // Unset collation here. - $platformOptions = $column->getPlatformOptions(); - unset($platformOptions['collation']); - $column->setPlatformOptions($platformOptions); - - $this->sqls = DB::getDoctrineConnection()->getDatabasePlatform()->getAlterTableSQL(new TableDiff($this->tableName, [$column])); - } - - /** - * @inheritDoc - */ - public function getName(): string - { - return $this->name; - } - - /** - * @inheritDoc - */ - public function getTableName(): string - { - return $this->tableName; - } - - /** - * @inheritDoc - */ - public function getSqls(): array - { - return $this->sqls; - } -} diff --git a/src/DBAL/Models/DBALIndex.php b/src/DBAL/Models/DBALIndex.php deleted file mode 100644 index ee526bc7..00000000 --- a/src/DBAL/Models/DBALIndex.php +++ /dev/null @@ -1,121 +0,0 @@ - - */ - protected $lengths; - - /** - * @var string - */ - protected $name; - - /** - * @var string - */ - protected $tableName; - - /** - * @var \KitLoong\MigrationsGenerator\Enum\Migrations\Method\IndexType - */ - protected $type; - - /** - * Create an index instance. - */ - public function __construct(string $table, DoctrineDBALIndex $index) - { - $this->tableName = $table; - $this->name = $index->getName(); - $this->columns = $index->getUnquotedColumns(); - $this->type = $this->getIndexType($index); - $this->lengths = $index->getOptions()['lengths'] ?? array_fill(0, count($this->columns), null); - - $this->handle(); - } - - /** - * Instance extend this abstract may run special handling. - */ - abstract protected function handle(): void; - - /** - * @inheritDoc - */ - public function getName(): string - { - return $this->name; - } - - /** - * @inheritDoc - */ - public function getTableName(): string - { - return $this->tableName; - } - - /** - * @inheritDoc - */ - public function getColumns(): array - { - return $this->columns; - } - - /** - * @inheritDoc - */ - public function getLengths(): array - { - return $this->lengths; - } - - /** - * @inheritDoc - */ - public function getType(): IndexType - { - return $this->type; - } - - /** - * Get the index type. - */ - private function getIndexType(DoctrineDBALIndex $index): IndexType - { - if ($index->isPrimary()) { - return IndexType::PRIMARY(); - } - - if ($index->isUnique()) { - return IndexType::UNIQUE(); - } - - if ($index->hasFlag('spatial')) { - return IndexType::SPATIAL_INDEX(); - } - - if ($this->hasFullText() && $index->hasFlag('fulltext')) { - return IndexType::FULLTEXT(); - } - - return IndexType::INDEX(); - } -} diff --git a/src/DBAL/Models/DBALTable.php b/src/DBAL/Models/DBALTable.php deleted file mode 100644 index 64780d61..00000000 --- a/src/DBAL/Models/DBALTable.php +++ /dev/null @@ -1,149 +0,0 @@ - - */ - protected $columns; - - /** - * @var \Illuminate\Support\Collection - */ - protected $customColumns; - - /** - * @var string - */ - protected $name; - - /** - * @var string|null - */ - protected $comment; - - /** - * @var \Illuminate\Support\Collection - */ - protected $indexes; - - /** - * Create a new instance. - * - * @param array $columns Key is quoted name. - * @param array $indexes Key is name. - */ - public function __construct(DoctrineDBALTable $table, array $columns, array $indexes) - { - $this->name = $table->getName(); - $this->comment = $table->getComment(); - $this->collation = $table->getOptions()['collation'] ?? null; - - $this->columns = (new Collection($columns))->reduce(function (Collection $columns, DoctrineDBALColumn $column) use ($table) { - if (!$column->getType() instanceof CustomType) { - $columns->push($this->makeColumn($table->getName(), $column)); - } - - return $columns; - }, new Collection())->values(); - - $this->customColumns = (new Collection($columns))->reduce(function (Collection $columns, DoctrineDBALColumn $column) use ($table) { - if ($column->getType() instanceof CustomType) { - $columns->push($this->makeCustomColumn($table->getName(), $column)); - } - - return $columns; - }, new Collection())->values(); - - $this->indexes = (new Collection($indexes))->map(function (DoctrineDBALIndex $index) use ($table) { - return $this->makeIndex($table->getName(), $index); - })->values(); - - $this->handle(); - } - - /** - * Instance extend this abstract may run special handling. - */ - abstract protected function handle(): void; - - /** - * Make a Column instance. - */ - abstract protected function makeColumn(string $table, DoctrineDBALColumn $column): Column; - - /** - * Make a CustomColumn instance. - */ - abstract protected function makeCustomColumn(string $table, DoctrineDBALColumn $column): CustomColumn; - - /** - * Make an Index instance. - */ - abstract protected function makeIndex(string $table, DoctrineDBALIndex $index): Index; - - /** - * @inheritDoc - */ - public function getName(): string - { - return $this->name; - } - - /** - * @inheritDoc - */ - public function getComment(): ?string - { - return $this->comment; - } - - /** - * @inheritDoc - */ - public function getColumns(): Collection - { - return $this->columns; - } - - /** - * @inheritDoc - */ - public function getCustomColumns(): Collection - { - return $this->customColumns; - } - - /** - * @inheritDoc - */ - public function getIndexes(): Collection - { - return $this->indexes; - } - - /** - * @inheritDoc - */ - public function getCollation(): ?string - { - return $this->collation; - } -} diff --git a/src/DBAL/Models/DBALView.php b/src/DBAL/Models/DBALView.php deleted file mode 100644 index fecc6efa..00000000 --- a/src/DBAL/Models/DBALView.php +++ /dev/null @@ -1,72 +0,0 @@ -name = $this->trimQuotes($view->getName()); - $this->quotedName = DB::getDoctrineConnection()->quoteIdentifier($this->name); - $this->definition = ''; - $this->dropDefinition = "DROP VIEW IF EXISTS $this->quotedName"; - - $this->handle($view); - } - - /** - * Instance extend this abstract may run special handling. - */ - abstract protected function handle(DoctrineDBALView $view): void; - - /** - * @inheritDoc - */ - public function getName(): string - { - return $this->name; - } - - /** - * @inheritDoc - */ - public function getDefinition(): string - { - return $this->definition; - } - - /** - * @inheritDoc - */ - public function getDropDefinition(): string - { - return $this->dropDefinition; - } -} diff --git a/src/DBAL/Models/MySQL/MySQLColumn.php b/src/DBAL/Models/MySQL/MySQLColumn.php deleted file mode 100644 index 4ea7f1b3..00000000 --- a/src/DBAL/Models/MySQL/MySQLColumn.php +++ /dev/null @@ -1,240 +0,0 @@ -mysqlRepository = app(MySQLRepository::class); - $this->mariaDBRepository = app(MariaDBRepository::class); - - $this->setTypeToIncrements(true); - $this->setTypeToUnsigned(); - - switch ($this->type) { - case ColumnType::UNSIGNED_TINY_INTEGER(): - case ColumnType::TINY_INTEGER(): - if ($this->isBoolean()) { - $this->type = ColumnType::BOOLEAN(); - } - - break; - - case ColumnType::ENUM(): - $this->presetValues = $this->getEnumPresetValues(); - break; - - case ColumnType::TEXT(): - $this->type = $this->getTextTypeByLength(); - break; - - case ColumnType::SET(): - $this->useSetOrString(); - break; - - case ColumnType::SOFT_DELETES(): - case ColumnType::SOFT_DELETES_TZ(): - case ColumnType::TIMESTAMP(): - case ColumnType::TIMESTAMP_TZ(): - $this->onUpdateCurrentTimestamp = $this->hasOnUpdateCurrentTimestamp(); - break; - - default: - } - - $this->setVirtualDefinition(); - $this->setStoredDefinition(); - - if (!$this->isMaria()) { - return; - } - - // Extra logic for MariaDB - switch ($this->type) { - case ColumnType::LONG_TEXT(): - if ($this->isJson()) { - $this->type = ColumnType::JSON(); - } - - break; - - default: - } - } - - /** - * Determine if the connected database is a MariaDB database. - */ - private function isMaria(): bool - { - return str_contains(DB::connection()->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION), 'MariaDB'); - } - - /** - * Check if the column is "tinyint(1)", if yes then generate as boolean. - */ - private function isBoolean(): bool - { - if ($this->autoincrement) { - return false; - } - - $showColumn = $this->mysqlRepository->showColumn($this->tableName, $this->name); - - if ($showColumn === null) { - return false; - } - - return Str::startsWith($showColumn->getType(), 'tinyint(1)'); - } - - /** - * Get the preset values if the column is "enum". - * - * @return string[] - */ - private function getEnumPresetValues(): array - { - return $this->mysqlRepository->getEnumPresetValues( - $this->tableName, - $this->name - )->toArray(); - } - - /** - * Get the preset values if the column is "set". - * - * @return string[] - */ - private function getSetPresetValues(): array - { - return $this->mysqlRepository->getSetPresetValues( - $this->tableName, - $this->name - )->toArray(); - } - - /** - * Check if "set" method is available, then get "set" preset values. - * If not available, change type to string with 255 length. - */ - private function useSetOrString(): void - { - if ($this->hasSet()) { - $this->presetValues = $this->getSetPresetValues(); - return; - } - - $this->type = ColumnType::STRING(); - $this->length = 255; // Framework default string length. - } - - /** - * Check if the column uses "on update CURRENT_TIMESTAMP". - */ - private function hasOnUpdateCurrentTimestamp(): bool - { - return $this->mysqlRepository->isOnUpdateCurrentTimestamp($this->tableName, $this->name); - } - - /** - * MariaDB return `longText` instead of `json` column. - * Check the check constraint of this column to check if type is `json`. - * Return true if check constraint contains `json_valid` keyword. - */ - private function isJson(): bool - { - $checkConstraint = $this->mariaDBRepository->getCheckConstraintForJson($this->tableName, $this->name); - return $checkConstraint !== null; - } - - private function getTextTypeByLength(): ColumnType - { - switch ($this->length) { - case MySQLPlatform::LENGTH_LIMIT_TINYTEXT: - if ($this->hasTinyText()) { - return ColumnType::TINY_TEXT(); - } - - return ColumnType::TEXT(); - - case MySQLPlatform::LENGTH_LIMIT_TEXT: - return ColumnType::TEXT(); - - case MySQLPlatform::LENGTH_LIMIT_MEDIUMTEXT: - return ColumnType::MEDIUM_TEXT(); - - default: - return ColumnType::LONG_TEXT(); - } - } - - /** - * Set virtual definition if the column is virtual. - */ - private function setVirtualDefinition(): void - { - $virtualDefinition = $this->mysqlRepository->getVirtualDefinition($this->tableName, $this->name); - - if ($virtualDefinition === null) { - return; - } - - // The definition of MySQL8 returned `concat(string,_utf8mb4\' \',string_255)`. - // Replace `\'` to `'` here to avoid double escape. - $this->virtualDefinition = str_replace("\'", "'", $virtualDefinition); - } - - /** - * Set stored definition if the column is stored. - */ - private function setStoredDefinition(): void - { - $storedDefinition = $this->mysqlRepository->getStoredDefinition($this->tableName, $this->name); - - if ($storedDefinition === null) { - return; - } - - // The definition of MySQL8 returned `concat(string,_utf8mb4\' \',string_255)`. - // Replace `\'` to `'` here to avoid double escape. - $this->storedDefinition = str_replace("\'", "'", $storedDefinition); - } - - /** - * @inheritDoc - */ - protected function escapeDefault(?string $default): ?string - { - $default = parent::escapeDefault($default); - - if ($default === null) { - return null; - } - - return addcslashes($default, '\\'); - } -} diff --git a/src/DBAL/Models/MySQL/MySQLCustomColumn.php b/src/DBAL/Models/MySQL/MySQLCustomColumn.php deleted file mode 100644 index 29a6f9f9..00000000 --- a/src/DBAL/Models/MySQL/MySQLCustomColumn.php +++ /dev/null @@ -1,9 +0,0 @@ -type) { - case IndexType::PRIMARY(): - // Reset name to empty to indicate use the database platform naming. - $this->name = ''; - break; - - default: - } - } -} diff --git a/src/DBAL/Models/MySQL/MySQLProcedure.php b/src/DBAL/Models/MySQL/MySQLProcedure.php deleted file mode 100644 index 16af7fbe..00000000 --- a/src/DBAL/Models/MySQL/MySQLProcedure.php +++ /dev/null @@ -1,9 +0,0 @@ -definition = DB::getDoctrineConnection() - ->getDatabasePlatform() - ->getCreateViewSQL($this->quotedName, $view->getSql()); - } -} diff --git a/src/DBAL/Models/PgSQL/PgSQLColumn.php b/src/DBAL/Models/PgSQL/PgSQLColumn.php deleted file mode 100644 index af0a7659..00000000 --- a/src/DBAL/Models/PgSQL/PgSQLColumn.php +++ /dev/null @@ -1,203 +0,0 @@ -repository = app(PgSQLRepository::class); - - $this->setTypeToIncrements(false); - - switch ($this->type) { - case ColumnType::DATE(): - case ColumnType::DATETIME(): - case ColumnType::DATETIME_TZ(): - case ColumnType::TIME(): - case ColumnType::TIME_TZ(): - case ColumnType::TIMESTAMP(): - case ColumnType::TIMESTAMP_TZ(): - case ColumnType::SOFT_DELETES(): - case ColumnType::SOFT_DELETES_TZ(): - $this->length = $this->getDataTypeLength(); - $this->setRawDefault(); - break; - - case ColumnType::FLOAT(): - $this->fixFloatLength(); - break; - - case ColumnType::GEOMETRY(): - $this->type = $this->setGeometryType(); - break; - - case ColumnType::STRING(): - $this->presetValues = $this->getEnumPresetValues(); - - if (count($this->presetValues) > 0) { - $this->type = ColumnType::ENUM(); - } - - break; - - default: - } - - $this->setStoredDefinition(); - } - - /** - * Get the column length from DB. - */ - private function getDataTypeLength(): ?int - { - $dataType = $this->repository->getTypeByColumnName($this->tableName, $this->name); - - if ($dataType === null) { - return null; - } - - $length = Regex::getTextBetweenFirst($dataType); - - if ($length === null) { - return null; - } - - return (int) $length; - } - - /** - * Check and set to use raw default. - * Raw default will be generated with DB::raw(). - */ - private function setRawDefault(): void - { - // Reserve now() to generate as `useCurrent`. - if ($this->default === 'now()') { - return; - } - - $default = $this->repository->getDefaultByColumnName($this->tableName, $this->name); - - if ($default === null) { - return; - } - - // If default value is expression, eg: timezone('Europe/Rome'::text, now()) - if (!preg_match('/\((.?)\)/', $default)) { - return; - } - - $this->default = $default; - $this->rawDefault = true; - } - - /** - * Get geography mapping. - * - * @return array - */ - private function getGeographyMap(): array - { - return [ - 'geography(geometry,4326)' => ColumnType::GEOMETRY(), - 'geography(geometrycollection,4326)' => ColumnType::GEOMETRY_COLLECTION(), - 'geography(linestring,4326)' => ColumnType::LINE_STRING(), - 'geography(multilinestring,4326)' => ColumnType::MULTI_LINE_STRING(), - 'geography(multipoint,4326)' => ColumnType::MULTI_POINT(), - 'geography(multipolygon,4326)' => ColumnType::MULTI_POLYGON(), - 'geography(point,4326)' => ColumnType::POINT(), - 'geography(polygon,4326)' => ColumnType::POLYGON(), - ]; - } - - /** - * Set to geometry type base on geography map. - */ - private function setGeometryType(): ColumnType - { - $dataType = $this->repository->getTypeByColumnName($this->tableName, $this->name); - - if ($dataType === null) { - return $this->type; - } - - $dataType = strtolower($dataType); - $dataType = preg_replace('/\s+/', '', $dataType); - - $map = $this->getGeographyMap(); - - if (!isset($map[$dataType])) { - return $this->type; - } - - return $map[$dataType]; - } - - /** - * Get the preset values if the column is "enum". - * - * @return string[] - */ - private function getEnumPresetValues(): array - { - $definition = $this->repository->getCheckConstraintDefinition($this->tableName, $this->name); - - if ($definition === null) { - return []; - } - - if ($definition === '') { - return []; - } - - $presetValues = Regex::getTextBetweenAll($definition, "'", "'::"); - - if ($presetValues === null) { - return []; - } - - return $presetValues; - } - - /** - * The framework always create float without precision. - * However, Doctrine DBAL always return precisions 10 and scale 0. - * Reset precisions and scale to 0 here. - */ - private function fixFloatLength(): void - { - if ($this->precision !== 10 || $this->scale !== 0) { - return; - } - - $this->precision = 0; - $this->scale = 0; - } - - /** - * Set stored definition if the column is stored. - */ - private function setStoredDefinition(): void - { - $this->storedDefinition = $this->repository->getStoredDefinition($this->tableName, $this->name); - - // A generated column cannot have a column default or an identity definition. - if ($this->storedDefinition === null) { - return; - } - - $this->default = null; - } -} diff --git a/src/DBAL/Models/PgSQL/PgSQLCustomColumn.php b/src/DBAL/Models/PgSQL/PgSQLCustomColumn.php deleted file mode 100644 index 567ccbfc..00000000 --- a/src/DBAL/Models/PgSQL/PgSQLCustomColumn.php +++ /dev/null @@ -1,9 +0,0 @@ -repository = app(PgSQLRepository::class); - - $this->setTypeToSpatial(); - - switch ($this->type) { - case IndexType::PRIMARY(): - // Reset name to empty to indicate use the database platform naming. - $this->name = ''; - break; - - default: - } - } - - private function setTypeToSpatial(): void - { - $spatialNames = $this->repository->getSpatialIndexes($this->tableName) - ->map(function (IndexDefinition $indexDefinition) { - return $indexDefinition->getIndexName(); - }); - - if (!$spatialNames->contains($this->name)) { - return; - } - - $this->type = IndexType::SPATIAL_INDEX(); - } -} diff --git a/src/DBAL/Models/PgSQL/PgSQLProcedure.php b/src/DBAL/Models/PgSQL/PgSQLProcedure.php deleted file mode 100644 index 410b0066..00000000 --- a/src/DBAL/Models/PgSQL/PgSQLProcedure.php +++ /dev/null @@ -1,9 +0,0 @@ -repository = app(PgSQLRepository::class); - - $this->pushFulltextIndexes(); - - $this->indexes = $this->indexes->sortBy(function (Index $index) { - return $index->getName(); - })->values(); - } - - /** - * @inheritDoc - */ - protected function makeColumn(string $table, DoctrineDBALColumn $column): Column - { - return new PgSQLColumn($table, $column); - } - - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - protected function makeCustomColumn(string $table, DoctrineDBALColumn $column): CustomColumn - { - return new PgSQLCustomColumn($table, $column); - } - - /** - * @inheritDoc - */ - protected function makeIndex(string $table, DoctrineDBALIndex $index): Index - { - return new PgSQLIndex($table, $index); - } - - private function pushFulltextIndexes(): void - { - // Get fulltext indexes. - $fulltextIndexes = $this->repository->getFulltextIndexes($this->name); - $fulltextIndexes->each(function (IndexDefinition $indexDefinition): void { - // Get column names in array - // eg: CREATE INDEX fulltext_custom ON public.test_index_pgsql USING gin (to_tsvector('english'::regconfig, (fulltext_custom)::text)) - // Get "fulltext_custom" - preg_match_all('/to_tsvector\((.*), \((.*)\)::text/U', $indexDefinition->getIndexDef(), $matches); - - if (empty($matches[2])) { - return; - } - - $columns = $matches[2]; - - $this->indexes->push( - new PgSQLIndex( - $this->name, - new DoctrineDBALIndex( - $indexDefinition->getIndexName(), - $columns, - false, - false, - ['fulltext'], - [] - ) - ) - ); - }); - } -} diff --git a/src/DBAL/Models/PgSQL/PgSQLView.php b/src/DBAL/Models/PgSQL/PgSQLView.php deleted file mode 100644 index 0a9b8a9b..00000000 --- a/src/DBAL/Models/PgSQL/PgSQLView.php +++ /dev/null @@ -1,35 +0,0 @@ -getConfig('search_path') ?: DB::connection()->getConfig('schema'); - - if ($view->getNamespaceName() !== $searchPath) { - $this->definition = DB::getDoctrineConnection() - ->getDatabasePlatform() - ->getCreateViewSQL($this->quotedName, $view->getSql()); - return; - } - - // Strip namespace from name. - $name = $view->getShortestName($view->getNamespaceName()); - $this->name = $this->trimQuotes($name); - $this->quotedName = DB::getDoctrineConnection()->quoteIdentifier($this->name); - $this->definition = DB::getDoctrineConnection() - ->getDatabasePlatform() - ->getCreateViewSQL($this->quotedName, $view->getSql()); - $this->dropDefinition = "DROP VIEW IF EXISTS $this->quotedName"; - } -} diff --git a/src/DBAL/Models/SQLSrv/SQLSrvColumn.php b/src/DBAL/Models/SQLSrv/SQLSrvColumn.php deleted file mode 100644 index 7d0b53a3..00000000 --- a/src/DBAL/Models/SQLSrv/SQLSrvColumn.php +++ /dev/null @@ -1,151 +0,0 @@ -repository = app(SQLSrvRepository::class); - - $this->setTypeToIncrements(false); - - switch ($this->type) { - case ColumnType::DATE(): - case ColumnType::DATETIME(): - case ColumnType::DATETIME_TZ(): - case ColumnType::TIME(): - case ColumnType::TIME_TZ(): - case ColumnType::TIMESTAMP(): - case ColumnType::TIMESTAMP_TZ(): - case ColumnType::SOFT_DELETES(): - case ColumnType::SOFT_DELETES_TZ(): - $this->length = $this->getDataTypeLength(); - break; - - case ColumnType::FLOAT(): - $this->fixFloatLength(); - break; - - case ColumnType::STRING(): - if ($this->isText()) { - $this->type = ColumnType::TEXT(); - break; - } - - $this->presetValues = $this->getEnumPresetValues(); - - if (count($this->presetValues) > 0) { - $this->type = ColumnType::ENUM(); - } - - break; - - default: - } - } - - /** - * Get the datetime column length. - * MySQL and PgSQL use "length" for precision while SQLSrv uses "scale". - * Return "scale" as "length". - */ - private function getDataTypeLength(): ?int - { - $columnDef = $this->repository->getColumnDefinition($this->tableName, $this->name); - - if ($columnDef === null) { - return null; - } - - switch ($this->type) { - case ColumnType::DATETIME(): - if ( - $columnDef->getScale() === self::DATETIME_EMPTY_SCALE - && $columnDef->getLength() === self::DATETIME_EMPTY_LENGTH - ) { - return null; - } - - return $this->scale; - - case ColumnType::DATETIME_TZ(): - if ( - $columnDef->getScale() === self::DATETIME_TZ_EMPTY_SCALE - && $columnDef->getLength() === self::DATETIME_TZ_EMPTY_LENGTH - ) { - return null; - } - - return $this->scale; - - default: - return $this->scale; - } - } - - /** - * Check if the column type is "text". - */ - private function isText(): bool - { - $columnDef = $this->repository->getColumnDefinition($this->tableName, $this->name); - - if ($columnDef === null) { - return false; - } - - return $columnDef->getType() === self::TEXT_TYPE && $columnDef->getLength() === self::TEXT_LENGTH; - } - - /** - * The framework always create float without precision. - * However, Doctrine DBAL always return precisions 53 and scale 0. - * Reset precisions and scale to 0 here. - */ - private function fixFloatLength(): void - { - if ($this->precision !== 53 || $this->scale !== 0) { - return; - } - - $this->precision = 0; - $this->scale = 0; - } - - /** - * Get the preset values if the column is `enum`. - * - * @return string[] - */ - private function getEnumPresetValues(): array - { - return $this->repository->getEnumPresetValues( - $this->tableName, - $this->name - )->toArray(); - } -} diff --git a/src/DBAL/Models/SQLSrv/SQLSrvCustomColumn.php b/src/DBAL/Models/SQLSrv/SQLSrvCustomColumn.php deleted file mode 100644 index ab653e49..00000000 --- a/src/DBAL/Models/SQLSrv/SQLSrvCustomColumn.php +++ /dev/null @@ -1,9 +0,0 @@ -getSql() !== null && $view->getSql() !== '') { - // $view->getSql() contains full view definition. - $this->definition = $view->getSql(); - - return; - } - - // Use repository to get view definition. - $viewDefinition = $repository->getView($view->getName()); - - if ($viewDefinition === null) { - return; - } - - $this->definition = $viewDefinition->getDefinition(); - } -} diff --git a/src/DBAL/Models/SQLite/SQLiteCustomColumn.php b/src/DBAL/Models/SQLite/SQLiteCustomColumn.php deleted file mode 100644 index f49e10e9..00000000 --- a/src/DBAL/Models/SQLite/SQLiteCustomColumn.php +++ /dev/null @@ -1,9 +0,0 @@ -type) { - case IndexType::PRIMARY(): - // Reset name to empty to indicate use the database platform naming. - $this->name = ''; - break; - - default: - } - } -} diff --git a/src/DBAL/Models/SQLite/SQLiteTable.php b/src/DBAL/Models/SQLite/SQLiteTable.php deleted file mode 100644 index 0455e6b3..00000000 --- a/src/DBAL/Models/SQLite/SQLiteTable.php +++ /dev/null @@ -1,46 +0,0 @@ -definition = $view->getSql(); - } -} diff --git a/src/DBAL/MySQLSchema.php b/src/DBAL/MySQLSchema.php deleted file mode 100644 index 3749a802..00000000 --- a/src/DBAL/MySQLSchema.php +++ /dev/null @@ -1,93 +0,0 @@ - - */ -class MySQLSchema extends DBALSchema implements MySQLSchemaInterface -{ - /** - * @var \KitLoong\MigrationsGenerator\Repositories\MySQLRepository - */ - private $mySQLRepository; - - public function __construct(RegisterColumnType $registerColumnType, MySQLRepository $mySQLRepository) - { - parent::__construct($registerColumnType); - - $this->mySQLRepository = $mySQLRepository; - } - - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - public function getTable(string $name): Table - { - return new MySQLTable( - $this->introspectTable($name), - $this->dbalSchema->listTableColumns($name), - $this->dbalSchema->listTableIndexes($name) - ); - } - - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - public function getViewNames(): Collection - { - return $this->getViews()->map(function (View $view) { - return $view->getName(); - }); - } - - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - public function getViews(): Collection - { - return (new Collection($this->dbalSchema->listViews())) - ->map(function (DoctrineDBALView $view) { - return new MySQLView($view); - }); - } - - /** - * @inheritDoc - */ - public function getProcedures(): Collection - { - return (new Collection($this->mySQLRepository->getProcedures())) - ->map(function (ProcedureDefinition $procedureDefinition) { - return new MySQLProcedure($procedureDefinition->getName(), $procedureDefinition->getDefinition()); - }); - } - - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - public function getTableForeignKeys(string $table): Collection - { - return (new Collection($this->dbalSchema->listTableForeignKeys($table))) - ->map(function (ForeignKeyConstraint $foreignKeyConstraint) use ($table) { - return new MySQLForeignKey($table, $foreignKeyConstraint); - }); - } -} diff --git a/src/DBAL/PgSQLSchema.php b/src/DBAL/PgSQLSchema.php deleted file mode 100644 index 4e164b95..00000000 --- a/src/DBAL/PgSQLSchema.php +++ /dev/null @@ -1,130 +0,0 @@ - - */ -class PgSQLSchema extends DBALSchema -{ - /** - * @var \KitLoong\MigrationsGenerator\Repositories\PgSQLRepository - */ - private $pgSQLRepository; - - public function __construct(RegisterColumnType $registerColumnType, PgSQLRepository $pgSQLRepository) - { - parent::__construct($registerColumnType); - - $this->pgSQLRepository = $pgSQLRepository; - } - - /** - * @inheritDoc - */ - public function getTableNames(): Collection - { - return parent::getTableNames() - ->filter(function (string $table): bool { - // Checks if the table is from user defined "schema". - // If table name do not have namespace, it is using the default namespace. - if (strpos($table, '.') === false) { - return true; - } - - // Schema name defined in the framework configuration. - $searchPath = DB::connection()->getConfig('search_path') ?: DB::connection()->getConfig('schema'); - - $parts = explode('.', $table); - $namespace = $parts[0]; - - return $namespace === $searchPath; - }) - ->values(); - } - - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - public function getTable(string $name): Table - { - return new PgSQLTable( - $this->introspectTable($name), - $this->dbalSchema->listTableColumns($name), - $this->dbalSchema->listTableIndexes($name) - ); - } - - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - public function getViewNames(): Collection - { - return $this->getViews() - ->map(function (View $view) { - return $view->getName(); - }); - } - - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - public function getViews(): Collection - { - return (new Collection($this->dbalSchema->listViews())) - ->filter(function (DoctrineDBALView $view) { - if (in_array($view->getName(), ['public.geography_columns', 'public.geometry_columns'])) { - return false; - } - - // Start from Laravel 9, the `schema` configuration option used to configure Postgres connection search paths renamed to `search_path`. - // Fallback to `schema` if Laravel version is older than 9. - $searchPath = DB::connection()->getConfig('search_path') ?: DB::connection()->getConfig('schema'); - - return $view->getNamespaceName() === $searchPath; - }) - ->map(function (DoctrineDBALView $view) { - return new PgSQLView($view); - }) - ->values(); - } - - /** - * @inheritDoc - */ - public function getProcedures(): Collection - { - return (new Collection($this->pgSQLRepository->getProcedures())) - ->map(function (ProcedureDefinition $procedureDefinition) { - return new PgSQLProcedure($procedureDefinition->getName(), $procedureDefinition->getDefinition()); - }); - } - - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - public function getTableForeignKeys(string $table): Collection - { - return (new Collection($this->dbalSchema->listTableForeignKeys($table))) - ->map(function (ForeignKeyConstraint $foreignKeyConstraint) use ($table) { - return new PgSQLForeignKey($table, $foreignKeyConstraint); - }); - } -} diff --git a/src/DBAL/RegisterColumnType.php b/src/DBAL/RegisterColumnType.php deleted file mode 100644 index 7b28757a..00000000 --- a/src/DBAL/RegisterColumnType.php +++ /dev/null @@ -1,189 +0,0 @@ -pgSQLRepository = $pgSQLRepository; - $this->sqlSrvRepository = $sqlSrvRepository; - } - - /** - * @throws \Doctrine\DBAL\Exception - */ - public function handle(): void - { - $this->registerLaravelColumnType(); - $this->registerLaravelCustomColumnType(); - - $doctrineTypes = [ - Driver::MYSQL()->getValue() => [ - 'bit' => DoctrineDBALTypes::BOOLEAN, - 'geomcollection' => ColumnType::GEOMETRY_COLLECTION, - 'mediumint' => ColumnType::MEDIUM_INTEGER, - 'tinyint' => ColumnType::TINY_INTEGER, - ], - Driver::PGSQL()->getValue() => [ - '_int4' => DoctrineDBALTypes::TEXT, - '_int8' => DoctrineDBALTypes::TEXT, - '_numeric' => DoctrineDBALTypes::FLOAT, - '_text' => DoctrineDBALTypes::TEXT, - 'cidr' => DoctrineDBALTypes::STRING, - 'geography' => ColumnType::GEOMETRY, - 'inet' => ColumnType::IP_ADDRESS, - 'macaddr' => ColumnType::MAC_ADDRESS, - 'oid' => DoctrineDBALTypes::STRING, - ], - Driver::SQLITE()->getValue() => [], - Driver::SQLSRV()->getValue() => [ - 'geography' => ColumnType::GEOMETRY, - 'sysname' => DoctrineDBALTypes::STRING, - 'hierarchyid' => DoctrineDBALTypes::STRING, - 'money' => DoctrineDBALTypes::DECIMAL, - 'smallmoney' => DoctrineDBALTypes::DECIMAL, - 'tinyint' => ColumnType::TINY_INTEGER, - 'xml' => DoctrineDBALTypes::TEXT, - ], - ]; - - // Register DB specific type, and fallback to Laravel column types. - foreach ($doctrineTypes[DB::getDriverName()] as $dbType => $doctrineType) { - $this->registerDoctrineTypeMapping($dbType, $doctrineType); - } - } - - /** - * Register additional column types which are supported by the framework. - * - * @throws \Doctrine\DBAL\Exception - */ - private function registerLaravelColumnType(): void - { - $typesMap = array_flip(Types::ADDITIONAL_TYPES_MAP); - - foreach ($typesMap as $type => $doctrineTypeClassName) { - // Add a new type by providing a `type` name and a `\Doctrine\DBAL\Types\Type` class name. - // eg: `$type = double`, `$doctrineTypeClassName = \KitLoong\MigrationsGenerator\DBAL\Types\DoubleType` - $this->addOrOverrideType($type, $doctrineTypeClassName); - - // Register type mapping so that Doctrine DBAL can recognize the DB column type. - // eg: `$type = double` - // Now Doctrine DBAL can recognize `column double NOT NULL` and create a column instance with type `\KitLoong\MigrationsGenerator\DBAL\Types\DoubleType`. - $this->registerDoctrineTypeMapping($type, $type); - } - } - - /** - * Register additional column types which are not supported by the framework. - * - * @note Uses {@see \Doctrine\DBAL\Types\Type::__construct} instead of {@see \Doctrine\DBAL\Types\Type::addType} here as workaround. - * @throws \Doctrine\DBAL\Exception - * @SuppressWarnings(PHPMD.UnusedFormalParameter) to suppress `getSQLDeclaration` warning. - */ - private function registerLaravelCustomColumnType(): void - { - foreach ($this->getCustomTypes() as $type) { - $customType = new class () extends CustomType { - /** - * @var string - */ - public $type = ''; - - /** - * @inheritDoc - */ - public function getSQLDeclaration(array $column, AbstractPlatform $platform) - { - return $this->type; - } - - /** - * @inheritDoc - */ - public function getName() - { - return $this->type; - } - }; - - $customType->type = $type; - - if (!Type::hasType($type)) { - Type::getTypeRegistry()->register($type, $customType); - } - - $this->registerDoctrineTypeMapping($type, $type); - } - } - - /** - * Get a list of custom type names from DB. - * - * @return \Illuminate\Support\Collection - */ - private function getCustomTypes(): Collection - { - switch (DB::getDriverName()) { - case Driver::PGSQL(): - return $this->pgSQLRepository->getCustomDataTypes(); - - case Driver::SQLSRV(): - return $this->sqlSrvRepository->getCustomDataTypes(); - - default: - return new Collection(); - } - } - - /** - * Add or override doctrine type. - * - * @param class-string<\Doctrine\DBAL\Types\Type> $class The class name which is extends {@see \Doctrine\DBAL\Types\Type}. - * @throws \Doctrine\DBAL\Exception - */ - private function addOrOverrideType(string $type, string $class): void - { - if (!Type::hasType($type)) { - Type::addType($type, $class); - return; - } - - Type::overrideType($type, $class); - } - - /** - * Registers a doctrine type to be used in conjunction with a column type of this platform. - * - * @throws \Doctrine\DBAL\Exception - */ - private function registerDoctrineTypeMapping(string $dbType, string $doctrineType): void - { - DB::getDoctrineConnection() - ->getDatabasePlatform() - ->registerDoctrineTypeMapping($dbType, $doctrineType); - } -} diff --git a/src/DBAL/SQLSrvSchema.php b/src/DBAL/SQLSrvSchema.php deleted file mode 100644 index 0c27c39c..00000000 --- a/src/DBAL/SQLSrvSchema.php +++ /dev/null @@ -1,96 +0,0 @@ - - */ -class SQLSrvSchema extends DBALSchema -{ - /** - * @var \KitLoong\MigrationsGenerator\Repositories\SQLSrvRepository - */ - private $sqlSrvRepository; - - public function __construct(RegisterColumnType $registerColumnType, SQLSrvRepository $sqlSrvRepository) - { - parent::__construct($registerColumnType); - - $this->sqlSrvRepository = $sqlSrvRepository; - } - - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - public function getTable(string $name): Table - { - return new SQLSrvTable( - $this->introspectTable($name), - $this->dbalSchema->listTableColumns($name), - $this->dbalSchema->listTableIndexes($name) - ); - } - - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - public function getViewNames(): Collection - { - return $this->getViews()->map(function (View $view) { - return $view->getName(); - }); - } - - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - public function getViews(): Collection - { - return (new Collection($this->dbalSchema->listViews())) - ->map(function (DoctrineDBALView $view) { - return new SQLSrvView($view); - }) - ->filter(function (SQLSrvView $view) { - // `$view->getDefinition()` is empty string if the view definition is encrypted. - return $view->getDefinition() !== ''; - }); - } - - /** - * @inheritDoc - */ - public function getProcedures(): Collection - { - return (new Collection($this->sqlSrvRepository->getProcedures())) - ->map(function (ProcedureDefinition $procedureDefinition) { - return new PgSQLProcedure($procedureDefinition->getName(), $procedureDefinition->getDefinition()); - }); - } - - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - public function getTableForeignKeys(string $table): Collection - { - return (new Collection($this->dbalSchema->listTableForeignKeys($table))) - ->map(function (ForeignKeyConstraint $foreignKeyConstraint) use ($table) { - return new SQLSrvForeignKey($table, $foreignKeyConstraint); - }); - } -} diff --git a/src/DBAL/SQLiteSchema.php b/src/DBAL/SQLiteSchema.php deleted file mode 100644 index 042dae8b..00000000 --- a/src/DBAL/SQLiteSchema.php +++ /dev/null @@ -1,76 +0,0 @@ - - */ -class SQLiteSchema extends DBALSchema -{ - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - public function getTable(string $name): Table - { - return new SQLiteTable( - $this->introspectTable($name), - $this->dbalSchema->listTableColumns($name), - $this->dbalSchema->listTableIndexes($name) - ); - } - - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - public function getViewNames(): Collection - { - return $this->getViews()->map(function (View $view) { - return $view->getName(); - }); - } - - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - public function getViews(): Collection - { - return (new Collection($this->dbalSchema->listViews())) - ->map(function (DoctrineDBALView $view) { - return new SQLiteView($view); - }); - } - - /** - * @inheritDoc - */ - public function getProcedures(): Collection - { - // Stored procedure is not available. - // https://sqlite.org/forum/info/78a60bdeec7c1ee9 - return new Collection(); - } - - /** - * @inheritDoc - * @throws \Doctrine\DBAL\Exception - */ - public function getTableForeignKeys(string $table): Collection - { - return (new Collection($this->dbalSchema->listTableForeignKeys($table))) - ->map(function (ForeignKeyConstraint $foreignKeyConstraint) use ($table) { - return new SQLiteForeignKey($table, $foreignKeyConstraint); - }); - } -} diff --git a/src/DBAL/Types/CustomType.php b/src/DBAL/Types/CustomType.php deleted file mode 100644 index de14bc76..00000000 --- a/src/DBAL/Types/CustomType.php +++ /dev/null @@ -1,9 +0,0 @@ - ColumnType::STRING, - BigIntType::class => ColumnType::BIG_INTEGER, - BinaryType::class => ColumnType::BINARY, - BlobType::class => ColumnType::BINARY, - BooleanType::class => ColumnType::BOOLEAN, - DateType::class => ColumnType::DATE, - DateImmutableType::class => ColumnType::DATE, - DateIntervalType::class => ColumnType::DATE, - DateTimeType::class => ColumnType::DATETIME, - DateTimeImmutableType::class => ColumnType::DATETIME, - DateTimeTzType::class => ColumnType::DATETIME_TZ, - DateTimeTzImmutableType::class => ColumnType::DATETIME_TZ, - DecimalType::class => ColumnType::DECIMAL, - FloatType::class => ColumnType::FLOAT, - GuidType::class => ColumnType::UUID, - IntegerType::class => ColumnType::INTEGER, - JsonType::class => ColumnType::JSON, - SimpleArrayType::class => ColumnType::STRING, - SmallIntType::class => ColumnType::SMALL_INTEGER, - StringType::class => ColumnType::STRING, - TextType::class => ColumnType::TEXT, - TimeType::class => ColumnType::TIME, - TimeImmutableType::class => ColumnType::TIME, - ]; - - /** - * Additional types provided by Migration Generator. - */ - public const ADDITIONAL_TYPES_MAP = [ - DoubleType::class => ColumnType::DOUBLE, - EnumType::class => ColumnType::ENUM, - GeometryType::class => ColumnType::GEOMETRY, - GeometryCollectionType::class => ColumnType::GEOMETRY_COLLECTION, - IpAddressType::class => ColumnType::IP_ADDRESS, - JsonbType::class => ColumnType::JSONB, - LineStringType::class => ColumnType::LINE_STRING, - MacAddressType::class => ColumnType::MAC_ADDRESS, - MediumIntegerType::class => ColumnType::MEDIUM_INTEGER, - MultiLineStringType::class => ColumnType::MULTI_LINE_STRING, - MultiPointType::class => ColumnType::MULTI_POINT, - MultiPolygonType::class => ColumnType::MULTI_POLYGON, - PointType::class => ColumnType::POINT, - PolygonType::class => ColumnType::POLYGON, - SetType::class => ColumnType::SET, - TimestampType::class => ColumnType::TIMESTAMP, - TimestampTzType::class => ColumnType::TIMESTAMP_TZ, - TimeTzType::class => ColumnType::TIME_TZ, - TinyIntegerType::class => ColumnType::TINY_INTEGER, - UUIDType::class => ColumnType::UUID, - YearType::class => ColumnType::YEAR, - ]; -} diff --git a/src/DBAL/Types/UUIDType.php b/src/DBAL/Types/UUIDType.php deleted file mode 100644 index 4fc87980..00000000 --- a/src/DBAL/Types/UUIDType.php +++ /dev/null @@ -1,29 +0,0 @@ - $map + */ + protected static function mapToColumnType(array $map, string $dbType): ColumnType + { + return $map[strtolower($dbType)] ?? ColumnType::STRING; + } +} diff --git a/src/Database/DatabaseSchema.php b/src/Database/DatabaseSchema.php new file mode 100644 index 00000000..5ee67f35 --- /dev/null +++ b/src/Database/DatabaseSchema.php @@ -0,0 +1,127 @@ + + */ + protected array $tables = []; + + /** + * @inheritDoc + */ + public function getTableNames(): Collection + { + return new Collection(SchemaFacade::getTableListing()); + } + + /** + * Get a table from the schema by name. + * + * @return SchemaTable + */ + protected function getSchemaTable(string $name): array + { + if ($this->tables === []) { + foreach (SchemaFacade::getTables() as $table) { + $this->tables[$table['name']] = $table; + } + } + + return $this->tables[$name]; + } + + /** + * Get columns from the schema by table name. + * + * @return \Illuminate\Support\Collection + */ + protected function getSchemaColumns(string $table): Collection + { + return new Collection(SchemaFacade::getColumns($this->stripTablePrefix($table))); + } + + /** + * Get indexes from the schema by table name. + * + * @return \Illuminate\Support\Collection + */ + protected function getSchemaIndexes(string $table): Collection + { + return new Collection(SchemaFacade::getIndexes($this->stripTablePrefix($table))); + } + + /** + * Get views from the schema. + * + * @return \Illuminate\Support\Collection + */ + protected function getSchemaViews(): Collection + { + return new Collection(SchemaFacade::getViews()); + } + + /** + * Get foreign keys from the schema by table name. + * + * @return \Illuminate\Support\Collection + */ + protected function getSchemaForeignKeys(string $table): Collection + { + return new Collection(SchemaFacade::getForeignKeys($this->stripTablePrefix($table))); + } +} diff --git a/src/Database/Models/DatabaseColumn.php b/src/Database/Models/DatabaseColumn.php new file mode 100644 index 00000000..a9004743 --- /dev/null +++ b/src/Database/Models/DatabaseColumn.php @@ -0,0 +1,401 @@ +tableName = $table; + $this->name = $column['name']; + $this->type = $this->getColumnType($column['type_name']); + $this->length = $this->parseLength($column['type']); + [$this->precision, $this->scale] = $this->parsePrecisionAndScale($column['type']); + $this->comment = $this->escapeComment($column['comment']); + $this->notNull = !$column['nullable']; + $this->collation = $column['collation'] !== null && $column['collation'] !== '' ? $column['collation'] : null; + $this->charset = null; + $this->autoincrement = $column['auto_increment']; + $this->presetValues = []; + $this->onUpdateCurrentTimestamp = false; + $this->rawDefault = false; + $this->virtualDefinition = null; + $this->storedDefinition = null; + $this->spatialSubType = null; + $this->spatialSrID = null; + + $this->setTypeToSoftDeletes(); + $this->setTypeToRememberToken(); + } + + /** + * @inheritDoc + */ + public function getName(): string + { + return $this->name; + } + + /** + * @inheritDoc + */ + public function getTableName(): string + { + return $this->tableName; + } + + /** + * @inheritDoc + */ + public function getType(): ColumnType + { + return $this->type; + } + + /** + * @inheritDoc + */ + public function getLength(): ?int + { + return $this->length; + } + + /** + * @inheritDoc + */ + public function getScale(): int + { + return $this->scale; + } + + /** + * @inheritDoc + */ + public function getPrecision(): ?int + { + return $this->precision; + } + + /** + * @inheritDoc + */ + public function getComment(): ?string + { + return $this->comment; + } + + /** + * @inheritDoc + */ + public function isUnsigned(): bool + { + return $this->unsigned; + } + + /** + * @inheritDoc + */ + public function isNotNull(): bool + { + return $this->notNull; + } + + /** + * @inheritDoc + */ + public function getDefault(): ?string + { + return $this->default; + } + + /** + * @inheritDoc + */ + public function getCollation(): ?string + { + return $this->collation; + } + + /** + * @inheritDoc + */ + public function getCharset(): ?string + { + return $this->charset; + } + + /** + * @inheritDoc + */ + public function isAutoincrement(): bool + { + return $this->autoincrement; + } + + /** + * @inheritDoc + */ + public function getPresetValues(): array + { + return $this->presetValues; + } + + /** + * @inheritDoc + */ + public function getSpatialSubType(): ?string + { + return $this->spatialSubType; + } + + /** + * @inheritDoc + */ + public function getSpatialSrID(): ?int + { + return $this->spatialSrID; + } + + /** + * @inheritDoc + */ + public function isOnUpdateCurrentTimestamp(): bool + { + return $this->onUpdateCurrentTimestamp; + } + + /** + * @inheritDoc + */ + public function isRawDefault(): bool + { + return $this->rawDefault; + } + + /** + * @inheritDoc + */ + public function getVirtualDefinition(): ?string + { + return $this->virtualDefinition; + } + + /** + * @inheritDoc + */ + public function getStoredDefinition(): ?string + { + return $this->storedDefinition; + } + + /** + * Set the column type to "increments" or "*Increments" if the column is auto increment. + * If the DB supports unsigned, should check if the column is unsigned. + * + * @param bool $supportUnsigned DB support unsigned integer. + */ + protected function setTypeToIncrements(bool $supportUnsigned): void + { + if ( + !in_array($this->type, [ + ColumnType::BIG_INTEGER, + ColumnType::INTEGER, + ColumnType::MEDIUM_INTEGER, + ColumnType::SMALL_INTEGER, + ColumnType::TINY_INTEGER, + ]) + ) { + return; + } + + if (!$this->autoincrement) { + return; + } + + if ($supportUnsigned && !$this->unsigned) { + return; + } + + if ($this->type === ColumnType::INTEGER) { + $this->type = ColumnType::INCREMENTS; + return; + } + + $this->type = ColumnType::from(str_replace('Integer', 'Increments', $this->type->value)); + } + + /** + * Escape `'` with `''`. + */ + protected function escapeDefault(?string $default): ?string + { + if ($default === null) { + return null; + } + + $default = str_replace("'", "''", $default); + return addcslashes($default, '\\'); + } + + /** + * Escape `\` with `\\`. + */ + protected function escapeComment(?string $comment): ?string + { + if ($comment === null || $comment === '') { + return null; + } + + return addcslashes($comment, '\\'); + } + + /** + * Parse the length from the full definition type. + */ + protected function parseLength(string $fullDefinitionType): ?int + { + switch ($this->type) { + case ColumnType::CHAR: + case ColumnType::STRING: + case ColumnType::DATE: + case ColumnType::DATETIME: + case ColumnType::DATETIME_TZ: + case ColumnType::TIME: + case ColumnType::TIME_TZ: + case ColumnType::TIMESTAMP: + case ColumnType::TIMESTAMP_TZ: + if (preg_match('/\((\d*)\)/', $fullDefinitionType, $matches) === 1) { + return (int) $matches[1]; + } + + break; + + default: + } + + return null; + } + + /** + * Parse the precision and scale from the full definition type. + * + * @return array{0: int|null, 1: int} + */ + protected function parsePrecisionAndScale(string $fullDefinitionType): array + { + switch ($this->type) { + case ColumnType::DECIMAL: + case ColumnType::DOUBLE: + case ColumnType::FLOAT: + if (preg_match('/\((\d+)(?:,\s*(\d+))?\)?/', $fullDefinitionType, $matches) === 1) { + return [(int) $matches[1], isset($matches[2]) ? (int) $matches[2] : 0]; + } + + break; + + default: + } + + return [null, 0]; + } + + /** + * Set the column type to "softDeletes" or "softDeletesTz". + */ + private function setTypeToSoftDeletes(): void + { + if ($this->name !== ColumnName::DELETED_AT->value) { + return; + } + + switch ($this->type) { + case ColumnType::TIMESTAMP: + $this->type = ColumnType::SOFT_DELETES; + return; + + case ColumnType::TIMESTAMP_TZ: + $this->type = ColumnType::SOFT_DELETES_TZ; + return; + + default: + } + } + + /** + * Set the column type to "rememberToken". + */ + private function setTypeToRememberToken(): void + { + if ( + ColumnName::REMEMBER_TOKEN->value !== $this->name + || $this->length !== self::REMEMBER_TOKEN_LENGTH + ) { + return; + } + + $this->type = ColumnType::REMEMBER_TOKEN; + } +} diff --git a/src/DBAL/Models/DBALForeignKey.php b/src/Database/Models/DatabaseForeignKey.php similarity index 50% rename from src/DBAL/Models/DBALForeignKey.php rename to src/Database/Models/DatabaseForeignKey.php index 6b467916..5976099e 100644 --- a/src/DBAL/Models/DBALForeignKey.php +++ b/src/Database/Models/DatabaseForeignKey.php @@ -1,56 +1,46 @@ tableName = $table; - $this->name = $foreignKeyConstraint->getName(); - $this->localColumns = $foreignKeyConstraint->getUnquotedLocalColumns(); - $this->foreignColumns = $foreignKeyConstraint->getUnquotedForeignColumns(); - $this->foreignTableName = $foreignKeyConstraint->getForeignTableName(); - $this->onUpdate = $foreignKeyConstraint->getOption('onUpdate'); - $this->onDelete = $foreignKeyConstraint->getOption('onDelete'); + $this->name = $foreignKey['name']; + $this->localColumns = $foreignKey['columns']; + $this->foreignColumns = $foreignKey['foreign_columns']; + $this->foreignTableName = $foreignKey['foreign_table']; + $this->onUpdate = $foreignKey['on_update']; + $this->onDelete = $foreignKey['on_delete']; } /** diff --git a/src/Database/Models/DatabaseIndex.php b/src/Database/Models/DatabaseIndex.php new file mode 100644 index 00000000..c73eb743 --- /dev/null +++ b/src/Database/Models/DatabaseIndex.php @@ -0,0 +1,96 @@ +tableName = $table; + $this->name = $index['name']; + $this->columns = $index['columns']; + $this->type = $this->getIndexType($index); + } + + /** + * @inheritDoc + */ + public function getName(): string + { + return $this->name; + } + + /** + * @inheritDoc + */ + public function getTableName(): string + { + return $this->tableName; + } + + /** + * @inheritDoc + */ + public function getColumns(): array + { + return $this->columns; + } + + /** + * @inheritDoc + */ + public function getType(): IndexType + { + return $this->type; + } + + /** + * Get the index type. + * + * @param SchemaIndex $index + */ + private function getIndexType(array $index): IndexType + { + if ($index['primary'] === true) { + return IndexType::PRIMARY; + } + + if ($index['unique'] === true) { + return IndexType::UNIQUE; + } + + // pgsql uses gist + if ($index['type'] === 'spatial' || $index['type'] === 'gist') { + return IndexType::SPATIAL_INDEX; + } + + // pgsql uses gin + if ($index['type'] === 'fulltext' || $index['type'] === 'gin') { + return IndexType::FULLTEXT; + } + + return IndexType::INDEX; + } +} diff --git a/src/DBAL/Models/DBALProcedure.php b/src/Database/Models/DatabaseProcedure.php similarity index 54% rename from src/DBAL/Models/DBALProcedure.php rename to src/Database/Models/DatabaseProcedure.php index 7609be9c..6f83ba0d 100644 --- a/src/DBAL/Models/DBALProcedure.php +++ b/src/Database/Models/DatabaseProcedure.php @@ -1,30 +1,15 @@ name = $name; - $this->definition = $definition; $this->dropDefinition = "DROP PROCEDURE IF EXISTS $name"; } diff --git a/src/Database/Models/DatabaseTable.php b/src/Database/Models/DatabaseTable.php new file mode 100644 index 00000000..f56198fe --- /dev/null +++ b/src/Database/Models/DatabaseTable.php @@ -0,0 +1,140 @@ + + */ + protected Collection $columns; + + /** + * @var \Illuminate\Support\Collection + */ + protected Collection $udtColumns; + + protected string $name; + + protected ?string $comment = null; + + /** + * @var \Illuminate\Support\Collection + */ + protected Collection $indexes; + + /** + * Make a Column instance. + * + * @param SchemaColumn $column + */ + abstract protected function makeColumn(string $table, array $column): Column; + + /** + * Make a UDTColumn instance. + * + * @param SchemaColumn $column + */ + abstract protected function makeUDTColumn(string $table, array $column): UDTColumn; + + /** + * Make an Index instance. + * + * @param SchemaIndex $index + */ + abstract protected function makeIndex(string $table, array $index): Index; + + /** + * Create a new instance. + * + * @param SchemaTable $table + * @param \Illuminate\Support\Collection $columns Key is quoted name. + * @param \Illuminate\Support\Collection $indexes Key is name. + * @param \Illuminate\Support\Collection $userDefinedTypes + */ + public function __construct(array $table, Collection $columns, Collection $indexes, Collection $userDefinedTypes) + { + $this->name = $table['name']; + $this->comment = $table['comment']; + $this->collation = $table['collation']; + + $this->columns = $columns->reduce(function (Collection $columns, array $column) use ($userDefinedTypes) { + if (!$userDefinedTypes->contains($column['type_name'])) { + $columns->push($this->makeColumn($this->name, $column)); + } + + return $columns; + }, new Collection())->values(); + + $this->udtColumns = $columns->reduce(function (Collection $columns, array $column) use ($userDefinedTypes) { + if ($userDefinedTypes->contains($column['type_name'])) { + $columns->push($this->makeUDTColumn($this->name, $column)); + } + + return $columns; + }, new Collection())->values(); + + $this->indexes = $indexes->map(fn (array $index) => $this->makeIndex($this->name, $index))->values(); + } + + /** + * @inheritDoc + */ + public function getName(): string + { + return $this->name; + } + + /** + * @inheritDoc + */ + public function getComment(): ?string + { + return $this->comment; + } + + /** + * @inheritDoc + */ + public function getColumns(): Collection + { + return $this->columns; + } + + /** + * @inheritDoc + */ + public function getUdtColumns(): Collection + { + return $this->udtColumns; + } + + /** + * @inheritDoc + */ + public function getIndexes(): Collection + { + return $this->indexes; + } + + /** + * @inheritDoc + */ + public function getCollation(): ?string + { + return $this->collation; + } +} diff --git a/src/Database/Models/DatabaseUDTColumn.php b/src/Database/Models/DatabaseUDTColumn.php new file mode 100644 index 00000000..dea246f8 --- /dev/null +++ b/src/Database/Models/DatabaseUDTColumn.php @@ -0,0 +1,53 @@ +name = $column['name']; + $this->tableName = $table; + } + + /** + * @inheritDoc + */ + public function getName(): string + { + return $this->name; + } + + /** + * @inheritDoc + */ + public function getTableName(): string + { + return $this->tableName; + } + + /** + * @inheritDoc + */ + public function getSqls(): array + { + return $this->sqls; + } +} diff --git a/src/Database/Models/DatabaseView.php b/src/Database/Models/DatabaseView.php new file mode 100644 index 00000000..92463275 --- /dev/null +++ b/src/Database/Models/DatabaseView.php @@ -0,0 +1,57 @@ +name = $view['name']; + $this->quotedName = $this->quoteIdentifier($view['name']); + $this->definition = ''; + $this->dropDefinition = "DROP VIEW IF EXISTS $this->quotedName"; + } + + /** + * @inheritDoc + */ + public function getName(): string + { + return $this->name; + } + + /** + * @inheritDoc + */ + public function getDefinition(): string + { + return $this->definition; + } + + /** + * @inheritDoc + */ + public function getDropDefinition(): string + { + return $this->dropDefinition; + } +} diff --git a/src/Database/Models/MySQL/MySQLColumn.php b/src/Database/Models/MySQL/MySQLColumn.php new file mode 100644 index 00000000..64fa13f1 --- /dev/null +++ b/src/Database/Models/MySQL/MySQLColumn.php @@ -0,0 +1,353 @@ + "\0", + "\\'" => "'", + '\\"' => '"', + '\\b' => "\b", + '\\n' => "\n", + '\\r' => "\r", + '\\t' => "\t", + '\\Z' => "\x1a", + '\\\\' => '\\', + '\\%' => '%', + '\\_' => '_', + + // Internally, MariaDB escapes single quotes using the standard syntax + "''" => "'", + ]; + + private MySQLRepository $mysqlRepository; + + private MariaDBRepository $mariaDBRepository; + + /** + * @inheritDoc + */ + public function __construct(string $table, array $column) + { + parent::__construct($table, $column); + + $this->default = $this->escapeDefault($column['default']); + $this->unsigned = str_contains($column['type'], 'unsigned'); + + if ($this->isMaria()) { + $this->default = $this->getMariaDBColumnDefault($column['default']); + $this->default = $this->escapeDefault($this->default); + } + + $this->mysqlRepository = app(MySQLRepository::class); + $this->mariaDBRepository = app(MariaDBRepository::class); + + $this->setTypeToIncrements(true); + $this->setTypeToUnsigned(); + + switch ($this->type) { + case ColumnType::UNSIGNED_TINY_INTEGER: + case ColumnType::TINY_INTEGER: + if ($this->isBoolean()) { + $this->type = ColumnType::BOOLEAN; + } + + break; + + case ColumnType::ENUM: + $this->presetValues = $this->getEnumPresetValues($column['type']); + break; + + case ColumnType::SET: + $this->presetValues = $this->getSetPresetValues($column['type']); + break; + + case ColumnType::SOFT_DELETES: + case ColumnType::SOFT_DELETES_TZ: + case ColumnType::TIMESTAMP: + case ColumnType::TIMESTAMP_TZ: + $this->onUpdateCurrentTimestamp = $this->hasOnUpdateCurrentTimestamp(); + break; + + case ColumnType::GEOGRAPHY: + case ColumnType::GEOMETRY: + case ColumnType::GEOMETRY_COLLECTION: + case ColumnType::LINE_STRING: + case ColumnType::MULTI_LINE_STRING: + case ColumnType::POINT: + case ColumnType::MULTI_POINT: + case ColumnType::POLYGON: + case ColumnType::MULTI_POLYGON: + $this->setRealSpatialColumn(); + break; + + default: + } + + $this->setVirtualDefinition(); + $this->setStoredDefinition(); + + if (!$this->isMaria()) { + return; + } + + // Extra logic for MariaDB + switch ($this->type) { + case ColumnType::LONG_TEXT: + if ($this->isJson()) { + $this->type = ColumnType::JSON; + } + + break; + + default: + } + } + + /** + * @inheritDoc + */ + protected function getColumnType(string $type): ColumnType + { + return MySQLColumnType::toColumnType($type); + } + + /** + * @inheritDoc + */ + protected function escapeDefault(?string $default): ?string + { + $default = parent::escapeDefault($default); + + if ($default === null) { + return null; + } + + return addcslashes($default, '\\'); + } + + /** + * Determine if the connected database is a MariaDB database. + */ + private function isMaria(): bool + { + return str_contains(DB::connection()->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION), 'MariaDB'); + } + + /** + * Check if the column is "tinyint(1)". + */ + private function isBoolean(): bool + { + if ($this->autoincrement) { + return false; + } + + $showColumn = $this->mysqlRepository->showColumn($this->tableName, $this->name); + + if ($showColumn === null) { + return false; + } + + return Str::startsWith($showColumn->getType(), 'tinyint(1)'); + } + + /** + * Get the preset values if the column is "enum". + * + * @return string[] + */ + private function getEnumPresetValues(string $fullDefinition): array + { + $value = substr( + $fullDefinition, + strlen("enum('"), + -strlen("')"), + ); + return explode("','", $value); + } + + /** + * Get the preset values if the column is "set". + * + * @return string[] + */ + private function getSetPresetValues(string $fullDefinition): array + { + $value = substr( + $fullDefinition, + strlen("set('"), + -strlen("')"), + ); + return explode("','", $value); + } + + /** + * Check if the column uses "on update CURRENT_TIMESTAMP". + */ + private function hasOnUpdateCurrentTimestamp(): bool + { + return $this->mysqlRepository->isOnUpdateCurrentTimestamp($this->tableName, $this->name); + } + + /** + * MariaDB return `longText` instead of `json` column. + * Check the check constraint of this column to check if type is `json`. + * Return true if check constraint contains `json_valid` keyword. + */ + private function isJson(): bool + { + $checkConstraint = $this->mariaDBRepository->getCheckConstraintForJson($this->tableName, $this->name); + return $checkConstraint !== null; + } + + /** + * Set virtual definition if the column is virtual. + */ + private function setVirtualDefinition(): void + { + $virtualDefinition = $this->mysqlRepository->getVirtualDefinition($this->tableName, $this->name); + + if ($virtualDefinition === null) { + return; + } + + // The definition of MySQL8 returned `concat(string,_utf8mb4\' \',string_255)`. + // Replace `\'` to `'` here to avoid double escape. + $this->virtualDefinition = str_replace("\'", "'", $virtualDefinition); + } + + /** + * Set stored definition if the column is stored. + */ + private function setStoredDefinition(): void + { + $storedDefinition = $this->mysqlRepository->getStoredDefinition($this->tableName, $this->name); + + if ($storedDefinition === null) { + return; + } + + // The definition of MySQL8 returned `concat(string,_utf8mb4\' \',string_255)`. + // Replace `\'` to `'` here to avoid double escape. + $this->storedDefinition = str_replace("\'", "'", $storedDefinition); + } + + /** + * Set to geometry or geography. + */ + private function setRealSpatialColumn(): void + { + if (!$this->atLeastLaravel11()) { + return; + } + + switch ($this->type) { + case ColumnType::GEOMETRY_COLLECTION: + $this->spatialSubType = 'geometryCollection'; + break; + + case ColumnType::LINE_STRING: + $this->spatialSubType = 'lineString'; + break; + + case ColumnType::MULTI_LINE_STRING: + $this->spatialSubType = 'multiLineString'; + break; + + case ColumnType::POINT: + $this->spatialSubType = 'point'; + break; + + case ColumnType::MULTI_POINT: + $this->spatialSubType = 'multiPoint'; + break; + + case ColumnType::POLYGON: + $this->spatialSubType = 'polygon'; + break; + + case ColumnType::MULTI_POLYGON: + $this->spatialSubType = 'multiPolygon'; + break; + + default: + } + + $this->type = ColumnType::GEOMETRY; + + $this->spatialSrID = $this->mysqlRepository->getSrID($this->tableName, $this->name); + + if ($this->spatialSrID === null) { + return; + } + + $this->type = ColumnType::GEOGRAPHY; + } + + /** + * Set the column type to "unsigned*" if the column is unsigned. + */ + private function setTypeToUnsigned(): void + { + if ( + !in_array($this->type, [ + ColumnType::BIG_INTEGER, + ColumnType::INTEGER, + ColumnType::MEDIUM_INTEGER, + ColumnType::SMALL_INTEGER, + ColumnType::TINY_INTEGER, + ]) + || !$this->unsigned + ) { + return; + } + + $this->type = ColumnType::from('unsigned' . ucfirst($this->type->value)); + } + + /** + * Return Mysql column default values for MariaDB 10.2.7+ servers. + * + * - Since MariaDb 10.2.7 column defaults stored in information_schema are now quoted + * to distinguish them from expressions (see MDEV-10134). + * - CURRENT_TIMESTAMP, CURRENT_TIME, CURRENT_DATE are stored in information_schema + * as current_timestamp(), currdate(), currtime() + * - Quoted 'NULL' is not enforced by Maria, it is technically possible to have + * null in some circumstances (see https://jira.mariadb.org/browse/MDEV-14053) + * - \' is always stored as '' in information_schema (normalized) + * + * @link https://mariadb.com/kb/en/library/information-schema-columns-table/ + * @link https://jira.mariadb.org/browse/MDEV-13132 + * @param string|null $columnDefault default value as stored in information_schema for MariaDB >= 10.2.7 + */ + private function getMariaDBColumnDefault(?string $columnDefault): ?string + { + if ($columnDefault === 'NULL' || $columnDefault === null) { + return null; + } + + if (preg_match('/^\'(.*)\'$/', $columnDefault, $matches) === 1) { + return strtr($matches[1], self::MARIADB_ESCAPE_SEQUENCES); + } + + return match ($columnDefault) { + 'current_timestamp()' => 'CURRENT_TIMESTAMP', + 'curdate()' => 'CURRENT_DATE', + 'curtime()' => 'CURRENT_TIME', + default => $columnDefault, + }; + } +} diff --git a/src/Database/Models/MySQL/MySQLColumnType.php b/src/Database/Models/MySQL/MySQLColumnType.php new file mode 100644 index 00000000..570762d4 --- /dev/null +++ b/src/Database/Models/MySQL/MySQLColumnType.php @@ -0,0 +1,70 @@ + + */ + protected static array $map = [ + 'bigint' => ColumnType::BIG_INTEGER, + 'binary' => ColumnType::BINARY, + 'bit' => ColumnType::BOOLEAN, + 'blob' => ColumnType::BINARY, + 'char' => ColumnType::CHAR, + 'date' => ColumnType::DATE, + 'datetime' => ColumnType::DATETIME, + 'decimal' => ColumnType::DECIMAL, + 'double' => ColumnType::DOUBLE, + 'enum' => ColumnType::ENUM, + 'float' => ColumnType::FLOAT, + 'geography' => ColumnType::GEOGRAPHY, + 'geometry' => ColumnType::GEOMETRY, + 'int' => ColumnType::INTEGER, + 'integer' => ColumnType::INTEGER, + 'json' => ColumnType::JSON, + 'longblob' => ColumnType::BINARY, + 'longtext' => ColumnType::LONG_TEXT, + 'mediumblob' => ColumnType::BINARY, + 'mediumint' => ColumnType::MEDIUM_INTEGER, + 'mediumtext' => ColumnType::MEDIUM_TEXT, + 'numeric' => ColumnType::DECIMAL, + 'real' => ColumnType::FLOAT, + 'set' => ColumnType::SET, + 'smallint' => ColumnType::SMALL_INTEGER, + 'string' => ColumnType::STRING, + 'text' => ColumnType::TEXT, + 'time' => ColumnType::TIME, + 'timestamp' => ColumnType::TIMESTAMP, + 'tinyblob' => ColumnType::BINARY, + 'tinyint' => ColumnType::TINY_INTEGER, + 'tinytext' => ColumnType::TINY_TEXT, + 'varbinary' => ColumnType::BINARY, + 'varchar' => ColumnType::STRING, + 'year' => ColumnType::YEAR, + + // Removed from Laravel v11 + 'geomcollection' => ColumnType::GEOMETRY_COLLECTION, + 'linestring' => ColumnType::LINE_STRING, + 'multilinestring' => ColumnType::MULTI_LINE_STRING, + 'point' => ColumnType::POINT, + 'multipoint' => ColumnType::MULTI_POINT, + 'polygon' => ColumnType::POLYGON, + 'multipolygon' => ColumnType::MULTI_POLYGON, + + // For MariaDB + 'geometrycollection' => ColumnType::GEOMETRY_COLLECTION, + ]; + + /** + * @inheritDoc + */ + public static function toColumnType(string $dbType): ColumnType + { + return self::mapToColumnType(self::$map, $dbType); + } +} diff --git a/src/Database/Models/MySQL/MySQLForeignKey.php b/src/Database/Models/MySQL/MySQLForeignKey.php new file mode 100644 index 00000000..a78bb49b --- /dev/null +++ b/src/Database/Models/MySQL/MySQLForeignKey.php @@ -0,0 +1,9 @@ +type) { + case IndexType::PRIMARY: + // Reset name to empty to indicate use the database platform naming. + $this->name = ''; + break; + + default: + } + } +} diff --git a/src/Database/Models/MySQL/MySQLProcedure.php b/src/Database/Models/MySQL/MySQLProcedure.php new file mode 100644 index 00000000..58576aee --- /dev/null +++ b/src/Database/Models/MySQL/MySQLProcedure.php @@ -0,0 +1,9 @@ +definition = 'CREATE VIEW ' . $this->quotedName . ' AS ' . $view['definition']; + } +} diff --git a/src/Database/Models/PgSQL/PgSQLColumn.php b/src/Database/Models/PgSQL/PgSQLColumn.php new file mode 100644 index 00000000..e261e42a --- /dev/null +++ b/src/Database/Models/PgSQL/PgSQLColumn.php @@ -0,0 +1,195 @@ +default = $this->parseDefault($column['default'], $this->type); + $this->default = $this->escapeDefault($this->default); + + $this->repository = app(PgSQLRepository::class); + + $this->setTypeToIncrements(false); + + switch ($this->type) { + case ColumnType::DATE: + case ColumnType::DATETIME: + case ColumnType::DATETIME_TZ: + case ColumnType::TIME: + case ColumnType::TIME_TZ: + case ColumnType::TIMESTAMP: + case ColumnType::TIMESTAMP_TZ: + case ColumnType::SOFT_DELETES: + case ColumnType::SOFT_DELETES_TZ: + $this->setRawDefault(); + break; + + case ColumnType::GEOGRAPHY: + case ColumnType::GEOMETRY: + $this->setRealSpatialColumn($column['type']); + break; + + case ColumnType::STRING: + $this->presetValues = $this->getEnumPresetValues(); + + if (count($this->presetValues) > 0) { + $this->type = ColumnType::ENUM; + } + + break; + + default: + } + + $this->setStoredDefinition(); + } + + /** + * @inheritDoc + */ + protected function getColumnType(string $type): ColumnType + { + return PgSQLColumnType::toColumnType($type); + } + + /** + * @inheritDoc + */ + protected function escapeDefault(?string $default): ?string + { + if (preg_match('/\((.?)\)\)/', $default)) { + return $default; + } + + return parent::escapeDefault($default); + } + + /** + * Check and set to use raw default. + * Raw default will be generated with DB::raw(). + */ + private function setRawDefault(): void + { + if ($this->default === 'now()') { + $this->rawDefault = true; + return; + } + + // If default value is expression, eg: timezone('Europe/Rome'::text, now()) + if (!preg_match('/\((.?)\)/', $this->default)) { + return; + } + + $this->rawDefault = true; + } + + /** + * Get geometry mapping. + * + * @return array + */ + private function getGeometryMap(): array + { + return [ + 'geometry' => ColumnType::GEOMETRY, + 'geometrycollection' => ColumnType::GEOMETRY_COLLECTION, + 'linestring' => ColumnType::LINE_STRING, + 'multilinestring' => ColumnType::MULTI_LINE_STRING, + 'multipoint' => ColumnType::MULTI_POINT, + 'multipolygon' => ColumnType::MULTI_POLYGON, + 'point' => ColumnType::POINT, + 'polygon' => ColumnType::POLYGON, + ]; + } + + /** + * Set to geometry type base on geography map. + */ + private function setRealSpatialColumn(string $fullDefinitionType): void + { + $dataType = strtolower($fullDefinitionType); + $dataType = preg_replace('/\s+/', '', $dataType); + + if ($dataType === 'geography' || $dataType === 'geometry') { + return; + } + + if (!preg_match('/(\w+)(?:\((\w+)(?:,\s*(\w+))?\))?/', $dataType, $matches)) { + return; + } + + $spatialSubType = $matches[2]; + $spatialSrID = isset($matches[3]) ? (int) $matches[3] : null; + + if (!$this->atLeastLaravel11()) { + $map = $this->getGeometryMap(); + + if (!isset($map[$spatialSubType])) { + return; + } + + $this->type = $map[$spatialSubType]; + return; + } + + $this->spatialSubType = $spatialSubType; + $this->spatialSrID = $spatialSrID; + } + + /** + * Get the preset values if the column is "enum". + * + * @return string[] + */ + private function getEnumPresetValues(): array + { + $definition = $this->repository->getCheckConstraintDefinition($this->tableName, $this->name); + + if ($definition === null) { + return []; + } + + if ($definition === '') { + return []; + } + + $presetValues = Regex::getTextBetweenAll($definition, "'", "'::"); + + if ($presetValues === null) { + return []; + } + + return $presetValues; + } + + /** + * Set stored definition if the column is stored. + */ + private function setStoredDefinition(): void + { + $this->storedDefinition = $this->repository->getStoredDefinition($this->tableName, $this->name); + + // A generated column cannot have a column default or an identity definition. + if ($this->storedDefinition === null) { + return; + } + + $this->default = null; + } +} diff --git a/src/Database/Models/PgSQL/PgSQLColumnType.php b/src/Database/Models/PgSQL/PgSQLColumnType.php new file mode 100644 index 00000000..d09b413e --- /dev/null +++ b/src/Database/Models/PgSQL/PgSQLColumnType.php @@ -0,0 +1,68 @@ + + */ + protected static array $map = [ + '_text' => ColumnType::TEXT, + '_varchar' => ColumnType::STRING, + 'bigint' => ColumnType::BIG_INTEGER, + 'bigserial' => ColumnType::BIG_INTEGER, + 'bool' => ColumnType::BOOLEAN, + 'boolean' => ColumnType::BOOLEAN, + 'bpchar' => ColumnType::CHAR, + 'bytea' => ColumnType::BINARY, + 'char' => ColumnType::CHAR, + 'date' => ColumnType::DATE, + 'datetime' => ColumnType::DATETIME, + 'decimal' => ColumnType::DECIMAL, + 'double precision' => ColumnType::FLOAT, + 'double' => ColumnType::FLOAT, + 'float' => ColumnType::FLOAT, + 'float4' => ColumnType::FLOAT, + 'float8' => ColumnType::FLOAT, + 'geography' => ColumnType::GEOGRAPHY, + 'geometry' => ColumnType::GEOMETRY, + 'inet' => ColumnType::IP_ADDRESS, + 'int' => ColumnType::INTEGER, + 'int2' => ColumnType::SMALL_INTEGER, + 'int4' => ColumnType::INTEGER, + 'int8' => ColumnType::BIG_INTEGER, + 'integer' => ColumnType::INTEGER, + 'interval' => ColumnType::STRING, + 'json' => ColumnType::JSON, + 'jsonb' => ColumnType::JSONB, + 'macaddr' => ColumnType::MAC_ADDRESS, + 'money' => ColumnType::DECIMAL, + 'numeric' => ColumnType::DECIMAL, + 'real' => ColumnType::FLOAT, + 'serial' => ColumnType::INTEGER, + 'serial4' => ColumnType::INTEGER, + 'serial8' => ColumnType::BIG_INTEGER, + 'smallint' => ColumnType::SMALL_INTEGER, + 'text' => ColumnType::TEXT, + 'time' => ColumnType::TIME, + 'timestamp' => ColumnType::TIMESTAMP, + 'timestamptz' => ColumnType::TIMESTAMP_TZ, + 'timetz' => ColumnType::TIME_TZ, + 'tsvector' => ColumnType::TEXT, + 'uuid' => ColumnType::UUID, + 'varchar' => ColumnType::STRING, + 'year' => ColumnType::INTEGER, + ]; + + /** + * @inheritDoc + */ + public static function toColumnType(string $dbType): ColumnType + { + return self::mapToColumnType(self::$map, $dbType); + } +} diff --git a/src/Database/Models/PgSQL/PgSQLForeignKey.php b/src/Database/Models/PgSQL/PgSQLForeignKey.php new file mode 100644 index 00000000..eaea5a3d --- /dev/null +++ b/src/Database/Models/PgSQL/PgSQLForeignKey.php @@ -0,0 +1,9 @@ +type) { + case IndexType::PRIMARY: + // Reset name to empty to indicate use the database platform naming. + $this->name = ''; + break; + + default: + } + } +} diff --git a/src/Database/Models/PgSQL/PgSQLParser.php b/src/Database/Models/PgSQL/PgSQLParser.php new file mode 100644 index 00000000..a34a0fa1 --- /dev/null +++ b/src/Database/Models/PgSQL/PgSQLParser.php @@ -0,0 +1,32 @@ +repository = app(PgSQLRepository::class); + + $this->updateFulltextIndexes(); + + $this->indexes = $this->indexes->sortBy(static fn (Index $index) => $index->getName())->values(); + } + + /** + * @inheritDoc + */ + protected function makeColumn(string $table, array $column): Column + { + return new PgSQLColumn($table, $column); + } + + /** + * @inheritDoc + */ + protected function makeUDTColumn(string $table, array $column): UDTColumn + { + return new PgSQLUDTColumn($table, $column); + } + + /** + * @inheritDoc + */ + protected function makeIndex(string $table, array $index): Index + { + return new PgSQLIndex($table, $index); + } + + /** + * The fulltext index column is empty by default. + * This method query the DB to get the fulltext index columns and update the indexes collection. + */ + private function updateFulltextIndexes(): void + { + $tableName = $this->name; + + // Get fulltext indexes. + $fulltextIndexes = $this->repository->getFulltextIndexes($tableName) + ->keyBy(static fn (IndexDefinition $indexDefinition) => $indexDefinition->getIndexName()); + + $this->indexes = $this->indexes->map(static function (Index $index) use ($fulltextIndexes, $tableName) { + if (!($index->getType() === IndexType::FULLTEXT)) { + return $index; + } + + if (!$fulltextIndexes->has($index->getName())) { + return $index; + } + + preg_match_all('/to_tsvector\((.*), \((.*)\)::text/U', $fulltextIndexes->get($index->getName())->getIndexDef(), $matches); + + $columns = $matches[2]; + + return new PgSQLIndex( + $tableName, + [ + 'name' => $index->getName(), + 'columns' => $columns, + 'type' => 'gin', + 'unique' => false, + 'primary' => false, + ], + ); + }); + } +} diff --git a/src/Database/Models/PgSQL/PgSQLUDTColumn.php b/src/Database/Models/PgSQL/PgSQLUDTColumn.php new file mode 100644 index 00000000..53ba4069 --- /dev/null +++ b/src/Database/Models/PgSQL/PgSQLUDTColumn.php @@ -0,0 +1,40 @@ +stripTablePrefix($table)); + $blueprint->addColumn('string', $column['name'], [ + 'autoIncrement' => $column['auto_increment'], + 'collation' => $column['collation'], + 'comment' => $column['comment'], + 'default' => $this->parseDefault($column['default'], ColumnType::STRING), // Assume is string + 'nullable' => $column['nullable'], + // 'after' => "id", + ]); + + $sqls = $blueprint->toSql(Schema::getConnection(), Schema::getConnection()->getSchemaGrammar()); + $sqls[0] = Str::replaceFirst(' varchar ', ' ' . $column['type'] . ' ', $sqls[0]); + + $this->sqls = $sqls; + } +} diff --git a/src/Database/Models/PgSQL/PgSQLView.php b/src/Database/Models/PgSQL/PgSQLView.php new file mode 100644 index 00000000..9ab70240 --- /dev/null +++ b/src/Database/Models/PgSQL/PgSQLView.php @@ -0,0 +1,18 @@ +definition = 'CREATE VIEW ' . $this->quotedName . ' AS ' . trim($view['definition']); + } +} diff --git a/src/Database/Models/SQLSrv/SQLSrvColumn.php b/src/Database/Models/SQLSrv/SQLSrvColumn.php new file mode 100644 index 00000000..e25fab74 --- /dev/null +++ b/src/Database/Models/SQLSrv/SQLSrvColumn.php @@ -0,0 +1,179 @@ +default = $this->parseDefault($column['default']); + $this->default = $this->escapeDefault($this->default); + + $this->repository = app(SQLSrvRepository::class); + + $this->setTypeToIncrements(false); + $this->fixMoneyPrecision($column['type_name']); + + switch ($this->type) { + case ColumnType::DATE: + case ColumnType::DATETIME: + case ColumnType::DATETIME_TZ: + case ColumnType::TIME: + case ColumnType::TIME_TZ: + case ColumnType::TIMESTAMP: + case ColumnType::TIMESTAMP_TZ: + case ColumnType::SOFT_DELETES: + case ColumnType::SOFT_DELETES_TZ: + $this->length = $this->getDateTimeLength(); + break; + + case ColumnType::CHAR: + case ColumnType::STRING: + case ColumnType::TEXT: + $this->presetValues = $this->getEnumPresetValues(); + + if (count($this->presetValues) > 0) { + $this->type = ColumnType::ENUM; + break; + } + + if ($this->isText($column['type'])) { + $this->type = ColumnType::TEXT; + $this->length = null; + } + + $this->fixLength($column['type_name']); + + break; + + default: + } + } + + /** + * @inheritDoc + */ + protected function getColumnType(string $type): ColumnType + { + return SQLSrvColumnType::toColumnType($type); + } + + /** + * Get the datetime column length. + */ + private function getDateTimeLength(): ?int + { + $columnDef = $this->repository->getColumnDefinition($this->tableName, $this->name); + + if ($columnDef === null) { + return null; + } + + switch ($this->type) { + case ColumnType::DATETIME: + if ( + $columnDef->getScale() === self::DATETIME_EMPTY_SCALE + && $columnDef->getLength() === self::DATETIME_EMPTY_LENGTH + ) { + return null; + } + + return $columnDef->getScale(); + + case ColumnType::DATETIME_TZ: + if ( + $columnDef->getScale() === self::DATETIME_TZ_EMPTY_SCALE + && $columnDef->getLength() === self::DATETIME_TZ_EMPTY_LENGTH + ) { + return null; + } + + return $columnDef->getScale(); + + default: + return $columnDef->getScale(); + } + } + + /** + * Set precision to 19 and scale to 4. + */ + private function fixMoneyPrecision(string $dbType): void + { + if ($dbType === 'money') { + $this->precision = 19; + $this->scale = 4; + return; + } + + if ($dbType !== 'smallmoney') { + return; + } + + $this->precision = 10; + $this->scale = 4; + } + + /** + * Check if the column type is "text". + */ + private function isText(string $fullDefinitionType): bool + { + return $fullDefinitionType === 'nvarchar(max)' || $fullDefinitionType === 'varchar(max)'; + } + + /** + * Get the preset values if the column is `enum`. + * + * @return string[] + */ + private function getEnumPresetValues(): array + { + return $this->repository->getEnumPresetValues( + $this->tableName, + $this->name, + )->toArray(); + } + + /** + * Fix the unicode string length. + */ + private function fixLength(string $dbType): void + { + if ($this->length === null) { + return; + } + + switch ($dbType) { + case 'nchar': + case 'ntext': + case 'nvarchar': + // Unicode data requires 2 bytes per character + $this->length /= 2; + return; + + default: + } + } +} diff --git a/src/Database/Models/SQLSrv/SQLSrvColumnType.php b/src/Database/Models/SQLSrv/SQLSrvColumnType.php new file mode 100644 index 00000000..0b6e91fc --- /dev/null +++ b/src/Database/Models/SQLSrv/SQLSrvColumnType.php @@ -0,0 +1,56 @@ + + */ + protected static array $map = [ + 'bigint' => ColumnType::BIG_INTEGER, + 'binary' => ColumnType::BINARY, + 'bit' => ColumnType::BOOLEAN, + 'blob' => ColumnType::BINARY, + 'char' => ColumnType::STRING, + 'date' => ColumnType::DATE, + 'datetime' => ColumnType::DATETIME, + 'datetime2' => ColumnType::DATETIME, + 'datetimeoffset' => ColumnType::DATETIME_TZ, + 'decimal' => ColumnType::DECIMAL, + 'double precision' => ColumnType::FLOAT, + 'double' => ColumnType::FLOAT, + 'float' => ColumnType::FLOAT, + 'geography' => ColumnType::GEOGRAPHY, + 'geometry' => ColumnType::GEOMETRY, + 'image' => ColumnType::BINARY, + 'int' => ColumnType::INTEGER, + 'money' => ColumnType::DECIMAL, + 'nchar' => ColumnType::CHAR, + 'ntext' => ColumnType::TEXT, + 'numeric' => ColumnType::DECIMAL, + 'nvarchar' => ColumnType::STRING, + 'real' => ColumnType::FLOAT, + 'smalldatetime' => ColumnType::DATETIME, + 'smallint' => ColumnType::SMALL_INTEGER, + 'smallmoney' => ColumnType::DECIMAL, + 'text' => ColumnType::TEXT, + 'time' => ColumnType::TIME, + 'tinyint' => ColumnType::TINY_INTEGER, + 'uniqueidentifier' => ColumnType::UUID, + 'varbinary' => ColumnType::BINARY, + 'varchar' => ColumnType::STRING, + 'xml' => ColumnType::TEXT, + ]; + + /** + * @inheritDoc + */ + public static function toColumnType(string $dbType): ColumnType + { + return self::mapToColumnType(self::$map, $dbType); + } +} diff --git a/src/Database/Models/SQLSrv/SQLSrvForeignKey.php b/src/Database/Models/SQLSrv/SQLSrvForeignKey.php new file mode 100644 index 00000000..3888c6c1 --- /dev/null +++ b/src/Database/Models/SQLSrv/SQLSrvForeignKey.php @@ -0,0 +1,9 @@ +repository = app(SQLSrvRepository::class); + parent::__construct($table, $index); switch ($this->type) { - case IndexType::PRIMARY(): + case IndexType::PRIMARY: $this->resetPrimaryNameToEmptyIfIsDefaultName(); break; default: - $this->changeTypeToSpatial(); - } - } - - /** - * Change the index type to `spatial` if the name is in the spatial index name list. - */ - private function changeTypeToSpatial(): void - { - $spatialNames = $this->repository->getSpatialIndexNames($this->tableName); - - if (!$spatialNames->contains($this->name)) { - return; } - - $this->type = IndexType::SPATIAL_INDEX(); } /** @@ -50,7 +32,7 @@ private function changeTypeToSpatial(): void */ private function resetPrimaryNameToEmptyIfIsDefaultName(): void { - $prefix = 'PK__' . Str::substr($this->tableName, 0, 8) . '__'; + $prefix = 'pk__' . Str::substr($this->tableName, 0, 8) . '__'; // Can be improved by generate exact 16 characters of sequence number instead of `\w{16}` // if the rules of sequence number generation is known. diff --git a/src/Database/Models/SQLSrv/SQLSrvParser.php b/src/Database/Models/SQLSrv/SQLSrvParser.php new file mode 100644 index 00000000..45070ae7 --- /dev/null +++ b/src/Database/Models/SQLSrv/SQLSrvParser.php @@ -0,0 +1,34 @@ +stripTablePrefix($table)); + $blueprint->addColumn('string', $column['name'], [ + 'autoIncrement' => $column['auto_increment'], + 'default' => $this->parseDefault($column['default']), + 'nullable' => $column['nullable'], + // 'after' => "id", + ]); + + $sqls = $blueprint->toSql(Schema::getConnection(), Schema::getConnection()->getSchemaGrammar()); + $sqls[0] = Str::replaceFirst(' nvarchar() ', ' ' . $column['type'] . ' ', $sqls[0]); + + $this->sqls = $sqls; + } +} diff --git a/src/Database/Models/SQLSrv/SQLSrvView.php b/src/Database/Models/SQLSrv/SQLSrvView.php new file mode 100644 index 00000000..a860496a --- /dev/null +++ b/src/Database/Models/SQLSrv/SQLSrvView.php @@ -0,0 +1,18 @@ +definition = $view['definition']; + } +} diff --git a/src/DBAL/Models/SQLite/SQLiteColumn.php b/src/Database/Models/SQLite/SQLiteColumn.php similarity index 59% rename from src/DBAL/Models/SQLite/SQLiteColumn.php rename to src/Database/Models/SQLite/SQLiteColumn.php index 670bb2d9..88637485 100644 --- a/src/DBAL/Models/SQLite/SQLiteColumn.php +++ b/src/Database/Models/SQLite/SQLiteColumn.php @@ -1,53 +1,52 @@ default = $this->parseDefault($column['default']); + $this->default = $this->escapeDefault($this->default); + $this->repository = app(SQLiteRepository::class); $this->setAutoincrement(); $this->setTypeToIncrements(false); switch ($this->type) { - case ColumnType::STRING(): - $this->presetValues = $this->getEnumPresetValues(); - - if (count($this->presetValues) > 0) { - $this->type = ColumnType::ENUM(); + case ColumnType::INTEGER: + if ($this->isBoolean($column['type'])) { + $this->type = ColumnType::BOOLEAN; } break; - case ColumnType::DATETIME(): - if ($this->default === 'CURRENT_TIMESTAMP') { - $this->type = ColumnType::TIMESTAMP(); + case ColumnType::STRING: + $this->presetValues = $this->getEnumPresetValues(); + + if (count($this->presetValues) > 0) { + $this->type = ColumnType::ENUM; } break; - case ColumnType::DATETIME_TZ(): + case ColumnType::DATETIME: if ($this->default === 'CURRENT_TIMESTAMP') { - $this->type = ColumnType::TIMESTAMP_TZ(); + $this->type = ColumnType::TIMESTAMP; } break; @@ -56,10 +55,18 @@ protected function handle(): void } } + /** + * @inheritDoc + */ + protected function getColumnType(string $type): ColumnType + { + return SQLiteColumnType::toColumnType($type); + } + /** * If column is integer and primary key, - * doctrine/dbal assume the column is autoincrement, but it could be wrong. - * Should check full sql statement from sqlite_master to ensure autoincrement is written corretly. + * The column is autoincrement, but it could be wrong. + * Should check full sql statement from sqlite_master to ensure autoincrement is written correctly. */ private function setAutoincrement(): void { @@ -99,6 +106,18 @@ private function setAutoincrement(): void } } + /** + * Check if the column is "tinyint(1)". + */ + private function isBoolean(string $fullDefinitionType): bool + { + if ($this->autoincrement) { + return false; + } + + return $fullDefinitionType === 'tinyint(1)'; + } + /** * Get the preset values if the column is "enum". * @@ -122,4 +141,20 @@ private function getEnumPresetValues(): array return $values; } + + /** + * Parse the default value. + */ + private function parseDefault(?string $default): ?string + { + if ($default === null) { + return null; + } + + while (preg_match('/^\'(.*)\'$/s', $default, $matches)) { + $default = str_replace("''", "'", $matches[1]); + } + + return $default; + } } diff --git a/src/Database/Models/SQLite/SQLiteColumnType.php b/src/Database/Models/SQLite/SQLiteColumnType.php new file mode 100644 index 00000000..d0293665 --- /dev/null +++ b/src/Database/Models/SQLite/SQLiteColumnType.php @@ -0,0 +1,57 @@ + + */ + protected static array $map = [ + 'bigint' => ColumnType::BIG_INTEGER, + 'bigserial' => ColumnType::BIG_INTEGER, + 'blob' => ColumnType::BINARY, + 'boolean' => ColumnType::BOOLEAN, + 'char' => ColumnType::STRING, + 'clob' => ColumnType::TEXT, + 'date' => ColumnType::DATE, + 'datetime' => ColumnType::DATETIME, + 'decimal' => ColumnType::DECIMAL, + 'double' => ColumnType::DOUBLE, + 'double precision' => ColumnType::DOUBLE, + 'float' => ColumnType::FLOAT, + 'image' => ColumnType::STRING, + 'int' => ColumnType::INTEGER, + 'integer' => ColumnType::INTEGER, + 'geometry' => ColumnType::GEOMETRY, + 'longtext' => ColumnType::TEXT, + 'longvarchar' => ColumnType::STRING, + 'mediumint' => ColumnType::INTEGER, + 'mediumtext' => ColumnType::TEXT, + 'ntext' => ColumnType::STRING, + 'numeric' => ColumnType::DECIMAL, + 'nvarchar' => ColumnType::STRING, + 'real' => ColumnType::FLOAT, + 'serial' => ColumnType::INTEGER, + 'smallint' => ColumnType::INTEGER, + 'string' => ColumnType::STRING, + 'text' => ColumnType::TEXT, + 'time' => ColumnType::TIME, + 'timestamp' => ColumnType::DATETIME, + 'tinyint' => ColumnType::INTEGER, + 'tinytext' => ColumnType::TEXT, + 'varchar' => ColumnType::STRING, + 'varchar2' => ColumnType::STRING, + ]; + + /** + * @inheritDoc + */ + public static function toColumnType(string $dbType): ColumnType + { + return self::mapToColumnType(self::$map, $dbType); + } +} diff --git a/src/Database/Models/SQLite/SQLiteForeignKey.php b/src/Database/Models/SQLite/SQLiteForeignKey.php new file mode 100644 index 00000000..21060918 --- /dev/null +++ b/src/Database/Models/SQLite/SQLiteForeignKey.php @@ -0,0 +1,9 @@ +type) { + case IndexType::PRIMARY: + // Reset name to empty to indicate use the database platform naming. + $this->name = ''; + break; + + default: + } + } +} diff --git a/src/Database/Models/SQLite/SQLiteTable.php b/src/Database/Models/SQLite/SQLiteTable.php new file mode 100644 index 00000000..7355c9f1 --- /dev/null +++ b/src/Database/Models/SQLite/SQLiteTable.php @@ -0,0 +1,35 @@ +definition = $view['definition']; + } +} diff --git a/src/Database/MySQLSchema.php b/src/Database/MySQLSchema.php new file mode 100644 index 00000000..1501b540 --- /dev/null +++ b/src/Database/MySQLSchema.php @@ -0,0 +1,69 @@ +getSchemaTable($name), + $this->getSchemaColumns($name), + $this->getSchemaIndexes($name), + new Collection(), + ); + } + + /** + * @inheritDoc + */ + public function getViewNames(): Collection + { + return $this->getViews()->map(static fn (View $view) => $view->getName()); + } + + /** + * @inheritDoc + */ + public function getViews(): Collection + { + return $this->getSchemaViews() + ->map(static fn (array $view) => new MySQLView($view)); + } + + /** + * @inheritDoc + */ + public function getProcedures(): Collection + { + return $this->mySQLRepository->getProcedures() + ->map(static fn (ProcedureDefinition $procedureDefinition) => new MySQLProcedure($procedureDefinition->getName(), $procedureDefinition->getDefinition())); + } + + /** + * @inheritDoc + */ + public function getForeignKeys(string $table): Collection + { + return $this->getSchemaForeignKeys($table) + ->map(static fn (array $foreignKey) => new MySQLForeignKey($table, $foreignKey)); + } +} diff --git a/src/Database/PgSQLSchema.php b/src/Database/PgSQLSchema.php new file mode 100644 index 00000000..889a7e1f --- /dev/null +++ b/src/Database/PgSQLSchema.php @@ -0,0 +1,126 @@ + + */ + private Collection $userDefinedTypes; + + private bool $ranGetUserDefinedTypes = false; + + public function __construct(private readonly PgSQLRepository $pgSQLRepository) + { + $this->userDefinedTypes = new Collection(); + } + + /** + * @inheritDoc + */ + public function getTableNames(): Collection + { + return (new Collection(Schema::getTables())) + ->filter(static function (array $table): bool { + if ($table['name'] === 'spatial_ref_sys') { + return false; + } + + // Schema name defined in the framework configuration. + $searchPath = DB::connection()->getConfig('search_path') ?: DB::connection()->getConfig('schema'); + + return $table['schema'] === $searchPath; + }) + ->pluck('name') + ->values(); + } + + /** + * @inheritDoc + */ + public function getTable(string $name): Table + { + return new PgSQLTable( + $this->getSchemaTable($name), + $this->getSchemaColumns($name), + $this->getSchemaIndexes($name), + $this->getUserDefinedTypes(), + ); + } + + /** + * @inheritDoc + */ + public function getViewNames(): Collection + { + return $this->getViews() + ->map(static fn (View $view) => $view->getName()); + } + + /** + * @inheritDoc + */ + public function getViews(): Collection + { + return $this->getSchemaViews() + ->filter(static function (array $view) { + if (in_array($view['name'], ['geography_columns', 'geometry_columns'])) { + return false; + } + + // Start from Laravel 9, the `schema` configuration option used to configure Postgres connection search paths renamed to `search_path`. + // Fallback to `schema` if Laravel version is older than 9. + $searchPath = DB::connection()->getConfig('search_path') ?: DB::connection()->getConfig('schema'); + + return $view['schema'] === $searchPath; + }) + ->map(static fn (array $view) => new PgSQLView($view)) + ->values(); + } + + /** + * @inheritDoc + */ + public function getProcedures(): Collection + { + return $this->pgSQLRepository->getProcedures() + ->map(static fn (ProcedureDefinition $procedureDefinition) => new PgSQLProcedure($procedureDefinition->getName(), $procedureDefinition->getDefinition())); + } + + /** + * @inheritDoc + */ + public function getForeignKeys(string $table): Collection + { + return $this->getSchemaForeignKeys($table) + ->map(static fn (array $foreignKey) => new PgSQLForeignKey($table, $foreignKey)); + } + + /** + * Get user defined types from the schema. + * + * @return \Illuminate\Support\Collection + */ + private function getUserDefinedTypes(): Collection + { + if (!$this->ranGetUserDefinedTypes) { + $this->userDefinedTypes = new Collection(array_column(Schema::getTypes(), 'name')); + $this->ranGetUserDefinedTypes = true; + } + + return $this->userDefinedTypes; + } +} diff --git a/src/Database/SQLSrvSchema.php b/src/Database/SQLSrvSchema.php new file mode 100644 index 00000000..fe05b6c1 --- /dev/null +++ b/src/Database/SQLSrvSchema.php @@ -0,0 +1,92 @@ + + */ + private Collection $userDefinedTypes; + + private bool $ranGetUserDefinedTypes = false; + + public function __construct(private readonly SQLSrvRepository $sqlSrvRepository) + { + $this->userDefinedTypes = new Collection(); + } + + /** + * @inheritDoc + */ + public function getTable(string $name): Table + { + return new SQLSrvTable( + $this->getSchemaTable($name), + $this->getSchemaColumns($name), + $this->getSchemaIndexes($name), + $this->getUserDefinedTypes(), + ); + } + + /** + * @inheritDoc + */ + public function getViewNames(): Collection + { + return $this->getViews()->map(static fn (View $view) => $view->getName()); + } + + /** + * @inheritDoc + */ + public function getViews(): Collection + { + return $this->getSchemaViews() + ->map(static fn (array $view) => new SQLSrvView($view)) + ->filter(static fn (SQLSrvView $view) => $view->getDefinition() !== ''); + } + + /** + * @inheritDoc + */ + public function getProcedures(): Collection + { + return $this->sqlSrvRepository->getProcedures() + ->map(static fn (ProcedureDefinition $procedureDefinition) => new PgSQLProcedure($procedureDefinition->getName(), $procedureDefinition->getDefinition())); + } + + /** + * @inheritDoc + */ + public function getForeignKeys(string $table): Collection + { + return $this->getSchemaForeignKeys($table) + ->map(static fn (array $foreignKey) => new SQLSrvForeignKey($table, $foreignKey)); + } + + /** + * Get user defined types from the database. + * + * @return \Illuminate\Support\Collection + */ + private function getUserDefinedTypes(): Collection + { + if (!$this->ranGetUserDefinedTypes) { + $this->userDefinedTypes = $this->sqlSrvRepository->getUserDefinedTypes(); + $this->ranGetUserDefinedTypes = true; + } + + return $this->userDefinedTypes; + } +} diff --git a/src/Database/SQLiteSchema.php b/src/Database/SQLiteSchema.php new file mode 100644 index 00000000..938ff3dc --- /dev/null +++ b/src/Database/SQLiteSchema.php @@ -0,0 +1,62 @@ +getSchemaTable($name), + $this->getSchemaColumns($name), + $this->getSchemaIndexes($name), + new Collection(), + ); + } + + /** + * @inheritDoc + */ + public function getViewNames(): Collection + { + return $this->getViews()->map(static fn (View $view) => $view->getName()); + } + + /** + * @inheritDoc + */ + public function getViews(): Collection + { + return $this->getSchemaViews() + ->map(static fn (array $view) => new SQLiteView($view)); + } + + /** + * @inheritDoc + */ + public function getProcedures(): Collection + { + // Stored procedure does not available. + // https://sqlite.org/forum/info/78a60bdeec7c1ee9 + return new Collection(); + } + + /** + * @inheritDoc + */ + public function getForeignKeys(string $table): Collection + { + return $this->getSchemaForeignKeys($table) + ->map(static fn (array $foreignKey) => new SQLiteForeignKey($table, $foreignKey)); + } +} diff --git a/src/Enum/Driver.php b/src/Enum/Driver.php index f5f472d6..42704927 100644 --- a/src/Enum/Driver.php +++ b/src/Enum/Driver.php @@ -2,21 +2,13 @@ namespace KitLoong\MigrationsGenerator\Enum; -use MyCLabs\Enum\Enum; - /** * Framework DB connection driver name. - * - * @method static self MYSQL() - * @method static self PGSQL() - * @method static self SQLITE() - * @method static self SQLSRV() - * @extends \MyCLabs\Enum\Enum */ -final class Driver extends Enum +enum Driver: string { - private const MYSQL = 'mysql'; - private const PGSQL = 'pgsql'; - private const SQLITE = 'sqlite'; - private const SQLSRV = 'sqlsrv'; + case MYSQL = 'mysql'; + case PGSQL = 'pgsql'; + case SQLITE = 'sqlite'; + case SQLSRV = 'sqlsrv'; } diff --git a/src/Enum/Migrations/ColumnName.php b/src/Enum/Migrations/ColumnName.php index 944da132..26a72bd1 100644 --- a/src/Enum/Migrations/ColumnName.php +++ b/src/Enum/Migrations/ColumnName.php @@ -2,22 +2,15 @@ namespace KitLoong\MigrationsGenerator\Enum\Migrations; -use MyCLabs\Enum\Enum; - /** * Preserved column names used by the framework. * * @see https://laravel.com/docs/master/migrations#available-column-types - * @method static self CREATED_AT() - * @method static self DELETED_AT() - * @method static self REMEMBER_TOKEN() - * @method static self UPDATED_AT() - * @extends \MyCLabs\Enum\Enum */ -final class ColumnName extends Enum +enum ColumnName: string { - private const CREATED_AT = 'created_at'; - private const DELETED_AT = 'deleted_at'; - private const REMEMBER_TOKEN = 'remember_token'; - private const UPDATED_AT = 'updated_at'; + case CREATED_AT = 'created_at'; + case DELETED_AT = 'deleted_at'; + case REMEMBER_TOKEN = 'remember_token'; + case UPDATED_AT = 'updated_at'; } diff --git a/src/Enum/Migrations/Method/ColumnModifier.php b/src/Enum/Migrations/Method/ColumnModifier.php index 51526d5b..150fa2a2 100644 --- a/src/Enum/Migrations/Method/ColumnModifier.php +++ b/src/Enum/Migrations/Method/ColumnModifier.php @@ -2,40 +2,24 @@ namespace KitLoong\MigrationsGenerator\Enum\Migrations\Method; -use MyCLabs\Enum\Enum; - /** * Preserved column modifier of the framework. * * @see https://laravel.com/docs/master/migrations#column-modifiers - * @method static self ALWAYS() - * @method static self AUTO_INCREMENT() - * @method static self CHARSET() - * @method static self COLLATION() - * @method static self COMMENT() - * @method static self DEFAULT() - * @method static self GENERATED_AS() - * @method static self NULLABLE() - * @method static self STORED_AS() - * @method static self UNSIGNED() - * @method static self USE_CURRENT() - * @method static self USE_CURRENT_ON_UPDATE() - * @method static self VIRTUAL_AS() - * @extends \MyCLabs\Enum\Enum */ -final class ColumnModifier extends Enum +enum ColumnModifier: string implements MethodName { - private const ALWAYS = 'always'; - private const AUTO_INCREMENT = 'autoIncrement'; - private const CHARSET = 'charset'; - private const COLLATION = 'collation'; - private const COMMENT = 'comment'; - private const DEFAULT = 'default'; - private const GENERATED_AS = 'generatedAs'; - private const NULLABLE = 'nullable'; - private const STORED_AS = 'storedAs'; - private const UNSIGNED = 'unsigned'; - private const USE_CURRENT = 'useCurrent'; - private const USE_CURRENT_ON_UPDATE = 'useCurrentOnUpdate'; - private const VIRTUAL_AS = 'virtualAs'; + case ALWAYS = 'always'; + case AUTO_INCREMENT = 'autoIncrement'; + case CHARSET = 'charset'; + case COLLATION = 'collation'; + case COMMENT = 'comment'; + case DEFAULT = 'default'; + case GENERATED_AS = 'generatedAs'; + case NULLABLE = 'nullable'; + case STORED_AS = 'storedAs'; + case UNSIGNED = 'unsigned'; + case USE_CURRENT = 'useCurrent'; + case USE_CURRENT_ON_UPDATE = 'useCurrentOnUpdate'; + case VIRTUAL_AS = 'virtualAs'; } diff --git a/src/Enum/Migrations/Method/ColumnType.php b/src/Enum/Migrations/Method/ColumnType.php index 1cc5cd00..a1f6b1e2 100644 --- a/src/Enum/Migrations/Method/ColumnType.php +++ b/src/Enum/Migrations/Method/ColumnType.php @@ -2,161 +2,66 @@ namespace KitLoong\MigrationsGenerator\Enum\Migrations\Method; -use Doctrine\DBAL\Types\Type; -use KitLoong\MigrationsGenerator\DBAL\Types\Types; -use MyCLabs\Enum\Enum; -use UnexpectedValueException; - /** * Define column types of the framework. - * Keep const as public to allow used by: - * {@see \KitLoong\MigrationsGenerator\DBAL\RegisterColumnType::registerLaravelColumnType()} - * {@see \KitLoong\MigrationsGenerator\DBAL\Types\Types} * * @link https://laravel.com/docs/master/migrations#available-column-types - * @method static self BIG_INTEGER() - * @method static self BIG_INCREMENTS() - * @method static self BINARY() - * @method static self BOOLEAN() - * @method static self CHAR() - * @method static self DATE() - * @method static self DATETIME() - * @method static self DATETIME_TZ() - * @method static self DECIMAL() - * @method static self DOUBLE() - * @method static self ENUM() - * @method static self FLOAT() - * @method static self GEOMETRY() - * @method static self GEOMETRY_COLLECTION() - * @method static self INCREMENTS() - * @method static self INTEGER() - * @method static self IP_ADDRESS() - * @method static self JSON() - * @method static self JSONB() - * @method static self LINE_STRING() - * @method static self LONG_TEXT() - * @method static self MAC_ADDRESS() - * @method static self MEDIUM_INCREMENTS() - * @method static self MEDIUM_INTEGER() - * @method static self MEDIUM_TEXT() - * @method static self MULTI_LINE_STRING() - * @method static self MULTI_POINT() - * @method static self MULTI_POLYGON() - * @method static self POINT() - * @method static self POLYGON() - * @method static self REMEMBER_TOKEN() - * @method static self SET() - * @method static self SMALL_INCREMENTS() - * @method static self SMALL_INTEGER() - * @method static self SOFT_DELETES() - * @method static self SOFT_DELETES_TZ() - * @method static self STRING() - * @method static self TEXT() - * @method static self TIME() - * @method static self TIME_TZ() - * @method static self TIMESTAMP() - * @method static self TIMESTAMPS() - * @method static self TIMESTAMP_TZ() - * @method static self TIMESTAMPS_TZ() - * @method static self TINY_INCREMENTS() - * @method static self TINY_INTEGER() - * @method static self TINY_TEXT() - * @method static self UNSIGNED_BIG_INTEGER() - * @method static self UNSIGNED_DECIMAL() - * @method static self UNSIGNED_INTEGER() - * @method static self UNSIGNED_MEDIUM_INTEGER() - * @method static self UNSIGNED_SMALL_INTEGER() - * @method static self UNSIGNED_TINY_INTEGER() - * @method static self UUID() - * @method static self YEAR() - * @extends \MyCLabs\Enum\Enum */ -final class ColumnType extends Enum +enum ColumnType: string implements MethodName { - public const BIG_INTEGER = 'bigInteger'; - public const BIG_INCREMENTS = 'bigIncrements'; - public const BINARY = 'binary'; - public const BOOLEAN = 'boolean'; - public const CHAR = 'char'; - public const DATE = 'date'; - public const DATETIME = 'dateTime'; - public const DATETIME_TZ = 'dateTimeTz'; - public const DECIMAL = 'decimal'; - public const DOUBLE = 'double'; - public const ENUM = 'enum'; - public const FLOAT = 'float'; - public const GEOMETRY = 'geometry'; - public const GEOMETRY_COLLECTION = 'geometryCollection'; - public const INCREMENTS = 'increments'; - public const INTEGER = 'integer'; - public const IP_ADDRESS = 'ipAddress'; - public const JSON = 'json'; - public const JSONB = 'jsonb'; - public const LINE_STRING = 'lineString'; - public const LONG_TEXT = 'longText'; - public const MAC_ADDRESS = 'macAddress'; - public const MEDIUM_INCREMENTS = 'mediumIncrements'; - public const MEDIUM_INTEGER = 'mediumInteger'; - public const MEDIUM_TEXT = 'mediumText'; - public const MULTI_LINE_STRING = 'multiLineString'; - public const MULTI_POINT = 'multiPoint'; - public const MULTI_POLYGON = 'multiPolygon'; - public const POINT = 'point'; - public const POLYGON = 'polygon'; - public const REMEMBER_TOKEN = 'rememberToken'; - public const SET = 'set'; - public const SMALL_INCREMENTS = 'smallIncrements'; - public const SMALL_INTEGER = 'smallInteger'; - public const SOFT_DELETES = 'softDeletes'; - public const SOFT_DELETES_TZ = 'softDeletesTz'; - public const STRING = 'string'; - public const TEXT = 'text'; - public const TIME = 'time'; - public const TIME_TZ = 'timeTz'; - public const TIMESTAMP = 'timestamp'; - public const TIMESTAMPS = 'timestamps'; - public const TIMESTAMP_TZ = 'timestampTz'; - public const TIMESTAMPS_TZ = 'timestampsTz'; - public const TINY_INCREMENTS = 'tinyIncrements'; - public const TINY_INTEGER = 'tinyInteger'; - public const TINY_TEXT = 'tinyText'; - public const UNSIGNED_BIG_INTEGER = 'unsignedBigInteger'; - public const UNSIGNED_DECIMAL = 'unsignedDecimal'; - public const UNSIGNED_INTEGER = 'unsignedInteger'; - public const UNSIGNED_MEDIUM_INTEGER = 'unsignedMediumInteger'; - public const UNSIGNED_SMALL_INTEGER = 'unsignedSmallInteger'; - public const UNSIGNED_TINY_INTEGER = 'unsignedTinyInteger'; - public const UUID = 'uuid'; - public const YEAR = 'year'; - - /** - * Create instance from {@see \Doctrine\DBAL\Types\Type}. - * - * @return static - */ - public static function fromDBALType(Type $dbalType): self - { - $map = Types::BUILTIN_TYPES_MAP + Types::ADDITIONAL_TYPES_MAP; - return self::fromValue($map[get_class($dbalType)]); - } - - /** - * Initiate an instance from value. - * - * @return static - */ - public static function fromValue(string $value): self - { - if (method_exists(Enum::class, 'from')) { - return parent::from($value); - } - - $key = self::search($value); - - if ($key === false) { - throw new UnexpectedValueException("Value '$value' is not part of the enum " . self::class); - } - - return self::__callStatic($key, []); - } + case BIG_INTEGER = 'bigInteger'; + case BIG_INCREMENTS = 'bigIncrements'; + case BINARY = 'binary'; + case BOOLEAN = 'boolean'; + case CHAR = 'char'; + case DATE = 'date'; + case DATETIME = 'dateTime'; + case DATETIME_TZ = 'dateTimeTz'; + case DECIMAL = 'decimal'; + case DOUBLE = 'double'; + case ENUM = 'enum'; + case FLOAT = 'float'; + case GEOGRAPHY = 'geography'; + case GEOMETRY = 'geometry'; + case GEOMETRY_COLLECTION = 'geometryCollection'; + case INCREMENTS = 'increments'; + case INTEGER = 'integer'; + case IP_ADDRESS = 'ipAddress'; + case JSON = 'json'; + case JSONB = 'jsonb'; + case LINE_STRING = 'lineString'; + case LONG_TEXT = 'longText'; + case MAC_ADDRESS = 'macAddress'; + case MEDIUM_INCREMENTS = 'mediumIncrements'; + case MEDIUM_INTEGER = 'mediumInteger'; + case MEDIUM_TEXT = 'mediumText'; + case MULTI_LINE_STRING = 'multiLineString'; + case MULTI_POINT = 'multiPoint'; + case MULTI_POLYGON = 'multiPolygon'; + case POINT = 'point'; + case POLYGON = 'polygon'; + case REMEMBER_TOKEN = 'rememberToken'; + case SET = 'set'; + case SMALL_INCREMENTS = 'smallIncrements'; + case SMALL_INTEGER = 'smallInteger'; + case SOFT_DELETES = 'softDeletes'; + case SOFT_DELETES_TZ = 'softDeletesTz'; + case STRING = 'string'; + case TEXT = 'text'; + case TIME = 'time'; + case TIME_TZ = 'timeTz'; + case TIMESTAMP = 'timestamp'; + case TIMESTAMPS = 'timestamps'; + case TIMESTAMP_TZ = 'timestampTz'; + case TIMESTAMPS_TZ = 'timestampsTz'; + case TINY_INCREMENTS = 'tinyIncrements'; + case TINY_INTEGER = 'tinyInteger'; + case TINY_TEXT = 'tinyText'; + case UNSIGNED_BIG_INTEGER = 'unsignedBigInteger'; + case UNSIGNED_INTEGER = 'unsignedInteger'; + case UNSIGNED_MEDIUM_INTEGER = 'unsignedMediumInteger'; + case UNSIGNED_SMALL_INTEGER = 'unsignedSmallInteger'; + case UNSIGNED_TINY_INTEGER = 'unsignedTinyInteger'; + case UUID = 'uuid'; + case YEAR = 'year'; } diff --git a/src/Enum/Migrations/Method/DBBuilder.php b/src/Enum/Migrations/Method/DBBuilder.php new file mode 100644 index 00000000..0b93bffe --- /dev/null +++ b/src/Enum/Migrations/Method/DBBuilder.php @@ -0,0 +1,9 @@ + */ -class Foreign extends Enum +enum Foreign: string implements MethodName { - private const DROP_FOREIGN = 'dropForeign'; - private const FOREIGN = 'foreign'; - private const ON = 'on'; - private const ON_DELETE = 'onDelete'; - private const ON_UPDATE = 'onUpdate'; - private const REFERENCES = 'references'; + case DROP_FOREIGN = 'dropForeign'; + case FOREIGN = 'foreign'; + case ON = 'on'; + case ON_DELETE = 'onDelete'; + case ON_UPDATE = 'onUpdate'; + case REFERENCES = 'references'; } diff --git a/src/Enum/Migrations/Method/IndexType.php b/src/Enum/Migrations/Method/IndexType.php index 98913be8..3fe6b224 100644 --- a/src/Enum/Migrations/Method/IndexType.php +++ b/src/Enum/Migrations/Method/IndexType.php @@ -2,26 +2,17 @@ namespace KitLoong\MigrationsGenerator\Enum\Migrations\Method; -use MyCLabs\Enum\Enum; - /** * Predefined index types of the framework. * * @see https://laravel.com/docs/master/migrations#available-index-types - * @method static self FULLTEXT() - * @method static self FULLTEXT_CHAIN() - * @method static self INDEX() - * @method static self PRIMARY() - * @method static self SPATIAL_INDEX() - * @method static self UNIQUE() - * @extends \MyCLabs\Enum\Enum */ -final class IndexType extends Enum +enum IndexType: string implements MethodName { - private const FULLTEXT = 'fullText'; - private const FULLTEXT_CHAIN = 'fulltext'; // Use lowercase. - private const INDEX = 'index'; - private const PRIMARY = 'primary'; - private const SPATIAL_INDEX = 'spatialIndex'; - private const UNIQUE = 'unique'; + case FULLTEXT = 'fullText'; + case FULLTEXT_CHAIN = 'fulltext'; // Use lowercase. + case INDEX = 'index'; + case PRIMARY = 'primary'; + case SPATIAL_INDEX = 'spatialIndex'; + case UNIQUE = 'unique'; } diff --git a/src/Enum/Migrations/Method/MethodName.php b/src/Enum/Migrations/Method/MethodName.php new file mode 100644 index 00000000..0320c84a --- /dev/null +++ b/src/Enum/Migrations/Method/MethodName.php @@ -0,0 +1,9 @@ + */ -final class SchemaBuilder extends Enum +enum SchemaBuilder: string implements MethodName { - private const CONNECTION = 'connection'; - private const CREATE = 'create'; - private const DROP_IF_EXISTS = 'dropIfExists'; - private const HAS_TABLE = 'hasTable'; - private const TABLE = 'table'; + case CONNECTION = 'connection'; + case CREATE = 'create'; + case DROP_IF_EXISTS = 'dropIfExists'; + case HAS_TABLE = 'hasTable'; + case TABLE = 'table'; } diff --git a/src/Enum/Migrations/Method/TableMethod.php b/src/Enum/Migrations/Method/TableMethod.php index 6f123e40..ef86ed2d 100644 --- a/src/Enum/Migrations/Method/TableMethod.php +++ b/src/Enum/Migrations/Method/TableMethod.php @@ -2,16 +2,12 @@ namespace KitLoong\MigrationsGenerator\Enum\Migrations\Method; -use MyCLabs\Enum\Enum; - /** * Preserved table methods of the framework. * * @see https://laravel.com/docs/master/migrations#tables - * @method static self COMMENT() - * @extends \MyCLabs\Enum\Enum */ -final class TableMethod extends Enum +enum TableMethod: string implements MethodName { - private const COMMENT = 'comment'; + case COMMENT = 'comment'; } diff --git a/src/Enum/Migrations/Property/PropertyName.php b/src/Enum/Migrations/Property/PropertyName.php new file mode 100644 index 00000000..1c7d510f --- /dev/null +++ b/src/Enum/Migrations/Property/PropertyName.php @@ -0,0 +1,9 @@ + */ -final class TableProperty extends Enum +enum TableProperty: string implements PropertyName { - private const CHARSET = 'charset'; - private const COLLATION = 'collation'; - private const ENGINE = 'engine'; + case CHARSET = 'charset'; + case COLLATION = 'collation'; + case ENGINE = 'engine'; } diff --git a/src/MigrateGenerateCommand.php b/src/MigrateGenerateCommand.php index ef2e9154..059c756e 100644 --- a/src/MigrateGenerateCommand.php +++ b/src/MigrateGenerateCommand.php @@ -23,17 +23,13 @@ use KitLoong\MigrationsGenerator\Schema\Schema; use KitLoong\MigrationsGenerator\Schema\SQLiteSchema; use KitLoong\MigrationsGenerator\Schema\SQLSrvSchema; -use KitLoong\MigrationsGenerator\Support\CheckMigrationMethod; class MigrateGenerateCommand extends Command { - use CheckMigrationMethod; - /** * The name and signature of the console command. - * - * @var string */ + // phpcs:ignore protected $signature = 'migrate:generate {tables? : A list of tables or views you wish to generate migrations for separated by a comma: users,posts,comments} {--c|connection= : The database connection to use} @@ -59,72 +55,25 @@ class MigrateGenerateCommand extends Command /** * The console command description. - * - * @var string */ + // phpcs:ignore protected $description = 'Generate migrations from an existing table structure.'; - /** - * @var \KitLoong\MigrationsGenerator\Schema\Schema - */ - protected $schema; - - /** - * @var bool - */ - protected $shouldLog = false; - - /** - * @var int - */ - protected $nextBatchNumber = 0; + protected Schema $schema; - /** - * @var \Illuminate\Database\Migrations\MigrationRepositoryInterface - */ - protected $repository; + protected bool $shouldLog = false; - /** - * @var \KitLoong\MigrationsGenerator\Migration\Squash - */ - protected $squash; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\ForeignKeyMigration - */ - protected $foreignKeyMigration; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\ProcedureMigration - */ - protected $procedureMigration; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\TableMigration - */ - protected $tableMigration; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\ViewMigration - */ - protected $viewMigration; + protected int $nextBatchNumber = 0; public function __construct( - MigrationRepositoryInterface $repository, - Squash $squash, - ForeignKeyMigration $foreignKeyMigration, - ProcedureMigration $procedureMigration, - TableMigration $tableMigration, - ViewMigration $viewMigration + protected MigrationRepositoryInterface $repository, + protected Squash $squash, + protected ForeignKeyMigration $foreignKeyMigration, + protected ProcedureMigration $procedureMigration, + protected TableMigration $tableMigration, + protected ViewMigration $viewMigration, ) { parent::__construct(); - - $this->squash = $squash; - $this->repository = $repository; - $this->foreignKeyMigration = $foreignKeyMigration; - $this->procedureMigration = $procedureMigration; - $this->tableMigration = $tableMigration; - $this->viewMigration = $viewMigration; } /** @@ -159,7 +108,7 @@ public function handle(): void $this->info("\nFinished!\n"); - if (DB::getDriverName() === Driver::SQLITE()->getValue()) { + if (DB::getDriverName() === Driver::SQLITE->value) { $this->warn('SQLite only supports foreign keys upon creation of the table and not when tables are altered.'); $this->warn('See https://www.sqlite.org/omitted.html'); $this->warn('*_add_foreign_keys_* migrations were generated, however will get omitted if migrate to SQLite type database.'); @@ -187,29 +136,29 @@ protected function setup(string $connection): void $setting->setWithHasTable((bool) $this->option('with-has-table')); $setting->setPath( - $this->option('path') ?? Config::get('migrations-generator.migration_target_path') + $this->option('path') ?? Config::get('migrations-generator.migration_target_path'), ); $this->setStubPath($setting); $setting->setDate( - $this->option('date') ? Carbon::parse($this->option('date')) : Carbon::now() + $this->option('date') ? Carbon::parse($this->option('date')) : Carbon::now(), ); $setting->setTableFilename( - $this->option('table-filename') ?? Config::get('migrations-generator.filename_pattern.table') + $this->option('table-filename') ?? Config::get('migrations-generator.filename_pattern.table'), ); $setting->setViewFilename( - $this->option('view-filename') ?? Config::get('migrations-generator.filename_pattern.view') + $this->option('view-filename') ?? Config::get('migrations-generator.filename_pattern.view'), ); $setting->setProcedureFilename( - $this->option('proc-filename') ?? Config::get('migrations-generator.filename_pattern.procedure') + $this->option('proc-filename') ?? Config::get('migrations-generator.filename_pattern.procedure'), ); $setting->setFkFilename( - $this->option('fk-filename') ?? Config::get('migrations-generator.filename_pattern.foreign_key') + $this->option('fk-filename') ?? Config::get('migrations-generator.filename_pattern.foreign_key'), ); } @@ -218,14 +167,10 @@ protected function setup(string $connection): void */ protected function setStubPath(Setting $setting): void { - $defaultStub = Config::get('migrations-generator.migration_anonymous_template_path'); - - if (!$this->hasAnonymousMigration()) { - $defaultStub = Config::get('migrations-generator.migration_template_path'); - } + $defaultStub = Config::get('migrations-generator.migration_template_path'); $setting->setStubPath( - $this->option('template-path') ?? $defaultStub + $this->option('template-path') ?? $defaultStub, ); } @@ -295,13 +240,15 @@ protected function filterAndExcludeAsset(Collection $allAssets): Collection */ protected function getExcludedTables(): array { - $prefix = DB::getTablePrefix(); - $migrationTable = $prefix . Config::get('database.migrations'); + $prefix = DB::getTablePrefix(); + + // https://github.com/laravel/framework/pull/49330 + $migrationTable = $prefix . (Config::get('database.migrations.table') ?? Config::get('database.migrations')); $excludes = [$migrationTable]; $ignore = (string) $this->option('ignore'); - if (!empty($ignore)) { + if ($ignore !== '') { $excludes = array_merge($excludes, explode(',', $ignore)); } @@ -336,7 +283,7 @@ protected function askIfLogMigrationTable(string $defaultConnection): void if ( !$this->confirm( 'Log into current connection: ' . DB::getName() . '? [Y = ' . DB::getName() . ', n = ' . $defaultConnection . ' (default connection)]', - true + true, ) ) { $this->repository->setSource($defaultConnection); @@ -349,7 +296,7 @@ protected function askIfLogMigrationTable(string $defaultConnection): void $this->nextBatchNumber = $this->askInt( 'Next Batch Number is: ' . $this->repository->getNextBatchNumber() . '. We recommend using Batch Number 0 so that it becomes the "first" migration.', - 0 + 0, ); } @@ -500,7 +447,7 @@ protected function generateTables(Collection $tables): void { $tables->each(function (string $table): void { $path = $this->tableMigration->write( - $this->schema->getTable($table) + $this->schema->getTable($table), ); $this->info("Created: $path"); @@ -522,7 +469,7 @@ protected function generateTablesToTemp(Collection $tables): void { $tables->each(function (string $table): void { $this->tableMigration->writeToTemp( - $this->schema->getTable($table) + $this->schema->getTable($table), ); $this->info("Prepared: $table"); @@ -613,7 +560,7 @@ protected function generateProceduresToTemp(): void protected function generateForeignKeys(Collection $tables): void { $tables->each(function (string $table): void { - $foreignKeys = $this->schema->getTableForeignKeys($table); + $foreignKeys = $this->schema->getForeignKeys($table); if (!$foreignKeys->isNotEmpty()) { return; @@ -621,7 +568,7 @@ protected function generateForeignKeys(Collection $tables): void $path = $this->foreignKeyMigration->write( $table, - $foreignKeys + $foreignKeys, ); $this->info("Created: $path"); @@ -642,7 +589,7 @@ protected function generateForeignKeys(Collection $tables): void protected function generateForeignKeysToTemp(Collection $tables): void { $tables->each(function (string $table): void { - $foreignKeys = $this->schema->getTableForeignKeys($table); + $foreignKeys = $this->schema->getForeignKeys($table); if (!$foreignKeys->isNotEmpty()) { return; @@ -650,7 +597,7 @@ protected function generateForeignKeysToTemp(Collection $tables): void $this->foreignKeyMigration->writeToTemp( $table, - $foreignKeys + $foreignKeys, ); $this->info('Prepared: ' . $table); @@ -680,16 +627,16 @@ protected function makeSchema(): Schema } switch ($driver) { - case Driver::MYSQL(): + case Driver::MYSQL->value: return $this->schema = app(MySQLSchema::class); - case Driver::PGSQL(): + case Driver::PGSQL->value: return $this->schema = app(PgSQLSchema::class); - case Driver::SQLITE(): + case Driver::SQLITE->value: return $this->schema = app(SQLiteSchema::class); - case Driver::SQLSRV(): + case Driver::SQLSRV->value: return $this->schema = app(SQLSrvSchema::class); default: diff --git a/src/Migration/Blueprint/DBStatementBlueprint.php b/src/Migration/Blueprint/DBStatementBlueprint.php index 41dda22f..7156f0c6 100644 --- a/src/Migration/Blueprint/DBStatementBlueprint.php +++ b/src/Migration/Blueprint/DBStatementBlueprint.php @@ -2,6 +2,7 @@ namespace KitLoong\MigrationsGenerator\Migration\Blueprint; +use KitLoong\MigrationsGenerator\Enum\Migrations\Method\DBBuilder; use KitLoong\MigrationsGenerator\Migration\Blueprint\Support\MethodStringHelper; use KitLoong\MigrationsGenerator\Migration\Blueprint\Support\Stringable; @@ -23,19 +24,13 @@ class DBStatementBlueprint implements WritableBlueprint use Stringable; use MethodStringHelper; - /** - * @var string - */ - private $sql; - /** * DBStatementBlueprint constructor. * * @param string $sql The SQL statement. */ - public function __construct(string $sql) + public function __construct(private string $sql) { - $this->sql = $sql; } /** @@ -43,7 +38,7 @@ public function __construct(string $sql) */ public function toString(): string { - $method = $this->connection('DB', 'statement'); + $method = $this->connection('DB', DBBuilder::STATEMENT); $query = $this->escapeDoubleQuote($this->sql); return "$method(\"$query\");"; } diff --git a/src/Migration/Blueprint/DBUnpreparedBlueprint.php b/src/Migration/Blueprint/DBUnpreparedBlueprint.php index 5b60cfdb..a6b68020 100644 --- a/src/Migration/Blueprint/DBUnpreparedBlueprint.php +++ b/src/Migration/Blueprint/DBUnpreparedBlueprint.php @@ -2,6 +2,7 @@ namespace KitLoong\MigrationsGenerator\Migration\Blueprint; +use KitLoong\MigrationsGenerator\Enum\Migrations\Method\DBBuilder; use KitLoong\MigrationsGenerator\Migration\Blueprint\Support\MethodStringHelper; use KitLoong\MigrationsGenerator\Migration\Blueprint\Support\Stringable; @@ -23,19 +24,13 @@ class DBUnpreparedBlueprint implements WritableBlueprint use Stringable; use MethodStringHelper; - /** - * @var string - */ - private $sql; - /** * DBStatementBlueprint constructor. * * @param string $sql The SQL statement. */ - public function __construct(string $sql) + public function __construct(private string $sql) { - $this->sql = $sql; } /** @@ -43,7 +38,7 @@ public function __construct(string $sql) */ public function toString(): string { - $method = $this->connection('DB', 'unprepared'); + $method = $this->connection('DB', DBBuilder::UNPREPARED); $query = $this->escapeDoubleQuote($this->sql); return "$method(\"$query\");"; } diff --git a/src/Migration/Blueprint/Method.php b/src/Migration/Blueprint/Method.php index df3d0837..99d9fded 100644 --- a/src/Migration/Blueprint/Method.php +++ b/src/Migration/Blueprint/Method.php @@ -2,31 +2,33 @@ namespace KitLoong\MigrationsGenerator\Migration\Blueprint; +use KitLoong\MigrationsGenerator\Enum\Migrations\Method\MethodName; + class Method { - /** @var string */ - private $name; - - /** @var mixed[] */ - private $values; + /** + * @var mixed[] + */ + private array $values; - /** @var \KitLoong\MigrationsGenerator\Migration\Blueprint\Method[] */ - private $chains; + /** + * @var \KitLoong\MigrationsGenerator\Migration\Blueprint\Method[] + */ + private array $chains; /** * Method constructor. * - * @param string $name Method name. + * @param \KitLoong\MigrationsGenerator\Enum\Migrations\Method\MethodName $name Method name. * @param mixed ...$values Method arguments. */ - public function __construct(string $name, ...$values) + public function __construct(private MethodName $name, mixed ...$values) { - $this->name = $name; $this->values = $values; $this->chains = []; } - public function getName(): string + public function getName(): MethodName { return $this->name; } @@ -42,11 +44,11 @@ public function getValues(): array /** * Chain method. * - * @param string $name Method name. + * @param \KitLoong\MigrationsGenerator\Enum\Migrations\Method\MethodName $name Method name. * @param mixed ...$values Method arguments. * @return $this */ - public function chain(string $name, ...$values): self + public function chain(MethodName $name, mixed ...$values): self { $this->chains[] = new self($name, ...$values); return $this; @@ -55,9 +57,9 @@ public function chain(string $name, ...$values): self /** * Checks if chain name exists. * - * @param string $name Method name. + * @param \KitLoong\MigrationsGenerator\Enum\Migrations\Method\MethodName $name Method name. */ - public function hasChain(string $name): bool + public function hasChain(MethodName $name): bool { foreach ($this->chains as $chain) { if ($chain->getName() === $name) { diff --git a/src/Migration/Blueprint/Property.php b/src/Migration/Blueprint/Property.php index bbd26ba2..d1512878 100644 --- a/src/Migration/Blueprint/Property.php +++ b/src/Migration/Blueprint/Property.php @@ -2,34 +2,23 @@ namespace KitLoong\MigrationsGenerator\Migration\Blueprint; +use KitLoong\MigrationsGenerator\Enum\Migrations\Property\PropertyName; + class Property { - /** @var string */ - private $name; - - /** @var mixed */ - private $value; - /** * Property constructor. - * - * @param mixed $value */ - public function __construct(string $name, $value) + public function __construct(private PropertyName $name, private mixed $value) { - $this->name = $name; - $this->value = $value; } - public function getName(): string + public function getName(): PropertyName { return $this->name; } - /** - * @return mixed - */ - public function getValue() + public function getValue(): mixed { return $this->value; } diff --git a/src/Migration/Blueprint/SchemaBlueprint.php b/src/Migration/Blueprint/SchemaBlueprint.php index ae623d5b..b4345f42 100644 --- a/src/Migration/Blueprint/SchemaBlueprint.php +++ b/src/Migration/Blueprint/SchemaBlueprint.php @@ -39,18 +39,10 @@ class SchemaBlueprint implements WritableBlueprint /** * The table name without prefix. {@see \Illuminate\Support\Facades\DB::getTablePrefix()} - * - * @var string - */ - private $table; - - /** - * @var \KitLoong\MigrationsGenerator\Enum\Migrations\Method\SchemaBuilder */ - private $schemaBuilder; + private string $table; - /** @var \KitLoong\MigrationsGenerator\Migration\Blueprint\TableBlueprint|null */ - private $blueprint; + private ?TableBlueprint $blueprint = null; /** * SchemaBlueprint constructor. @@ -58,11 +50,10 @@ class SchemaBlueprint implements WritableBlueprint * @param string $table Table name. * @param \KitLoong\MigrationsGenerator\Enum\Migrations\Method\SchemaBuilder $schemaBuilder SchemaBuilder name. */ - public function __construct(string $table, SchemaBuilder $schemaBuilder) + public function __construct(string $table, private SchemaBuilder $schemaBuilder) { - $this->table = $this->stripTablePrefix($table); - $this->schemaBuilder = $schemaBuilder; - $this->blueprint = null; + $this->table = $this->stripTablePrefix($table); + $this->blueprint = null; } public function setBlueprint(TableBlueprint $blueprint): void @@ -86,7 +77,7 @@ private function getLines(): array { $schema = $this->connection('Schema', $this->schemaBuilder); - if ($this->schemaBuilder->equals(SchemaBuilder::DROP_IF_EXISTS())) { + if ($this->schemaBuilder === SchemaBuilder::DROP_IF_EXISTS) { return $this->getDropLines($schema); } @@ -96,7 +87,7 @@ private function getLines(): array return $tableLines; } - $schemaHasTable = $this->connection('Schema', SchemaBuilder::HAS_TABLE()); + $schemaHasTable = $this->connection('Schema', SchemaBuilder::HAS_TABLE); $lines = []; @@ -104,7 +95,7 @@ private function getLines(): array foreach ($tableLines as $tableLine) { // Add another tabulation to indent(prettify) blueprint definition. - $lines[] = Space::TAB() . $tableLine; + $lines[] = Space::TAB->value . $tableLine; } $lines[] = "}"; @@ -141,7 +132,7 @@ private function getTableLines(string $schema): array } // Add 1 tabulation to indent(prettify) blueprint definition. - $lines[] = Space::TAB() . $this->blueprint->toString(); + $lines[] = Space::TAB->value . $this->blueprint->toString(); $lines[] = "});"; return $lines; @@ -154,7 +145,7 @@ private function getTableLines(string $schema): array */ private function getIfCondition(string $schemaHasTable, string $tableWithoutPrefix): string { - if ($this->schemaBuilder->equals(SchemaBuilder::TABLE())) { + if ($this->schemaBuilder === SchemaBuilder::TABLE) { return "if ($schemaHasTable('$tableWithoutPrefix')) {"; } diff --git a/src/Migration/Blueprint/Support/MergeTimestamps.php b/src/Migration/Blueprint/Support/MergeTimestamps.php index 4e18b4dc..aca5f272 100644 --- a/src/Migration/Blueprint/Support/MergeTimestamps.php +++ b/src/Migration/Blueprint/Support/MergeTimestamps.php @@ -30,7 +30,7 @@ public function merge(array $lines, bool $tz): array continue; } - if (!$this->checkTimestamps(ColumnName::CREATED_AT(), $line, $tz)) { + if (!$this->checkTimestamps(ColumnName::CREATED_AT, $line, $tz)) { continue; } @@ -46,7 +46,7 @@ public function merge(array $lines, bool $tz): array return $lines; } - if (!$this->checkTimestamps(ColumnName::UPDATED_AT(), $updatedAt, $tz)) { + if (!$this->checkTimestamps(ColumnName::UPDATED_AT, $updatedAt, $tz)) { return $lines; } @@ -76,7 +76,7 @@ private function checkTimestamps(ColumnName $columnName, Method $method, bool $t return false; } - if ($method->getValues()[0] !== $columnName->getValue()) { + if ($method->getValues()[0] !== $columnName->value) { return false; } @@ -84,8 +84,8 @@ private function checkTimestamps(ColumnName $columnName, Method $method, bool $t return false; } - return $method->getChains()[0]->getName() === ColumnModifier::NULLABLE()->getValue() - && empty($method->getChains()[0]->getValues()); + return $method->getChains()[0]->getName() === ColumnModifier::NULLABLE + && count($method->getChains()[0]->getValues()) === 0; } /** @@ -95,11 +95,11 @@ private function checkTimestamps(ColumnName $columnName, Method $method, bool $t */ private function isPossibleTimestampsColumn(Method $method, bool $tz): bool { - if (Driver::SQLSRV()->getValue() === DB::getDriverName()) { - return $method->getName() === $this->sqlSrvTimestampsColumnType($tz)->getValue(); + if (Driver::SQLSRV->value === DB::getDriverName()) { + return $method->getName() === $this->sqlSrvTimestampsColumnType($tz); } - return $method->getName() === $this->timestampsColumnType($tz)->getValue(); + return $method->getName() === $this->timestampsColumnType($tz); } /** @@ -112,10 +112,10 @@ private function isPossibleTimestampsColumn(Method $method, bool $tz): bool private function sqlSrvTimestampsColumnType(bool $tz): ColumnType { if ($tz) { - return ColumnType::DATETIME_TZ(); + return ColumnType::DATETIME_TZ; } - return ColumnType::DATETIME(); + return ColumnType::DATETIME; } /** @@ -127,10 +127,10 @@ private function sqlSrvTimestampsColumnType(bool $tz): ColumnType private function timestampsColumnType(bool $tz): ColumnType { if ($tz) { - return ColumnType::TIMESTAMP_TZ(); + return ColumnType::TIMESTAMP_TZ; } - return ColumnType::TIMESTAMP(); + return ColumnType::TIMESTAMP; } /** @@ -138,7 +138,7 @@ private function timestampsColumnType(bool $tz): ColumnType * * @param bool $tz Is timezone. */ - private function timestamps(bool $tz): string + private function timestamps(bool $tz): ColumnType { if ($tz) { return ColumnType::TIMESTAMPS_TZ; diff --git a/src/Migration/Blueprint/Support/MethodStringHelper.php b/src/Migration/Blueprint/Support/MethodStringHelper.php index 63bf4f9b..50e9c395 100644 --- a/src/Migration/Blueprint/Support/MethodStringHelper.php +++ b/src/Migration/Blueprint/Support/MethodStringHelper.php @@ -3,6 +3,7 @@ namespace KitLoong\MigrationsGenerator\Migration\Blueprint\Support; use Illuminate\Support\Facades\DB; +use KitLoong\MigrationsGenerator\Enum\Migrations\Method\DBBuilder; use KitLoong\MigrationsGenerator\Enum\Migrations\Method\SchemaBuilder; use KitLoong\MigrationsGenerator\Setting; @@ -11,12 +12,12 @@ trait MethodStringHelper /** * Generates method string with `connection` if `--connection=other` option is used. */ - public function connection(string $class, string $method): string + public function connection(string $class, SchemaBuilder|DBBuilder $method): string { if (DB::getName() === app(Setting::class)->getDefaultConnection()) { - return "$class::$method"; + return "$class::$method->value"; } - return "$class::" . SchemaBuilder::CONNECTION() . "('" . DB::getName() . "')->$method"; + return "$class::" . SchemaBuilder::CONNECTION->value . "('" . DB::getName() . "')->$method->value"; } } diff --git a/src/Migration/Blueprint/Support/Stringable.php b/src/Migration/Blueprint/Support/Stringable.php index ca6cccdd..9cfee0c7 100644 --- a/src/Migration/Blueprint/Support/Stringable.php +++ b/src/Migration/Blueprint/Support/Stringable.php @@ -21,12 +21,12 @@ public function flattenLines(array $lines, int $numberOfPrefixTab): string foreach ($lines as $i => $line) { // Skip tab if the line is first line or line break. - if ($i === 0 || $line === Space::LINE_BREAK()->getValue()) { + if ($i === 0 || $line === Space::LINE_BREAK->value) { $content .= $line; continue; } - $content .= Space::LINE_BREAK() . str_repeat(Space::TAB(), $numberOfPrefixTab) . $line; + $content .= Space::LINE_BREAK->value . str_repeat(Space::TAB->value, $numberOfPrefixTab) . $line; } return $content; @@ -34,10 +34,8 @@ public function flattenLines(array $lines, int $numberOfPrefixTab): string /** * Convert $value to printable string. - * - * @param mixed $value */ - public function convertFromAnyTypeToString($value): string + public function convertFromAnyTypeToString(mixed $value): string { switch (gettype($value)) { case 'string': @@ -62,6 +60,10 @@ public function convertFromAnyTypeToString($value): string return 'DB::raw("' . $this->escapeDoubleQuote((string) DB::getQueryGrammar()->getValue($value)) . '")'; } + if ($value instanceof Space) { + return $value->value; + } + return (string) $value; } } @@ -90,8 +92,6 @@ public function escapeDoubleQuote(string $string): string */ public function mapArrayItemsToString(array $list): array { - return (new Collection($list))->map(function ($v) { - return $this->convertFromAnyTypeToString($v); - })->toArray(); + return (new Collection($list))->map(fn ($v) => $this->convertFromAnyTypeToString($v))->toArray(); } } diff --git a/src/Migration/Blueprint/TableBlueprint.php b/src/Migration/Blueprint/TableBlueprint.php index 71e0d062..5cb0e95f 100644 --- a/src/Migration/Blueprint/TableBlueprint.php +++ b/src/Migration/Blueprint/TableBlueprint.php @@ -3,6 +3,8 @@ namespace KitLoong\MigrationsGenerator\Migration\Blueprint; use Illuminate\Support\Collection; +use KitLoong\MigrationsGenerator\Enum\Migrations\Method\MethodName; +use KitLoong\MigrationsGenerator\Enum\Migrations\Property\PropertyName; use KitLoong\MigrationsGenerator\Migration\Blueprint\Support\MergeTimestamps; use KitLoong\MigrationsGenerator\Migration\Blueprint\Support\Stringable; use KitLoong\MigrationsGenerator\Migration\Enum\Space; @@ -33,15 +35,15 @@ class TableBlueprint implements WritableBlueprint use MergeTimestamps; use Stringable; - /** @var \KitLoong\MigrationsGenerator\Migration\Blueprint\Property[]|\KitLoong\MigrationsGenerator\Migration\Blueprint\Method[]|string[] */ - private $lines; + /** + * @var array + */ + private array $lines; /** * By default, generate 3 tabs for each line. - * - * @var int */ - private $numberOfPrefixTab = 3; + private int $numberOfPrefixTab = 3; public function __construct() { @@ -49,10 +51,9 @@ public function __construct() } /** - * @param string $name Property name. - * @param mixed $value + * @param \KitLoong\MigrationsGenerator\Enum\Migrations\Property\PropertyName $name Property name. */ - public function setProperty(string $name, $value): Property + public function setProperty(PropertyName $name, mixed $value): Property { $property = new Property($name, $value); $this->lines[] = $property; @@ -60,10 +61,10 @@ public function setProperty(string $name, $value): Property } /** - * @param string $name Method name. + * @param \KitLoong\MigrationsGenerator\Enum\Migrations\Method\MethodName $name Method name. * @param mixed ...$values Method arguments. */ - public function setMethodByName(string $name, ...$values): Method + public function setMethodByName(MethodName $name, mixed ...$values): Method { $method = new Method($name, ...$values); $this->lines[] = $method; @@ -78,19 +79,11 @@ public function setMethod(Method $method): Method public function setLineBreak(): void { - $this->lines[] = Space::LINE_BREAK(); - } - - /** - * @return \KitLoong\MigrationsGenerator\Migration\Blueprint\Method|\KitLoong\MigrationsGenerator\Migration\Blueprint\Property|string|null - */ - public function removeLastLine() - { - return array_pop($this->lines); + $this->lines[] = Space::LINE_BREAK; } /** - * @return \KitLoong\MigrationsGenerator\Migration\Blueprint\Property[]|\KitLoong\MigrationsGenerator\Migration\Blueprint\Method[]|string[] + * @return array */ public function getLines(): array { @@ -151,7 +144,7 @@ public function toString(): string private function propertyToString(Property $property): string { $v = $this->convertFromAnyTypeToString($property->getValue()); - return '$table->' . $property->getName() . " = $v;"; + return '$table->' . $property->getName()->value . " = $v;"; } /** @@ -181,9 +174,7 @@ private function methodToString(Method $method): string */ private function flattenMethod(Method $method): string { - $v = (new Collection($method->getValues()))->map(function ($v) { - return $this->convertFromAnyTypeToString($v); - })->implode(', '); - return $method->getName() . "($v)"; + $v = (new Collection($method->getValues()))->map(fn ($v) => $this->convertFromAnyTypeToString($v))->implode(', '); + return $method->getName()->value . "($v)"; } } diff --git a/src/Migration/Blueprint/WritableBlueprint.php b/src/Migration/Blueprint/WritableBlueprint.php index 92a8f5c3..21b2624e 100644 --- a/src/Migration/Blueprint/WritableBlueprint.php +++ b/src/Migration/Blueprint/WritableBlueprint.php @@ -4,5 +4,8 @@ interface WritableBlueprint { + /** + * Convert the object to its string representation. + */ public function toString(): string; } diff --git a/src/Migration/Enum/MigrationFileType.php b/src/Migration/Enum/MigrationFileType.php index 50bec1aa..bd579d13 100644 --- a/src/Migration/Enum/MigrationFileType.php +++ b/src/Migration/Enum/MigrationFileType.php @@ -2,19 +2,10 @@ namespace KitLoong\MigrationsGenerator\Migration\Enum; -use MyCLabs\Enum\Enum; - -/** - * @method static self FOREIGN_KEY() - * @method static self TABLE() - * @method static self VIEW() - * @method static self PROCEDURE() - * @extends \MyCLabs\Enum\Enum - */ -final class MigrationFileType extends Enum +enum MigrationFileType: string { - private const FOREIGN_KEY = 'foreign_key'; - private const TABLE = 'table'; - private const VIEW = 'view'; - private const PROCEDURE = 'procedure'; + case FOREIGN_KEY = 'foreign_key'; + case TABLE = 'table'; + case VIEW = 'view'; + case PROCEDURE = 'procedure'; } diff --git a/src/Migration/Enum/Space.php b/src/Migration/Enum/Space.php index c7d87088..acf4e713 100644 --- a/src/Migration/Enum/Space.php +++ b/src/Migration/Enum/Space.php @@ -2,15 +2,8 @@ namespace KitLoong\MigrationsGenerator\Migration\Enum; -use MyCLabs\Enum\Enum; - -/** - * @method static self LINE_BREAK() - * @method static self TAB() - * @extends \MyCLabs\Enum\Enum - */ -final class Space extends Enum +enum Space: string { - private const LINE_BREAK = PHP_EOL; - private const TAB = ' '; // 4 spaces tab + case LINE_BREAK = "\n"; + case TAB = ' '; // 4 spaces tab } diff --git a/src/Migration/ForeignKeyMigration.php b/src/Migration/ForeignKeyMigration.php index 93b92dab..95d86843 100644 --- a/src/Migration/ForeignKeyMigration.php +++ b/src/Migration/ForeignKeyMigration.php @@ -18,43 +18,13 @@ class ForeignKeyMigration { use TableName; - /** - * @var \KitLoong\MigrationsGenerator\Migration\Generator\ForeignKeyGenerator - */ - private $foreignKeyGenerator; - - /** - * @var \KitLoong\MigrationsGenerator\Support\MigrationNameHelper - */ - private $migrationNameHelper; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\Writer\MigrationWriter - */ - private $migrationWriter; - - /** - * @var \KitLoong\MigrationsGenerator\Setting - */ - private $setting; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\Writer\SquashWriter - */ - private $squashWriter; - public function __construct( - ForeignKeyGenerator $foreignKeyGenerator, - MigrationNameHelper $migrationNameHelper, - MigrationWriter $migrationWriter, - Setting $setting, - SquashWriter $squashWriter + private ForeignKeyGenerator $foreignKeyGenerator, + private MigrationNameHelper $migrationNameHelper, + private MigrationWriter $migrationWriter, + private Setting $setting, + private SquashWriter $squashWriter, ) { - $this->foreignKeyGenerator = $foreignKeyGenerator; - $this->migrationNameHelper = $migrationNameHelper; - $this->migrationWriter = $migrationWriter; - $this->setting = $setting; - $this->squashWriter = $squashWriter; } /** @@ -74,7 +44,7 @@ public function write(string $table, Collection $foreignKeys): string $this->makeMigrationClassName($table), new Collection([$up]), new Collection([$down]), - MigrationFileType::FOREIGN_KEY() + MigrationFileType::FOREIGN_KEY, ); return $path; @@ -143,7 +113,7 @@ private function makeMigrationClassName(string $table): string $withoutPrefix = $this->stripTablePrefix($table); return $this->migrationNameHelper->makeClassName( $this->setting->getFkFilename(), - $withoutPrefix + $withoutPrefix, ); } @@ -158,7 +128,7 @@ private function makeMigrationPath(string $table): string return $this->migrationNameHelper->makeFilename( $this->setting->getFkFilename(), $this->setting->getDateForMigrationFilename(), - $withoutPrefix + $withoutPrefix, ); } @@ -166,7 +136,7 @@ private function getSchemaBlueprint(string $table): SchemaBlueprint { return new SchemaBlueprint( $table, - SchemaBuilder::TABLE() + SchemaBuilder::TABLE, ); } } diff --git a/src/Migration/Generator/ColumnGenerator.php b/src/Migration/Generator/ColumnGenerator.php index 254cd438..518fcb44 100644 --- a/src/Migration/Generator/ColumnGenerator.php +++ b/src/Migration/Generator/ColumnGenerator.php @@ -18,64 +18,16 @@ class ColumnGenerator { - /** - * @var \KitLoong\MigrationsGenerator\Migration\Generator\Modifiers\CharsetModifier - */ - private $charsetModifier; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\Generator\Modifiers\CollationModifier - */ - private $collationModifier; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\Generator\Modifiers\CommentModifier - */ - private $commentModifier; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\Generator\Modifiers\DefaultModifier - */ - private $defaultModifier; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\Generator\Modifiers\IndexModifier - */ - private $indexModifier; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\Generator\Modifiers\NullableModifier - */ - private $nullableModifier; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\Generator\Modifiers\StoredAsModifier - */ - private $storedAsModifier; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\Generator\Modifiers\VirtualAsModifier - */ - private $virtualAsModifier; - public function __construct( - CharsetModifier $charsetModifier, - CollationModifier $collationModifier, - CommentModifier $commentModifier, - DefaultModifier $defaultModifier, - IndexModifier $indexModifier, - NullableModifier $nullableModifier, - StoredAsModifier $storedAsModifier, - VirtualAsModifier $virtualAsModifier + private CharsetModifier $charsetModifier, + private CollationModifier $collationModifier, + private CommentModifier $commentModifier, + private DefaultModifier $defaultModifier, + private IndexModifier $indexModifier, + private NullableModifier $nullableModifier, + private StoredAsModifier $storedAsModifier, + private VirtualAsModifier $virtualAsModifier, ) { - $this->charsetModifier = $charsetModifier; - $this->collationModifier = $collationModifier; - $this->commentModifier = $commentModifier; - $this->defaultModifier = $defaultModifier; - $this->indexModifier = $indexModifier; - $this->nullableModifier = $nullableModifier; - $this->storedAsModifier = $storedAsModifier; - $this->virtualAsModifier = $virtualAsModifier; } /** @@ -100,7 +52,7 @@ public function generate(Table $table, Column $column, Collection $chainableInde private function createMethodFromColumn(Table $table, Column $column): Method { /** @var \KitLoong\MigrationsGenerator\Migration\Generator\Columns\ColumnTypeGenerator $generator */ - $generator = app(ColumnType::class . '\\' . $column->getType()->getKey()); + $generator = app(ColumnType::class . '\\' . $column->getType()->name); return $generator->generate($table, $column); } } diff --git a/src/Migration/Generator/Columns/BooleanColumn.php b/src/Migration/Generator/Columns/BooleanColumn.php index 91293b1f..2dc7ef9e 100644 --- a/src/Migration/Generator/Columns/BooleanColumn.php +++ b/src/Migration/Generator/Columns/BooleanColumn.php @@ -17,7 +17,7 @@ public function generate(Table $table, Column $column): Method $method = new Method($column->getType(), $column->getName()); if ($column->isUnsigned()) { - $method->chain(ColumnModifier::UNSIGNED()); + $method->chain(ColumnModifier::UNSIGNED); } return $method; diff --git a/src/Migration/Generator/Columns/DatetimeColumn.php b/src/Migration/Generator/Columns/DatetimeColumn.php index 59ea17dc..275ace1a 100644 --- a/src/Migration/Generator/Columns/DatetimeColumn.php +++ b/src/Migration/Generator/Columns/DatetimeColumn.php @@ -6,12 +6,9 @@ use KitLoong\MigrationsGenerator\Migration\Blueprint\Method; use KitLoong\MigrationsGenerator\Schema\Models\Column; use KitLoong\MigrationsGenerator\Schema\Models\Table; -use KitLoong\MigrationsGenerator\Support\CheckMigrationMethod; class DatetimeColumn implements ColumnTypeGenerator { - use CheckMigrationMethod; - private const DEFAULT_PRECISION = 0; /** @@ -21,8 +18,8 @@ public function generate(Table $table, Column $column): Method { $method = $this->makeMethod($column); - if ($column->isOnUpdateCurrentTimestamp() && $this->hasUseCurrentOnUpdate()) { - $method->chain(ColumnModifier::USE_CURRENT_ON_UPDATE()); + if ($column->isOnUpdateCurrentTimestamp()) { + $method->chain(ColumnModifier::USE_CURRENT_ON_UPDATE); } return $method; diff --git a/src/Migration/Generator/Columns/DecimalColumn.php b/src/Migration/Generator/Columns/DecimalColumn.php index d170f889..aa4de31c 100644 --- a/src/Migration/Generator/Columns/DecimalColumn.php +++ b/src/Migration/Generator/Columns/DecimalColumn.php @@ -2,6 +2,7 @@ namespace KitLoong\MigrationsGenerator\Migration\Generator\Columns; +use KitLoong\MigrationsGenerator\Enum\Migrations\Method\ColumnModifier; use KitLoong\MigrationsGenerator\Migration\Blueprint\Method; use KitLoong\MigrationsGenerator\Schema\Models\Column; use KitLoong\MigrationsGenerator\Schema\Models\Table; @@ -18,7 +19,14 @@ class DecimalColumn implements ColumnTypeGenerator public function generate(Table $table, Column $column): Method { $precisions = $this->getDecimalPrecisions($column->getPrecision(), $column->getScale()); - return new Method($column->getType(), $column->getName(), ...$precisions); + + $method = new Method($column->getType(), $column->getName(), ...$precisions); + + if ($column->isUnsigned()) { + $method->chain(ColumnModifier::UNSIGNED); + } + + return $method; } /** @@ -27,8 +35,12 @@ public function generate(Table $table, Column $column): Method * * @return int[] "[]|[precision]|[precision, scale]" */ - private function getDecimalPrecisions(int $precision, int $scale): array + private function getDecimalPrecisions(?int $precision, int $scale): array { + if ($precision === null) { + return []; + } + if ($precision === self::DEFAULT_PRECISION && $scale === self::DEFAULT_SCALE) { return []; } diff --git a/src/Migration/Generator/Columns/DoubleColumn.php b/src/Migration/Generator/Columns/DoubleColumn.php index 2ee09c74..9ec49110 100644 --- a/src/Migration/Generator/Columns/DoubleColumn.php +++ b/src/Migration/Generator/Columns/DoubleColumn.php @@ -6,9 +6,12 @@ use KitLoong\MigrationsGenerator\Migration\Blueprint\Method; use KitLoong\MigrationsGenerator\Schema\Models\Column; use KitLoong\MigrationsGenerator\Schema\Models\Table; +use KitLoong\MigrationsGenerator\Support\CheckLaravelVersion; class DoubleColumn implements ColumnTypeGenerator { + use CheckLaravelVersion; + private const EMPTY_PRECISION = 0; private const EMPTY_SCALE = 0; @@ -22,7 +25,7 @@ public function generate(Table $table, Column $column): Method $method = new Method($column->getType(), $column->getName(), ...$precisions); if ($column->isUnsigned()) { - $method->chain(ColumnModifier::UNSIGNED()); + $method->chain(ColumnModifier::UNSIGNED); } return $method; @@ -36,6 +39,10 @@ public function generate(Table $table, Column $column): Method */ private function getPrecisions(Column $column): array { + if ($this->atLeastLaravel11()) { + return []; + } + if ( $column->getPrecision() === self::EMPTY_PRECISION && $column->getScale() === self::EMPTY_SCALE diff --git a/src/Migration/Generator/Columns/FloatColumn.php b/src/Migration/Generator/Columns/FloatColumn.php index c925d440..592505ff 100644 --- a/src/Migration/Generator/Columns/FloatColumn.php +++ b/src/Migration/Generator/Columns/FloatColumn.php @@ -6,13 +6,18 @@ use KitLoong\MigrationsGenerator\Migration\Blueprint\Method; use KitLoong\MigrationsGenerator\Schema\Models\Column; use KitLoong\MigrationsGenerator\Schema\Models\Table; +use KitLoong\MigrationsGenerator\Support\CheckLaravelVersion; class FloatColumn implements ColumnTypeGenerator { - // Framework set (8, 2) as default precision. + use CheckLaravelVersion; + + // Laravel version before 11 set (8, 2) as default precision. private const DEFAULT_PRECISION = 8; private const DEFAULT_SCALE = 2; + private const DEFAULT_PRECISION_V11 = 53; + /** * @inheritDoc */ @@ -23,7 +28,7 @@ public function generate(Table $table, Column $column): Method $method = new Method($column->getType(), $column->getName(), ...$precisions); if ($column->isUnsigned()) { - $method->chain(ColumnModifier::UNSIGNED()); + $method->chain(ColumnModifier::UNSIGNED); } return $method; @@ -37,6 +42,14 @@ public function generate(Table $table, Column $column): Method */ private function getPrecisions(Column $column): array { + if ($this->atLeastLaravel11()) { + if ($column->getPrecision() === null || $column->getPrecision() === self::DEFAULT_PRECISION_V11) { + return []; + } + + return [$column->getPrecision()]; + } + if ( $column->getPrecision() === self::DEFAULT_PRECISION && $column->getScale() === self::DEFAULT_SCALE diff --git a/src/Migration/Generator/Columns/SoftDeleteColumn.php b/src/Migration/Generator/Columns/SoftDeleteColumn.php index edc963b1..95121ae4 100644 --- a/src/Migration/Generator/Columns/SoftDeleteColumn.php +++ b/src/Migration/Generator/Columns/SoftDeleteColumn.php @@ -19,7 +19,7 @@ public function generate(Table $table, Column $column): Method $method = $this->makeMethod($column); if ($column->isOnUpdateCurrentTimestamp()) { - $method->chain(ColumnModifier::USE_CURRENT_ON_UPDATE()); + $method->chain(ColumnModifier::USE_CURRENT_ON_UPDATE); } return $method; diff --git a/src/Migration/Generator/Columns/SpatialColumn.php b/src/Migration/Generator/Columns/SpatialColumn.php new file mode 100644 index 00000000..beec1fb2 --- /dev/null +++ b/src/Migration/Generator/Columns/SpatialColumn.php @@ -0,0 +1,77 @@ +hasGeography()) { + if ($column->getType() === ColumnType::GEOGRAPHY) { + return new Method(ColumnType::GEOMETRY, $column->getName()); + } + + return new Method($column->getType(), $column->getName()); + } + + $methodValues = [$column->getName()]; + + if ($column->getSpatialSubType() !== null) { + $methodValues[] = $column->getSpatialSubType(); + } + + $srID = $this->getSrIDArg($column); + + if ($srID !== null) { + if (count($methodValues) === 1) { + $methodValues[] = null; + } + + $methodValues[] = $srID; + } + + return new Method($column->getType(), ...$methodValues); + } + + /** + * Get the SRID argument for spatial column. + * Return null if the SRID is null or it matches the default SRID. + */ + private function getSrIDArg(Column $column): ?int + { + if ($column->getSpatialSrID() === null) { + return null; + } + + switch ($column->getType()) { + case ColumnType::GEOMETRY: + if ($column->getSpatialSrID() !== self::GEOMETRY_DEFAULT_SRID) { + return $column->getSpatialSrID(); + } + + break; + + default: + if ($column->getSpatialSrID() !== self::GEOGRAPHY_DEFAULT_SRID) { + return $column->getSpatialSrID(); + } + } + + return null; + } +} diff --git a/src/Migration/Generator/ForeignKeyGenerator.php b/src/Migration/Generator/ForeignKeyGenerator.php index 6f7db098..d83b78e8 100644 --- a/src/Migration/Generator/ForeignKeyGenerator.php +++ b/src/Migration/Generator/ForeignKeyGenerator.php @@ -19,15 +19,15 @@ public function generate(ForeignKey $foreignKey): Method { $method = $this->makeMethod($foreignKey); - $method->chain(Foreign::REFERENCES(), $foreignKey->getForeignColumns()) - ->chain(Foreign::ON(), $this->stripTablePrefix($foreignKey->getForeignTableName())); + $method->chain(Foreign::REFERENCES, $foreignKey->getForeignColumns()) + ->chain(Foreign::ON, $this->stripTablePrefix($foreignKey->getForeignTableName())); if ($foreignKey->getOnUpdate() !== null) { - $method->chain(Foreign::ON_UPDATE(), $foreignKey->getOnUpdate()); + $method->chain(Foreign::ON_UPDATE, $foreignKey->getOnUpdate()); } if ($foreignKey->getOnDelete() !== null) { - $method->chain(Foreign::ON_DELETE(), $foreignKey->getOnDelete()); + $method->chain(Foreign::ON_DELETE, $foreignKey->getOnDelete()); } return $method; @@ -39,10 +39,22 @@ public function generate(ForeignKey $foreignKey): Method public function generateDrop(ForeignKey $foreignKey): Method { if ($this->shouldSkipName($foreignKey)) { - return new Method(Foreign::DROP_FOREIGN(), $this->makeLaravelForeignKeyName($foreignKey)); + return new Method(Foreign::DROP_FOREIGN, $this->makeLaravelForeignKeyName($foreignKey)); } - return new Method(Foreign::DROP_FOREIGN(), $foreignKey->getName()); + return new Method(Foreign::DROP_FOREIGN, $foreignKey->getName()); + } + + /** + * Create a new Method with foreignKey and columns. + */ + public function makeMethod(ForeignKey $foreignKey): Method + { + if ($this->shouldSkipName($foreignKey)) { + return new Method(Foreign::FOREIGN, $foreignKey->getLocalColumns()); + } + + return new Method(Foreign::FOREIGN, $foreignKey->getLocalColumns(), $foreignKey->getName()); } /** @@ -63,17 +75,8 @@ private function shouldSkipName(ForeignKey $foreignKey): bool private function makeLaravelForeignKeyName(ForeignKey $foreignKey): string { $name = strtolower( - $foreignKey->getTableName() . '_' . implode('_', $foreignKey->getLocalColumns()) . '_foreign' + $foreignKey->getTableName() . '_' . implode('_', $foreignKey->getLocalColumns()) . '_foreign', ); return str_replace(['-', '.'], '_', $name); } - - public function makeMethod(ForeignKey $foreignKey): Method - { - if ($this->shouldSkipName($foreignKey)) { - return new Method(Foreign::FOREIGN(), $foreignKey->getLocalColumns()); - } - - return new Method(Foreign::FOREIGN(), $foreignKey->getLocalColumns(), $foreignKey->getName()); - } } diff --git a/src/Migration/Generator/IndexGenerator.php b/src/Migration/Generator/IndexGenerator.php index 63873ac9..2504dc3b 100644 --- a/src/Migration/Generator/IndexGenerator.php +++ b/src/Migration/Generator/IndexGenerator.php @@ -11,14 +11,8 @@ class IndexGenerator { - /** - * @var \KitLoong\MigrationsGenerator\Support\IndexNameHelper - */ - private $indexNameHelper; - - public function __construct(IndexNameHelper $indexNameHelper) + public function __construct(private IndexNameHelper $indexNameHelper) { - $this->indexNameHelper = $indexNameHelper; } public function generate(Table $table, Index $index): Method @@ -61,7 +55,7 @@ public function getChainableIndexes(string $name, Collection $indexes): Collecti // Check if index is using framework default naming. // The older version "spatialIndex" modifier does not accept index name as argument. if ( - $index->getType()->equals(IndexType::SPATIAL_INDEX()) + $index->getType() === IndexType::SPATIAL_INDEX && !$this->indexNameHelper->shouldSkipName($name, $index) ) { return $carry; @@ -69,7 +63,7 @@ public function getChainableIndexes(string $name, Collection $indexes): Collecti // If name is not empty, primary name should be set explicitly. if ( - $index->getType()->equals(IndexType::PRIMARY()) + $index->getType() === IndexType::PRIMARY && $index->getName() !== '' ) { return $carry; @@ -99,13 +93,9 @@ public function getChainableIndexes(string $name, Collection $indexes): Collecti */ public function getNotChainableIndexes(Collection $indexes, Collection $chainableIndexes): Collection { - $chainableNames = $chainableIndexes->map(function (Index $index) { - return $index->getName(); - }); + $chainableNames = $chainableIndexes->map(static fn (Index $index) => $index->getName()); - return $indexes->filter(function (Index $index) use ($chainableNames) { - return !$chainableNames->contains($index->getName()); - }); + return $indexes->filter(static fn (Index $index) => !$chainableNames->contains($index->getName())); } /** diff --git a/src/Migration/Generator/Modifiers/CharsetModifier.php b/src/Migration/Generator/Modifiers/CharsetModifier.php index bd8961e3..e008d2f8 100644 --- a/src/Migration/Generator/Modifiers/CharsetModifier.php +++ b/src/Migration/Generator/Modifiers/CharsetModifier.php @@ -11,20 +11,14 @@ class CharsetModifier implements Modifier { - /** - * @var \KitLoong\MigrationsGenerator\Setting - */ - private $setting; - - public function __construct(Setting $setting) + public function __construct(private Setting $setting) { - $this->setting = $setting; } /** * @inheritDoc */ - public function chain(Method $method, Table $table, Column $column, ...$args): Method + public function chain(Method $method, Table $table, Column $column, mixed ...$args): Method { if (!$this->setting->isUseDBCollation()) { return $method; @@ -37,7 +31,7 @@ public function chain(Method $method, Table $table, Column $column, ...$args): M $charset = $column->getCharset(); if ($charset !== null && $charset !== $tableCharset) { - $method->chain(ColumnModifier::CHARSET(), $charset); + $method->chain(ColumnModifier::CHARSET, $charset); } return $method; diff --git a/src/Migration/Generator/Modifiers/CollationModifier.php b/src/Migration/Generator/Modifiers/CollationModifier.php index 6d39a35e..b7b5bf50 100644 --- a/src/Migration/Generator/Modifiers/CollationModifier.php +++ b/src/Migration/Generator/Modifiers/CollationModifier.php @@ -10,20 +10,14 @@ class CollationModifier implements Modifier { - /** - * @var \KitLoong\MigrationsGenerator\Setting - */ - private $setting; - - public function __construct(Setting $setting) + public function __construct(private Setting $setting) { - $this->setting = $setting; } /** * @inheritDoc */ - public function chain(Method $method, Table $table, Column $column, ...$args): Method + public function chain(Method $method, Table $table, Column $column, mixed ...$args): Method { if (!$this->setting->isUseDBCollation()) { return $method; @@ -35,7 +29,7 @@ public function chain(Method $method, Table $table, Column $column, ...$args): M $collation = $column->getCollation(); if ($collation !== null && $collation !== $tableCollation) { - $method->chain(ColumnModifier::COLLATION(), $collation); + $method->chain(ColumnModifier::COLLATION, $collation); } return $method; diff --git a/src/Migration/Generator/Modifiers/CommentModifier.php b/src/Migration/Generator/Modifiers/CommentModifier.php index 33cec84b..65a393d2 100644 --- a/src/Migration/Generator/Modifiers/CommentModifier.php +++ b/src/Migration/Generator/Modifiers/CommentModifier.php @@ -12,10 +12,10 @@ class CommentModifier implements Modifier /** * @inheritDoc */ - public function chain(Method $method, Table $table, Column $column, ...$args): Method + public function chain(Method $method, Table $table, Column $column, mixed ...$args): Method { if ($column->getComment() !== null) { - $method->chain(ColumnModifier::COMMENT(), $column->getComment()); + $method->chain(ColumnModifier::COMMENT, $column->getComment()); } return $method; diff --git a/src/Migration/Generator/Modifiers/DefaultModifier.php b/src/Migration/Generator/Modifiers/DefaultModifier.php index 916d8a00..22c48ad4 100644 --- a/src/Migration/Generator/Modifiers/DefaultModifier.php +++ b/src/Migration/Generator/Modifiers/DefaultModifier.php @@ -8,85 +8,73 @@ use KitLoong\MigrationsGenerator\Migration\Blueprint\Method; use KitLoong\MigrationsGenerator\Schema\Models\Column; use KitLoong\MigrationsGenerator\Schema\Models\Table; -use KitLoong\MigrationsGenerator\Support\CheckMigrationMethod; class DefaultModifier implements Modifier { - use CheckMigrationMethod; - /** * @var array */ - private $chainerMap = []; + private array $chainerMap = []; public function __construct() { foreach ( [ - ColumnType::BIG_INTEGER(), - ColumnType::INTEGER(), - ColumnType::MEDIUM_INTEGER(), - ColumnType::SMALL_INTEGER(), - ColumnType::TINY_INTEGER(), - ColumnType::UNSIGNED_BIG_INTEGER(), - ColumnType::UNSIGNED_INTEGER(), - ColumnType::UNSIGNED_MEDIUM_INTEGER(), - ColumnType::UNSIGNED_SMALL_INTEGER(), - ColumnType::UNSIGNED_TINY_INTEGER(), + ColumnType::BIG_INTEGER, + ColumnType::INTEGER, + ColumnType::MEDIUM_INTEGER, + ColumnType::SMALL_INTEGER, + ColumnType::TINY_INTEGER, + ColumnType::UNSIGNED_BIG_INTEGER, + ColumnType::UNSIGNED_INTEGER, + ColumnType::UNSIGNED_MEDIUM_INTEGER, + ColumnType::UNSIGNED_SMALL_INTEGER, + ColumnType::UNSIGNED_TINY_INTEGER, ] as $columnType ) { - $this->chainerMap[$columnType->getValue()] = function (Method $method, Column $column): Method { - return call_user_func([$this, 'chainDefaultForInteger'], $method, $column); - }; + $this->chainerMap[$columnType->value] = fn (Method $method, Column $column): Method => call_user_func([$this, 'chainDefaultForInteger'], $method, $column); } foreach ( [ - ColumnType::DECIMAL(), - ColumnType::UNSIGNED_DECIMAL(), - ColumnType::FLOAT(), - ColumnType::DOUBLE(), + ColumnType::DECIMAL, + ColumnType::FLOAT, + ColumnType::DOUBLE, ] as $columnType ) { - $this->chainerMap[$columnType->getValue()] = function (Method $method, Column $column): Method { - return call_user_func([$this, 'chainDefaultForDecimal'], $method, $column); - }; + $this->chainerMap[$columnType->value] = fn (Method $method, Column $column): Method => call_user_func([$this, 'chainDefaultForDecimal'], $method, $column); } - $this->chainerMap[ColumnType::BOOLEAN()->getValue()] = function (Method $method, Column $column): Method { - return call_user_func([$this, 'chainDefaultForBoolean'], $method, $column); - }; + $this->chainerMap[ColumnType::BOOLEAN->value] = fn (Method $method, Column $column): Method => call_user_func([$this, 'chainDefaultForBoolean'], $method, $column); foreach ( [ - ColumnType::SOFT_DELETES(), - ColumnType::SOFT_DELETES_TZ(), - ColumnType::DATE(), - ColumnType::DATETIME(), - ColumnType::DATETIME_TZ(), - ColumnType::TIME(), - ColumnType::TIME_TZ(), - ColumnType::TIMESTAMP(), - ColumnType::TIMESTAMP_TZ(), + ColumnType::SOFT_DELETES, + ColumnType::SOFT_DELETES_TZ, + ColumnType::DATE, + ColumnType::DATETIME, + ColumnType::DATETIME_TZ, + ColumnType::TIME, + ColumnType::TIME_TZ, + ColumnType::TIMESTAMP, + ColumnType::TIMESTAMP_TZ, ] as $columnType ) { - $this->chainerMap[$columnType->getValue()] = function (Method $method, Column $column): Method { - return call_user_func([$this, 'chainDefaultForDatetime'], $method, $column); - }; + $this->chainerMap[$columnType->value] = fn (Method $method, Column $column): Method => call_user_func([$this, 'chainDefaultForDatetime'], $method, $column); } } /** * @inheritDoc */ - public function chain(Method $method, Table $table, Column $column, ...$args): Method + public function chain(Method $method, Table $table, Column $column, mixed ...$args): Method { if ($column->getDefault() === null) { return $method; } - if (isset($this->chainerMap[$column->getType()->getValue()])) { - return $this->chainerMap[$column->getType()->getValue()]($method, $column); + if (isset($this->chainerMap[$column->getType()->value])) { + return $this->chainerMap[$column->getType()->value]($method, $column); } return $this->chainDefaultForString($method, $column); @@ -97,7 +85,7 @@ public function chain(Method $method, Table $table, Column $column, ...$args): M */ protected function chainDefaultForInteger(Method $method, Column $column): Method { - $method->chain(ColumnModifier::DEFAULT(), (int) $column->getDefault()); + $method->chain(ColumnModifier::DEFAULT, (int) $column->getDefault()); return $method; } @@ -106,7 +94,7 @@ protected function chainDefaultForInteger(Method $method, Column $column): Metho */ protected function chainDefaultForDecimal(Method $method, Column $column): Method { - $method->chain(ColumnModifier::DEFAULT(), (float) $column->getDefault()); + $method->chain(ColumnModifier::DEFAULT, (float) $column->getDefault()); return $method; } @@ -115,7 +103,12 @@ protected function chainDefaultForDecimal(Method $method, Column $column): Metho */ protected function chainDefaultForBoolean(Method $method, Column $column): Method { - $method->chain(ColumnModifier::DEFAULT(), (int) $column->getDefault() === 1); + $default = match ($column->getDefault()) { + 'true', '1' => true, + default => false, + }; + + $method->chain(ColumnModifier::DEFAULT, $default); return $method; } @@ -125,23 +118,8 @@ protected function chainDefaultForBoolean(Method $method, Column $column): Metho protected function chainDefaultForDatetime(Method $method, Column $column): Method { switch ($column->getDefault()) { - case 'now()': case 'CURRENT_TIMESTAMP': - // By default, `timestamp()` and `timestampTz()` will generate column as: - // `DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP`, migration translated to `useCurrent()` and `useCurrentOnUpdate()`. - // Due to old Laravel version does not have `useCurrentOnUpdate()`, - // if column has both `DEFAULT CURRENT_TIMESTAMP` and `ON UPDATE CURRENT_TIMESTAMP`, - // we need to generate column without chain. - // New laravel is okay to chain `useCurrent()` and `useCurrentOnUpdate()`. - if (!$this->hasUseCurrentOnUpdate()) { - if (!$column->isOnUpdateCurrentTimestamp()) { - $method->chain(ColumnModifier::USE_CURRENT()); - } - - break; - } - - $method->chain(ColumnModifier::USE_CURRENT()); + $method->chain(ColumnModifier::USE_CURRENT); break; default: @@ -153,7 +131,7 @@ protected function chainDefaultForDatetime(Method $method, Column $column): Meth $default = DB::raw($default); } - $method->chain(ColumnModifier::DEFAULT(), $default); + $method->chain(ColumnModifier::DEFAULT, $default); } return $method; @@ -164,7 +142,7 @@ protected function chainDefaultForDatetime(Method $method, Column $column): Meth */ protected function chainDefaultForString(Method $method, Column $column): Method { - $method->chain(ColumnModifier::DEFAULT(), $column->getDefault()); + $method->chain(ColumnModifier::DEFAULT, $column->getDefault()); return $method; } diff --git a/src/Migration/Generator/Modifiers/IndexModifier.php b/src/Migration/Generator/Modifiers/IndexModifier.php index b6ebab76..e0074159 100644 --- a/src/Migration/Generator/Modifiers/IndexModifier.php +++ b/src/Migration/Generator/Modifiers/IndexModifier.php @@ -10,20 +10,14 @@ class IndexModifier implements Modifier { - /** - * @var \KitLoong\MigrationsGenerator\Support\IndexNameHelper - */ - private $indexNameHelper; - - public function __construct(IndexNameHelper $indexNameHelper) + public function __construct(private IndexNameHelper $indexNameHelper) { - $this->indexNameHelper = $indexNameHelper; } /** * @inheritDoc */ - public function chain(Method $method, Table $table, Column $column, ...$args): Method + public function chain(Method $method, Table $table, Column $column, mixed ...$args): Method { /** @var \Illuminate\Support\Collection $chainableIndexes Key is column name. */ $chainableIndexes = $args[0]; @@ -36,7 +30,7 @@ public function chain(Method $method, Table $table, Column $column, ...$args): M $index = $chainableIndexes->get($column->getName()); // "increment" will add primary key by default. No need explicitly declare "primary" index here. - if ($column->isAutoincrement() && $index->getType()->equals(IndexType::PRIMARY())) { + if ($column->isAutoincrement() && $index->getType() === IndexType::PRIMARY) { return $method; } @@ -57,8 +51,8 @@ public function chain(Method $method, Table $table, Column $column, ...$args): M */ private function adjustIndexType(IndexType $indexType): IndexType { - if ($indexType->equals(IndexType::FULLTEXT())) { - return IndexType::FULLTEXT_CHAIN(); + if ($indexType === IndexType::FULLTEXT) { + return IndexType::FULLTEXT_CHAIN; } return $indexType; diff --git a/src/Migration/Generator/Modifiers/Modifier.php b/src/Migration/Generator/Modifiers/Modifier.php index 2302a368..7b332919 100644 --- a/src/Migration/Generator/Modifiers/Modifier.php +++ b/src/Migration/Generator/Modifiers/Modifier.php @@ -10,8 +10,6 @@ interface Modifier { /** * Chain column modifier. - * - * @param mixed ...$args */ - public function chain(Method $method, Table $table, Column $column, ...$args): Method; + public function chain(Method $method, Table $table, Column $column, mixed ...$args): Method; } diff --git a/src/Migration/Generator/Modifiers/NullableModifier.php b/src/Migration/Generator/Modifiers/NullableModifier.php index 676b6c73..75cf70c1 100644 --- a/src/Migration/Generator/Modifiers/NullableModifier.php +++ b/src/Migration/Generator/Modifiers/NullableModifier.php @@ -13,18 +13,18 @@ class NullableModifier implements Modifier /** * @inheritDoc */ - public function chain(Method $method, Table $table, Column $column, ...$args): Method + public function chain(Method $method, Table $table, Column $column, mixed ...$args): Method { if ($column->isNotNull()) { if ($this->shouldAddNotNullModifier($column->getType())) { - $method->chain(ColumnModifier::NULLABLE(), false); + $method->chain(ColumnModifier::NULLABLE, false); } return $method; } if ($this->shouldAddNullableModifier($column->getType())) { - $method->chain(ColumnModifier::NULLABLE()); + $method->chain(ColumnModifier::NULLABLE); } return $method; @@ -39,11 +39,11 @@ private function shouldAddNullableModifier(ColumnType $columnType): bool return !in_array( $columnType, [ - ColumnType::SOFT_DELETES(), - ColumnType::SOFT_DELETES_TZ(), - ColumnType::REMEMBER_TOKEN(), - ColumnType::TIMESTAMPS(), - ] + ColumnType::SOFT_DELETES, + ColumnType::SOFT_DELETES_TZ, + ColumnType::REMEMBER_TOKEN, + ColumnType::TIMESTAMPS, + ], ); } @@ -56,10 +56,10 @@ private function shouldAddNotNullModifier(ColumnType $columnType): bool return in_array( $columnType, [ - ColumnType::SOFT_DELETES(), - ColumnType::SOFT_DELETES_TZ(), - ColumnType::REMEMBER_TOKEN(), - ] + ColumnType::SOFT_DELETES, + ColumnType::SOFT_DELETES_TZ, + ColumnType::REMEMBER_TOKEN, + ], ); } } diff --git a/src/Migration/Generator/Modifiers/StoredAsModifier.php b/src/Migration/Generator/Modifiers/StoredAsModifier.php index 62138227..d469e7b3 100644 --- a/src/Migration/Generator/Modifiers/StoredAsModifier.php +++ b/src/Migration/Generator/Modifiers/StoredAsModifier.php @@ -12,10 +12,10 @@ class StoredAsModifier implements Modifier /** * @inheritDoc */ - public function chain(Method $method, Table $table, Column $column, ...$args): Method + public function chain(Method $method, Table $table, Column $column, mixed ...$args): Method { if ($column->getStoredDefinition() !== null) { - $method->chain(ColumnModifier::STORED_AS(), $column->getStoredDefinition()); + $method->chain(ColumnModifier::STORED_AS, $column->getStoredDefinition()); } return $method; diff --git a/src/Migration/Generator/Modifiers/VirtualAsModifier.php b/src/Migration/Generator/Modifiers/VirtualAsModifier.php index 51b9b267..5d306ec9 100644 --- a/src/Migration/Generator/Modifiers/VirtualAsModifier.php +++ b/src/Migration/Generator/Modifiers/VirtualAsModifier.php @@ -12,10 +12,10 @@ class VirtualAsModifier implements Modifier /** * @inheritDoc */ - public function chain(Method $method, Table $table, Column $column, ...$args): Method + public function chain(Method $method, Table $table, Column $column, mixed ...$args): Method { if ($column->getVirtualDefinition() !== null) { - $method->chain(ColumnModifier::VIRTUAL_AS(), $column->getVirtualDefinition()); + $method->chain(ColumnModifier::VIRTUAL_AS, $column->getVirtualDefinition()); } return $method; diff --git a/src/Migration/Migrator/Migrator.php b/src/Migration/Migrator/Migrator.php index 1e82a4b2..5fc2fd70 100644 --- a/src/Migration/Migrator/Migrator.php +++ b/src/Migration/Migrator/Migrator.php @@ -73,10 +73,8 @@ protected function getMigrationQueries(string $path): array /** * Resolve migration instance with backward compatibility. - * - * @return object */ - protected function resolveMigration(string $path) + protected function resolveMigration(string $path): object { if (method_exists(DefaultMigrator::class, 'resolvePath')) { return $this->resolvePath($path); @@ -84,7 +82,7 @@ protected function resolveMigration(string $path) // @codeCoverageIgnoreStart return $this->resolve( - $this->getMigrationName($path) + $this->getMigrationName($path), ); // @codeCoverageIgnoreEnd } diff --git a/src/Migration/ProcedureMigration.php b/src/Migration/ProcedureMigration.php index fc46a84a..b229ec5c 100644 --- a/src/Migration/ProcedureMigration.php +++ b/src/Migration/ProcedureMigration.php @@ -13,36 +13,12 @@ class ProcedureMigration { - /** - * @var \KitLoong\MigrationsGenerator\Support\MigrationNameHelper - */ - private $migrationNameHelper; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\Writer\MigrationWriter - */ - private $migrationWriter; - - /** - * @var \KitLoong\MigrationsGenerator\Setting - */ - private $setting; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\Writer\SquashWriter - */ - private $squashWriter; - public function __construct( - MigrationNameHelper $migrationNameHelper, - MigrationWriter $migrationWriter, - Setting $setting, - SquashWriter $squashWriter + private MigrationNameHelper $migrationNameHelper, + private MigrationWriter $migrationWriter, + private Setting $setting, + private SquashWriter $squashWriter, ) { - $this->migrationNameHelper = $migrationNameHelper; - $this->migrationWriter = $migrationWriter; - $this->setting = $setting; - $this->squashWriter = $squashWriter; } /** @@ -61,7 +37,7 @@ public function write(Procedure $procedure): string $this->makeMigrationClassName($procedure->getName()), new Collection([$up]), new Collection([$down]), - MigrationFileType::PROCEDURE() + MigrationFileType::PROCEDURE, ); return $path; @@ -103,7 +79,7 @@ private function makeMigrationClassName(string $procedure): string { return $this->migrationNameHelper->makeClassName( $this->setting->getProcedureFilename(), - $procedure + $procedure, ); } @@ -117,7 +93,7 @@ private function makeMigrationPath(string $procedure): string return $this->migrationNameHelper->makeFilename( $this->setting->getProcedureFilename(), $this->setting->getDateForMigrationFilename(), - $procedure + $procedure, ); } } diff --git a/src/Migration/Squash.php b/src/Migration/Squash.php index 1504dc91..d8dc55bd 100644 --- a/src/Migration/Squash.php +++ b/src/Migration/Squash.php @@ -9,26 +9,8 @@ class Squash { - /** - * @var \KitLoong\MigrationsGenerator\Migration\Writer\SquashWriter - */ - private $squashWriter; - - /** - * @var \KitLoong\MigrationsGenerator\Support\MigrationNameHelper - */ - private $migrationNameHelper; - - /** - * @var \KitLoong\MigrationsGenerator\Setting - */ - private $setting; - - public function __construct(SquashWriter $squashWriter, MigrationNameHelper $migrationNameHelper, Setting $setting) + public function __construct(private SquashWriter $squashWriter, private MigrationNameHelper $migrationNameHelper, private Setting $setting) { - $this->squashWriter = $squashWriter; - $this->migrationNameHelper = $migrationNameHelper; - $this->setting = $setting; } /** @@ -50,12 +32,12 @@ public function squashMigrations(): string $path = $this->migrationNameHelper->makeFilename( $this->setting->getTableFilename(), $this->setting->getDateForMigrationFilename(), - DB::getDatabaseName() + DB::getDatabaseName(), ); $className = $this->migrationNameHelper->makeClassName( $this->setting->getTableFilename(), - DB::getDatabaseName() + DB::getDatabaseName(), ); $this->squashWriter->squashMigrations($path, $this->setting->getStubPath(), $className); return $path; diff --git a/src/Migration/TableMigration.php b/src/Migration/TableMigration.php index 79bad50e..2b14ea97 100644 --- a/src/Migration/TableMigration.php +++ b/src/Migration/TableMigration.php @@ -20,59 +20,21 @@ use KitLoong\MigrationsGenerator\Migration\Writer\SquashWriter; use KitLoong\MigrationsGenerator\Schema\Models\Table; use KitLoong\MigrationsGenerator\Setting; -use KitLoong\MigrationsGenerator\Support\CheckMigrationMethod; use KitLoong\MigrationsGenerator\Support\MigrationNameHelper; use KitLoong\MigrationsGenerator\Support\TableName; class TableMigration { - use CheckMigrationMethod; use TableName; - /** - * @var \KitLoong\MigrationsGenerator\Migration\Generator\ColumnGenerator - */ - private $columnGenerator; - - /** - * @var \KitLoong\MigrationsGenerator\Support\MigrationNameHelper - */ - private $migrationNameHelper; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\Generator\IndexGenerator - */ - private $indexGenerator; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\Writer\MigrationWriter - */ - private $migrationWriter; - - /** - * @var \KitLoong\MigrationsGenerator\Setting - */ - private $setting; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\Writer\SquashWriter - */ - private $squashWriter; - public function __construct( - ColumnGenerator $columnGenerator, - MigrationNameHelper $migrationNameHelper, - IndexGenerator $indexGenerator, - MigrationWriter $migrationWriter, - Setting $setting, - SquashWriter $squashWriter + private ColumnGenerator $columnGenerator, + private MigrationNameHelper $migrationNameHelper, + private IndexGenerator $indexGenerator, + private MigrationWriter $migrationWriter, + private Setting $setting, + private SquashWriter $squashWriter, ) { - $this->columnGenerator = $columnGenerator; - $this->migrationNameHelper = $migrationNameHelper; - $this->indexGenerator = $indexGenerator; - $this->migrationWriter = $migrationWriter; - $this->setting = $setting; - $this->squashWriter = $squashWriter; } /** @@ -85,7 +47,7 @@ public function write(Table $table): string $upList = new Collection(); $upList->push($this->up($table)); - if ($table->getCustomColumns()->isNotEmpty()) { + if ($table->getUdtColumns()->isNotEmpty()) { foreach ($this->upAdditionalStatements($table) as $statement) { $upList->push($statement); } @@ -99,7 +61,7 @@ public function write(Table $table): string $this->makeMigrationClassName($table->getName()), $upList, new Collection([$down]), - MigrationFileType::TABLE() + MigrationFileType::TABLE, ); return $path; @@ -113,7 +75,7 @@ public function writeToTemp(Table $table): void $upList = new Collection(); $upList->push($this->up($table)); - if ($table->getCustomColumns()->isNotEmpty()) { + if ($table->getUdtColumns()->isNotEmpty()) { foreach ($this->upAdditionalStatements($table) as $statement) { $upList->push($statement); } @@ -129,7 +91,7 @@ public function writeToTemp(Table $table): void */ private function up(Table $table): SchemaBlueprint { - $up = $this->getSchemaBlueprint($table, SchemaBuilder::CREATE()); + $up = $this->getSchemaBlueprint($table, SchemaBuilder::CREATE); $blueprint = new TableBlueprint(); @@ -138,8 +100,8 @@ private function up(Table $table): SchemaBlueprint $blueprint->setLineBreak(); } - if ($this->hasTableComment() && $table->getComment() !== null && $table->getComment() !== '') { - $blueprint->setMethod(new Method(TableMethod::COMMENT(), $table->getComment())); + if ($table->getComment() !== null && $table->getComment() !== '') { + $blueprint->setMethod(new Method(TableMethod::COMMENT, $table->getComment())); } $chainableIndexes = $this->indexGenerator->getChainableIndexes($table->getName(), $table->getIndexes()); @@ -175,7 +137,7 @@ private function upAdditionalStatements(Table $table): array { $statements = []; - foreach ($table->getCustomColumns() as $column) { + foreach ($table->getUdtColumns() as $column) { foreach ($column->getSqls() as $sql) { $statements[] = new DBStatementBlueprint($sql); } @@ -189,7 +151,7 @@ private function upAdditionalStatements(Table $table): array */ private function down(Table $table): SchemaBlueprint { - return $this->getSchemaBlueprint($table, SchemaBuilder::DROP_IF_EXISTS()); + return $this->getSchemaBlueprint($table, SchemaBuilder::DROP_IF_EXISTS); } /** @@ -202,7 +164,7 @@ private function makeMigrationClassName(string $table): string $withoutPrefix = $this->stripTablePrefix($table); return $this->migrationNameHelper->makeClassName( $this->setting->getTableFilename(), - $withoutPrefix + $withoutPrefix, ); } @@ -217,7 +179,7 @@ private function makeMigrationPath(string $table): string return $this->migrationNameHelper->makeFilename( $this->setting->getTableFilename(), $this->setting->getDateForMigrationFilename(), - $withoutPrefix + $withoutPrefix, ); } @@ -226,7 +188,7 @@ private function makeMigrationPath(string $table): string */ private function shouldSetCharset(): bool { - if (DB::getDriverName() !== Driver::MYSQL()->getValue()) { + if (DB::getDriverName() !== Driver::MYSQL->value) { return false; } @@ -236,8 +198,8 @@ private function shouldSetCharset(): bool private function setTableCharset(TableBlueprint $blueprint, Table $table): TableBlueprint { $blueprint->setProperty( - TableProperty::COLLATION(), - $collation = $table->getCollation() + TableProperty::COLLATION, + $collation = $table->getCollation(), ); if ($collation === null) { @@ -245,7 +207,7 @@ private function setTableCharset(TableBlueprint $blueprint, Table $table): Table } $charset = Str::before($collation, '_'); - $blueprint->setProperty(TableProperty::CHARSET(), $charset); + $blueprint->setProperty(TableProperty::CHARSET, $charset); return $blueprint; } @@ -254,7 +216,7 @@ private function getSchemaBlueprint(Table $table, SchemaBuilder $schemaBuilder): { return new SchemaBlueprint( $table->getName(), - $schemaBuilder + $schemaBuilder, ); } } diff --git a/src/Migration/ViewMigration.php b/src/Migration/ViewMigration.php index 0176cfee..214cab2c 100644 --- a/src/Migration/ViewMigration.php +++ b/src/Migration/ViewMigration.php @@ -16,36 +16,12 @@ class ViewMigration { use TableName; - /** - * @var \KitLoong\MigrationsGenerator\Support\MigrationNameHelper - */ - private $migrationNameHelper; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\Writer\MigrationWriter - */ - private $migrationWriter; - - /** - * @var \KitLoong\MigrationsGenerator\Setting - */ - private $setting; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\Writer\SquashWriter - */ - private $squashWriter; - public function __construct( - MigrationNameHelper $migrationNameHelper, - MigrationWriter $migrationWriter, - Setting $setting, - SquashWriter $squashWriter + private MigrationNameHelper $migrationNameHelper, + private MigrationWriter $migrationWriter, + private Setting $setting, + private SquashWriter $squashWriter, ) { - $this->migrationNameHelper = $migrationNameHelper; - $this->migrationWriter = $migrationWriter; - $this->setting = $setting; - $this->squashWriter = $squashWriter; } /** @@ -64,7 +40,7 @@ public function write(View $view): string $this->makeMigrationClassName($view->getName()), new Collection([$up]), new Collection([$down]), - MigrationFileType::VIEW() + MigrationFileType::VIEW, ); return $path; @@ -107,7 +83,7 @@ private function makeMigrationClassName(string $view): string $withoutPrefix = $this->stripTablePrefix($view); return $this->migrationNameHelper->makeClassName( $this->setting->getViewFilename(), - $withoutPrefix + $withoutPrefix, ); } @@ -122,7 +98,7 @@ private function makeMigrationPath(string $view): string return $this->migrationNameHelper->makeFilename( $this->setting->getViewFilename(), $this->setting->getDateForMigrationFilename(), - $withoutPrefix + $withoutPrefix, ); } } diff --git a/src/Migration/Writer/MigrationStub.php b/src/Migration/Writer/MigrationStub.php index 66aefb59..aa0b949b 100644 --- a/src/Migration/Writer/MigrationStub.php +++ b/src/Migration/Writer/MigrationStub.php @@ -30,7 +30,7 @@ public function populateStub( string $use, string $className, string $upContent, - string $downContent + string $downContent, ): string { $content = $stub; $replace = [ diff --git a/src/Migration/Writer/MigrationWriter.php b/src/Migration/Writer/MigrationWriter.php index e113f243..77235903 100644 --- a/src/Migration/Writer/MigrationWriter.php +++ b/src/Migration/Writer/MigrationWriter.php @@ -12,14 +12,8 @@ class MigrationWriter { - /** - * @var \KitLoong\MigrationsGenerator\Migration\Writer\MigrationStub - */ - private $migrationStub; - - public function __construct(MigrationStub $migrationStub) + public function __construct(private MigrationStub $migrationStub) { - $this->migrationStub = $migrationStub; } /** @@ -36,7 +30,7 @@ public function writeTo( string $className, Collection $up, Collection $down, - MigrationFileType $migrationFileType + MigrationFileType $migrationFileType, ): void { try { $stub = $this->migrationStub->getStub($stubPath); @@ -50,7 +44,7 @@ public function writeTo( $useDBFacade = true; } - $use = implode(Space::LINE_BREAK(), $this->getNamespaces($migrationFileType, $useDBFacade)); + $use = implode(Space::LINE_BREAK->value, $this->getNamespaces($migrationFileType, $useDBFacade)); // Create directory if it doesn't exist $directory = dirname($path); @@ -61,9 +55,9 @@ public function writeTo( File::put( $path, - $this->migrationStub->populateStub($stub, $use, $className, $upString, $downString) + $this->migrationStub->populateStub($stub, $use, $className, $upString, $downString), ); - } catch (FileNotFoundException $e) { + } catch (FileNotFoundException) { // Do nothing. } } @@ -74,8 +68,8 @@ public function writeTo( private function getNamespaces(MigrationFileType $migrationFileType, bool $useDBFacade): array { if ( - $migrationFileType->equals(MigrationFileType::VIEW()) - || $migrationFileType->equals(MigrationFileType::PROCEDURE()) + $migrationFileType === MigrationFileType::VIEW + || $migrationFileType === MigrationFileType::PROCEDURE ) { return [ 'use Illuminate\Database\Migrations\Migration;', @@ -105,8 +99,6 @@ private function getNamespaces(MigrationFileType $migrationFileType, bool $useDB */ private function prettifyToString(Collection $blueprints): string { - return $blueprints->map(function (WritableBlueprint $blueprint) { - return $blueprint->toString(); - })->implode(Space::LINE_BREAK() . Space::TAB() . Space::TAB()); // Add tab to prettify + return $blueprints->map(static fn (WritableBlueprint $blueprint) => $blueprint->toString())->implode(Space::LINE_BREAK->value . Space::TAB->value . Space::TAB->value); // Add tab to prettify } } diff --git a/src/Migration/Writer/SquashWriter.php b/src/Migration/Writer/SquashWriter.php index b0fe90cd..f5b93764 100644 --- a/src/Migration/Writer/SquashWriter.php +++ b/src/Migration/Writer/SquashWriter.php @@ -11,20 +11,8 @@ class SquashWriter { - /** - * @var \KitLoong\MigrationsGenerator\Support\MigrationNameHelper - */ - private $migrationNameHelper; - - /** - * @var \KitLoong\MigrationsGenerator\Migration\Writer\MigrationStub - */ - private $migrationStub; - - public function __construct(MigrationNameHelper $migrationNameHelper, MigrationStub $migrationStub) + public function __construct(private MigrationNameHelper $migrationNameHelper, private MigrationStub $migrationStub) { - $this->migrationNameHelper = $migrationNameHelper; - $this->migrationStub = $migrationStub; } /** @@ -39,16 +27,12 @@ public function writeToTemp(Collection $upBlueprints, Collection $downBlueprints { $upTempPath = $this->migrationNameHelper->makeUpTempPath(); $prettySpace = $this->getSpaceIfFileExists($upTempPath); - $upString = $upBlueprints->map(function (WritableBlueprint $up) { - return $up->toString(); - })->implode(Space::LINE_BREAK() . Space::TAB() . Space::TAB()); // Add tab to prettify + $upString = $upBlueprints->map(static fn (WritableBlueprint $up) => $up->toString())->implode(Space::LINE_BREAK->value . Space::TAB->value . Space::TAB->value); // Add tab to prettify File::append($upTempPath, $prettySpace . $upString); $downTempPath = $this->migrationNameHelper->makeDownTempPath(); $prettySpace = $this->getSpaceIfFileExists($downTempPath); - $downString = $downBlueprints->map(function (WritableBlueprint $down) { - return $down->toString(); - })->implode(Space::LINE_BREAK() . Space::TAB() . Space::TAB()); // Add tab to prettify + $downString = $downBlueprints->map(static fn (WritableBlueprint $down) => $down->toString())->implode(Space::LINE_BREAK->value . Space::TAB->value . Space::TAB->value); // Add tab to prettify File::prepend($downTempPath, $downString . $prettySpace); } @@ -69,7 +53,7 @@ public function cleanTemps(): void */ public function squashMigrations(string $path, string $stubPath, string $className): void { - $use = implode(Space::LINE_BREAK(), [ + $use = implode(Space::LINE_BREAK->value, [ 'use Illuminate\Database\Migrations\Migration;', 'use Illuminate\Database\Schema\Blueprint;', 'use Illuminate\Support\Facades\DB;', @@ -87,10 +71,10 @@ public function squashMigrations(string $path, string $stubPath, string $classNa $use, $className, File::get($upTempPath), - File::get($downTempPath) - ) + File::get($downTempPath), + ), ); - } catch (FileNotFoundException $e) { + } catch (FileNotFoundException) { // Do nothing. } finally { File::delete($upTempPath); @@ -104,6 +88,6 @@ private function getSpaceIfFileExists(string $path): string return ''; } - return Space::LINE_BREAK() . Space::LINE_BREAK() . Space::TAB() . Space::TAB(); + return Space::LINE_BREAK->value . Space::LINE_BREAK->value . Space::TAB->value . Space::TAB->value; } } diff --git a/src/MigrationsGeneratorServiceProvider.php b/src/MigrationsGeneratorServiceProvider.php index b5d66a22..881c779e 100644 --- a/src/MigrationsGeneratorServiceProvider.php +++ b/src/MigrationsGeneratorServiceProvider.php @@ -4,10 +4,10 @@ use Illuminate\Database\Migrations\MigrationRepositoryInterface; use Illuminate\Support\ServiceProvider; -use KitLoong\MigrationsGenerator\DBAL\MySQLSchema as DBALMySQLSchema; -use KitLoong\MigrationsGenerator\DBAL\PgSQLSchema as DBALPgSQLSchema; -use KitLoong\MigrationsGenerator\DBAL\SQLiteSchema as DBALSQLiteSchema; -use KitLoong\MigrationsGenerator\DBAL\SQLSrvSchema as DBALSQLSrvSchema; +use KitLoong\MigrationsGenerator\Database\MySQLSchema as DBALMySQLSchema; +use KitLoong\MigrationsGenerator\Database\PgSQLSchema as DBALPgSQLSchema; +use KitLoong\MigrationsGenerator\Database\SQLiteSchema as DBALSQLiteSchema; +use KitLoong\MigrationsGenerator\Database\SQLSrvSchema as DBALSQLSrvSchema; use KitLoong\MigrationsGenerator\Enum\Migrations\Method\ColumnType; use KitLoong\MigrationsGenerator\Migration\Generator\Columns\BooleanColumn; use KitLoong\MigrationsGenerator\Migration\Generator\Columns\DatetimeColumn; @@ -19,6 +19,7 @@ use KitLoong\MigrationsGenerator\Migration\Generator\Columns\OmitNameColumn; use KitLoong\MigrationsGenerator\Migration\Generator\Columns\PresetValuesColumn; use KitLoong\MigrationsGenerator\Migration\Generator\Columns\SoftDeleteColumn; +use KitLoong\MigrationsGenerator\Migration\Generator\Columns\SpatialColumn; use KitLoong\MigrationsGenerator\Migration\Generator\Columns\StringColumn; use KitLoong\MigrationsGenerator\Migration\Migrator\Migrator; use KitLoong\MigrationsGenerator\Repositories\MariaDBRepository; @@ -69,19 +70,17 @@ public function register(): void // Bind the Repository Interface to $app['migrations.repository'] $this->app->singleton( MigrationRepositoryInterface::class, - function ($app) { - return $app['migration.repository']; - } + static fn ($app) => $app['migration.repository'], ); // Backward compatible for older Laravel version which failed to resolve Illuminate\Database\ConnectionResolverInterface. $this->app->singleton( Migrator::class, - function ($app) { + static function ($app) { $repository = $app['migration.repository']; return new Migrator($repository, $app['db'], $app['files'], $app['events']); - } + }, ); $this->registerColumnTypeGenerator(); @@ -113,7 +112,7 @@ protected function registerConfig(): void */ protected function columnTypeSingleton(ColumnType $type, string $columnTypeGenerator): void { - $this->app->singleton(ColumnType::class . '\\' . $type->getKey(), $columnTypeGenerator); + $this->app->singleton(ColumnType::class . '\\' . $type->name, $columnTypeGenerator); } /** @@ -121,17 +120,17 @@ protected function columnTypeSingleton(ColumnType $type, string $columnTypeGener */ protected function registerColumnTypeGenerator(): void { - foreach (ColumnType::values() as $columnType) { + foreach (ColumnType::cases() as $columnType) { $this->columnTypeSingleton($columnType, MiscColumn::class); } foreach ( [ - ColumnType::BIG_INTEGER(), - ColumnType::INTEGER(), - ColumnType::MEDIUM_INTEGER(), - ColumnType::SMALL_INTEGER(), - ColumnType::TINY_INTEGER(), + ColumnType::BIG_INTEGER, + ColumnType::INTEGER, + ColumnType::MEDIUM_INTEGER, + ColumnType::SMALL_INTEGER, + ColumnType::TINY_INTEGER, ] as $columnType ) { $this->columnTypeSingleton($columnType, IntegerColumn::class); @@ -139,13 +138,13 @@ protected function registerColumnTypeGenerator(): void foreach ( [ - ColumnType::DATE(), - ColumnType::DATETIME(), - ColumnType::DATETIME_TZ(), - ColumnType::TIME(), - ColumnType::TIME_TZ(), - ColumnType::TIMESTAMP(), - ColumnType::TIMESTAMP_TZ(), + ColumnType::DATE, + ColumnType::DATETIME, + ColumnType::DATETIME_TZ, + ColumnType::TIME, + ColumnType::TIME_TZ, + ColumnType::TIMESTAMP, + ColumnType::TIMESTAMP_TZ, ] as $columnType ) { $this->columnTypeSingleton($columnType, DatetimeColumn::class); @@ -153,8 +152,8 @@ protected function registerColumnTypeGenerator(): void foreach ( [ - ColumnType::SOFT_DELETES(), - ColumnType::SOFT_DELETES_TZ(), + ColumnType::SOFT_DELETES, + ColumnType::SOFT_DELETES_TZ, ] as $columnType ) { $this->columnTypeSingleton($columnType, SoftDeleteColumn::class); @@ -162,8 +161,7 @@ protected function registerColumnTypeGenerator(): void foreach ( [ - ColumnType::DECIMAL(), - ColumnType::UNSIGNED_DECIMAL(), + ColumnType::DECIMAL, ] as $columnType ) { $this->columnTypeSingleton($columnType, DecimalColumn::class); @@ -171,8 +169,8 @@ protected function registerColumnTypeGenerator(): void foreach ( [ - ColumnType::ENUM(), - ColumnType::SET(), + ColumnType::ENUM, + ColumnType::SET, ] as $columnType ) { $this->columnTypeSingleton($columnType, PresetValuesColumn::class); @@ -180,16 +178,32 @@ protected function registerColumnTypeGenerator(): void foreach ( [ - ColumnType::CHAR(), - ColumnType::STRING(), + ColumnType::CHAR, + ColumnType::STRING, ] as $columnType ) { $this->columnTypeSingleton($columnType, StringColumn::class); } - $this->columnTypeSingleton(ColumnType::BOOLEAN(), BooleanColumn::class); - $this->columnTypeSingleton(ColumnType::DOUBLE(), DoubleColumn::class); - $this->columnTypeSingleton(ColumnType::FLOAT(), FloatColumn::class); - $this->columnTypeSingleton(ColumnType::REMEMBER_TOKEN(), OmitNameColumn::class); + foreach ( + [ + ColumnType::GEOGRAPHY, + ColumnType::GEOMETRY, + ColumnType::GEOMETRY_COLLECTION, + ColumnType::LINE_STRING, + ColumnType::MULTI_LINE_STRING, + ColumnType::POINT, + ColumnType::MULTI_POINT, + ColumnType::MULTI_POLYGON, + ColumnType::POLYGON, + ] as $columnType + ) { + $this->columnTypeSingleton($columnType, SpatialColumn::class); + } + + $this->columnTypeSingleton(ColumnType::BOOLEAN, BooleanColumn::class); + $this->columnTypeSingleton(ColumnType::DOUBLE, DoubleColumn::class); + $this->columnTypeSingleton(ColumnType::FLOAT, FloatColumn::class); + $this->columnTypeSingleton(ColumnType::REMEMBER_TOKEN, OmitNameColumn::class); } } diff --git a/src/Repositories/Entities/MariaDB/CheckConstraint.php b/src/Repositories/Entities/MariaDB/CheckConstraint.php index f8378eec..ab8822a1 100644 --- a/src/Repositories/Entities/MariaDB/CheckConstraint.php +++ b/src/Repositories/Entities/MariaDB/CheckConstraint.php @@ -14,31 +14,23 @@ */ class CheckConstraint { - /** @var string */ - private $constraintCatalog; + private string $constraintCatalog; - /** @var string */ - private $constraintSchema; + private string $constraintSchema; - /** @var string */ - private $tableName; + private string $tableName; - /** @var string */ - private $constraintName; + private string $constraintName; - /** @var string|null */ - private $level; + private ?string $level = null; - /** @var string */ - private $checkClause; + private string $checkClause; public function __construct(stdClass $column) { // Convert column property to case-insensitive // Issue https://github.com/kitloong/laravel-migrations-generator/issues/34 - $lowerKey = (new Collection((array) $column))->mapWithKeys(function ($item, $key) { - return [strtolower($key) => $item]; - }); + $lowerKey = (new Collection((array) $column))->mapWithKeys(static fn ($item, $key) => [strtolower($key) => $item]); $this->constraintCatalog = $lowerKey['constraint_catalog']; $this->constraintSchema = $lowerKey['constraint_schema']; diff --git a/src/Repositories/Entities/MySQL/ShowColumn.php b/src/Repositories/Entities/MySQL/ShowColumn.php index 1ae44178..9d21e85d 100644 --- a/src/Repositories/Entities/MySQL/ShowColumn.php +++ b/src/Repositories/Entities/MySQL/ShowColumn.php @@ -15,31 +15,23 @@ */ class ShowColumn { - /** @var string */ - private $field; + private string $field; - /** @var string */ - private $type; + private string $type; - /** @var string */ - private $null; + private string $null; - /** @var string */ - private $key; + private string $key; - /** @var string|null */ - private $default; + private ?string $default = null; - /** @var string */ - private $extra; + private string $extra; public function __construct(stdClass $column) { // Convert column property to case-insensitive // Issue https://github.com/kitloong/laravel-migrations-generator/issues/34 - $lowerKey = (new Collection((array) $column))->mapWithKeys(function ($item, $key) { - return [strtolower($key) => $item]; - }); + $lowerKey = (new Collection((array) $column))->mapWithKeys(static fn ($item, $key) => [strtolower($key) => $item]); $this->field = $lowerKey['field']; $this->type = $lowerKey['type']; diff --git a/src/Repositories/Entities/PgSQL/IndexDefinition.php b/src/Repositories/Entities/PgSQL/IndexDefinition.php index 9bc2a520..82b6867e 100644 --- a/src/Repositories/Entities/PgSQL/IndexDefinition.php +++ b/src/Repositories/Entities/PgSQL/IndexDefinition.php @@ -4,26 +4,8 @@ class IndexDefinition { - /** - * @var string - */ - private $tableName; - - /** - * @var string - */ - private $indexName; - - /** - * @var string - */ - private $indexDef; - - public function __construct(string $tableName, string $indexName, string $indexDef) + public function __construct(private string $tableName, private string $indexName, private string $indexDef) { - $this->tableName = $tableName; - $this->indexName = $indexName; - $this->indexDef = $indexDef; } public function getTableName(): string diff --git a/src/Repositories/Entities/ProcedureDefinition.php b/src/Repositories/Entities/ProcedureDefinition.php index 9e34193c..867e9adb 100644 --- a/src/Repositories/Entities/ProcedureDefinition.php +++ b/src/Repositories/Entities/ProcedureDefinition.php @@ -4,16 +4,8 @@ class ProcedureDefinition { - /** @var string */ - private $name; - - /** @var string */ - private $definition; - - public function __construct(string $name, string $definition) + public function __construct(private string $name, private string $definition) { - $this->name = $name; - $this->definition = $definition; } public function getName(): string diff --git a/src/Repositories/Entities/SQLSrv/ColumnDefinition.php b/src/Repositories/Entities/SQLSrv/ColumnDefinition.php index a45a8aab..96236c00 100644 --- a/src/Repositories/Entities/SQLSrv/ColumnDefinition.php +++ b/src/Repositories/Entities/SQLSrv/ColumnDefinition.php @@ -7,42 +7,30 @@ class ColumnDefinition { - /** @var string */ - private $name; + private string $name; - /** @var string */ - private $type; + private string $type; - /** @var int */ - private $length; + private int $length; - /** @var bool */ - private $notnull; + private bool $notnull; - /** @var string|null */ - private $default; + private ?string $default = null; - /** @var int */ - private $scale; + private int $scale; - /** @var int */ - private $precision; + private int $precision; - /** @var bool */ - private $autoincrement; + private bool $autoincrement; - /** @var string|null */ - private $collation; + private ?string $collation = null; - /** @var string|null */ - private $comment; + private ?string $comment = null; public function __construct(stdClass $column) { // Convert column property to case-insensitive - $lowerKey = (new Collection((array) $column))->mapWithKeys(function ($item, $key) { - return [strtolower($key) => $item]; - }); + $lowerKey = (new Collection((array) $column))->mapWithKeys(static fn ($item, $key) => [strtolower($key) => $item]); $this->name = $lowerKey['name']; $this->type = $lowerKey['type']; diff --git a/src/Repositories/Entities/SQLSrv/ViewDefinition.php b/src/Repositories/Entities/SQLSrv/ViewDefinition.php index a1298874..d1a7deb9 100644 --- a/src/Repositories/Entities/SQLSrv/ViewDefinition.php +++ b/src/Repositories/Entities/SQLSrv/ViewDefinition.php @@ -4,20 +4,8 @@ class ViewDefinition { - /** - * @var string - */ - private $name; - - /** - * @var string - */ - private $definition; - - public function __construct(string $name, string $definition) + public function __construct(private string $name, private string $definition) { - $this->name = $name; - $this->definition = $definition; } public function getName(): string diff --git a/src/Repositories/MariaDBRepository.php b/src/Repositories/MariaDBRepository.php index 568b6542..44a4486f 100644 --- a/src/Repositories/MariaDBRepository.php +++ b/src/Repositories/MariaDBRepository.php @@ -23,10 +23,10 @@ public function getCheckConstraintForJson(string $table, string $column): ?Check "SELECT * FROM information_schema.CHECK_CONSTRAINTS WHERE TABLE_NAME = '$table' AND CONSTRAINT_SCHEMA = '" . DB::getDatabaseName() . "' - AND CHECK_CLAUSE LIKE '%json_valid(`$column`)%'" + AND CHECK_CLAUSE LIKE '%json_valid(`$column`)%'", ); return $column === null ? null : new CheckConstraint($column); - } catch (QueryException $exception) { + } catch (QueryException) { return null; } } diff --git a/src/Repositories/MySQLRepository.php b/src/Repositories/MySQLRepository.php index 05d53600..c4f4ce54 100644 --- a/src/Repositories/MySQLRepository.php +++ b/src/Repositories/MySQLRepository.php @@ -23,54 +23,6 @@ public function showColumn(string $table, string $column): ?ShowColumn return $result === null ? null : new ShowColumn($result); } - /** - * Get enum values. - * - * @param string $table Table name. - * @param string $column Column name. - * @return \Illuminate\Support\Collection - */ - public function getEnumPresetValues(string $table, string $column): Collection - { - $result = DB::selectOne("SHOW COLUMNS FROM `$table` WHERE Field = '$column' AND Type LIKE 'enum(%'"); - - if ($result === null) { - return new Collection(); - } - - $showColumn = new ShowColumn($result); - $value = substr( - str_replace('enum(\'', '', $showColumn->getType()), - 0, - -2 - ); - return new Collection(explode("','", $value)); - } - - /** - * Get set values. - * - * @param string $table Table name. - * @param string $column Column name. - * @return \Illuminate\Support\Collection - */ - public function getSetPresetValues(string $table, string $column): Collection - { - $result = DB::selectOne("SHOW COLUMNS FROM `$table` WHERE Field = '$column' AND Type LIKE 'set(%'"); - - if ($result === null) { - return new Collection(); - } - - $showColumn = new ShowColumn($result); - $value = substr( - str_replace('set(\'', '', $showColumn->getType()), - 0, - -2 - ); - return new Collection(explode("','", $value)); - } - /** * Checks if column has `on update CURRENT_TIMESTAMP` * @@ -85,7 +37,7 @@ public function isOnUpdateCurrentTimestamp(string $table, string $column): bool "SHOW COLUMNS FROM `$table` WHERE Field = '$column' AND Type = 'timestamp' - AND Extra LIKE '%on update CURRENT_TIMESTAMP%'" + AND Extra LIKE '%on update CURRENT_TIMESTAMP%'", ); return !($result === null); } @@ -137,13 +89,47 @@ public function getProcedures(): Collection return $list; } + /** + * Get the SRID by table and column name. + */ + public function getSrID(string $table, string $column): ?int + { + try { + $srsID = DB::selectOne( + "SELECT SRS_ID + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = '" . DB::getDatabaseName() . "' + AND TABLE_NAME = '" . $table . "' + AND COLUMN_NAME = '" . $column . "'", + ); + } catch (QueryException $exception) { + if ( + Str::contains( + $exception->getMessage(), + "SQLSTATE[42S22]: Column not found: 1054 Unknown column 'SRS_ID'", + true, + ) + ) { + return null; + } + + throw $exception; + } + + if ($srsID === null) { + return null; + } + + $srsIDArr = array_change_key_case((array) $srsID); + return $srsIDArr['srs_id'] ?? null; + } + /** * Get single stored procedure by name. * * @param string $procedure Procedure name. - * @return mixed */ - private function getProcedure(string $procedure) + private function getProcedure(string $procedure): mixed { return DB::selectOne("SHOW CREATE PROCEDURE $procedure"); } @@ -161,7 +147,7 @@ private function getGenerationExpression(string $table, string $column, string $ FROM information_schema.COLUMNS WHERE TABLE_NAME = '$table' AND COLUMN_NAME = '$column' - AND EXTRA = '$extra'" + AND EXTRA = '$extra'", ); } catch (QueryException $exception) { // Check if error caused by missing column 'GENERATION_EXPRESSION'. @@ -172,7 +158,7 @@ private function getGenerationExpression(string $table, string $column, string $ Str::contains( $exception->getMessage(), "SQLSTATE[42S22]: Column not found: 1054 Unknown column 'GENERATION_EXPRESSION'", - true + true, ) ) { return null; diff --git a/src/Repositories/PgSQLRepository.php b/src/Repositories/PgSQLRepository.php index 687117b5..d456d11b 100644 --- a/src/Repositories/PgSQLRepository.php +++ b/src/Repositories/PgSQLRepository.php @@ -31,9 +31,9 @@ public function getTypeByColumnName(string $table, string $column): ?string WHERE c.relname ~ '^($table)$' AND pg_catalog.pg_table_is_visible(c.oid) ) - AND a.attname='$column'" + AND a.attname='$column'", ); - return $result === null ? null : $result->datatype; + return $result?->datatype; } /** @@ -60,9 +60,9 @@ public function getDefaultByColumnName(string $table, string $column): ?string WHERE c.relname ~ '^($table)$' AND pg_catalog.pg_table_is_visible(c.oid) ) - AND a.attname='$column'" + AND a.attname='$column'", ); - return $result === null ? null : $result->default_value; + return $result?->default_value; } /** @@ -88,9 +88,9 @@ public function getCheckConstraintDefinition(string $table, string $column): ?st AND nsp.nspname = ccu.constraint_schema WHERE contype ='c' AND ccu.table_name='$table' - AND ccu.column_name='$column'" + AND ccu.column_name='$column'", ); - return $result === null ? null : $result->definition; + return $result?->definition; } /** @@ -107,7 +107,7 @@ public function getSpatialIndexes(string $table): Collection indexdef FROM pg_indexes WHERE tablename = '$table' - AND indexdef LIKE '% USING gist %'" + AND indexdef LIKE '% USING gist %'", ); $definitions = new Collection(); @@ -117,8 +117,8 @@ public function getSpatialIndexes(string $table): Collection new IndexDefinition( $column->tablename, $column->indexname, - $column->indexdef - ) + $column->indexdef, + ), ); } } @@ -141,7 +141,7 @@ public function getFulltextIndexes(string $table): Collection FROM pg_indexes WHERE tablename = '$table' AND indexdef LIKE '%to_tsvector(%' - ORDER BY indexname" + ORDER BY indexname", ); $definitions = new Collection(); @@ -151,8 +151,8 @@ public function getFulltextIndexes(string $table): Collection new IndexDefinition( $column->tablename, $column->indexname, - $column->indexdef - ) + $column->indexdef, + ), ); } } @@ -160,35 +160,6 @@ public function getFulltextIndexes(string $table): Collection return $definitions; } - /** - * Get a list of custom data types. - * - * @source https://stackoverflow.com/questions/3660787/how-to-list-custom-types-using-postgres-information-schema - * @return \Illuminate\Support\Collection - */ - public function getCustomDataTypes(): Collection - { - $searchPath = DB::connection()->getConfig('search_path') ?: DB::connection()->getConfig('schema'); - - $rows = DB::select( - "SELECT n.nspname AS schema, t.typname AS type - FROM pg_type t - LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace - WHERE (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid)) - AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid) - AND n.nspname IN ('$searchPath');" - ); - $types = new Collection(); - - if (count($rows) > 0) { - foreach ($rows as $row) { - $types->push($row->type); - } - } - - return $types; - } - /** * Get a list of stored procedures. * @@ -205,7 +176,7 @@ public function getProcedures(): Collection FROM pg_catalog.pg_proc JOIN pg_namespace ON pg_catalog.pg_proc.pronamespace = pg_namespace.oid WHERE prokind = 'p' - AND pg_namespace.nspname = '$searchPath'" + AND pg_namespace.nspname = '$searchPath'", ); foreach ($procedures as $procedure) { @@ -230,7 +201,7 @@ public function getStoredDefinition(string $table, string $column): ?string FROM information_schema.columns WHERE table_name = '$table' AND column_name = '$column' - AND is_generated = 'ALWAYS'" + AND is_generated = 'ALWAYS'", ); if ($definition === null) { diff --git a/src/Repositories/Repository.php b/src/Repositories/Repository.php index 1111efc5..426d6b2d 100644 --- a/src/Repositories/Repository.php +++ b/src/Repositories/Repository.php @@ -12,7 +12,6 @@ abstract class Repository * * @param string $str The literal string to be quoted. * @return string The quoted literal string. - * @see https://github.com/doctrine/dbal/blob/3.1.x/src/Platforms/AbstractPlatform.php#L3560 */ protected function quoteStringLiteral(string $str): string { @@ -23,8 +22,6 @@ protected function quoteStringLiteral(string $str): string /** * Gets the character used for string literal quoting. - * - * @see https://github.com/doctrine/dbal/blob/3.1.x/src/Platforms/AbstractPlatform.php#L3572 */ protected function getStringLiteralQuoteCharacter(): string { diff --git a/src/Repositories/SQLSrvRepository.php b/src/Repositories/SQLSrvRepository.php index 16df5c25..254dab06 100644 --- a/src/Repositories/SQLSrvRepository.php +++ b/src/Repositories/SQLSrvRepository.php @@ -34,7 +34,7 @@ public function getSpatialIndexNames(string $table): Collection JOIN sys.index_columns AS idxcol ON idx.object_id = idxcol.object_id AND idx.index_id = idxcol.index_id JOIN sys.columns AS col ON idxcol.object_id = col.object_id AND idxcol.column_id = col.column_id WHERE " . $this->getTableWhereClause($table, 'scm.name', 'tbl.name') . " - AND idx.type = " . self::SPATIAL_INDEX_ID + AND idx.type = " . self::SPATIAL_INDEX_ID, ); $definitions = new Collection(); @@ -82,7 +82,7 @@ public function getColumnDefinition(string $table, string $column): ?ColumnDefin AND prop.name = 'MS_Description' WHERE obj.type = 'U' AND " . $this->getTableWhereClause($table, 'scm.name', 'obj.name') . " - AND col.name = " . $this->quoteStringLiteral($column) + AND col.name = " . $this->quoteStringLiteral($column), ); return $result === null ? null : new ColumnDefinition($result); } @@ -106,33 +106,11 @@ public function getView(string $name): ?ViewDefinition '$name' ) AND definition IS NOT NULL - ORDER BY name" + ORDER BY name", ); return $view === null ? null : new ViewDefinition($view->name, $view->definition); } - /** - * Returns the where clause to filter schema and table name in a query. - * - * @param string $table The full qualified name of the table. - * @param string $schemaColumn The name of the column to compare the schema to in the where clause. - * @param string $tableColumn The name of the column to compare the table to in the where clause. - * @see https://github.com/doctrine/dbal/blob/3.1.x/src/Platforms/SQLServer2012Platform.php#L1064 - */ - private function getTableWhereClause(string $table, string $schemaColumn, string $tableColumn): string - { - $schema = 'SCHEMA_NAME()'; - - if (strpos($table, '.') !== false) { - [$schema, $table] = explode('.', $table); - $schema = $this->quoteStringLiteral($schema); - } - - $table = $this->quoteStringLiteral($table); - - return sprintf('(%s = %s AND %s = %s)', $tableColumn, $table, $schemaColumn, $schema); - } - /** * Get a list of stored procedures. * @@ -147,7 +125,7 @@ public function getProcedures(): Collection INNER JOIN sys.sql_modules ON (sys.sysobjects.id = sys.sql_modules.object_id) WHERE type = 'P' AND definition IS NOT NULL - ORDER BY name" + ORDER BY name", ); foreach ($procedures as $procedure) { @@ -176,7 +154,7 @@ public function getEnumPresetValues(string $table, string $column): Collection AND con.parent_object_id = col.object_id WHERE t.name = '$table' AND col.name = '$column' - AND con.definition IS NOT NULL" + AND con.definition IS NOT NULL", ); if ($result === null) { @@ -193,11 +171,11 @@ public function getEnumPresetValues(string $table, string $column): Collection } /** - * Get a list of custom data types. + * Get a list of user-defined types. * * @return \Illuminate\Support\Collection */ - public function getCustomDataTypes(): Collection + public function getUserDefinedTypes(): Collection { $rows = DB::select("SELECT * FROM sys.types WHERE is_user_defined = 1"); $types = new Collection(); @@ -210,4 +188,25 @@ public function getCustomDataTypes(): Collection return $types; } + + /** + * Returns the where clause to filter schema and table name in a query. + * + * @param string $table The full qualified name of the table. + * @param string $schemaColumn The name of the column to compare the schema to in the where clause. + * @param string $tableColumn The name of the column to compare the table to in the where clause. + */ + private function getTableWhereClause(string $table, string $schemaColumn, string $tableColumn): string + { + $schema = 'SCHEMA_NAME()'; + + if (strpos($table, '.') !== false) { + [$schema, $table] = explode('.', $table); + $schema = $this->quoteStringLiteral($schema); + } + + $table = $this->quoteStringLiteral($table); + + return sprintf('(%s = %s AND %s = %s)', $tableColumn, $table, $schemaColumn, $schema); + } } diff --git a/src/Schema/Models/Column.php b/src/Schema/Models/Column.php index 054c2f82..7139a3a1 100644 --- a/src/Schema/Models/Column.php +++ b/src/Schema/Models/Column.php @@ -40,11 +40,6 @@ public function getScale(): int; */ public function isUnsigned(): bool; - /** - * Check if the column is fixed. - */ - public function isFixed(): bool; - /** * Check if the column is not null. */ @@ -73,7 +68,7 @@ public function isAutoincrement(): bool; /** * Get the column precision. */ - public function getPrecision(): int; + public function getPrecision(): ?int; /** * Get the column comment. @@ -88,6 +83,16 @@ public function getComment(): ?string; */ public function getPresetValues(): array; + /** + * Get the spatial column subtype. + */ + public function getSpatialSubType(): ?string; + + /** + * Get the spatial column srID. + */ + public function getSpatialSrID(): ?int; + /** * Check if the column uses "on update CURRENT_TIMESTAMP". * This is usually used for MySQL `timestamp` and `timestampTz`. diff --git a/src/Schema/Models/Index.php b/src/Schema/Models/Index.php index 3a21f2eb..2c7ace36 100644 --- a/src/Schema/Models/Index.php +++ b/src/Schema/Models/Index.php @@ -24,13 +24,6 @@ public function getTableName(): string; */ public function getColumns(): array; - /** - * Get the index column lengths, always same size with {@see self::getColumns()}. - * - * @return array - */ - public function getLengths(): array; - /** * Get the index type. */ diff --git a/src/Schema/Models/Table.php b/src/Schema/Models/Table.php index c44e219b..37181186 100644 --- a/src/Schema/Models/Table.php +++ b/src/Schema/Models/Table.php @@ -24,11 +24,11 @@ public function getComment(): ?string; public function getColumns(): Collection; /** - * Get a list of custom columns. + * Get a list of user-defined type columns. * - * @return \Illuminate\Support\Collection + * @return \Illuminate\Support\Collection */ - public function getCustomColumns(): Collection; + public function getUdtColumns(): Collection; /** * Get a list of indexes. diff --git a/src/Schema/Models/CustomColumn.php b/src/Schema/Models/UDTColumn.php similarity index 76% rename from src/Schema/Models/CustomColumn.php rename to src/Schema/Models/UDTColumn.php index 4a030e52..43291b52 100644 --- a/src/Schema/Models/CustomColumn.php +++ b/src/Schema/Models/UDTColumn.php @@ -3,9 +3,9 @@ namespace KitLoong\MigrationsGenerator\Schema\Models; /** - * Table column. Column type which is not supported by the framework. + * Table column. User-defined type column which is not supported by the framework. */ -interface CustomColumn extends Model +interface UDTColumn extends Model { /** * Get the column name. diff --git a/src/Schema/Schema.php b/src/Schema/Schema.php index d109ef5a..a4edaccc 100644 --- a/src/Schema/Schema.php +++ b/src/Schema/Schema.php @@ -40,7 +40,7 @@ public function getViews(): Collection; * * @return \Illuminate\Support\Collection */ - public function getTableForeignKeys(string $table): Collection; + public function getForeignKeys(string $table): Collection; /** * Get a list of store procedures. diff --git a/src/Setting.php b/src/Setting.php index d5b0f5d8..15c585cc 100644 --- a/src/Setting.php +++ b/src/Setting.php @@ -9,46 +9,32 @@ class Setting /** * The default DB connection name, also known as "previous" connection name if migration is called * with `--connection=other` option. - * - * @var string */ - private $defaultConnection; + private string $defaultConnection; - /** @var bool */ - private $useDBCollation; + private bool $useDBCollation; - /** @var bool */ - private $ignoreIndexNames; + private bool $ignoreIndexNames; - /** @var bool */ - private $ignoreForeignKeyNames; + private bool $ignoreForeignKeyNames; - /** @var bool */ - private $squash; + private bool $squash; - /** @var string */ - private $path; + private string $path; - /** @var string */ - private $stubPath; + private string $stubPath; - /** @var \Carbon\Carbon */ - private $date; + private Carbon $date; - /** @var string */ - private $tableFilename; + private string $tableFilename; - /** @var string */ - private $viewFilename; + private string $viewFilename; - /** @var string */ - private $procedureFilename; + private string $procedureFilename; - /** @var string */ - private $fkFilename; + private string $fkFilename; - /** @var bool */ - private $withHasTable; + private bool $withHasTable; public function getDefaultConnection(): string { diff --git a/src/Support/AssetNameQuote.php b/src/Support/AssetNameQuote.php index cf11f8bc..6c09d797 100644 --- a/src/Support/AssetNameQuote.php +++ b/src/Support/AssetNameQuote.php @@ -2,12 +2,13 @@ namespace KitLoong\MigrationsGenerator\Support; +use Illuminate\Support\Facades\DB; +use KitLoong\MigrationsGenerator\Enum\Driver; + trait AssetNameQuote { /** * Checks if this identifier is quoted. - * - * @see \Doctrine\DBAL\Schema\AbstractAsset::isIdentifierQuoted() */ public function isIdentifierQuoted(string $identifier): bool { @@ -16,11 +17,30 @@ public function isIdentifierQuoted(string $identifier): bool /** * Trim quotes from the identifier. - * - * @see \Doctrine\DBAL\Schema\AbstractAsset::trimQuotes() */ public function trimQuotes(string $identifier): string { return str_replace(['`', '"', '[', ']'], '', $identifier); } + + /** + * Wrap a single string in keyword identifiers. + */ + public function quoteIdentifier(string $value): string + { + switch (DB::getDriverName()) { + case Driver::SQLSRV->value: + return $value === '*' ? $value : '[' . str_replace(']', ']]', $value) . ']'; + + case Driver::MYSQL->value: + return $value === '*' ? $value : '`' . str_replace('`', '``', $value) . '`'; + + default: + if ($value !== '*') { + return '"' . str_replace('"', '""', $value) . '"'; + } + + return $value; + } + } } diff --git a/src/Support/CheckLaravelVersion.php b/src/Support/CheckLaravelVersion.php index 50b066e7..90110c47 100644 --- a/src/Support/CheckLaravelVersion.php +++ b/src/Support/CheckLaravelVersion.php @@ -6,34 +6,13 @@ trait CheckLaravelVersion { - public function atLeastLaravel5Dot7(): bool + public function atLeastLaravel11(): bool { - return $this->atLeastLaravelVersion('5.7.0'); - } - - public function atLeastLaravel5Dot8(): bool - { - return $this->atLeastLaravelVersion('5.8.0'); - } - - public function atLeastLaravel6(): bool - { - return $this->atLeastLaravelVersion('6.0'); - } + if (App::version() === '11.x-dev') { + return true; + } - public function atLeastLaravel7(): bool - { - return $this->atLeastLaravelVersion('7.0'); - } - - public function atLeastLaravel8(): bool - { - return $this->atLeastLaravelVersion('8.0'); - } - - public function atLeastLaravel9(): bool - { - return $this->atLeastLaravelVersion('9.0'); + return $this->atLeastLaravelVersion('11.0'); } private function atLeastLaravelVersion(string $version): bool diff --git a/src/Support/CheckMigrationMethod.php b/src/Support/CheckMigrationMethod.php index e1434901..9c29c182 100644 --- a/src/Support/CheckMigrationMethod.php +++ b/src/Support/CheckMigrationMethod.php @@ -2,69 +2,15 @@ namespace KitLoong\MigrationsGenerator\Support; -use Illuminate\Database\Migrations\Migrator; use Illuminate\Database\Schema\Blueprint; -use Illuminate\Database\Schema\Grammars\Grammar; trait CheckMigrationMethod { - use CheckLaravelVersion; - - /** - * `useCurrentOnUpdate` added since Laravel 8. - */ - public function hasUseCurrentOnUpdate(): bool - { - return $this->atLeastLaravel8(); - } - - /** - * `set` added since Laravel 5.8. - */ - public function hasSet(): bool - { - return method_exists(Blueprint::class, 'set'); - } - - /** - * `fulltext` added since Laravel 8. - */ - public function hasFullText(): bool - { - return method_exists(Grammar::class, 'compileFulltext'); - } - - /** - * `tinyText` added since Laravel 8. - */ - public function hasTinyText(): bool - { - return method_exists(Blueprint::class, 'tinyText'); - } - - /** - * `tinyText` added since Laravel 9. - */ - public function hasULID(): bool - { - return method_exists(Blueprint::class, 'ulid'); - } - - /** - * Check if support anonymous migration. - * This feature is added in late Laravel v8 and above. - */ - public function hasAnonymousMigration(): bool - { - return method_exists(Migrator::class, 'getMigrationClass'); - } - /** - * Check if support add comment to a table. - * This feature is added since Laravel v9. + * `geography` added since Laravel 11. */ - public function hasTableComment(): bool + public function hasGeography(): bool { - return method_exists(Blueprint::class, 'comment'); + return method_exists(Blueprint::class, 'geography'); } } diff --git a/src/Support/IndexNameHelper.php b/src/Support/IndexNameHelper.php index 1026dac4..34a05695 100644 --- a/src/Support/IndexNameHelper.php +++ b/src/Support/IndexNameHelper.php @@ -8,14 +8,8 @@ class IndexNameHelper { - /** - * @var \KitLoong\MigrationsGenerator\Setting - */ - private $setting; - - public function __construct(Setting $setting) + public function __construct(private Setting $setting) { - $this->setting = $setting; } /** @@ -31,13 +25,13 @@ public function shouldSkipName(string $table, Index $index): bool } if ( - $index->getType()->equals(IndexType::PRIMARY()) + $index->getType() === IndexType::PRIMARY && $index->getName() === '' ) { return true; } - $indexName = strtolower($table . '_' . implode('_', $index->getColumns()) . '_' . $index->getType()); + $indexName = strtolower($table . '_' . implode('_', $index->getColumns()) . '_' . $index->getType()->value); $indexName = (string) str_replace(['-', '.'], '_', $indexName); return $indexName === $index->getName(); } diff --git a/src/Support/MigrationNameHelper.php b/src/Support/MigrationNameHelper.php index c6e3fecb..3293ebcc 100644 --- a/src/Support/MigrationNameHelper.php +++ b/src/Support/MigrationNameHelper.php @@ -9,12 +9,8 @@ class MigrationNameHelper { use TableName; - /** @var \KitLoong\MigrationsGenerator\Setting */ - private $setting; - - public function __construct(Setting $setting) + public function __construct(private Setting $setting) { - $this->setting = $setting; } /** diff --git a/stubs/migration.generate.anonymous.stub b/stubs/migration.generate.anonymous.stub deleted file mode 100644 index 82673456..00000000 --- a/stubs/migration.generate.anonymous.stub +++ /dev/null @@ -1,26 +0,0 @@ -loadDotenv(); - } catch (InvalidPathException $exception) { + } catch (InvalidPathException) { $this->markTestSkipped('Skipped feature tests.'); } } @@ -91,71 +90,36 @@ protected function getStorageSqlPath(string $path = ''): string return storage_path('sql') . ($path ? DIRECTORY_SEPARATOR . $path : $path); } - protected function migrateGeneral(string $connection): void + protected function migrateGeneral(): void { - $this->migrateFromTemplate($connection, base_path('tests/resources/database/migrations/general')); + $this->migrateFromTemplate(base_path('tests/resources/database/migrations/general')); } - protected function migrateCollation(string $connection): void + protected function migrateCollation(): void { - $this->migrateFromTemplate($connection, base_path('tests/resources/database/migrations/collation')); + $this->migrateFromTemplate(base_path('tests/resources/database/migrations/collation')); } - protected function migrateVendors(string $connection): void + protected function migrateVendors(): void { - $this->migrateFromVendorsTemplate($connection, base_path('tests/resources/database/migrations/vendors')); + $this->migrateFromVendorsTemplate(base_path('tests/resources/database/migrations/vendors')); } - protected function migrateFromTemplate(string $connection, string $templatePath): void + protected function migrateFromTemplate(string $templatePath): void { File::copyDirectory($templatePath, $this->getStorageFromPath()); - - foreach (File::files($this->getStorageFromPath()) as $file) { - $content = str_replace([ - '[db]', - '_DB_', - ], [ - $connection, - ucfirst("$connection"), - ], $file->getContents()); - - File::put($this->getStorageFromPath($file->getBasename()), $content); - File::move( - $this->getStorageFromPath($file->getBasename()), - $this->getStorageFromPath(str_replace('_db_', "_{$connection}_", $file->getBasename())) - ); - } - - $this->runMigrationsFrom($connection, $this->getStorageFromPath()); + $this->runMigrationsFrom($this->getStorageFromPath()); } - protected function migrateFromVendorsTemplate(string $connection, string $templatePath): void + protected function migrateFromVendorsTemplate(string $templatePath): void { File::copyDirectory($templatePath, $this->getStorageFromVendorsPath()); - - foreach (File::files($this->getStorageFromVendorsPath()) as $file) { - $content = str_replace([ - '[db]', - '_DB_', - ], [ - $connection, - ucfirst("$connection"), - ], $file->getContents()); - - File::put($this->getStorageFromVendorsPath($file->getBasename()), $content); - File::move( - $this->getStorageFromVendorsPath($file->getBasename()), - $this->getStorageFromVendorsPath(str_replace('_db_', "_{$connection}_", $file->getBasename())) - ); - } - - $this->runMigrationsFrom($connection, $this->getStorageFromVendorsPath()); + $this->runMigrationsFrom($this->getStorageFromVendorsPath()); } - protected function runMigrationsFrom(string $connection, string $path): void + protected function runMigrationsFrom(string $path): void { $this->artisan('migrate', [ - '--database' => $connection, '--realpath' => true, '--path' => $path, ]); @@ -191,20 +155,20 @@ protected function generateMigrations(array $options = []): void 'migrate:generate', array_merge([ '--path' => $this->getStorageMigrationsPath(), - ], $options) + ], $options), ); $command->expectsQuestion('Do you want to log these migrations in the migrations table?', true); if ($expectConnectionQuestion) { $command->expectsQuestion( 'Log into current connection: ' . $options['--connection'] . '? [Y = ' . $options['--connection'] . ', n = ' . config('database.default') . ' (default connection)]', - true + true, ); } $command->expectsQuestion( 'Next Batch Number is: 1. We recommend using Batch Number 0 so that it becomes the "first" migration. [Default: 0]', - '0' + '0', ); } @@ -238,17 +202,7 @@ protected function truncateMigrationsTable(): void */ protected function getTableNames(): array { - return collect(DB::getDoctrineSchemaManager()->listTableNames()) - ->map(function ($table) { - // The table name may contain quotes. - // Always trim quotes before set into list. - if ($this->isIdentifierQuoted($table)) { - return $this->trimQuotes($table); - } - - return $table; - }) - ->toArray(); + return Schema::getTableListing(); } /** @@ -258,12 +212,6 @@ protected function getTableNames(): array */ protected function getViewNames(): array { - return collect(DB::getDoctrineSchemaManager()->listViews()) - ->map(function (View $view) { - return $view->getName(); - }) - ->toArray(); + return array_column(Schema::getViews(), 'name'); } - - abstract protected function refreshDatabase(): void; } diff --git a/tests/Feature/MariaDB/CommandTest.php b/tests/Feature/MariaDB/CommandTest.php index a62b95de..a52ace62 100644 --- a/tests/Feature/MariaDB/CommandTest.php +++ b/tests/Feature/MariaDB/CommandTest.php @@ -3,20 +3,13 @@ namespace KitLoong\MigrationsGenerator\Tests\Feature\MariaDB; use Illuminate\Support\Facades\DB; -use KitLoong\MigrationsGenerator\Support\CheckMigrationMethod; -/** - * @runTestsInSeparateProcesses - * @preserveGlobalState disabled - */ class CommandTest extends MariaDBTestCase { - use CheckMigrationMethod; - public function testRun(): void { $migrateTemplates = function (): void { - $this->migrateGeneral('mariadb'); + $this->migrateGeneral(); }; $generateMigrations = function (): void { @@ -28,7 +21,7 @@ public function testRun(): void public function testDown(): void { - $this->migrateGeneral('mariadb'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); @@ -47,7 +40,7 @@ public function testDown(): void public function testCollation(): void { $migrateTemplates = function (): void { - $this->migrateCollation('mariadb'); + $this->migrateCollation(); }; $generateMigrations = function (): void { @@ -70,14 +63,14 @@ private function verify(callable $migrateTemplates, callable $generateMigrations $this->refreshDatabase(); - $this->runMigrationsFrom('mariadb', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); $this->truncateMigrationsTable(); $this->dumpSchemaAs($this->getStorageSqlPath('actual.sql')); $this->assertFileEqualsIgnoringOrder( $this->getStorageSqlPath('expected.sql'), - $this->getStorageSqlPath('actual.sql') + $this->getStorageSqlPath('actual.sql'), ); } } diff --git a/tests/Feature/MariaDB/MariaDBTestCase.php b/tests/Feature/MariaDB/MariaDBTestCase.php index 6b86d3a0..d4f50ea4 100644 --- a/tests/Feature/MariaDB/MariaDBTestCase.php +++ b/tests/Feature/MariaDB/MariaDBTestCase.php @@ -40,7 +40,7 @@ protected function getEnvironmentSetUp($app): void protected function dumpSchemaAs(string $destination): void { - $password = (!empty(config('database.connections.mariadb.password')) ? + $password = (config('database.connections.mariadb.password') !== '' ? '-p\'' . config('database.connections.mariadb.password') . '\'' : ''); @@ -56,7 +56,7 @@ protected function dumpSchemaAs(string $destination): void config('database.connections.mariadb.port'), config('database.connections.mariadb.username'), config('database.connections.mariadb.database'), - $destination + $destination, ); exec($command); } diff --git a/tests/Feature/MariaDB/TablePrefixTest.php b/tests/Feature/MariaDB/TablePrefixTest.php new file mode 100644 index 00000000..107a1e02 --- /dev/null +++ b/tests/Feature/MariaDB/TablePrefixTest.php @@ -0,0 +1,53 @@ +migrateGeneral(); + }; + + $generateMigrations = function (): void { + $this->generateMigrations(); + }; + + $this->verify($migrateTemplates, $generateMigrations); + } + + /** + * @inheritDoc + */ + protected function getEnvironmentSetUp($app): void + { + parent::getEnvironmentSetUp($app); + + $app['config']->set('database.connections.mariadb.prefix', 'prefix_'); + } + + private function verify(callable $migrateTemplates, callable $generateMigrations): void + { + $migrateTemplates(); + + $this->truncateMigrationsTable(); + $this->dumpSchemaAs($this->getStorageSqlPath('expected.sql')); + + $generateMigrations(); + + $this->assertMigrations(); + + $this->refreshDatabase(); + + $this->runMigrationsFrom($this->getStorageMigrationsPath()); + + $this->truncateMigrationsTable(); + $this->dumpSchemaAs($this->getStorageSqlPath('actual.sql')); + + $this->assertFileEqualsIgnoringOrder( + $this->getStorageSqlPath('expected.sql'), + $this->getStorageSqlPath('actual.sql'), + ); + } +} diff --git a/tests/Feature/MySQL57/CommandTest.php b/tests/Feature/MySQL57/CommandTest.php index 033e2882..ccfed9ad 100644 --- a/tests/Feature/MySQL57/CommandTest.php +++ b/tests/Feature/MySQL57/CommandTest.php @@ -10,21 +10,14 @@ use KitLoong\MigrationsGenerator\Schema\Models\ForeignKey; use KitLoong\MigrationsGenerator\Schema\Models\Index; use KitLoong\MigrationsGenerator\Schema\MySQLSchema; -use KitLoong\MigrationsGenerator\Support\CheckMigrationMethod; use Throwable; -/** - * @runTestsInSeparateProcesses - * @preserveGlobalState disabled - */ class CommandTest extends MySQL57TestCase { - use CheckMigrationMethod; - public function testRun(): void { $migrateTemplates = function (): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); }; $generateMigrations = function (): void { @@ -36,7 +29,7 @@ public function testRun(): void public function testDown(): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); @@ -55,7 +48,7 @@ public function testDown(): void public function testCollation(): void { $migrateTemplates = function (): void { - $this->migrateCollation('mysql57'); + $this->migrateCollation(); }; $generateMigrations = function (): void { @@ -68,7 +61,7 @@ public function testCollation(): void public function testSquashUp(): void { $migrateTemplates = function (): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); }; $generateMigrations = function (): void { @@ -80,7 +73,7 @@ public function testSquashUp(): void public function testSquashDown(): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); @@ -98,21 +91,21 @@ public function testSquashDown(): void public function testTables(): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); $this->generateMigrations([ '--tables' => implode(',', [ - 'all_columns_mysql57', - 'users_mysql57', - 'users_mysql57_view', + 'all_columns', + 'users', + 'users_view', ]), ]); $this->refreshDatabase(); - $this->runMigrationsFrom('mysql57', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); $tables = $this->getTableNames(); $views = $this->getViewNames(); @@ -120,25 +113,25 @@ public function testTables(): void $this->assertCount(3, $tables); $this->assertCount(1, $views); - $this->assertContains('all_columns_mysql57', $tables); + $this->assertContains('all_columns', $tables); $this->assertContains('migrations', $tables); - $this->assertContains('users_mysql57', $tables); - $this->assertContains('users_mysql57_view', $views); + $this->assertContains('users', $tables); + $this->assertContains('users_view', $views); } public function testIgnore(): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); $allAssets = count($this->getTableNames()) + count($this->getViewNames()); $ignores = [ - 'quoted-name-foreign-mysql57', - 'increments_mysql57', - 'timestamps_mysql57', - 'users_mysql57_view', + 'quoted-name-foreign', + 'increments', + 'timestamps', + 'users_view', ]; $ignoreNotExists = ['not_exists']; @@ -149,7 +142,7 @@ public function testIgnore(): void $this->refreshDatabase(); - $this->runMigrationsFrom('mysql57', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); $tables = $this->getTableNames(); $views = $this->getViewNames(); @@ -160,68 +153,61 @@ public function testIgnore(): void public function testDefaultIndexNames(): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); $this->generateMigrations([ - '--tables' => 'test_index_mysql57', + '--tables' => 'test_index', '--default-index-names' => true, ]); $this->refreshDatabase(); - $this->runMigrationsFrom('mysql57', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); $indexes = app(MySQLSchema::class) - ->getTable('test_index_mysql57') + ->getTable('test_index') ->getIndexes(); - $actualIndexes = $indexes->map(function (Index $index) { - return $index->getName(); - })->toArray(); + $actualIndexes = $indexes->map(static fn (Index $index) => $index->getName())->toArray(); $expectedIndexes = [ '', // PRIMARY - 'test_index_mysql57_chain_index', - 'test_index_mysql57_chain_unique', - 'test_index_mysql57_col_multi1_col_multi2_index', -// 'test_index_mysql57_col_multi1_col_multi2(16)_index', - 'test_index_mysql57_col_multi1_col_multi2_unique', - 'test_index_mysql57_col_multi_custom1_col_multi_custom2_index', - 'test_index_mysql57_col_multi_custom1_col_multi_custom2_unique', - 'test_index_mysql57_column_hyphen_index', - 'test_index_mysql57_index_custom_index', - 'test_index_mysql57_index_index', - 'test_index_mysql57_spatial_index_custom_spatialindex', - 'test_index_mysql57_spatial_index_spatialindex', - 'test_index_mysql57_unique_custom_unique', - 'test_index_mysql57_unique_unique', -// 'test_index_mysql57_with_length(16)_index', -// 'test_index_mysql57_with_length_custom(16)_index', + 'test_index_chain_index', + 'test_index_chain_unique', + 'test_index_col_multi1_col_multi2_index', +// 'test_index_col_multi1_col_multi2(16)_index', + 'test_index_col_multi1_col_multi2_unique', + 'test_index_col_multi_custom1_col_multi_custom2_index', + 'test_index_col_multi_custom1_col_multi_custom2_unique', + 'test_index_column_hyphen_index', + 'test_index_index_custom_index', + 'test_index_index_index', + 'test_index_spatial_index_custom_spatialindex', + 'test_index_spatial_index_spatialindex', + 'test_index_unique_custom_unique', + 'test_index_unique_unique', +// 'test_index_with_length(16)_index', +// 'test_index_with_length_custom(16)_index', + 'test_index_chain_fulltext', + 'test_index_col_multi1_col_multi2_fulltext', + 'test_index_fulltext_custom_fulltext', + 'test_index_fulltext_fulltext', ]; - if ($this->hasFullText()) { - $expectedIndexes = array_merge($expectedIndexes, [ - 'test_index_mysql57_chain_fulltext', - 'test_index_mysql57_col_multi1_col_multi2_fulltext', - 'test_index_mysql57_fulltext_custom_fulltext', - 'test_index_mysql57_fulltext_fulltext', - ]); - } - sort($actualIndexes); sort($expectedIndexes); $this->assertSame( $expectedIndexes, - $actualIndexes + $actualIndexes, ); } public function testDefaultFKNames(): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); @@ -229,25 +215,23 @@ public function testDefaultFKNames(): void $this->refreshDatabase(); - $this->runMigrationsFrom('mysql57', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); - $foreignKeys = app(MySQLSchema::class)->getTableForeignKeys('user_profile_mysql57'); - $foreignKeyNames = $foreignKeys->map(function (ForeignKey $foreignKey) { - return $foreignKey->getName(); - }) + $foreignKeys = app(MySQLSchema::class)->getForeignKeys('user_profile'); + $foreignKeyNames = $foreignKeys->map(static fn (ForeignKey $foreignKey) => $foreignKey->getName()) ->sort() ->values() ->toArray(); $this->assertSame( [ - 'user_profile_mysql57_user_id_fk_constraint_foreign', - 'user_profile_mysql57_user_id_fk_custom_foreign', - 'user_profile_mysql57_user_id_foreign', - 'user_profile_mysql57_user_id_user_sub_id_fk_custom_foreign', - 'user_profile_mysql57_user_id_user_sub_id_foreign', + 'user_profile_user_id_fk_constraint_foreign', + 'user_profile_user_id_fk_custom_foreign', + 'user_profile_user_id_foreign', + 'user_profile_user_id_user_sub_id_fk_custom_foreign', + 'user_profile_user_id_user_sub_id_foreign', ], - $foreignKeyNames + $foreignKeyNames, ); $this->rollbackMigrationsFrom('mysql57', $this->getStorageMigrationsPath()); @@ -256,7 +240,7 @@ public function testDefaultFKNames(): void public function testDate(): void { $migrateTemplates = function (): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); }; $generateMigrations = function (): void { @@ -268,7 +252,7 @@ public function testDate(): void public function testTableFilenameAndViewFilename(): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); @@ -283,13 +267,13 @@ public function testTableFilenameAndViewFilename(): void $migrations[] = substr($migration->getFilenameWithoutExtension(), 18); } - $this->assertContains('custom_all_columns_mysql57_table', $migrations); - $this->assertContains('custom_users_mysql57_view_view', $migrations); + $this->assertContains('custom_all_columns_table', $migrations); + $this->assertContains('custom_users_view_view', $migrations); } public function testProcedureFilename(): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); @@ -301,12 +285,12 @@ public function testProcedureFilename(): void $migrations[] = substr($migration->getFilenameWithoutExtension(), 18); } - $this->assertContains('custom_findNameWithHyphenmysql57_proc', $migrations); + $this->assertContains('custom_findNameWithHyphen_proc', $migrations); } public function testFKFilename(): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); @@ -318,12 +302,12 @@ public function testFKFilename(): void $migrations[] = substr($migration->getFilenameWithoutExtension(), 18); } - $this->assertContains('custom_user_profile_mysql57_table', $migrations); + $this->assertContains('custom_user_profile_table', $migrations); } public function testSkipView(): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); @@ -336,13 +320,13 @@ public function testSkipView(): void $migrations[] = substr($migration->getFilenameWithoutExtension(), $prefixLength); } - $this->assertContains('create_all_columns_mysql57_table', $migrations); - $this->assertNotContains('create_users_mysql57_view_view', $migrations); + $this->assertContains('create_all_columns_table', $migrations); + $this->assertNotContains('create_users_view_view', $migrations); } public function testSkipProcedure(): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); @@ -355,14 +339,14 @@ public function testSkipProcedure(): void $migrations[] = substr($migration->getFilenameWithoutExtension(), $prefixLength); } - $this->assertContains('create_all_columns_mysql57_table', $migrations); + $this->assertContains('create_all_columns_table', $migrations); $this->assertNotContains('create_getNameWithHyphen_proc', $migrations); } public function testWithHasTable(): void { $migrateTemplates = function (): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); }; $generateMigrations = function (): void { @@ -375,7 +359,7 @@ public function testWithHasTable(): void public function testWithHasTableSquash(): void { $migrateTemplates = function (): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); }; $generateMigrations = function (): void { @@ -387,7 +371,7 @@ public function testWithHasTableSquash(): void public function testWillCreateMigrationTable(): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); Schema::dropIfExists('migrations'); $this->generateMigrations(); @@ -397,7 +381,7 @@ public function testWillCreateMigrationTable(): void public function testNoInteraction(): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); $this->dumpSchemaAs($this->getStorageSqlPath('expected.sql')); @@ -406,7 +390,7 @@ public function testNoInteraction(): void [ '--path' => $this->getStorageMigrationsPath(), '--no-interaction' => true, - ] + ], ); $this->assertSame(0, DB::table('migrations')->count()); @@ -414,13 +398,13 @@ public function testNoInteraction(): void $this->assertFileEqualsIgnoringOrder( $this->getStorageSqlPath('expected.sql'), - $this->getStorageSqlPath('actual.sql') + $this->getStorageSqlPath('actual.sql'), ); } public function testSkipLog(): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); $this->dumpSchemaAs($this->getStorageSqlPath('expected.sql')); @@ -429,7 +413,7 @@ public function testSkipLog(): void [ '--path' => $this->getStorageMigrationsPath(), '--skip-log' => true, - ] + ], ); $this->assertSame(0, DB::table('migrations')->count()); @@ -437,13 +421,13 @@ public function testSkipLog(): void $this->assertFileEqualsIgnoringOrder( $this->getStorageSqlPath('expected.sql'), - $this->getStorageSqlPath('actual.sql') + $this->getStorageSqlPath('actual.sql'), ); } public function testLogWithBatch0(): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); $this->dumpSchemaAs($this->getStorageSqlPath('expected.sql')); @@ -453,7 +437,7 @@ public function testLogWithBatch0(): void [ '--path' => $this->getStorageMigrationsPath(), '--log-with-batch' => '0', - ] + ], ); $this->assertMigrations(); @@ -463,13 +447,13 @@ public function testLogWithBatch0(): void $this->assertFileEqualsIgnoringOrder( $this->getStorageSqlPath('expected.sql'), - $this->getStorageSqlPath('actual.sql') + $this->getStorageSqlPath('actual.sql'), ); } public function testLogWithBatch99(): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); $this->dumpSchemaAs($this->getStorageSqlPath('expected.sql')); @@ -479,7 +463,7 @@ public function testLogWithBatch99(): void [ '--path' => $this->getStorageMigrationsPath(), '--log-with-batch' => '99', - ] + ], ); $this->assertMigrations(); @@ -491,7 +475,7 @@ public function testLogWithBatch99(): void $this->assertFileEqualsIgnoringOrder( $this->getStorageSqlPath('expected.sql'), - $this->getStorageSqlPath('actual.sql') + $this->getStorageSqlPath('actual.sql'), ); } @@ -505,15 +489,15 @@ public function testLogWithBatchNaN(): void [ '--path' => $this->getStorageMigrationsPath(), '--log-with-batch' => 'Not a number', - ] + ], ); } public function testSkipVendor(): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); - $this->migrateVendors('mysql57'); + $this->migrateVendors(); // Load migrations from vendors path to mock vendors migration. // Loaded migrations should not be generated. @@ -522,19 +506,17 @@ public function testSkipVendor(): void $tables = $this->getTableNames(); $vendors = [ - 'personal_access_tokens_mysql57', - 'telescope_entries_mysql57', - 'telescope_entries_tags_mysql57', - 'telescope_monitoring_mysql57', + 'personal_access_tokens', + 'telescope_entries', + 'telescope_entries_tags', + 'telescope_monitoring', ]; foreach ($vendors as $vendor) { $this->assertContains($vendor, $tables); } - $tablesWithoutVendors = (new Collection($tables))->filter(function ($table) use ($vendors) { - return !in_array($table, $vendors); - }) + $tablesWithoutVendors = (new Collection($tables))->filter(static fn ($table) => !in_array($table, $vendors)) ->values() ->all(); @@ -544,7 +526,7 @@ public function testSkipVendor(): void $this->refreshDatabase(); - $this->runMigrationsFrom('mysql57', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); $generatedTables = $this->getTableNames(); @@ -564,14 +546,14 @@ private function verify(callable $migrateTemplates, callable $generateMigrations $this->refreshDatabase(); - $this->runMigrationsFrom('mysql57', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); $this->truncateMigrationsTable(); $this->dumpSchemaAs($this->getStorageSqlPath('actual.sql')); $this->assertFileEqualsIgnoringOrder( $this->getStorageSqlPath('expected.sql'), - $this->getStorageSqlPath('actual.sql') + $this->getStorageSqlPath('actual.sql'), ); } } diff --git a/tests/Feature/MySQL57/DBConnectionTest.php b/tests/Feature/MySQL57/DBConnectionTest.php index 217bec8b..d8b7a1bb 100644 --- a/tests/Feature/MySQL57/DBConnectionTest.php +++ b/tests/Feature/MySQL57/DBConnectionTest.php @@ -7,41 +7,8 @@ use Illuminate\Support\Facades\Schema; use PDO; -/** - * @runTestsInSeparateProcesses - * @preserveGlobalState disabled - */ class DBConnectionTest extends MySQL57TestCase { - /** - * @inheritDoc - */ - protected function getEnvironmentSetUp($app): void - { - parent::getEnvironmentSetUp($app); - - $app['config']->set('database.default', 'mysql8'); - $app['config']->set('database.connections.mysql8', [ - 'driver' => 'mysql', - 'url' => null, - 'host' => env('MYSQL8_HOST'), - 'port' => env('MYSQL8_PORT'), - 'database' => env('MYSQL8_DATABASE'), - 'username' => env('MYSQL8_USERNAME'), - 'password' => env('MYSQL8_PASSWORD'), - 'unix_socket' => env('DB_SOCKET', ''), - 'charset' => 'utf8mb4', - 'collation' => 'utf8mb4_general_ci', - 'prefix' => '', - 'prefix_indexes' => true, - 'strict' => true, - 'engine' => null, - 'options' => extension_loaded('pdo_mysql') ? array_filter([ - PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), - ]) : [], - ]); - } - public function tearDown(): void { // Clean "migrations" table after test. @@ -56,13 +23,10 @@ public function tearDown(): void public function testDBConnection(): void { $migrateTemplates = function (): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); }; $generateMigrations = function (): void { - // Needed for Laravel 6 and below. - DB::setDefaultConnection('mysql8'); - $this->generateMigrations(['--connection' => 'mysql57']); $totalMigrations = count(File::allFiles($this->getStorageMigrationsPath())); @@ -74,15 +38,15 @@ public function testDBConnection(): void $this->assertStringContainsString( 'Schema::connection', - File::files($this->getStorageMigrationsPath())[0]->getContents() + File::files($this->getStorageMigrationsPath())[0]->getContents(), ); } public function testLogMigrationToAnotherSource(): void { - $this->migrateGeneral('mysql57'); + DB::setDefaultConnection('mysql57'); + $this->migrateGeneral(); - // Needed for Laravel 6 and below. DB::setDefaultConnection('mysql8'); $this->artisan( @@ -90,16 +54,16 @@ public function testLogMigrationToAnotherSource(): void [ '--connection' => 'mysql57', '--path' => $this->getStorageMigrationsPath(), - ] + ], ) ->expectsQuestion('Do you want to log these migrations in the migrations table?', true) ->expectsQuestion( 'Log into current connection: mysql57? [Y = mysql57, n = mysql8 (default connection)]', - false + false, ) ->expectsQuestion( 'Next Batch Number is: 1. We recommend using Batch Number 0 so that it becomes the "first" migration. [Default: 0]', - '0' + '0', ); $totalMigrations = count(File::allFiles($this->getStorageMigrationsPath())); @@ -107,27 +71,60 @@ public function testLogMigrationToAnotherSource(): void $this->assertSame($totalMigrations, DB::connection('mysql8')->table('migrations')->count()); } + /** + * @inheritDoc + */ + protected function getEnvironmentSetUp($app): void + { + parent::getEnvironmentSetUp($app); + + $app['config']->set('database.default', 'mysql8'); + $app['config']->set('database.connections.mysql8', [ + 'driver' => 'mysql', + 'url' => null, + 'host' => env('MYSQL8_HOST'), + 'port' => env('MYSQL8_PORT'), + 'database' => env('MYSQL8_DATABASE'), + 'username' => env('MYSQL8_USERNAME'), + 'password' => env('MYSQL8_PASSWORD'), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_general_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ]); + } + private function verify(callable $migrateTemplates, callable $generateMigrations): void { + DB::setDefaultConnection('mysql57'); $migrateTemplates(); DB::connection('mysql57')->table('migrations')->truncate(); $this->dumpSchemaAs($this->getStorageSqlPath('expected.sql')); + DB::setDefaultConnection('mysql8'); $generateMigrations(); $this->assertMigrations(); + DB::setDefaultConnection('mysql57'); $this->refreshDatabase(); - $this->runMigrationsFrom('mysql57', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); + DB::setDefaultConnection('mysql57'); DB::connection('mysql57')->table('migrations')->truncate(); $this->dumpSchemaAs($this->getStorageSqlPath('actual.sql')); $this->assertFileEqualsIgnoringOrder( $this->getStorageSqlPath('expected.sql'), - $this->getStorageSqlPath('actual.sql') + $this->getStorageSqlPath('actual.sql'), ); } } diff --git a/tests/Feature/MySQL57/MySQL57TestCase.php b/tests/Feature/MySQL57/MySQL57TestCase.php index 3d8dcdfb..6d154df9 100644 --- a/tests/Feature/MySQL57/MySQL57TestCase.php +++ b/tests/Feature/MySQL57/MySQL57TestCase.php @@ -40,7 +40,7 @@ protected function getEnvironmentSetUp($app): void protected function dumpSchemaAs(string $destination): void { - $password = (!empty(config('database.connections.mysql57.password')) ? + $password = (config('database.connections.mysql57.password') !== '' ? '-p\'' . config('database.connections.mysql57.password') . '\'' : ''); @@ -57,7 +57,7 @@ protected function dumpSchemaAs(string $destination): void config('database.connections.mysql57.port'), config('database.connections.mysql57.username'), config('database.connections.mysql57.database'), - $destination + $destination, ); exec($command); } diff --git a/tests/Feature/MySQL57/StackedCommandTest.php b/tests/Feature/MySQL57/StackedCommandTest.php index 5ae3f905..8530f8e4 100644 --- a/tests/Feature/MySQL57/StackedCommandTest.php +++ b/tests/Feature/MySQL57/StackedCommandTest.php @@ -7,12 +7,48 @@ use Illuminate\Support\Facades\Schema; use Illuminate\Support\Str; use KitLoong\MigrationsGenerator\Setting; -use KitLoong\MigrationsGenerator\Support\CheckLaravelVersion; use PDO; class StackedCommandTest extends MySQL57TestCase { - use CheckLaravelVersion; + public function testRunAsCall(): void + { + Schema::create('migration_table', static function (Blueprint $table): void { + $table->increments('id'); + }); + + Schema::connection('migration2')->create('migration2_table', static function (Blueprint $table): void { + $table->increments('id'); + }); + + $this->assertTrue(Schema::hasTable('migration_table')); + $this->assertTrue(Schema::connection('migration2')->hasTable('migration2_table')); + + $this->generateMigrations(); + + // Setting should reset. + $this->assertEquals(app(Setting::class), new Setting()); + + $this->generateMigrations(['--connection' => 'migration2']); + + $files = File::files($this->getStorageMigrationsPath()); + $this->assertCount(2, $files); + + foreach ($files as $file) { + if (Str::contains($file->getBasename(), 'create_migration_table')) { + $this->assertStringContainsString( + 'migration_table', + $file->getContents(), + ); + continue; + } + + $this->assertStringContainsString( + 'migration2_table', + $file->getContents(), + ); + } + } /** * @inheritDoc @@ -46,11 +82,6 @@ protected function setUp(): void { parent::setUp(); - // `Schema::createDatabase` available since Laravel 8. - if (!$this->atLeastLaravel8()) { - $this->markTestSkipped(); - } - Schema::createDatabase('migration2'); } @@ -60,43 +91,4 @@ protected function tearDown(): void parent::tearDown(); } - - public function testRunAsCall(): void - { - Schema::create('migration_table', function (Blueprint $table): void { - $table->increments('id'); - }); - - Schema::connection('migration2')->create('migration2_table', function (Blueprint $table): void { - $table->increments('id'); - }); - - $this->assertTrue(Schema::hasTable('migration_table')); - $this->assertTrue(Schema::connection('migration2')->hasTable('migration2_table')); - - $this->generateMigrations(); - - // Setting should reset. - $this->assertEquals(app(Setting::class), new Setting()); - - $this->generateMigrations(['--connection' => 'migration2']); - - $files = File::files($this->getStorageMigrationsPath()); - $this->assertCount(2, $files); - - foreach ($files as $file) { - if (Str::contains($file->getBasename(), 'create_migration_table')) { - $this->assertStringContainsString( - 'migration_table', - $file->getContents() - ); - continue; - } - - $this->assertStringContainsString( - 'migration2_table', - $file->getContents() - ); - } - } } diff --git a/tests/Feature/MySQL57/TablePrefixTest.php b/tests/Feature/MySQL57/TablePrefixTest.php index af32a27f..a72a8d66 100644 --- a/tests/Feature/MySQL57/TablePrefixTest.php +++ b/tests/Feature/MySQL57/TablePrefixTest.php @@ -4,20 +4,10 @@ class TablePrefixTest extends MySQL57TestCase { - /** - * @inheritDoc - */ - protected function getEnvironmentSetUp($app): void - { - parent::getEnvironmentSetUp($app); - - $app['config']->set('database.connections.mysql57.prefix', 'kit_'); - } - public function testTablePrefix(): void { $migrateTemplates = function (): void { - $this->migrateGeneral('mysql57'); + $this->migrateGeneral(); }; $generateMigrations = function (): void { @@ -27,6 +17,16 @@ public function testTablePrefix(): void $this->verify($migrateTemplates, $generateMigrations); } + /** + * @inheritDoc + */ + protected function getEnvironmentSetUp($app): void + { + parent::getEnvironmentSetUp($app); + + $app['config']->set('database.connections.mysql57.prefix', 'prefix_'); + } + private function verify(callable $migrateTemplates, callable $generateMigrations): void { $migrateTemplates(); @@ -40,14 +40,14 @@ private function verify(callable $migrateTemplates, callable $generateMigrations $this->refreshDatabase(); - $this->runMigrationsFrom('mysql57', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); $this->truncateMigrationsTable(); $this->dumpSchemaAs($this->getStorageSqlPath('actual.sql')); $this->assertFileEqualsIgnoringOrder( $this->getStorageSqlPath('expected.sql'), - $this->getStorageSqlPath('actual.sql') + $this->getStorageSqlPath('actual.sql'), ); } } diff --git a/tests/Feature/MySQL8/CommandTest.php b/tests/Feature/MySQL8/CommandTest.php index c877d0e3..cd84211e 100644 --- a/tests/Feature/MySQL8/CommandTest.php +++ b/tests/Feature/MySQL8/CommandTest.php @@ -5,16 +5,12 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; -/** - * @runTestsInSeparateProcesses - * @preserveGlobalState disabled - */ class CommandTest extends MySQL8TestCase { public function testRun(): void { $migrateTemplates = function (): void { - $this->migrateGeneral('mysql8'); + $this->migrateGeneral(); }; $generateMigrations = function (): void { @@ -26,7 +22,7 @@ public function testRun(): void public function testDown(): void { - $this->migrateGeneral('mysql8'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); @@ -45,7 +41,7 @@ public function testDown(): void public function testCollation(): void { $migrateTemplates = function (): void { - $this->migrateCollation('mysql8'); + $this->migrateCollation(); }; $generateMigrations = function (): void { @@ -57,9 +53,9 @@ public function testCollation(): void public function testSkipVendor(): void { - $this->migrateGeneral('mysql8'); + $this->migrateGeneral(); - $this->migrateVendors('mysql8'); + $this->migrateVendors(); // Load migrations from vendors path to mock vendors migration. // Loaded migrations should not be generated. @@ -68,19 +64,17 @@ public function testSkipVendor(): void $tables = $this->getTableNames(); $vendors = [ - 'personal_access_tokens_mysql8', - 'telescope_entries_mysql8', - 'telescope_entries_tags_mysql8', - 'telescope_monitoring_mysql8', + 'personal_access_tokens', + 'telescope_entries', + 'telescope_entries_tags', + 'telescope_monitoring', ]; foreach ($vendors as $vendor) { $this->assertContains($vendor, $tables); } - $tablesWithoutVendors = (new Collection($tables))->filter(function ($table) use ($vendors) { - return !in_array($table, $vendors); - }) + $tablesWithoutVendors = (new Collection($tables))->filter(static fn ($table) => !in_array($table, $vendors)) ->values() ->all(); @@ -90,7 +84,7 @@ public function testSkipVendor(): void $this->refreshDatabase(); - $this->runMigrationsFrom('mysql8', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); $generatedTables = $this->getTableNames(); @@ -110,14 +104,14 @@ private function verify(callable $migrateTemplates, callable $generateMigrations $this->refreshDatabase(); - $this->runMigrationsFrom('mysql8', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); $this->truncateMigrationsTable(); $this->dumpSchemaAs($this->getStorageSqlPath('actual.sql')); $this->assertFileEqualsIgnoringOrder( $this->getStorageSqlPath('expected.sql'), - $this->getStorageSqlPath('actual.sql') + $this->getStorageSqlPath('actual.sql'), ); } } diff --git a/tests/Feature/MySQL8/MySQL8TestCase.php b/tests/Feature/MySQL8/MySQL8TestCase.php index adddb883..90a7666a 100644 --- a/tests/Feature/MySQL8/MySQL8TestCase.php +++ b/tests/Feature/MySQL8/MySQL8TestCase.php @@ -40,7 +40,7 @@ protected function getEnvironmentSetUp($app): void protected function dumpSchemaAs(string $destination): void { - $password = (!empty(config('database.connections.mysql8.password')) ? + $password = (config('database.connections.mysql8.password') !== '' ? '-p\'' . config('database.connections.mysql8.password') . '\'' : ''); @@ -56,7 +56,7 @@ protected function dumpSchemaAs(string $destination): void config('database.connections.mysql8.port'), config('database.connections.mysql8.username'), config('database.connections.mysql8.database'), - $destination + $destination, ); exec($command); } diff --git a/tests/Feature/MySQL8/TablePrefixTest.php b/tests/Feature/MySQL8/TablePrefixTest.php new file mode 100644 index 00000000..2b13639b --- /dev/null +++ b/tests/Feature/MySQL8/TablePrefixTest.php @@ -0,0 +1,53 @@ +migrateGeneral(); + }; + + $generateMigrations = function (): void { + $this->generateMigrations(); + }; + + $this->verify($migrateTemplates, $generateMigrations); + } + + /** + * @inheritDoc + */ + protected function getEnvironmentSetUp($app): void + { + parent::getEnvironmentSetUp($app); + + $app['config']->set('database.connections.mysql8.prefix', 'prefix_'); + } + + private function verify(callable $migrateTemplates, callable $generateMigrations): void + { + $migrateTemplates(); + + $this->truncateMigrationsTable(); + $this->dumpSchemaAs($this->getStorageSqlPath('expected.sql')); + + $generateMigrations(); + + $this->assertMigrations(); + + $this->refreshDatabase(); + + $this->runMigrationsFrom($this->getStorageMigrationsPath()); + + $this->truncateMigrationsTable(); + $this->dumpSchemaAs($this->getStorageSqlPath('actual.sql')); + + $this->assertFileEqualsIgnoringOrder( + $this->getStorageSqlPath('expected.sql'), + $this->getStorageSqlPath('actual.sql'), + ); + } +} diff --git a/tests/Feature/PgSQL/CommandTest.php b/tests/Feature/PgSQL/CommandTest.php index 816a7386..a237565c 100644 --- a/tests/Feature/PgSQL/CommandTest.php +++ b/tests/Feature/PgSQL/CommandTest.php @@ -4,14 +4,8 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\Config; -use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\File; use KitLoong\MigrationsGenerator\Support\CheckLaravelVersion; -/** - * @runTestsInSeparateProcesses - * @preserveGlobalState disabled - */ class CommandTest extends PgSQLTestCase { use CheckLaravelVersion; @@ -19,49 +13,20 @@ class CommandTest extends PgSQLTestCase public function testRun(): void { $migrateTemplates = function (): void { - $this->migrateGeneral('pgsql'); - - // Test timestamp default now() - DB::statement( - "ALTER TABLE all_columns_pgsql ADD COLUMN timestamp_defaultnow timestamp(0) without time zone DEFAULT now() NOT NULL" - ); - - DB::statement( - "ALTER TABLE all_columns_pgsql ADD COLUMN status my_status NOT NULL" - ); - - DB::statement( - "ALTER TABLE all_columns_pgsql ADD COLUMN timestamp_default_timezone_now timestamp(0) without time zone DEFAULT timezone('Europe/Rome'::text, now()) NOT NULL" - ); + $this->migrateGeneral(); }; $generateMigrations = function (): void { $this->generateMigrations(); }; - $beforeVerify = function (): void { - $this->assertLineExistsThenReplace( - $this->getStorageSqlPath('actual.sql'), - 'timestamp_defaultnow timestamp(0) without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL' - ); - - $this->assertLineExistsThenReplace( - $this->getStorageSqlPath('expected.sql'), - 'timestamp_defaultnow timestamp(0) without time zone DEFAULT now() NOT NULL' - ); - }; - - $this->verify($migrateTemplates, $generateMigrations, $beforeVerify); + $this->verify($migrateTemplates, $generateMigrations); } public function testSquashUp(): void { $migrateTemplates = function (): void { - $this->migrateGeneral('pgsql'); - - DB::statement( - "ALTER TABLE all_columns_pgsql ADD COLUMN status my_status NOT NULL" - ); + $this->migrateGeneral(); }; $generateMigrations = function (): void { @@ -74,7 +39,7 @@ public function testSquashUp(): void public function testCollation(): void { $migrateTemplates = function (): void { - $this->migrateCollation('pgsql'); + $this->migrateCollation(); }; $generateMigrations = function (): void { @@ -86,26 +51,26 @@ public function testCollation(): void public function testIgnore(): void { - $this->migrateGeneral('pgsql'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); $this->generateMigrations([ '--ignore' => implode(',', [ - 'name-with-hyphen-pgsql', - 'name-with-hyphen-pgsql_view', + 'name-with-hyphen', + 'name-with-hyphen_view', ]), ]); $this->refreshDatabase(); - $this->runMigrationsFrom('pgsql', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); $tables = $this->getTableNames(); $views = $this->getViewNames(); - $this->assertNotContains('name-with-hyphen-pgsql', $tables); - $this->assertNotContains('public."name-with-hyphen-pgsql_view"', $views); + $this->assertNotContains('name-with-hyphen', $tables); + $this->assertNotContains('public."name-with-hyphen_view"', $views); } /** @@ -113,12 +78,8 @@ public function testIgnore(): void * * @see https://laravel.com/docs/9.x/upgrade#postgres-schema-configuration */ - public function testRunWithSearchPath(): void + public function testWithSearchPath(): void { - if (!$this->atLeastLaravel9()) { - $this->markTestSkipped(); - } - // Unset `schema` Config::set('database.connections.pgsql.schema'); $this->assertNull(Config::get('database.connections.pgsql.schema')); @@ -127,7 +88,7 @@ public function testRunWithSearchPath(): void Config::set('database.connections.pgsql.search_path', 'public'); $migrateTemplates = function (): void { - $this->migrateGeneral('pgsql'); + $this->migrateGeneral(); }; $generateMigrations = function (): void { @@ -140,11 +101,7 @@ public function testRunWithSearchPath(): void public function testWithHasTable(): void { $migrateTemplates = function (): void { - $this->migrateGeneral('pgsql'); - - DB::statement( - "ALTER TABLE all_columns_pgsql ADD COLUMN status my_status NOT NULL" - ); + $this->migrateGeneral(); }; $generateMigrations = function (): void { @@ -157,11 +114,7 @@ public function testWithHasTable(): void public function testWithHasTableSquash(): void { $migrateTemplates = function (): void { - $this->migrateGeneral('pgsql'); - - DB::statement( - "ALTER TABLE all_columns_pgsql ADD COLUMN status my_status NOT NULL" - ); + $this->migrateGeneral(); }; $generateMigrations = function (): void { @@ -173,9 +126,9 @@ public function testWithHasTableSquash(): void public function testSkipVendor(): void { - $this->migrateGeneral('pgsql'); + $this->migrateGeneral(); - $this->migrateVendors('pgsql'); + $this->migrateVendors(); // Load migrations from vendors path to mock vendors migration. // Loaded migrations should not be generated. @@ -184,19 +137,17 @@ public function testSkipVendor(): void $tables = $this->getTableNames(); $vendors = [ - 'personal_access_tokens_pgsql', - 'telescope_entries_pgsql', - 'telescope_entries_tags_pgsql', - 'telescope_monitoring_pgsql', + 'personal_access_tokens', + 'telescope_entries', + 'telescope_entries_tags', + 'telescope_monitoring', ]; foreach ($vendors as $vendor) { $this->assertContains($vendor, $tables); } - $tablesWithoutVendors = (new Collection($tables))->filter(function ($table) use ($vendors) { - return !in_array($table, $vendors); - }) + $tablesWithoutVendors = (new Collection($tables))->filter(static fn ($table) => !in_array($table, $vendors)) ->values() ->all(); @@ -206,7 +157,7 @@ public function testSkipVendor(): void $this->refreshDatabase(); - $this->runMigrationsFrom('pgsql', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); $generatedTables = $this->getTableNames(); @@ -216,7 +167,7 @@ public function testSkipVendor(): void $this->assertSame($tablesWithoutVendors, $generatedTables); } - private function verify(callable $migrateTemplates, callable $generateMigrations, ?callable $beforeVerify = null): void + private function verify(callable $migrateTemplates, callable $generateMigrations): void { $migrateTemplates(); @@ -229,35 +180,14 @@ private function verify(callable $migrateTemplates, callable $generateMigrations $this->refreshDatabase(); - $this->runMigrationsFrom('pgsql', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); $this->truncateMigrationsTable(); $this->dumpSchemaAs($this->getStorageSqlPath('actual.sql')); - $beforeVerify === null ?: $beforeVerify(); - $this->assertFileEqualsIgnoringOrder( $this->getStorageSqlPath('expected.sql'), - $this->getStorageSqlPath('actual.sql') - ); - } - - private function assertLineExistsThenReplace(string $file, string $line): void - { - $this->assertTrue( - str_contains( - File::get($file), - $line - ) - ); - - File::put( - $file, - str_replace( - $line, - 'replaced', - File::get($file) - ) + $this->getStorageSqlPath('actual.sql'), ); } } diff --git a/tests/Feature/PgSQL/PgSQLTestCase.php b/tests/Feature/PgSQL/PgSQLTestCase.php index bfa72095..f4419b81 100644 --- a/tests/Feature/PgSQL/PgSQLTestCase.php +++ b/tests/Feature/PgSQL/PgSQLTestCase.php @@ -2,8 +2,9 @@ namespace KitLoong\MigrationsGenerator\Tests\Feature\PgSQL; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Str; +use Illuminate\Support\Facades\Schema; use KitLoong\MigrationsGenerator\Tests\Feature\FeatureTestCase; abstract class PgSQLTestCase extends FeatureTestCase @@ -36,7 +37,7 @@ protected function setUp(): void { parent::setUp(); - // Create for custom column type test. + // Create for user defined type column test. DB::statement("CREATE TYPE my_status AS enum ('PENDING', 'ACTIVE', 'SUSPENDED')"); } @@ -56,7 +57,7 @@ protected function dumpSchemaAs(string $destination): void config('database.connections.pgsql.port'), config('database.connections.pgsql.username'), config('database.connections.pgsql.database'), - $destination + $destination, ); exec($command); } @@ -69,21 +70,23 @@ protected function refreshDatabase(): void protected function dropAllTablesAndViews(): void { - $tables = DB::getDoctrineSchemaManager()->listTableNames(); - - foreach ($tables as $table) { - if (Str::startsWith($table, 'tiger.')) { - continue; - } - - if (Str::startsWith($table, 'topology.')) { - continue; - } - - // CASCADE, automatically drop objects that depend on the table. - // This statement will drop views which depend on the table. - DB::statement("DROP TABLE IF EXISTS $table cascade"); - } + (new Collection(Schema::getTables())) + ->each(static function (array $table): void { + if ($table['name'] === 'spatial_ref_sys') { + return; + } + + // Schema name defined in the framework configuration. + $searchPath = DB::connection()->getConfig('search_path') ?: DB::connection()->getConfig('schema'); + + if ($table['schema'] !== $searchPath) { + return; + } + + // CASCADE, automatically drop objects that depend on the table. + // This statement will drop views which depend on the table. + DB::statement("DROP TABLE IF EXISTS \"" . $table['name'] . "\" cascade"); + }); } protected function dropAllProcedures(): void @@ -95,7 +98,7 @@ protected function dropAllProcedures(): void FROM pg_catalog.pg_proc JOIN pg_namespace ON pg_catalog.pg_proc.pronamespace = pg_namespace.oid WHERE prokind = 'p' - AND pg_namespace.nspname = '" . $searchPath . "'" + AND pg_namespace.nspname = '" . $searchPath . "'", ); foreach ($procedures as $procedure) { diff --git a/tests/Feature/PgSQL/TablePrefixTest.php b/tests/Feature/PgSQL/TablePrefixTest.php index 0aef6b48..83f529aa 100644 --- a/tests/Feature/PgSQL/TablePrefixTest.php +++ b/tests/Feature/PgSQL/TablePrefixTest.php @@ -2,28 +2,12 @@ namespace KitLoong\MigrationsGenerator\Tests\Feature\PgSQL; -use Illuminate\Support\Facades\DB; - class TablePrefixTest extends PgSQLTestCase { - /** - * @inheritDoc - */ - protected function getEnvironmentSetUp($app): void - { - parent::getEnvironmentSetUp($app); - - $app['config']->set('database.connections.pgsql.prefix', 'kit_'); - } - public function testTablePrefix(): void { $migrateTemplates = function (): void { - $this->migrateGeneral('pgsql'); - - DB::statement( - "ALTER TABLE kit_all_columns_pgsql ADD COLUMN status my_status NOT NULL" - ); + $this->migrateGeneral(); }; $generateMigrations = function (): void { @@ -33,6 +17,16 @@ public function testTablePrefix(): void $this->verify($migrateTemplates, $generateMigrations); } + /** + * @inheritDoc + */ + protected function getEnvironmentSetUp($app): void + { + parent::getEnvironmentSetUp($app); + + $app['config']->set('database.connections.pgsql.prefix', 'prefix_'); + } + private function verify(callable $migrateTemplates, callable $generateMigrations): void { $migrateTemplates(); @@ -46,14 +40,14 @@ private function verify(callable $migrateTemplates, callable $generateMigrations $this->refreshDatabase(); - $this->runMigrationsFrom('pgsql', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); $this->truncateMigrationsTable(); $this->dumpSchemaAs($this->getStorageSqlPath('actual.sql')); $this->assertFileEqualsIgnoringOrder( $this->getStorageSqlPath('expected.sql'), - $this->getStorageSqlPath('actual.sql') + $this->getStorageSqlPath('actual.sql'), ); } } diff --git a/tests/Feature/SQLSrv/CommandTest.php b/tests/Feature/SQLSrv/CommandTest.php index c5e03b92..6e850271 100644 --- a/tests/Feature/SQLSrv/CommandTest.php +++ b/tests/Feature/SQLSrv/CommandTest.php @@ -6,39 +6,12 @@ use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\File; -/** - * @runTestsInSeparateProcesses - * @preserveGlobalState disabled - */ class CommandTest extends SQLSrvTestCase { - protected function setUp(): void - { - parent::setUp(); - - // Method `typeDateTime` is using different implementation since v5.8 - // It is hard to create unified UT across Laravel <= v5.7 and >= v5.8 - // To simplify, dropping UT check for version <= 5.7. - // https://github.com/laravel/framework/blob/5.7/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php#L523 - // https://github.com/laravel/framework/blob/5.8/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php#L538 - if ($this->atLeastLaravel5Dot8()) { - return; - } - - $this->markTestSkipped(); - } - - /** - * @throws \Doctrine\DBAL\Exception - */ public function testRun(): void { $migrateTemplates = function (): void { - $this->migrateGeneral('sqlsrv'); - - DB::statement( - "ALTER TABLE all_columns_sqlsrv ADD accountnumber accountnumber NOT NULL" - ); + $this->migrateGeneral(); }; $generateMigrations = function (): void { @@ -51,11 +24,11 @@ public function testRun(): void public function testUnsupportedColumns(): void { DB::statement( - "CREATE TABLE custom_sqlsrv ( + "CREATE TABLE custom ( money money, smallmoney smallmoney, [name.dot] varchar(255) - )" + )", ); $this->generateMigrations(); @@ -65,23 +38,23 @@ public function testUnsupportedColumns(): void $this->assertStringContainsString( '$table->decimal(\'money\', 19, 4)->nullable();', - $migration->getContents() + $migration->getContents(), ); $this->assertStringContainsString( '$table->decimal(\'smallmoney\', 10, 4)->nullable();', - $migration->getContents() + $migration->getContents(), ); $this->assertStringContainsString( '$table->string(\'name.dot\')->nullable()', - $migration->getContents() + $migration->getContents(), ); } public function testDown(): void { - $this->migrateGeneral('sqlsrv'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); @@ -97,13 +70,10 @@ public function testDown(): void $this->assertSame(0, DB::table('migrations')->count()); } - /** - * @throws \Doctrine\DBAL\Exception - */ public function testCollation(): void { $migrateTemplates = function (): void { - $this->migrateCollation('sqlsrv'); + $this->migrateCollation(); }; $generateMigrations = function (): void { @@ -115,10 +85,10 @@ public function testCollation(): void public function testGenerateXml(): void { - $this->migrateGeneral('sqlsrv'); + $this->migrateGeneral(); // Test xml column - DB::statement("alter table all_columns_sqlsrv add xml xml"); + DB::statement("alter table all_columns add xml xml"); $this->truncateMigrationsTable(); @@ -129,9 +99,9 @@ public function testGenerateXml(): void public function testSkipVendor(): void { - $this->migrateGeneral('sqlsrv'); + $this->migrateGeneral(); - $this->migrateVendors('sqlsrv'); + $this->migrateVendors(); // Load migrations from vendors path to mock vendors migration. // Loaded migrations should not be generated. @@ -140,19 +110,17 @@ public function testSkipVendor(): void $tables = $this->getTableNames(); $vendors = [ - 'personal_access_tokens_sqlsrv', - 'telescope_entries_sqlsrv', - 'telescope_entries_tags_sqlsrv', - 'telescope_monitoring_sqlsrv', + 'personal_access_tokens', + 'telescope_entries', + 'telescope_entries_tags', + 'telescope_monitoring', ]; foreach ($vendors as $vendor) { $this->assertContains($vendor, $tables); } - $tablesWithoutVendors = (new Collection($tables))->filter(function ($table) use ($vendors) { - return !in_array($table, $vendors); - }) + $tablesWithoutVendors = (new Collection($tables))->filter(static fn ($table) => !in_array($table, $vendors)) ->values() ->all(); @@ -162,16 +130,13 @@ public function testSkipVendor(): void $this->refreshDatabase(); - $this->runMigrationsFrom('sqlsrv', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); $generatedTables = $this->getTableNames(); $this->assertSame($tablesWithoutVendors, $generatedTables); } - /** - * @throws \Doctrine\DBAL\Exception - */ private function verify(callable $migrateTemplates, callable $generateMigrations): void { $migrateTemplates(); @@ -185,14 +150,14 @@ private function verify(callable $migrateTemplates, callable $generateMigrations $this->refreshDatabase(); - $this->runMigrationsFrom('sqlsrv', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); $this->truncateMigrationsTable(); $this->dumpSchemaAs($this->getStorageSqlPath('actual.sql')); $this->assertFileEqualsIgnoringOrder( $this->getStorageSqlPath('expected.sql'), - $this->getStorageSqlPath('actual.sql') + $this->getStorageSqlPath('actual.sql'), ); } } diff --git a/tests/Feature/SQLSrv/SQLSrvTestCase.php b/tests/Feature/SQLSrv/SQLSrvTestCase.php index dcf555e8..0b43a6e3 100644 --- a/tests/Feature/SQLSrv/SQLSrvTestCase.php +++ b/tests/Feature/SQLSrv/SQLSrvTestCase.php @@ -5,13 +5,10 @@ use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Schema; -use KitLoong\MigrationsGenerator\Support\CheckLaravelVersion; use KitLoong\MigrationsGenerator\Tests\Feature\FeatureTestCase; abstract class SQLSrvTestCase extends FeatureTestCase { - use CheckLaravelVersion; - /** * @inheritDoc */ @@ -41,23 +38,23 @@ protected function setUp(): void // Drop first. DB::statement("DROP TYPE IF EXISTS accountnumber"); - // Create for custom column type test. + // Create for user defined type column test. DB::statement("CREATE TYPE accountnumber FROM [nvarchar](15) NULL"); } protected function dumpSchemaAs(string $destination): void { - $tables = DB::getDoctrineSchemaManager()->listTableNames(); + $tables = Schema::getTableListing(); $sqls = []; foreach ($tables as $table) { $sqls[] = "EXEC sp_help '" . $table . "';"; } - $views = DB::getDoctrineSchemaManager()->listViews(); + $views = array_column(Schema::getViews(), 'name'); foreach ($views as $view) { - $sqls[] = "EXEC sp_helptext '" . $view->getName() . "';"; + $sqls[] = "EXEC sp_helptext '" . $view . "';"; } $procedures = $this->getAllProcedures(); @@ -74,7 +71,7 @@ protected function dumpSchemaAs(string $destination): void config('database.connections.sqlsrv.password'), config('database.connections.sqlsrv.database'), implode('', $sqls), - $this->getStorageSqlPath('temp.sql') + $this->getStorageSqlPath('temp.sql'), ); exec($command); @@ -83,29 +80,11 @@ protected function dumpSchemaAs(string $destination): void protected function refreshDatabase(): void { - $this->dropAllViews(); + Schema::dropAllViews(); Schema::dropAllTables(); $this->dropAllProcedures(); } - protected function dropAllViews(): void - { - // `dropAllViews` available in Laravel >= 6.x - if ($this->atLeastLaravel6()) { - Schema::dropAllViews(); - return; - } - - // See https://github.com/laravel/framework/blob/6.x/src/Illuminate/Database/Schema/Grammars/SqlServerGrammar.php#L360 - DB::statement( - "DECLARE @sql NVARCHAR(MAX) = N''; - SELECT @sql += 'DROP VIEW ' + QUOTENAME(OBJECT_SCHEMA_NAME(object_id)) + '.' + QUOTENAME(name) + ';' - FROM sys.views; - - EXEC sp_executesql @sql;" - ); - } - protected function dropAllProcedures(): void { $procedures = $this->getAllProcedures(); @@ -126,7 +105,7 @@ protected function getAllProcedures(): array INNER JOIN sys.sql_modules ON (sys.sysobjects.id = sys.sql_modules.object_id) WHERE type = 'P' AND definition IS NOT NULL - ORDER BY name" + ORDER BY name", ); } diff --git a/tests/Feature/SQLSrv/TablePrefixTest.php b/tests/Feature/SQLSrv/TablePrefixTest.php new file mode 100644 index 00000000..1b8489fc --- /dev/null +++ b/tests/Feature/SQLSrv/TablePrefixTest.php @@ -0,0 +1,53 @@ +migrateGeneral(); + }; + + $generateMigrations = function (): void { + $this->generateMigrations(); + }; + + $this->verify($migrateTemplates, $generateMigrations); + } + + /** + * @inheritDoc + */ + protected function getEnvironmentSetUp($app): void + { + parent::getEnvironmentSetUp($app); + + $app['config']->set('database.connections.sqlsrv.prefix', 'prefix_'); + } + + private function verify(callable $migrateTemplates, callable $generateMigrations): void + { + $migrateTemplates(); + + $this->truncateMigrationsTable(); + $this->dumpSchemaAs($this->getStorageSqlPath('expected.sql')); + + $generateMigrations(); + + $this->assertMigrations(); + + $this->refreshDatabase(); + + $this->runMigrationsFrom($this->getStorageMigrationsPath()); + + $this->truncateMigrationsTable(); + $this->dumpSchemaAs($this->getStorageSqlPath('actual.sql')); + + $this->assertFileEqualsIgnoringOrder( + $this->getStorageSqlPath('expected.sql'), + $this->getStorageSqlPath('actual.sql'), + ); + } +} diff --git a/tests/Feature/SQLite/CommandTest.php b/tests/Feature/SQLite/CommandTest.php index 4ded17f3..4f84cf4d 100644 --- a/tests/Feature/SQLite/CommandTest.php +++ b/tests/Feature/SQLite/CommandTest.php @@ -4,20 +4,13 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; -use KitLoong\MigrationsGenerator\Support\CheckMigrationMethod; -/** - * @runTestsInSeparateProcesses - * @preserveGlobalState disabled - */ class CommandTest extends SQLiteTestCase { - use CheckMigrationMethod; - public function testRun(): void { $migrateTemplates = function (): void { - $this->migrateGeneral('sqlite'); + $this->migrateGeneral(); }; $generateMigrations = function (): void { @@ -29,7 +22,7 @@ public function testRun(): void public function testDown(): void { - $this->migrateGeneral('sqlite'); + $this->migrateGeneral(); $this->truncateMigrationsTable(); @@ -45,24 +38,24 @@ public function testDown(): void $this->assertSame(0, DB::table('migrations')->count()); } - public function testCollation(): void - { - $migrateTemplates = function (): void { - $this->migrateCollation('sqlite'); - }; - - $generateMigrations = function (): void { - $this->generateMigrations(['--use-db-collation' => true]); - }; - - $this->verify($migrateTemplates, $generateMigrations); - } +// public function testCollation(): void +// { +// $migrateTemplates = function (): void { +// $this->migrateCollation(); +// }; +// +// $generateMigrations = function (): void { +// $this->generateMigrations(['--use-db-collation' => true]); +// }; +// +// $this->verify($migrateTemplates, $generateMigrations); +// } public function testSkipVendor(): void { - $this->migrateGeneral('sqlite'); + $this->migrateGeneral(); - $this->migrateVendors('sqlite'); + $this->migrateVendors(); // Load migrations from vendors path to mock vendors migration. // Loaded migrations should not be generated. @@ -71,19 +64,17 @@ public function testSkipVendor(): void $tables = $this->getTableNames(); $vendors = [ - 'personal_access_tokens_sqlite', - 'telescope_entries_sqlite', - 'telescope_entries_tags_sqlite', - 'telescope_monitoring_sqlite', + 'personal_access_tokens', + 'telescope_entries', + 'telescope_entries_tags', + 'telescope_monitoring', ]; foreach ($vendors as $vendor) { $this->assertContains($vendor, $tables); } - $tablesWithoutVendors = (new Collection($tables))->filter(function ($table) use ($vendors) { - return !in_array($table, $vendors); - }) + $tablesWithoutVendors = (new Collection($tables))->filter(static fn ($table) => !in_array($table, $vendors)) ->values() ->all(); @@ -93,7 +84,7 @@ public function testSkipVendor(): void $this->refreshDatabase(); - $this->runMigrationsFrom('sqlite', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); $generatedTables = $this->getTableNames(); @@ -113,14 +104,14 @@ private function verify(callable $migrateTemplates, callable $generateMigrations $this->refreshDatabase(); - $this->runMigrationsFrom('sqlite', $this->getStorageMigrationsPath()); + $this->runMigrationsFrom($this->getStorageMigrationsPath()); $this->truncateMigrationsTable(); $this->dumpSchemaAs($this->getStorageSqlPath('actual.sql')); $this->assertFileEqualsIgnoringOrder( $this->getStorageSqlPath('expected.sql'), - $this->getStorageSqlPath('actual.sql') + $this->getStorageSqlPath('actual.sql'), ); } } diff --git a/tests/Feature/SQLite/SQLiteTestCase.php b/tests/Feature/SQLite/SQLiteTestCase.php index 2591282c..599dee5a 100644 --- a/tests/Feature/SQLite/SQLiteTestCase.php +++ b/tests/Feature/SQLite/SQLiteTestCase.php @@ -31,7 +31,7 @@ protected function dumpSchemaAs(string $destination): void $command = sprintf( 'sqlite3 %s .dump > %s', config('database.connections.sqlite.database'), - $destination + $destination, ); exec($command); } diff --git a/tests/MigrationWriterTest.php b/tests/MigrationWriterTest.php index 631a3669..1fdd338d 100644 --- a/tests/MigrationWriterTest.php +++ b/tests/MigrationWriterTest.php @@ -4,7 +4,10 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; +use KitLoong\MigrationsGenerator\Enum\Migrations\Method\ColumnModifier; +use KitLoong\MigrationsGenerator\Enum\Migrations\Method\ColumnType; use KitLoong\MigrationsGenerator\Enum\Migrations\Method\SchemaBuilder; +use KitLoong\MigrationsGenerator\Enum\Migrations\Property\TableProperty; use KitLoong\MigrationsGenerator\Migration\Blueprint\SchemaBlueprint; use KitLoong\MigrationsGenerator\Migration\Blueprint\TableBlueprint; use KitLoong\MigrationsGenerator\Migration\Enum\MigrationFileType; @@ -21,26 +24,26 @@ public function testWrite(): void $setting->setDefaultConnection(DB::getDefaultConnection()); $setting->setWithHasTable(false); - $this->mock(TableName::class, function (MockInterface $mock): void { + $this->mock(TableName::class, static function (MockInterface $mock): void { $mock->shouldReceive('stripPrefix') ->andReturn('test'); }); - $up = new SchemaBlueprint('users', SchemaBuilder::CREATE()); + $up = new SchemaBlueprint('users', SchemaBuilder::CREATE); $blueprint = new TableBlueprint(); - $blueprint->setProperty('collation', 'utf-8'); - $blueprint->setProperty('something', 1); - $blueprint->setProperty('something', true); - $blueprint->setProperty('something', false); - $blueprint->setProperty('something', null); - $blueprint->setProperty('something', [1, 2, 3, 'abc', null, true, false, ['a', 2, 'c']]); + $blueprint->setProperty(TableProperty::COLLATION, 'utf-8'); + $blueprint->setProperty(TableProperty::CHARSET, 1); + $blueprint->setProperty(TableProperty::CHARSET, true); + $blueprint->setProperty(TableProperty::CHARSET, false); + $blueprint->setProperty(TableProperty::CHARSET, null); + $blueprint->setProperty(TableProperty::CHARSET, [1, 2, 3, 'abc', null, true, false, ['a', 2, 'c']]); $blueprint->setLineBreak(); - $blueprint->setMethodByName('string', 'name', 100) - ->chain('comment', 'Hello') - ->chain('default', 'Test'); + $blueprint->setMethodByName(ColumnType::STRING, 'name', 100) + ->chain(ColumnModifier::COMMENT, 'Hello') + ->chain(ColumnModifier::DEFAULT, 'Test'); $up->setBlueprint($blueprint); - $down = new SchemaBlueprint('users', SchemaBuilder::DROP_IF_EXISTS()); + $down = new SchemaBlueprint('users', SchemaBuilder::DROP_IF_EXISTS); $migration = app(MigrationWriter::class); $migration->writeTo( @@ -49,7 +52,7 @@ public function testWrite(): void 'Tester', new Collection([$up]), new Collection([$down]), - MigrationFileType::TABLE() + MigrationFileType::TABLE, ); $this->assertFileExists(storage_path('migration.php')); diff --git a/tests/Support/CheckLaravelVersionTest.php b/tests/Support/CheckLaravelVersionTest.php deleted file mode 100644 index 99a113ae..00000000 --- a/tests/Support/CheckLaravelVersionTest.php +++ /dev/null @@ -1,80 +0,0 @@ -andReturn('5.6.0')->once(); - $this->assertFalse($this->stubInstance()->atLeastLaravel5Dot7()); - - App::shouldReceive('version')->andReturn('5.7.0')->once(); - $this->assertTrue($this->stubInstance()->atLeastLaravel5Dot7()); - - App::shouldReceive('version')->andReturn('5.8.0')->once(); - $this->assertTrue($this->stubInstance()->atLeastLaravel5Dot7()); - } - - public function testAtLeastLaravel5Dot8(): void - { - App::shouldReceive('version')->andReturn('5.7.0')->once(); - $this->assertFalse($this->stubInstance()->atLeastLaravel5Dot8()); - - App::shouldReceive('version')->andReturn('5.8.0')->once(); - $this->assertTrue($this->stubInstance()->atLeastLaravel5Dot8()); - - App::shouldReceive('version')->andReturn('6.0.0')->once(); - $this->assertTrue($this->stubInstance()->atLeastLaravel5Dot8()); - } - - public function testAtLeastLaravel6(): void - { - App::shouldReceive('version')->andReturn('5.8.0')->once(); - $this->assertFalse($this->stubInstance()->atLeastLaravel6()); - - App::shouldReceive('version')->andReturn('6.0.0')->once(); - $this->assertTrue($this->stubInstance()->atLeastLaravel6()); - - App::shouldReceive('version')->andReturn('7.0.0')->once(); - $this->assertTrue($this->stubInstance()->atLeastLaravel6()); - } - - public function testAtLeastLaravel7(): void - { - App::shouldReceive('version')->andReturn('6.0.0')->once(); - $this->assertFalse($this->stubInstance()->atLeastLaravel7()); - - App::shouldReceive('version')->andReturn('7.0.0')->once(); - $this->assertTrue($this->stubInstance()->atLeastLaravel7()); - - App::shouldReceive('version')->andReturn('8.0.0')->once(); - $this->assertTrue($this->stubInstance()->atLeastLaravel7()); - } - - public function testAtLeastLaravel8(): void - { - App::shouldReceive('version')->andReturn('7.0.0')->once(); - $this->assertFalse($this->stubInstance()->atLeastLaravel8()); - - App::shouldReceive('version')->andReturn('8.0.0')->once(); - $this->assertTrue($this->stubInstance()->atLeastLaravel8()); - - App::shouldReceive('version')->andReturn('9.0.0')->once(); - $this->assertTrue($this->stubInstance()->atLeastLaravel8()); - } - - /** - * @return object - */ - private function stubInstance() - { - return new class () { - use CheckLaravelVersion; - }; - } -} diff --git a/tests/TestCase.php b/tests/TestCase.php index a460ed79..cdb5697c 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -10,30 +10,6 @@ abstract class TestCase extends Testbench { - /** - * @inheritDoc - */ - protected function getPackageProviders($app) - { - return [ - MigrationsGeneratorServiceProvider::class, - ]; - } - - /** - * @inheritDoc - */ - protected function getEnvironmentSetUp($app): void - { - parent::getEnvironmentSetUp($app); - - // Tests run with @runTestsInSeparateProcesses failed since https://github.com/sebastianbergmann/phpunit/commit/3291172e198f044a922af8036378719f71267a51 and https://github.com/php/php-src/pull/11169. - // The root caused is not determined yet, however, by revert `set_error_handler` (https://github.com/laravel/framework/blob/2967d89906708cc7d619fc130e835c8002b7d3e3/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php#L45C20-L45C20) to default seems fix the failed test for now. - restore_error_handler(); - - app()->setBasePath(__DIR__ . '/../'); - } - /** * Asserts that the contents of one file is equal to the contents of another file, ignore the ordering. * Also, strip all end of line commas. @@ -43,11 +19,9 @@ public static function assertFileEqualsIgnoringOrder(string $expected, string $a static::assertFileExists($expected, $message); static::assertFileExists($actual, $message); - $removeLastComma = function (string $line): string { - return Str::endsWith($line, ',' . PHP_EOL) + $removeLastComma = static fn (string $line): string => Str::endsWith($line, ',' . PHP_EOL) ? Str::replaceLast(',' . PHP_EOL, PHP_EOL, $line) : $line; - }; $expectedFiles = file($expected) ?: []; $expectedContent = new Collection($expectedFiles); @@ -61,4 +35,24 @@ public static function assertFileEqualsIgnoringOrder(string $expected, string $a static::assertThat($actualContent->values(), $constraint, $message); } + + /** + * @inheritDoc + */ + protected function getPackageProviders($app) + { + return [ + MigrationsGeneratorServiceProvider::class, + ]; + } + + /** + * @inheritDoc + */ + protected function getEnvironmentSetUp($app): void + { + parent::getEnvironmentSetUp($app); + + app()->setBasePath(__DIR__ . '/../'); + } } diff --git a/tests/TestMigration.php b/tests/TestMigration.php index f776f23c..eb23dede 100644 --- a/tests/TestMigration.php +++ b/tests/TestMigration.php @@ -3,12 +3,9 @@ namespace KitLoong\MigrationsGenerator\Tests; use Illuminate\Database\Migrations\Migration; -use Illuminate\Support\Facades\DB; +use KitLoong\MigrationsGenerator\Support\AssetNameQuote; abstract class TestMigration extends Migration { - protected function quoteIdentifier(string $string): string - { - return DB::getDoctrineConnection()->quoteIdentifier($string); - } + use AssetNameQuote; } diff --git a/tests/resources/database/migrations/collation/2020_03_21_000000_expected_create_collations_db_table.php b/tests/resources/database/migrations/collation/2020_03_21_000000_expected_create_collations_table.php similarity index 70% rename from tests/resources/database/migrations/collation/2020_03_21_000000_expected_create_collations_db_table.php rename to tests/resources/database/migrations/collation/2020_03_21_000000_expected_create_collations_table.php index 5e1f1de9..2904e922 100644 --- a/tests/resources/database/migrations/collation/2020_03_21_000000_expected_create_collations_db_table.php +++ b/tests/resources/database/migrations/collation/2020_03_21_000000_expected_create_collations_table.php @@ -8,13 +8,10 @@ use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; use KitLoong\MigrationsGenerator\Enum\Driver; -use KitLoong\MigrationsGenerator\Support\CheckLaravelVersion; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class ExpectedCreateCollations_DB_Table extends TestMigration +return new class extends TestMigration { - use CheckLaravelVersion; - /** * Run the migrations. * @@ -22,19 +19,22 @@ class ExpectedCreateCollations_DB_Table extends TestMigration */ public function up() { - Schema::create('collations_[db]', function (Blueprint $table) { + Schema::create('collations', function (Blueprint $table) { $table->charset = 'utf8mb4'; $table->collation = 'utf8mb4_general_ci'; switch (DB::getDriverName()) { - case Driver::PGSQL(): + case Driver::PGSQL->value: $collation = 'en_US.utf8'; break; - case Driver::SQLSRV(): + case Driver::SQLSRV->value: $collation = 'Latin1_General_100_CI_AI_SC_UTF8'; break; - default: + case Driver::MYSQL->value: $collation = 'utf8_unicode_ci'; + break; + default: + $collation = 'BINARY'; } $table->char('char'); @@ -42,8 +42,8 @@ public function up() // sqlsrv does not support collation with enum switch (DB::getDriverName()) { - case Driver::MYSQL(): - case Driver::PGSQL(): + case Driver::MYSQL->value: + case Driver::PGSQL->value: $table->enum('enum', ['easy', 'hard']); $table->enum('enum_charset', ['easy', 'hard'])->charset('utf8'); $table->enum('enum_collation', ['easy', 'hard'])->collation($collation); @@ -61,13 +61,11 @@ public function up() $table->text('text_charset')->charset('utf8'); $table->text('text_collation')->collation($collation); - if (DB::getDriverName() === Driver::MYSQL()->getValue()) { - if ($this->atLeastLaravel5Dot8()) { - $table->set('set', ['strawberry', 'vanilla']); - $table->set('set_default', ['strawberry', 'vanilla'])->default('strawberry'); - $table->set('set_charset', ['strawberry', 'vanilla'])->charset('utf8'); - $table->set('set_collation', ['strawberry', 'vanilla'])->collation($collation); - } + if (DB::getDriverName() === Driver::MYSQL->value) { + $table->set('set', ['strawberry', 'vanilla']); + $table->set('set_default', ['strawberry', 'vanilla'])->default('strawberry'); + $table->set('set_charset', ['strawberry', 'vanilla'])->charset('utf8'); + $table->set('set_collation', ['strawberry', 'vanilla'])->collation($collation); } $table->string('string'); @@ -83,6 +81,6 @@ public function up() */ public function down() { - Schema::dropIfExists('all_columns_[db]'); + Schema::dropIfExists('all_columns'); } -} +}; diff --git a/tests/resources/database/migrations/collation/2020_03_21_000000_expected_create_user_collations_db_table.php b/tests/resources/database/migrations/collation/2020_03_21_000000_expected_create_user_collations_table.php similarity index 84% rename from tests/resources/database/migrations/collation/2020_03_21_000000_expected_create_user_collations_db_table.php rename to tests/resources/database/migrations/collation/2020_03_21_000000_expected_create_user_collations_table.php index 8540b390..496d8196 100644 --- a/tests/resources/database/migrations/collation/2020_03_21_000000_expected_create_user_collations_db_table.php +++ b/tests/resources/database/migrations/collation/2020_03_21_000000_expected_create_user_collations_table.php @@ -8,7 +8,7 @@ use Illuminate\Support\Facades\Schema; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class ExpectedCreateUserCollations_DB_Table extends TestMigration +return new class extends TestMigration { /** * Run the migrations. @@ -17,7 +17,7 @@ class ExpectedCreateUserCollations_DB_Table extends TestMigration */ public function up() { - Schema::create('user_collations_[db]', function (Blueprint $table) { + Schema::create('user_collations', function (Blueprint $table) { $table->unsignedBigInteger('id'); $table->unsignedInteger('sub_id'); $table->string('name'); @@ -40,6 +40,6 @@ public function up() */ public function down() { - Schema::dropIfExists('users_[db]'); + Schema::dropIfExists('users'); } -} +}; diff --git a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_all_columns_db_table.php b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_all_columns_table.php similarity index 58% rename from tests/resources/database/migrations/general/2020_03_21_000000_expected_create_all_columns_db_table.php rename to tests/resources/database/migrations/general/2020_03_21_000000_expected_create_all_columns_table.php index 673b81d4..f5762da6 100644 --- a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_all_columns_db_table.php +++ b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_all_columns_table.php @@ -8,12 +8,12 @@ use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; use KitLoong\MigrationsGenerator\Enum\Driver; -use KitLoong\MigrationsGenerator\Support\CheckMigrationMethod; +use KitLoong\MigrationsGenerator\Support\CheckLaravelVersion; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class ExpectedCreateAllColumns_DB_Table extends TestMigration +return new class extends TestMigration { - use CheckMigrationMethod; + use CheckLaravelVersion; /** * Run the migrations. @@ -22,10 +22,8 @@ class ExpectedCreateAllColumns_DB_Table extends TestMigration */ public function up() { - Schema::create('all_columns_[db]', function (Blueprint $table) { - if ($this->hasTableComment()) { - $table->comment('A table comment.'); - } + Schema::create('all_columns', function (Blueprint $table) { + $table->comment('A table comment.'); $table->bigInteger('bigInteger'); $table->bigInteger('bigInteger_default')->default(1080); @@ -62,37 +60,65 @@ public function up() $table->decimal('decimal_53', 5, 3); $table->decimal('decimal_default')->default(10.8); $table->double('double'); - $table->double('double_82', 8, 2); - $table->double('double_83', 8, 3); - $table->double('double_92', 9, 2); - $table->double('double_53', 5, 3); $table->double('double_default')->default(10.8); $table->enum('enum', ['easy', 'hard']); $table->enum('enum_default', ['easy', 'hard'])->default('easy'); $table->float('float'); - $table->float('float_82', 8, 2); - $table->float('float_83', 8, 3); - $table->float('float_92', 9, 2); - $table->float('float_53', 5, 3); $table->float('float_default')->default(10.8); - $table->geometry('geometry'); - $table->geometryCollection('geometryCollection'); $table->integer('integer'); $table->integer('integer_default')->default(1080); $table->ipAddress('ipAddress'); $table->ipAddress('ipAddress_default')->default('10.0.0.8'); $table->json('json'); $table->jsonb('jsonb'); - $table->lineString('lineString'); $table->longText('longText'); $table->mediumInteger('mediumInteger'); $table->mediumInteger('mediumInteger_default')->default(1080); $table->mediumText('mediumText'); - $table->multiLineString('multiLineString'); - $table->multiPoint('multiPoint'); - $table->multiPolygon('multiPolygon'); - $table->point('point'); - $table->polygon('polygon'); + $table->geometry('geometry'); + + // https://github.com/laravel/framework/pull/49634 + if ($this->atLeastLaravel11()) { + if ( + DB::getDriverName() !== Driver::MYSQL->value || + version_compare(DB::getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION), '5.8', '>') + ) { + $table->geography('geography'); + $table->geography('geographyGeometryCollection', 'geometryCollection'); + $table->geography('geographyLineString', 'lineString'); + $table->geography('geographyMultiLineString', 'multiLineString'); + $table->geography('geographyMultiPoint', 'multiPoint'); + $table->geography('geographyMultiPolygon', 'multiPolygon'); + $table->geography('geographyPoint', 'point'); + $table->geography('geographyPolygon', 'polygon'); + $table->geography('geography1', null, 3857); + + if (DB::getDriverName() !== Driver::PGSQL->value) { + $table->geography('geography2', 'geometryCollection', 3857); + } + + $table->geometry('geometryCollection', 'geometryCollection'); + $table->geometry('lineString', 'lineString'); + $table->geometry('multiLineString', 'multiLineString'); + $table->geometry('multiPoint', 'multiPoint'); + $table->geometry('multiPolygon', 'multiPolygon'); + $table->geometry('point', 'point'); + $table->geometry('polygon', 'polygon'); + $table->geometry('geometry1', null, 3857); + $table->geometry('geometry2', 'geometryCollection', 3857); + } + } + + if (!$this->atLeastLaravel11() && DB::getDriverName() !== Driver::SQLITE->value) { + $table->geometryCollection('geometryCollection'); // @phpstan-ignore-line + $table->lineString('lineString'); // @phpstan-ignore-line + $table->multiLineString('multiLineString'); // @phpstan-ignore-line + $table->multiPoint('multiPoint'); // @phpstan-ignore-line + $table->multiPolygon('multiPolygon'); // @phpstan-ignore-line + $table->point('point'); // @phpstan-ignore-line + $table->polygon('polygon'); // @phpstan-ignore-line + } + $table->smallInteger('smallInteger'); $table->smallInteger('smallInteger_default')->default(1080); $table->string('string'); @@ -130,6 +156,20 @@ public function up() $table->decimal('unsignedDecimal')->unsigned(); $table->double('unsignedDouble')->unsigned(); $table->float('unsignedFloat')->unsigned(); + + if (!$this->atLeastLaravel11()) { + // https://github.com/laravel/framework/pull/48861 + $table->double('double_82', 8, 2); // @phpstan-ignore-line + $table->double('double_83', 8, 3); // @phpstan-ignore-line + $table->double('double_92', 9, 2); // @phpstan-ignore-line + $table->double('double_53', 5, 3); // @phpstan-ignore-line + $table->float('float_82', 8, 2); // @phpstan-ignore-line + $table->float('float_83', 8, 3); // @phpstan-ignore-line + $table->float('float_92', 9, 2); // @phpstan-ignore-line + $table->float('float_53', 5, 3); // @phpstan-ignore-line + $table->float('float_56', 50); + } + $table->unsignedInteger('unsignedInteger'); $table->unsignedMediumInteger('unsignedMediumInteger'); $table->unsignedSmallInteger('unsignedSmallInteger'); @@ -144,34 +184,55 @@ public function up() ->default('string !@#$%^^&*()_+-=[]{};:,./<>?~`| \ \\ \\\ \\\\ \'\' \\\\\'\' " \" \\" \\\" \\\\" quotes') ->comment('string !@#$%^^&*()_+-=[]{};:,./<>?~`| \ \\ \\\ \\\\ \'\' \\\\\'\' " \" \\" \\\" \\\\" quotes'); - if ($this->hasTinyText()) { - $table->tinyText('tinyText'); - } + $table->tinyText('tinyText'); - if ($this->hasULID()) { - $table->ulid('ulid'); - } + $table->ulid('ulid'); switch (DB::getDriverName()) { - case Driver::MYSQL(): - if ($this->hasSet()) { - $table->set('set', ['strawberry', 'vanilla']); - } + case Driver::MYSQL->value: + $table->set('set', ['strawberry', 'vanilla']); break; default: } switch (DB::getDriverName()) { - case Driver::MYSQL(): + case Driver::MYSQL->value: $table->string('virtual')->nullable()->virtualAs('CONCAT(string, " ", string_255)'); $table->string('stored')->nullable()->storedAs("CONCAT(string_255, ' ', string)"); break; - case Driver::PGSQL(): + case Driver::PGSQL->value: $table->string('stored')->nullable()->storedAs("string_255 || ' ' || string"); break; default: } }); + + switch (DB::getDriverName()) { + case Driver::PGSQL->value: + // Test timestamp default now() + DB::statement( + "ALTER TABLE ".DB::getTablePrefix()."all_columns ADD COLUMN timestamp_defaultnow timestamp(0) without time zone DEFAULT now() NOT NULL", + ); + + DB::statement( + "ALTER TABLE ".DB::getTablePrefix()."all_columns ADD COLUMN status my_status NOT NULL DEFAULT 'PENDING'", + ); + + DB::statement( + "COMMENT ON column ".DB::getTablePrefix()."all_columns.status IS 'comment a'", + ); + + DB::statement( + "ALTER TABLE ".DB::getTablePrefix()."all_columns ADD COLUMN timestamp_default_timezone_now timestamp(0) without time zone DEFAULT timezone('Europe/Rome'::text, now()) NOT NULL", + ); + break; + + case Driver::SQLSRV->value: + DB::statement( + "ALTER TABLE ".DB::getTablePrefix()."all_columns ADD accountnumber accountnumber NOT NULL DEFAULT '1008'", + ); + break; + } } /** @@ -181,6 +242,6 @@ public function up() */ public function down() { - Schema::dropIfExists('all_columns_[db]'); + Schema::dropIfExists('all_columns'); } -} +}; diff --git a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_increments_db_table.php b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_increments_table.php similarity index 53% rename from tests/resources/database/migrations/general/2020_03_21_000000_expected_create_increments_db_table.php rename to tests/resources/database/migrations/general/2020_03_21_000000_expected_create_increments_table.php index 70c76dc2..0b421749 100644 --- a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_increments_db_table.php +++ b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_increments_table.php @@ -8,7 +8,7 @@ use Illuminate\Support\Facades\Schema; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class ExpectedCreateIncrements_DB_Table extends TestMigration +return new class extends TestMigration { /** * Run the migrations. @@ -17,31 +17,31 @@ class ExpectedCreateIncrements_DB_Table extends TestMigration */ public function up() { - Schema::create('big_increments_[db]', function (Blueprint $table) { + Schema::create('big_increments', function (Blueprint $table) { $table->bigIncrements('id')->comment('Increments'); }); - Schema::create('increments_[db]', function (Blueprint $table) { + Schema::create('increments', function (Blueprint $table) { $table->increments('id')->comment('Increments'); }); - Schema::create('medium_increments_[db]', function (Blueprint $table) { + Schema::create('medium_increments', function (Blueprint $table) { $table->mediumIncrements('id')->comment('Increments'); }); - Schema::create('small_increments_[db]', function (Blueprint $table) { + Schema::create('small_increments', function (Blueprint $table) { $table->smallIncrements('id')->comment('Increments'); }); - Schema::create('tiny_increments_[db]', function (Blueprint $table) { + Schema::create('tiny_increments', function (Blueprint $table) { $table->tinyIncrements('id')->comment('Increments'); }); - Schema::create('signed_increments_[db]', function (Blueprint $table) { + Schema::create('signed_increments', function (Blueprint $table) { $table->integer('id', true)->comment('Increments'); }); -// Schema::create('increments_not_primary_[db]', function (Blueprint $table) { +// Schema::create('increments_not_primary', function (Blueprint $table) { // $table->increments('id'); // $table->unique('id'); // $table->dropPrimary(); @@ -55,11 +55,11 @@ public function up() */ public function down() { - Schema::dropIfExists('big_increments_[db]'); - Schema::dropIfExists('increments_[db]'); - Schema::dropIfExists('medium_increments_[db]'); - Schema::dropIfExists('small_increments_[db]'); - Schema::dropIfExists('tiny_increments_[db]'); - Schema::dropIfExists('signed_increments_[db]'); + Schema::dropIfExists('big_increments'); + Schema::dropIfExists('increments'); + Schema::dropIfExists('medium_increments'); + Schema::dropIfExists('small_increments'); + Schema::dropIfExists('tiny_increments'); + Schema::dropIfExists('signed_increments'); } -} +}; diff --git a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_primary_db_table.php b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_primary_table.php similarity index 59% rename from tests/resources/database/migrations/general/2020_03_21_000000_expected_create_primary_db_table.php rename to tests/resources/database/migrations/general/2020_03_21_000000_expected_create_primary_table.php index 59b9e5a3..ef903ad0 100644 --- a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_primary_db_table.php +++ b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_primary_table.php @@ -8,7 +8,7 @@ use Illuminate\Support\Facades\Schema; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class ExpectedCreatePrimary_DB_Table extends TestMigration +return new class extends TestMigration { /** * Run the migrations. @@ -17,29 +17,29 @@ class ExpectedCreatePrimary_DB_Table extends TestMigration */ public function up() { - Schema::create('primary_id_[db]', function (Blueprint $table) { + Schema::create('primary_id', function (Blueprint $table) { $table->unsignedInteger('id'); $table->primary('id'); }); - Schema::create('primary_name_[db]', function (Blueprint $table) { + Schema::create('primary_name', function (Blueprint $table) { $table->string('name'); $table->primary('name', 'primary_custom'); }); - Schema::create('signed_primary_id_[db]', function (Blueprint $table) { + Schema::create('signed_primary_id', function (Blueprint $table) { $table->integer('id'); $table->primary('id'); }); - Schema::create('composite_primary_[db]', function (Blueprint $table) { + Schema::create('composite_primary', function (Blueprint $table) { $table->unsignedInteger('id'); $table->unsignedInteger('sub_id'); $table->primary(['id', 'sub_id']); }); // Test short table name - Schema::create('s_[db]', function (Blueprint $table) { + Schema::create('s', function (Blueprint $table) { $table->bigIncrements('id'); }); } @@ -51,10 +51,10 @@ public function up() */ public function down() { - Schema::dropIfExists('primary_id_[db]'); - Schema::dropIfExists('primary_name_[db]'); - Schema::dropIfExists('signed_primary_id_[db]'); - Schema::dropIfExists('composite_primary_[db]'); - Schema::dropIfExists('s_[db]'); + Schema::dropIfExists('primary_id'); + Schema::dropIfExists('primary_name'); + Schema::dropIfExists('signed_primary_id'); + Schema::dropIfExists('composite_primary'); + Schema::dropIfExists('s'); } -} +}; diff --git a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_quoted_name_db_table.php b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_quoted_name_table.php similarity index 74% rename from tests/resources/database/migrations/general/2020_03_21_000000_expected_create_quoted_name_db_table.php rename to tests/resources/database/migrations/general/2020_03_21_000000_expected_create_quoted_name_table.php index 2eecaaaf..cacf00bc 100644 --- a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_quoted_name_db_table.php +++ b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_quoted_name_table.php @@ -8,7 +8,7 @@ use Illuminate\Support\Facades\Schema; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class ExpectedCreateQuotedName_DB_Table extends TestMigration +return new class extends TestMigration { /** * Run the migrations. @@ -17,7 +17,7 @@ class ExpectedCreateQuotedName_DB_Table extends TestMigration */ public function up() { - Schema::create('quoted-name-[db]', function (Blueprint $table) { + Schema::create('quoted-name', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('quoted-column'); }); @@ -30,6 +30,6 @@ public function up() */ public function down() { - Schema::dropIfExists('quoted-name-[db]'); + Schema::dropIfExists('quoted-name'); } -} +}; diff --git a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_reserved_name_modifier_db_table.php b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_reserved_name_modifier_table.php similarity index 70% rename from tests/resources/database/migrations/general/2020_03_21_000000_expected_create_reserved_name_modifier_db_table.php rename to tests/resources/database/migrations/general/2020_03_21_000000_expected_create_reserved_name_modifier_table.php index cc34efc2..aa6bbb1e 100644 --- a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_reserved_name_modifier_db_table.php +++ b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_reserved_name_modifier_table.php @@ -8,7 +8,7 @@ use Illuminate\Support\Facades\Schema; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class ExpectedCreateReservedNameModifier_DB_Table extends TestMigration +return new class extends TestMigration { /** * Run the migrations. @@ -17,13 +17,13 @@ class ExpectedCreateReservedNameModifier_DB_Table extends TestMigration */ public function up() { - Schema::create('reserved_name_modifier_[db]', function (Blueprint $table) { + Schema::create('reserved_name_modifier', function (Blueprint $table) { $table->increments('id'); $table->softDeletes()->nullable(false)->comment('Soft deletes')->default('2020-10-08'); $table->rememberToken()->nullable(false)->comment('Remember token')->default('default'); }); - Schema::create('reserved_name_modifier2_[db]', function (Blueprint $table) { + Schema::create('reserved_name_modifier2', function (Blueprint $table) { $table->increments('id'); $table->softDeletesTz()->nullable(false)->comment('Soft deletes tz')->default('2020-10-08'); }); @@ -36,7 +36,7 @@ public function up() */ public function down() { - Schema::dropIfExists('reserved_name_modifier_[db]'); - Schema::dropIfExists('reserved_name_modifier2_[db]'); + Schema::dropIfExists('reserved_name_modifier'); + Schema::dropIfExists('reserved_name_modifier2'); } -} +}; diff --git a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_reserved_name_with_precision_db_table.php b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_reserved_name_with_precision_table.php similarity index 74% rename from tests/resources/database/migrations/general/2020_03_21_000000_expected_create_reserved_name_with_precision_db_table.php rename to tests/resources/database/migrations/general/2020_03_21_000000_expected_create_reserved_name_with_precision_table.php index 14040059..732f9439 100644 --- a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_reserved_name_with_precision_db_table.php +++ b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_reserved_name_with_precision_table.php @@ -8,7 +8,7 @@ use Illuminate\Support\Facades\Schema; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class ExpectedCreateReservedNameWithPrecision_DB_Table extends TestMigration +return new class extends TestMigration { /** * Run the migrations. @@ -17,7 +17,7 @@ class ExpectedCreateReservedNameWithPrecision_DB_Table extends TestMigration */ public function up() { - Schema::create('reserved_name_with_precision_[db]', function (Blueprint $table) { + Schema::create('reserved_name_with_precision', function (Blueprint $table) { $table->increments('id'); $table->softDeletes('deleted_at', 2); $table->softDeletesTz('deleted_at_tz', 2); @@ -33,6 +33,6 @@ public function up() */ public function down() { - Schema::dropIfExists('reserved_name_with_precision_[db]'); + Schema::dropIfExists('reserved_name_with_precision'); } -} +}; diff --git a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_test_index_db_table.php b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_test_index_table.php similarity index 70% rename from tests/resources/database/migrations/general/2020_03_21_000000_expected_create_test_index_db_table.php rename to tests/resources/database/migrations/general/2020_03_21_000000_expected_create_test_index_table.php index 8f0a694f..7e372171 100644 --- a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_test_index_db_table.php +++ b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_test_index_table.php @@ -11,7 +11,7 @@ use KitLoong\MigrationsGenerator\Support\CheckMigrationMethod; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class ExpectedCreateTestIndex_DB_Table extends TestMigration +return new class extends TestMigration { use CheckMigrationMethod; @@ -22,7 +22,7 @@ class ExpectedCreateTestIndex_DB_Table extends TestMigration */ public function up() { - Schema::create('test_index_[db]', function (Blueprint $table) { + Schema::create('test_index', function (Blueprint $table) { $table->increments('id'); $table->string('index')->index(); $table->string('index_custom')->index('index_custom'); @@ -42,16 +42,20 @@ public function up() $table->unique('chain'); // SQLite does not support spatial index. - if (DB::getDriverName() !== Driver::SQLITE()->getValue()) { - $table->lineString('spatial_index')->spatialIndex(); - $table->lineString('spatial_index_custom'); - $table->spatialIndex('spatial_index_custom', 'spatial_index_custom'); + if (DB::getDriverName() !== Driver::SQLITE->value) { + if ($this->hasGeography()) { + $table->geography('spatial_index', null, 0)->spatialIndex(); + $table->geography('spatial_index_custom', null, 0); + $table->spatialIndex('spatial_index_custom', 'spatial_index_custom'); + } + if (!$this->hasGeography()) { + $table->geometry('spatial_index')->spatialIndex(); + $table->geometry('spatial_index_custom'); + $table->spatialIndex('spatial_index_custom', 'spatial_index_custom'); + } } - if ( - in_array(DB::getDriverName(), [Driver::MYSQL()->getValue(), Driver::PGSQL()->getValue()]) - && $this->hasFullText() - ) { + if (in_array(DB::getDriverName(), [Driver::MYSQL->value, Driver::PGSQL->value])) { $table->string('fulltext')->fulltext(); $table->string('fulltext_custom')->fulltext('fulltext_custom'); $table->fullText(['col_multi1', 'col_multi2']); @@ -59,7 +63,7 @@ public function up() } // TODO Laravel 10 does not support `$table->index(DB::raw("with_length(16)"))` -// if (DB::getDriverName() === Driver::MYSQL()->getValue()) { +// if (DB::getDriverName() === Driver::MYSQL->value) { // $table->index(['col_multi1', DB::raw('col_multi2(16)')], 'with_length_multi_custom'); // $table->string('with_length'); // $table->string('with_length_custom'); @@ -76,6 +80,6 @@ public function up() */ public function down() { - Schema::dropIfExists('test_index_[db]'); + Schema::dropIfExists('test_index'); } -} +}; diff --git a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_timestamps_db_table.php b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_timestamps_table.php similarity index 63% rename from tests/resources/database/migrations/general/2020_03_21_000000_expected_create_timestamps_db_table.php rename to tests/resources/database/migrations/general/2020_03_21_000000_expected_create_timestamps_table.php index 6e586b16..347c47d7 100644 --- a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_timestamps_db_table.php +++ b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_timestamps_table.php @@ -8,7 +8,7 @@ use Illuminate\Support\Facades\Schema; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class ExpectedCreateTimestamps_DB_Table extends TestMigration +return new class extends TestMigration { /** * Run the migrations. @@ -17,71 +17,71 @@ class ExpectedCreateTimestamps_DB_Table extends TestMigration */ public function up() { - Schema::create('timestamps_[db]', function (Blueprint $table) { + Schema::create('timestamps', function (Blueprint $table) { $table->increments('id'); $table->timestamps(); }); - Schema::create('timestamps_precision_[db]', function (Blueprint $table) { + Schema::create('timestamps_precision', function (Blueprint $table) { $table->increments('id'); $table->timestamps(2); }); - Schema::create('timestamps_tz_[db]', function (Blueprint $table) { + Schema::create('timestamps_tz', function (Blueprint $table) { $table->increments('id'); $table->timestampsTz(); }); - Schema::create('timestamps_tz_precision_[db]', function (Blueprint $table) { + Schema::create('timestamps_tz_precision', function (Blueprint $table) { $table->increments('id'); $table->timestampsTz(2); }); - Schema::create('not_timestamps_[db]', function (Blueprint $table) { + Schema::create('not_timestamps', function (Blueprint $table) { $table->increments('id'); $table->timestamp('created_at')->nullable(); $table->timestamp('deleted_at')->nullable(); $table->timestamp('update_at')->nullable(); }); - Schema::create('not_timestamps2_[db]', function (Blueprint $table) { + Schema::create('not_timestamps2', function (Blueprint $table) { $table->increments('id'); $table->timestamp('created_at'); $table->timestamp('update_at')->nullable(); }); - Schema::create('not_timestamps3_[db]', function (Blueprint $table) { + Schema::create('not_timestamps3', function (Blueprint $table) { $table->increments('id'); $table->timestamp('created_at')->nullable()->comment('Created at'); $table->timestamp('update_at')->nullable(); }); - Schema::create('not_timestamps4_[db]', function (Blueprint $table) { + Schema::create('not_timestamps4', function (Blueprint $table) { $table->increments('id'); $table->timestamp('created_at')->nullable(); $table->timestamp('update_at')->nullable()->useCurrent()->useCurrentOnUpdate(); }); - Schema::create('not_timestamps_tz_[db]', function (Blueprint $table) { + Schema::create('not_timestamps_tz', function (Blueprint $table) { $table->increments('id'); $table->timestampTz('created_at')->nullable(); $table->timestampTz('deleted_at')->nullable(); $table->timestampTz('update_at')->nullable(); }); - Schema::create('not_timestamps_tz2_[db]', function (Blueprint $table) { + Schema::create('not_timestamps_tz2', function (Blueprint $table) { $table->increments('id'); $table->timestampTz('created_at'); $table->timestampTz('update_at')->nullable(); }); - Schema::create('not_timestamps_tz3_[db]', function (Blueprint $table) { + Schema::create('not_timestamps_tz3', function (Blueprint $table) { $table->increments('id'); $table->timestampTz('created_at')->nullable()->comment('Created at'); $table->timestampTz('update_at')->nullable(); }); - Schema::create('not_timestamps_tz4_[db]', function (Blueprint $table) { + Schema::create('not_timestamps_tz4', function (Blueprint $table) { $table->increments('id'); $table->timestampTz('created_at')->nullable(); $table->timestampTz('update_at')->nullable()->useCurrent()->useCurrentOnUpdate(); @@ -95,13 +95,13 @@ public function up() */ public function down() { - Schema::dropIfExists('timestamps_[db]'); - Schema::dropIfExists('timestamps_precision_[db]'); - Schema::dropIfExists('timestamps_tz_[db]'); - Schema::dropIfExists('timestamps_tz_precision_[db]'); - Schema::dropIfExists('not_timestamps_[db]'); - Schema::dropIfExists('not_timestamps2_[db]'); - Schema::dropIfExists('not_timestamps3_[db]'); - Schema::dropIfExists('not_timestamps4_[db]'); + Schema::dropIfExists('timestamps'); + Schema::dropIfExists('timestamps_precision'); + Schema::dropIfExists('timestamps_tz'); + Schema::dropIfExists('timestamps_tz_precision'); + Schema::dropIfExists('not_timestamps'); + Schema::dropIfExists('not_timestamps2'); + Schema::dropIfExists('not_timestamps3'); + Schema::dropIfExists('not_timestamps4'); } -} +}; diff --git a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_users_db_table.php b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_users_table.php similarity index 84% rename from tests/resources/database/migrations/general/2020_03_21_000000_expected_create_users_db_table.php rename to tests/resources/database/migrations/general/2020_03_21_000000_expected_create_users_table.php index 35d54fc3..e5627b6a 100644 --- a/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_users_db_table.php +++ b/tests/resources/database/migrations/general/2020_03_21_000000_expected_create_users_table.php @@ -8,7 +8,7 @@ use Illuminate\Support\Facades\Schema; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class ExpectedCreateUsers_DB_Table extends TestMigration +return new class extends TestMigration { /** * Run the migrations. @@ -17,7 +17,7 @@ class ExpectedCreateUsers_DB_Table extends TestMigration */ public function up() { - Schema::create('users_[db]', function (Blueprint $table) { + Schema::create('users', function (Blueprint $table) { $table->bigIncrements('id'); $table->unsignedBigInteger('sub_id'); $table->string('name'); @@ -39,6 +39,6 @@ public function up() */ public function down() { - Schema::dropIfExists('users_[db]'); + Schema::dropIfExists('users'); } -} +}; diff --git a/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_foreign_db_table.php b/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_foreign_table.php similarity index 72% rename from tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_foreign_db_table.php rename to tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_foreign_table.php index aaa3c1fc..1c091627 100644 --- a/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_foreign_db_table.php +++ b/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_foreign_table.php @@ -10,7 +10,7 @@ use KitLoong\MigrationsGenerator\Enum\Driver; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class ExpectedCreateQuotedNameForeign_DB_Table extends TestMigration +return new class extends TestMigration { /** * Run the migrations. @@ -19,14 +19,14 @@ class ExpectedCreateQuotedNameForeign_DB_Table extends TestMigration */ public function up() { - Schema::create('quoted-name-foreign-[db]', function (Blueprint $table) { + Schema::create('quoted-name-foreign', function (Blueprint $table) { $table->bigIncrements('id'); $table->unsignedBigInteger('quoted-name-id'); // SQLite does not support alter add foreign key. // https://www.sqlite.org/omitted.html - if (DB::getDriverName() !== Driver::SQLITE()->getValue()) { - $table->foreign('quoted-name-id')->references('id')->on('quoted-name-[db]'); + if (DB::getDriverName() !== Driver::SQLITE->value) { + $table->foreign('quoted-name-id')->references('id')->on('quoted-name'); } }); } @@ -38,6 +38,6 @@ public function up() */ public function down() { - Schema::dropIfExists('quoted-name-foreign-[db]'); + Schema::dropIfExists('quoted-name-foreign'); } -} +}; diff --git a/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_db_proc.php b/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_proc.php similarity index 72% rename from tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_db_proc.php rename to tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_proc.php index c5d394a8..50d4e4c9 100644 --- a/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_db_proc.php +++ b/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_proc.php @@ -8,7 +8,7 @@ use KitLoong\MigrationsGenerator\Enum\Driver; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class ExpectedCreateQuotedName_DB_Proc extends TestMigration +return new class extends TestMigration { /** * Run the migrations. @@ -18,29 +18,29 @@ class ExpectedCreateQuotedName_DB_Proc extends TestMigration public function up() { switch (DB::getDriverName()) { - case Driver::MYSQL(): + case Driver::MYSQL->value: DB::unprepared( - "CREATE PROCEDURE findNameWithHyphen[db]() + "CREATE PROCEDURE findNameWithHyphen() BEGIN - SELECT * from " . $this->quoteIdentifier('name-with-hyphen-[db]') . "; + SELECT * from " . $this->quoteIdentifier('name-with-hyphen') . "; END" ); break; - case Driver::PGSQL(): + case Driver::PGSQL->value: DB::unprepared( - "CREATE PROCEDURE findNameWithHyphen[db]() + "CREATE PROCEDURE findNameWithHyphen() language plpgsql as $$ BEGIN - SELECT * from " . $this->quoteIdentifier('name-with-hyphen-[db]') . "; + SELECT * from " . $this->quoteIdentifier('name-with-hyphen') . "; END;$$" ); break; - case Driver::SQLSRV(): + case Driver::SQLSRV->value: DB::unprepared( - "CREATE PROCEDURE findNameWithHyphen[db] + "CREATE PROCEDURE findNameWithHyphen AS - SELECT * from " . $this->quoteIdentifier('name-with-hyphen-[db]') . ";" + SELECT * from " . $this->quoteIdentifier('name-with-hyphen') . ";" ); break; default: @@ -54,6 +54,6 @@ public function up() */ public function down() { - DB::unprepared("DROP PROCEDURE IF EXISTS findNameWithHyphen[db]"); + DB::unprepared("DROP PROCEDURE IF EXISTS findNameWithHyphen"); } -} +}; diff --git a/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_db_view.php b/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_view.php similarity index 78% rename from tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_db_view.php rename to tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_view.php index 201eaf03..e16ae6f8 100644 --- a/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_db_view.php +++ b/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_quoted_name_view.php @@ -7,7 +7,7 @@ use Illuminate\Support\Facades\DB; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class ExpectedCreateQuotedName_DB_View extends TestMigration +return new class extends TestMigration { /** * Run the migrations. @@ -17,8 +17,8 @@ class ExpectedCreateQuotedName_DB_View extends TestMigration public function up() { DB::statement( - "CREATE VIEW " . $this->quoteIdentifier(DB::getTablePrefix() . 'quoted-name-[db]-view') - . " AS SELECT * from " . $this->quoteIdentifier(DB::getTablePrefix() . 'quoted-name-[db]') + "CREATE VIEW " . $this->quoteIdentifier(DB::getTablePrefix() . 'quoted-name-view') + . " AS SELECT * from " . $this->quoteIdentifier(DB::getTablePrefix() . 'quoted-name') ); } @@ -29,6 +29,6 @@ public function up() */ public function down() { - DB::statement("DROP VIEW IF EXISTS " . $this->quoteIdentifier('quoted-name-[db]-view')); + DB::statement("DROP VIEW IF EXISTS " . $this->quoteIdentifier('quoted-name-view')); } -} +}; diff --git a/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_user_profile_db_table.php b/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_user_profile_table.php similarity index 72% rename from tests/resources/database/migrations/general/2020_03_21_000001_expected_create_user_profile_db_table.php rename to tests/resources/database/migrations/general/2020_03_21_000001_expected_create_user_profile_table.php index 8425b9d2..20d3758b 100644 --- a/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_user_profile_db_table.php +++ b/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_user_profile_table.php @@ -10,7 +10,7 @@ use KitLoong\MigrationsGenerator\Enum\Driver; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class ExpectedCreateUserProfile_DB_Table extends TestMigration +return new class extends TestMigration { /** * Run the migrations. @@ -19,7 +19,7 @@ class ExpectedCreateUserProfile_DB_Table extends TestMigration */ public function up() { - Schema::create('user_profile_[db]', function (Blueprint $table) { + Schema::create('user_profile', function (Blueprint $table) { $table->increments('id'); $table->bigInteger('user_id')->unsigned(); $table->bigInteger('user_id_fk_custom')->unsigned(); @@ -33,16 +33,16 @@ public function up() // SQLite does not support alter add foreign key. // https://www.sqlite.org/omitted.html - if (DB::getDriverName() !== Driver::SQLITE()->getValue()) { - $table->foreign('user_id')->references('id')->on('users_[db]'); - $table->foreign('user_id_fk_custom', 'users_[db]_foreign_custom')->references('id')->on('users_[db]'); - $table->foreign('user_id_fk_constraint', 'users_[db]_foreign_constraint')->references('id')->on( - 'users_[db]' + if (DB::getDriverName() !== Driver::SQLITE->value) { + $table->foreign('user_id')->references('id')->on('users'); + $table->foreign('user_id_fk_custom', 'users_foreign_custom')->references('id')->on('users'); + $table->foreign('user_id_fk_constraint', 'users_foreign_constraint')->references('id')->on( + 'users' )->onDelete('cascade')->onUpdate('cascade'); - $table->foreign(['user_id', 'user_sub_id'])->references(['id', 'sub_id'])->on('users_[db]'); - $table->foreign(['user_id', 'user_sub_id_fk_custom'], 'users_[db]_composite_foreign_custom') + $table->foreign(['user_id', 'user_sub_id'])->references(['id', 'sub_id'])->on('users'); + $table->foreign(['user_id', 'user_sub_id_fk_custom'], 'users_composite_foreign_custom') ->references(['id', 'sub_id']) - ->on('users_[db]'); + ->on('users'); } }); } @@ -54,6 +54,6 @@ public function up() */ public function down() { - Schema::dropIfExists('user_profile_[db]'); + Schema::dropIfExists('user_profile'); } -} +}; diff --git a/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_users_db_view.php b/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_users_view.php similarity index 63% rename from tests/resources/database/migrations/general/2020_03_21_000001_expected_create_users_db_view.php rename to tests/resources/database/migrations/general/2020_03_21_000001_expected_create_users_view.php index 6b98da7d..3c4017a4 100644 --- a/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_users_db_view.php +++ b/tests/resources/database/migrations/general/2020_03_21_000001_expected_create_users_view.php @@ -7,7 +7,7 @@ use Illuminate\Support\Facades\DB; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class ExpectedCreateUsers_DB_View extends TestMigration +return new class extends TestMigration { /** * Run the migrations. @@ -16,7 +16,7 @@ class ExpectedCreateUsers_DB_View extends TestMigration */ public function up() { - DB::statement("CREATE VIEW users_[db]_view AS SELECT * from " . DB::getTablePrefix() . "users_[db]"); + DB::statement("CREATE VIEW users_view AS SELECT * from " . DB::getTablePrefix() . "users"); } /** @@ -26,6 +26,6 @@ public function up() */ public function down() { - DB::statement("DROP VIEW IF EXISTS users_[db]_view"); + DB::statement("DROP VIEW IF EXISTS users_view"); } -} +}; diff --git a/tests/resources/database/migrations/vendors/2018_08_08_100000_create_telescope_db_table.php b/tests/resources/database/migrations/vendors/2018_08_08_100000_create_telescope_table.php similarity index 76% rename from tests/resources/database/migrations/vendors/2018_08_08_100000_create_telescope_db_table.php rename to tests/resources/database/migrations/vendors/2018_08_08_100000_create_telescope_table.php index e59a0fe5..20838107 100644 --- a/tests/resources/database/migrations/vendors/2018_08_08_100000_create_telescope_db_table.php +++ b/tests/resources/database/migrations/vendors/2018_08_08_100000_create_telescope_table.php @@ -8,7 +8,7 @@ use Illuminate\Support\Facades\Schema; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class CreateTelescope_DB_Table extends TestMigration +return new class extends TestMigration { /** * Get the migration connection name. @@ -25,7 +25,7 @@ public function up(): void { $schema = Schema::connection($this->getConnection()); - $schema->create('telescope_entries_[db]', function (Blueprint $table) { + $schema->create('telescope_entries', function (Blueprint $table) { $table->bigIncrements('sequence'); $table->uuid('uuid'); $table->uuid('batch_id'); @@ -42,7 +42,7 @@ public function up(): void $table->index(['type', 'should_display_on_index']); }); - $schema->create('telescope_entries_tags_[db]', function (Blueprint $table) { + $schema->create('telescope_entries_tags', function (Blueprint $table) { $table->uuid('entry_uuid'); $table->string('tag'); @@ -51,11 +51,11 @@ public function up(): void $table->foreign('entry_uuid') ->references('uuid') - ->on('telescope_entries_[db]') + ->on('telescope_entries') ->onDelete('cascade'); }); - $schema->create('telescope_monitoring_[db]', function (Blueprint $table) { + $schema->create('telescope_monitoring', function (Blueprint $table) { $table->string('tag'); }); } @@ -67,8 +67,8 @@ public function down(): void { $schema = Schema::connection($this->getConnection()); - $schema->dropIfExists('telescope_entries_tags_[db]'); - $schema->dropIfExists('telescope_entries_[db]'); - $schema->dropIfExists('telescope_monitoring_[db]'); + $schema->dropIfExists('telescope_entries_tags'); + $schema->dropIfExists('telescope_entries'); + $schema->dropIfExists('telescope_monitoring'); } -} +}; diff --git a/tests/resources/database/migrations/vendors/2019_12_14_000001_create_personal_access_tokens_db_table.php b/tests/resources/database/migrations/vendors/2019_12_14_000001_create_personal_access_tokens_table.php similarity index 78% rename from tests/resources/database/migrations/vendors/2019_12_14_000001_create_personal_access_tokens_db_table.php rename to tests/resources/database/migrations/vendors/2019_12_14_000001_create_personal_access_tokens_table.php index 5e1d9010..233c289e 100644 --- a/tests/resources/database/migrations/vendors/2019_12_14_000001_create_personal_access_tokens_db_table.php +++ b/tests/resources/database/migrations/vendors/2019_12_14_000001_create_personal_access_tokens_table.php @@ -8,14 +8,14 @@ use Illuminate\Support\Facades\Schema; use KitLoong\MigrationsGenerator\Tests\TestMigration; -class CreatePersonalAccessTokens_DB_Table extends TestMigration +return new class extends TestMigration { /** * Run the migrations. */ public function up(): void { - Schema::create('personal_access_tokens_[db]', function (Blueprint $table) { + Schema::create('personal_access_tokens', function (Blueprint $table) { $table->bigIncrements('id'); $table->morphs('tokenable'); $table->string('name'); @@ -32,6 +32,6 @@ public function up(): void */ public function down(): void { - Schema::dropIfExists('personal_access_tokens_[db]'); + Schema::dropIfExists('personal_access_tokens'); } -} +};