diff --git a/composer.json b/composer.json index b24984a7006..c238422a3fb 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,6 @@ "require-dev": { "anourvalar/eloquent-serialize": "^1.2", "danharrin/livewire-rate-limiting": "^0.3|^1.0", - "doctrine/dbal": "^3.2", "filament/support": "*", "kirschbaum-development/eloquent-power-joins": "^3.0", "larastan/larastan": "^2.2", diff --git a/composer.lock b/composer.lock index 765c65908de..a641edce83c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "282351566cb9fb9c4ee2b530e40b36ec", + "content-hash": "52e2c5aa84e2b1d4b7afb44512079b31", "packages": [], "packages-dev": [ { @@ -129,16 +129,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.300.1", + "version": "3.300.3", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "fb67a49453c2b1a51268156b70adeba6c619e743" + "reference": "a1202d1a82593fc2cdad54d627e54c518a984738" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/fb67a49453c2b1a51268156b70adeba6c619e743", - "reference": "fb67a49453c2b1a51268156b70adeba6c619e743", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/a1202d1a82593fc2cdad54d627e54c518a984738", + "reference": "a1202d1a82593fc2cdad54d627e54c518a984738", "shasum": "" }, "require": { @@ -218,9 +218,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.300.1" + "source": "https://github.com/aws/aws-sdk-php/tree/3.300.3" }, - "time": "2024-02-20T19:05:00+00:00" + "time": "2024-02-22T19:20:21+00:00" }, { "name": "blade-ui-kit/blade-heroicons", @@ -800,212 +800,6 @@ }, "time": "2022-10-27T11:44:00+00:00" }, - { - "name": "doctrine/cache", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/cache.git", - "reference": "1ca8f21980e770095a31456042471a57bc4c68fb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb", - "reference": "1ca8f21980e770095a31456042471a57bc4c68fb", - "shasum": "" - }, - "require": { - "php": "~7.1 || ^8.0" - }, - "conflict": { - "doctrine/common": ">2.2,<2.4" - }, - "require-dev": { - "cache/integration-tests": "dev-master", - "doctrine/coding-standard": "^9", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psr/cache": "^1.0 || ^2.0 || ^3.0", - "symfony/cache": "^4.4 || ^5.4 || ^6", - "symfony/var-exporter": "^4.4 || ^5.4 || ^6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", - "homepage": "https://www.doctrine-project.org/projects/cache.html", - "keywords": [ - "abstraction", - "apcu", - "cache", - "caching", - "couchdb", - "memcached", - "php", - "redis", - "xcache" - ], - "support": { - "issues": "https://github.com/doctrine/cache/issues", - "source": "https://github.com/doctrine/cache/tree/2.2.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache", - "type": "tidelift" - } - ], - "time": "2022-05-20T20:07:39+00:00" - }, - { - "name": "doctrine/dbal", - "version": "3.8.2", - "source": { - "type": "git", - "url": "https://github.com/doctrine/dbal.git", - "reference": "a19a1d05ca211f41089dffcc387733a6875196cb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/a19a1d05ca211f41089dffcc387733a6875196cb", - "reference": "a19a1d05ca211f41089dffcc387733a6875196cb", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2", - "doctrine/cache": "^1.11|^2.0", - "doctrine/deprecations": "^0.5.3|^1", - "doctrine/event-manager": "^1|^2", - "php": "^7.4 || ^8.0", - "psr/cache": "^1|^2|^3", - "psr/log": "^1|^2|^3" - }, - "require-dev": { - "doctrine/coding-standard": "12.0.0", - "fig/log-test": "^1", - "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "1.10.57", - "phpstan/phpstan-strict-rules": "^1.5", - "phpunit/phpunit": "9.6.16", - "psalm/plugin-phpunit": "0.18.4", - "slevomat/coding-standard": "8.13.1", - "squizlabs/php_codesniffer": "3.8.1", - "symfony/cache": "^5.4|^6.0|^7.0", - "symfony/console": "^4.4|^5.4|^6.0|^7.0", - "vimeo/psalm": "4.30.0" - }, - "suggest": { - "symfony/console": "For helpful console commands such as SQL execution and import of files." - }, - "bin": [ - "bin/doctrine-dbal" - ], - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\DBAL\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - } - ], - "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", - "homepage": "https://www.doctrine-project.org/projects/dbal.html", - "keywords": [ - "abstraction", - "database", - "db2", - "dbal", - "mariadb", - "mssql", - "mysql", - "oci8", - "oracle", - "pdo", - "pgsql", - "postgresql", - "queryobject", - "sasql", - "sql", - "sqlite", - "sqlserver", - "sqlsrv" - ], - "support": { - "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.8.2" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal", - "type": "tidelift" - } - ], - "time": "2024-02-12T18:36:36+00:00" - }, { "name": "doctrine/deprecations", "version": "1.1.3", @@ -1053,97 +847,6 @@ }, "time": "2024-01-30T19:34:25+00:00" }, - { - "name": "doctrine/event-manager", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/event-manager.git", - "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/event-manager/zipball/750671534e0241a7c50ea5b43f67e23eb5c96f32", - "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "conflict": { - "doctrine/common": "<2.9" - }, - "require-dev": { - "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.8.8", - "phpunit/phpunit": "^9.5", - "vimeo/psalm": "^4.28" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - }, - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com" - } - ], - "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", - "homepage": "https://www.doctrine-project.org/projects/event-manager.html", - "keywords": [ - "event", - "event dispatcher", - "event manager", - "event system", - "events" - ], - "support": { - "issues": "https://github.com/doctrine/event-manager/issues", - "source": "https://github.com/doctrine/event-manager/tree/2.0.0" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", - "type": "tidelift" - } - ], - "time": "2022-10-12T20:59:15+00:00" - }, { "name": "doctrine/inflector", "version": "2.0.10", @@ -1570,15 +1273,14 @@ "dist": { "type": "path", "url": "packages/support", - "reference": "781c8793577215de042b467bd1520c9ac877dd28" + "reference": "5d5df437d6ecd6b2f7947e23bb9b199f434980fd" }, "require": { "blade-ui-kit/blade-heroicons": "^2.2.1", - "doctrine/dbal": "^3.2", "ext-intl": "*", - "illuminate/contracts": "^10.0|^11.0", - "illuminate/support": "^10.0|^11.0", - "illuminate/view": "^10.0|^11.0", + "illuminate/contracts": "^10.45|^11.0", + "illuminate/support": "^10.45|^11.0", + "illuminate/view": "^10.45|^11.0", "livewire/livewire": "^3.2.3", "php": "^8.1", "ryangjchandler/blade-capture-directive": "^0.2|^0.3", @@ -2593,16 +2295,16 @@ }, { "name": "laravel/framework", - "version": "v10.45.0", + "version": "v10.45.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "8b08d8cd79f8093eb51a8c59e21647bedfbf05f2" + "reference": "dcf5d1d722b84ad38a5e053289130b6962f830bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/8b08d8cd79f8093eb51a8c59e21647bedfbf05f2", - "reference": "8b08d8cd79f8093eb51a8c59e21647bedfbf05f2", + "url": "https://api.github.com/repos/laravel/framework/zipball/dcf5d1d722b84ad38a5e053289130b6962f830bd", + "reference": "dcf5d1d722b84ad38a5e053289130b6962f830bd", "shasum": "" }, "require": { @@ -2795,7 +2497,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-02-20T15:32:48+00:00" + "time": "2024-02-21T14:07:36+00:00" }, { "name": "laravel/pint", @@ -4645,16 +4347,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.0.0", + "version": "v5.0.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc" + "reference": "2218c2252c874a4624ab2f613d86ac32d227bc69" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4a21235f7e56e713259a6f76bf4b5ea08502b9dc", - "reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/2218c2252c874a4624ab2f613d86ac32d227bc69", + "reference": "2218c2252c874a4624ab2f613d86ac32d227bc69", "shasum": "" }, "require": { @@ -4697,9 +4399,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.1" }, - "time": "2024-01-07T17:17:35+00:00" + "time": "2024-02-21T19:24:10+00:00" }, { "name": "nunomaduro/collision", @@ -5180,16 +4882,16 @@ }, { "name": "orchestra/testbench-core", - "version": "v8.21.1", + "version": "v8.22.1", "source": { "type": "git", "url": "https://github.com/orchestral/testbench-core.git", - "reference": "c6bf49a1b7ef4afe58e5bda676158c4de41f9a81" + "reference": "b0006c092694828f4b0fa409a369b798e5e26f8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/orchestral/testbench-core/zipball/c6bf49a1b7ef4afe58e5bda676158c4de41f9a81", - "reference": "c6bf49a1b7ef4afe58e5bda676158c4de41f9a81", + "url": "https://api.github.com/repos/orchestral/testbench-core/zipball/b0006c092694828f4b0fa409a369b798e5e26f8d", + "reference": "b0006c092694828f4b0fa409a369b798e5e26f8d", "shasum": "" }, "require": { @@ -5268,7 +4970,7 @@ "issues": "https://github.com/orchestral/testbench/issues", "source": "https://github.com/orchestral/testbench-core" }, - "time": "2024-01-22T01:45:47+00:00" + "time": "2024-02-21T23:33:22+00:00" }, { "name": "orchestra/workbench", @@ -5886,21 +5588,21 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.8.1", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "bc3dc91a5e9b14aa06d1d9e90647c5c5a2cc5353" + "reference": "153ae662783729388a584b4361f2545e4d841e3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/bc3dc91a5e9b14aa06d1d9e90647c5c5a2cc5353", - "reference": "bc3dc91a5e9b14aa06d1d9e90647c5c5a2cc5353", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", + "reference": "153ae662783729388a584b4361f2545e4d841e3c", "shasum": "" }, "require": { "doctrine/deprecations": "^1.0", - "php": "^7.4 || ^8.0", + "php": "^7.3 || ^8.0", "phpdocumentor/reflection-common": "^2.0", "phpstan/phpdoc-parser": "^1.13" }, @@ -5938,9 +5640,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.1" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" }, - "time": "2024-01-18T19:15:27+00:00" + "time": "2024-02-23T11:10:43+00:00" }, { "name": "phpmyadmin/sql-parser", @@ -6733,55 +6435,6 @@ }, "time": "2021-10-28T11:13:42+00:00" }, - { - "name": "psr/cache", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for caching libraries", - "keywords": [ - "cache", - "psr", - "psr-6" - ], - "support": { - "source": "https://github.com/php-fig/cache/tree/3.0.0" - }, - "time": "2021-02-03T23:26:27+00:00" - }, { "name": "psr/clock", "version": "1.0.0", @@ -8930,16 +8583,16 @@ }, { "name": "spatie/laravel-medialibrary", - "version": "11.4.3", + "version": "11.4.4", "source": { "type": "git", "url": "https://github.com/spatie/laravel-medialibrary.git", - "reference": "d36b8f2b39811e0269b213df6b166890304360f4" + "reference": "bb29cacfe209856db304adc6c5ba8c05ff7a8802" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-medialibrary/zipball/d36b8f2b39811e0269b213df6b166890304360f4", - "reference": "d36b8f2b39811e0269b213df6b166890304360f4", + "url": "https://api.github.com/repos/spatie/laravel-medialibrary/zipball/bb29cacfe209856db304adc6c5ba8c05ff7a8802", + "reference": "bb29cacfe209856db304adc6c5ba8c05ff7a8802", "shasum": "" }, "require": { @@ -9022,7 +8675,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-medialibrary/issues", - "source": "https://github.com/spatie/laravel-medialibrary/tree/11.4.3" + "source": "https://github.com/spatie/laravel-medialibrary/tree/11.4.4" }, "funding": [ { @@ -9034,7 +8687,7 @@ "type": "github" } ], - "time": "2024-02-20T15:26:46+00:00" + "time": "2024-02-22T09:39:31+00:00" }, { "name": "spatie/laravel-package-tools", diff --git a/packages/actions/docs/07-prebuilt-actions/08-import.md b/packages/actions/docs/07-prebuilt-actions/08-import.md index 0486e600696..ff64226ab74 100644 --- a/packages/actions/docs/07-prebuilt-actions/08-import.md +++ b/packages/actions/docs/07-prebuilt-actions/08-import.md @@ -67,8 +67,6 @@ If you'd like to save time, Filament can automatically generate the [columns](#d php artisan make:filament-importer Product --generate ``` -> If your table contains ENUM columns, the `doctrine/dbal` package we use is unable to scan your table and will crash. Hence, Filament is unable to generate the columns for your importer if it contains an ENUM column. Read more about this issue [here](https://github.com/doctrine/dbal/issues/3819#issuecomment-573419808). - ## Defining importer columns To define the columns that can be imported, you need to override the `getColumns()` method on your importer class, returning an array of `ImportColumn` objects: diff --git a/packages/actions/docs/07-prebuilt-actions/09-export.md b/packages/actions/docs/07-prebuilt-actions/09-export.md index 38f7cdbe143..08f1c4980d4 100644 --- a/packages/actions/docs/07-prebuilt-actions/09-export.md +++ b/packages/actions/docs/07-prebuilt-actions/09-export.md @@ -82,8 +82,6 @@ If you'd like to save time, Filament can automatically generate the [columns](#d php artisan make:filament-exporter Product --generate ``` -> If your table contains ENUM columns, the `doctrine/dbal` package we use is unable to scan your table and will crash. Hence, Filament is unable to generate the columns for your exporter if it contains an ENUM column. Read more about this issue [here](https://github.com/doctrine/dbal/issues/3819#issuecomment-573419808). - ## Defining exporter columns To define the columns that can be exported, you need to override the `getColumns()` method on your exporter class, returning an array of `ExportColumn` objects: diff --git a/packages/actions/src/Commands/Concerns/CanGenerateExporterColumns.php b/packages/actions/src/Commands/Concerns/CanGenerateExporterColumns.php index abb6a4a7544..b4eed679c58 100644 --- a/packages/actions/src/Commands/Concerns/CanGenerateExporterColumns.php +++ b/packages/actions/src/Commands/Concerns/CanGenerateExporterColumns.php @@ -14,17 +14,12 @@ protected function getExporterColumns(string $model): string return '//'; } + $schema = $this->getModelSchema($model); $table = $this->getModelTable($model); - if (blank($table)) { - return '//'; - } - $columns = []; - foreach ($table->getColumns() as $column) { - $columnName = $column->getName(); - + foreach ($schema->getColumnListing($table) as $columnName) { if (str($columnName)->endsWith([ '_token', ])) { @@ -38,10 +33,10 @@ protected function getExporterColumns(string $model): string } if (str($columnName)->endsWith('_id')) { - $guessedRelationshipName = $this->guessBelongsToRelationshipName($column, $model); + $guessedRelationshipName = $this->guessBelongsToRelationshipName($columnName, $model); if (filled($guessedRelationshipName)) { - $guessedRelationshipTitleColumnName = $this->guessBelongsToRelationshipTitleColumnName($column, app($model)->{$guessedRelationshipName}()->getModel()::class); + $guessedRelationshipTitleColumnName = $this->guessBelongsToRelationshipTitleColumnName($columnName, app($model)->{$guessedRelationshipName}()->getModel()::class); $columnName = "{$guessedRelationshipName}.{$guessedRelationshipTitleColumnName}"; } diff --git a/packages/actions/src/Commands/Concerns/CanGenerateImporterColumns.php b/packages/actions/src/Commands/Concerns/CanGenerateImporterColumns.php index be0560fc651..c3b6f4c6bbb 100644 --- a/packages/actions/src/Commands/Concerns/CanGenerateImporterColumns.php +++ b/packages/actions/src/Commands/Concerns/CanGenerateImporterColumns.php @@ -2,7 +2,6 @@ namespace Filament\Actions\Commands\Concerns; -use Doctrine\DBAL\Types; use Illuminate\Support\Str; trait CanGenerateImporterColumns @@ -15,20 +14,17 @@ protected function getImporterColumns(string $model): string return '//'; } + $schema = $this->getModelSchema($model); $table = $this->getModelTable($model); - if (blank($table)) { - return '//'; - } - $columns = []; - foreach ($table->getColumns() as $column) { - if ($column->getAutoincrement()) { + foreach ($schema->getColumns($table) as $column) { + if ($column['auto_increment']) { continue; } - $columnName = $column->getName(); + $columnName = $column['name']; if (str($columnName)->is([ app($model)->getKeyName(), @@ -50,7 +46,7 @@ protected function getImporterColumns(string $model): string $columnData['label'] = [Str::upper($columnName)]; } - if ($column->getNotnull()) { + if (! $column['nullable']) { $columnData['rules'][0][] = 'required'; $columnData['requiredMapping'] = []; } @@ -59,44 +55,39 @@ protected function getImporterColumns(string $model): string $columnData['rules'][0][] = 'email'; } + $type = $this->parseColumnType($column); + if ( str($columnName)->endsWith('_id') && - filled($guessedRelationshipName = $this->guessBelongsToRelationshipName($column, $model)) + filled($guessedRelationshipName = $this->guessBelongsToRelationshipName($columnName, $model)) ) { $columnName = $guessedRelationshipName; $columnData['relationship'] = []; - } elseif (in_array($column->getType()::class, [ - Types\BooleanType::class, + } elseif (in_array($type['name'], [ + 'boolean', ])) { $columnData['rules'][0][] = 'boolean'; $columnData['boolean'] = []; - } elseif (in_array($column->getType()::class, [ - Types\DateImmutableType::class, - Types\DateType::class, + } elseif (in_array($type['name'], [ + 'date', ])) { $columnData['rules'][0][] = 'date'; - } elseif (in_array($column->getType()::class, [ - Types\DateTimeImmutableType::class, - Types\DateTimeType::class, - Types\DateTimeTzImmutableType::class, - Types\DateTimeTzType::class, + } elseif (in_array($type['name'], [ + 'datetime', + 'timestamp', ])) { $columnData['rules'][0][] = 'datetime'; - } elseif (in_array($column->getType()::class, [ - Types\IntegerType::class, - Types\SmallIntType::class, - Types\BigIntType::class, - ])) { - $columnData['rules'][0][] = 'integer'; - $columnData['numeric'] = []; - } elseif (in_array($column->getType()::class, [ - Types\DecimalType::class, - Types\FloatType::class, + } elseif (in_array($type['name'], [ + 'integer', + 'decimal', + 'float', + 'double', + 'money', ])) { $columnData['rules'][0][] = 'integer'; $columnData['numeric'] = []; - } elseif ($length = $column->getLength()) { - $columnData['rules'][0][] = "max:{$length}"; + } elseif (isset($type['length'])) { + $columnData['rules'][0][] = "max:{$type['length']}"; } // Move rules to the end of the column definition. diff --git a/packages/forms/docs/08-adding-a-form-to-a-livewire-component.md b/packages/forms/docs/08-adding-a-form-to-a-livewire-component.md index e819e152494..6d3a9cb0e2a 100644 --- a/packages/forms/docs/08-adding-a-form-to-a-livewire-component.md +++ b/packages/forms/docs/08-adding-a-form-to-a-livewire-component.md @@ -329,5 +329,3 @@ Filament is also able to guess which form fields you want in the schema, based o ```bash php artisan make:livewire-form Products/CreateProduct --generate ``` - -> If your table contains ENUM columns, the `doctrine/dbal` package we use is unable to scan your table and will crash. Hence, Filament is unable to generate the schema for your table if it contains an ENUM column. Read more about this issue [here](https://github.com/doctrine/dbal/issues/3819#issuecomment-573419808). diff --git a/packages/forms/src/Commands/Concerns/CanGenerateForms.php b/packages/forms/src/Commands/Concerns/CanGenerateForms.php index 0e63c788488..e888f2eb406 100644 --- a/packages/forms/src/Commands/Concerns/CanGenerateForms.php +++ b/packages/forms/src/Commands/Concerns/CanGenerateForms.php @@ -2,7 +2,6 @@ namespace Filament\Forms\Commands\Concerns; -use Doctrine\DBAL\Types; use Filament\Forms; use Illuminate\Support\Str; @@ -16,20 +15,17 @@ protected function getResourceFormSchema(string $model): string return '//'; } + $schema = $this->getModelSchema($model); $table = $this->getModelTable($model); - if (blank($table)) { - return '//'; - } - $components = []; - foreach ($table->getColumns() as $column) { - if ($column->getAutoincrement()) { + foreach ($schema->getColumns($table) as $column) { + if ($column['auto_increment']) { continue; } - $columnName = $column->getName(); + $columnName = $column['name']; if (str($columnName)->is([ app($model)->getKeyName(), @@ -41,22 +37,24 @@ protected function getResourceFormSchema(string $model): string continue; } + $type = $this->parseColumnType($column); + $componentData = []; $componentData['type'] = match (true) { - $column->getType()::class === Types\BooleanType::class => Forms\Components\Toggle::class, - in_array($column->getType()::class, [Types\DateImmutableType::class, Types\DateType::class]) => Forms\Components\DatePicker::class, - in_array($column->getType()::class, [Types\DateTimeImmutableType::class, Types\DateTimeType::class, Types\DateTimeTzImmutableType::class, Types\DateTimeTzType::class]) => Forms\Components\DateTimePicker::class, - $column->getType()::class === Types\TextType::class => Forms\Components\Textarea::class, + $type['name'] === 'boolean' => Forms\Components\Toggle::class, + $type['name'] === 'date' => Forms\Components\DatePicker::class, + in_array($type['name'], ['datetime', 'timestamp']) => Forms\Components\DateTimePicker::class, + $type['name'] === 'text' => Forms\Components\Textarea::class, $columnName === 'image', str($columnName)->startsWith('image_'), str($columnName)->contains('_image_'), str($columnName)->endsWith('_image') => Forms\Components\FileUpload::class, default => Forms\Components\TextInput::class, }; if (str($columnName)->endsWith('_id')) { - $guessedRelationshipName = $this->guessBelongsToRelationshipName($column, $model); + $guessedRelationshipName = $this->guessBelongsToRelationshipName($columnName, $model); if (filled($guessedRelationshipName)) { - $guessedRelationshipTitleColumnName = $this->guessBelongsToRelationshipTitleColumnName($column, app($model)->{$guessedRelationshipName}()->getModel()::class); + $guessedRelationshipTitleColumnName = $this->guessBelongsToRelationshipTitleColumnName($columnName, app($model)->{$guessedRelationshipName}()->getModel()::class); $componentData['type'] = Forms\Components\Select::class; $componentData['relationship'] = [$guessedRelationshipName, $guessedRelationshipTitleColumnName]; @@ -89,40 +87,40 @@ protected function getResourceFormSchema(string $model): string $componentData['image'] = []; } - if ($column->getNotnull()) { + if (! $column['nullable']) { $componentData['required'] = []; } - if (in_array($column->getType()::class, [ - Types\BigIntType::class, - Types\DecimalType::class, - Types\FloatType::class, - Types\IntegerType::class, - Types\SmallIntType::class, + if (in_array($type['name'], [ + 'integer', + 'decimal', + 'float', + 'double', + 'money', ])) { if ($componentData['type'] === Forms\Components\TextInput::class) { $componentData['numeric'] = []; } - if (filled($column->getDefault())) { - $componentData['default'] = [$column->getDefault()]; + if (filled($column['default'])) { + $componentData['default'] = [$this->parseDefaultExpression($column, $model)]; } if (in_array($columnName, [ 'cost', 'money', 'price', - ])) { + ]) || $type['name'] === 'money') { $componentData['prefix'] = ['$']; } } elseif (in_array($componentData['type'], [ Forms\Components\TextInput::class, Forms\Components\Textarea::class, - ]) && ($length = $column->getLength())) { - $componentData['maxLength'] = [$length]; + ]) && isset($type['length'])) { + $componentData['maxLength'] = [$type['length']]; - if (filled($column->getDefault())) { - $componentData['default'] = [$column->getDefault()]; + if (filled($column['default'])) { + $componentData['default'] = [$this->parseDefaultExpression($column, $model)]; } } diff --git a/packages/panels/docs/03-resources/01-getting-started.md b/packages/panels/docs/03-resources/01-getting-started.md index 9e90d29f4b6..15321c9baed 100644 --- a/packages/panels/docs/03-resources/01-getting-started.md +++ b/packages/panels/docs/03-resources/01-getting-started.md @@ -60,8 +60,6 @@ If you'd like to save time, Filament can automatically generate the [form](#reso php artisan make:filament-resource Customer --generate ``` -> If your table contains ENUM columns, the `doctrine/dbal` package we use is unable to scan your table and will crash. Hence, Filament is unable to generate the schema for your resource if it contains an ENUM column. Read more about this issue [here](https://github.com/doctrine/dbal/issues/3819#issuecomment-573419808). - ### Handling soft deletes By default, you will not be able to interact with deleted records in the app. If you'd like to add functionality to restore, force delete and filter trashed records in your resource, use the `--soft-deletes` flag when generating the resource: diff --git a/packages/support/composer.json b/packages/support/composer.json index 2b55d9a6ba6..7719b6fb66e 100644 --- a/packages/support/composer.json +++ b/packages/support/composer.json @@ -10,7 +10,6 @@ "require": { "php": "^8.1", "blade-ui-kit/blade-heroicons": "^2.2.1", - "doctrine/dbal": "^3.2", "ext-intl": "*", "illuminate/contracts": "^10.45|^11.0", "illuminate/support": "^10.45|^11.0", diff --git a/packages/support/src/Commands/Concerns/CanReadModelSchemas.php b/packages/support/src/Commands/Concerns/CanReadModelSchemas.php index 241d1ab1b5e..c3df34f1cdc 100644 --- a/packages/support/src/Commands/Concerns/CanReadModelSchemas.php +++ b/packages/support/src/Commands/Concerns/CanReadModelSchemas.php @@ -2,15 +2,12 @@ namespace Filament\Support\Commands\Concerns; -use Doctrine\DBAL\Schema\AbstractAsset; -use Doctrine\DBAL\Schema\Table; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Facades\Schema; use Illuminate\Support\Str; use ReflectionClass; use ReflectionException; -use Throwable; trait CanReadModelSchemas { @@ -23,29 +20,24 @@ protected function getModel(string $model): ?string return $model; } - protected function getModelTable(string $model): ?Table + protected function getModelSchema(string $model): Schema { - $modelClass = $model; - $model = app($model); - - try { - return $model - ->getConnection() - ->getDoctrineSchemaManager() - ->listTableDetails($model->getTable()); - } catch (Throwable $exception) { - $this->components->warn("Unable to read table schema for model [{$modelClass}]: {$exception->getMessage()}"); + return app($model) + ->getConnection() + ->getSchemaBuilder(); + } - return null; - } + protected function getModelTable(string $model): string + { + return app($model)->getTable(); } - protected function guessBelongsToRelationshipName(AbstractAsset $column, string $model): ?string + protected function guessBelongsToRelationshipName(string $column, string $model): ?string { /** @var Model $modelInstance */ $modelInstance = app($model); $modelInstanceReflection = new ReflectionClass($modelInstance); - $guessedRelationshipName = str($column->getName())->beforeLast('_id'); + $guessedRelationshipName = str($column)->beforeLast('_id'); $hasRelationship = $modelInstanceReflection->hasMethod($guessedRelationshipName); if (! $hasRelationship) { @@ -74,9 +66,9 @@ protected function guessBelongsToRelationshipName(AbstractAsset $column, string return $guessedRelationshipName; } - protected function guessBelongsToRelationshipTableName(AbstractAsset $column): ?string + protected function guessBelongsToRelationshipTableName(string $column): ?string { - $tableName = str($column->getName())->beforeLast('_id'); + $tableName = str($column)->beforeLast('_id'); if (Schema::hasTable(Str::plural($tableName))) { return Str::plural($tableName); @@ -89,15 +81,12 @@ protected function guessBelongsToRelationshipTableName(AbstractAsset $column): ? return $tableName; } - protected function guessBelongsToRelationshipTitleColumnName(AbstractAsset $column, string $model): string + protected function guessBelongsToRelationshipTitleColumnName(string $column, string $model): string { - $schema = $this->getModelTable($model); + $schema = $this->getModelSchema($model); + $table = $this->getModelTable($model); - if ($schema === null) { - return 'id'; - } - - $columns = collect(array_keys($schema->getColumns())); + $columns = collect($schema->getColumnListing($table)); if ($columns->contains('name')) { return 'name'; @@ -107,6 +96,113 @@ protected function guessBelongsToRelationshipTitleColumnName(AbstractAsset $colu return 'title'; } - return $schema->getPrimaryKey()->getColumns()[0]; + return collect($schema->getIndexes($table))->firstWhere('primary')['columns'][0] ?? 'id'; + } + + /** + * @param array $column + * @return array + */ + protected function parseColumnType(array $column): array + { + $type = match ($column['type']) { + 'tinyint(1)', 'bit' => 'boolean', + 'varchar(max)', 'nvarchar(max)' => 'text', + default => null, + }; + + $type ??= match ($column['type_name']) { + 'boolean', 'bool' => 'boolean', + 'char', 'bpchar', 'nchar' => 'char', + 'varchar', 'nvarchar' => 'string', + 'integer', 'int', 'int4', 'tinyint', 'smallint', 'int2', 'mediumint', 'bigint', 'int8' => 'integer', + 'decimal', 'numeric' => 'decimal', + 'float', 'real', 'float4' => 'float', + 'double', 'float8' => 'double', + 'money', 'smallmoney' => 'money', + 'date' => 'date', + 'time', 'timetz' => 'time', + 'datetime', 'datetime2', 'smalldatetime', 'datetimeoffset' => 'datetime', + 'timestamp', 'timestamptz' => 'timestamp', + 'text', 'tinytext', 'longtext', 'mediumtext', 'ntext' => 'text', + 'json', 'jsonb' => 'json', + default => $column['type_name'], + }; + + $values = str_contains($column['type'], '(') + ? str_getcsv(Str::between($column['type'], '(', ')'), ',', "'") + : null; + + $values = is_null($values) ? [] : match ($type) { + 'string', 'char', 'binary', 'bit' => ['length' => (int) $values[0]], + default => [], + }; + + return array_merge(['name' => $type], array_filter($values)); + } + + /** + * @param array $column + */ + protected function parseDefaultExpression(array $column, string $model): mixed + { + $default = $column['default']; + + if (blank($default)) { + return null; + } + + $driver = app($model)->getConnection()->getDriverName(); + + if ($driver === 'mysql') { + if ($default === 'NULL' + || preg_match("/^\(.*\)$/", $default) === 1 + || str_ends_with($default, '()') + || str_starts_with(strtolower($default), 'current_timestamp')) { + return null; + } + + if (preg_match("/^'(.*)'$/", $default, $matches) === 1) { + return str_replace("''", "'", $matches[1]); + } + } + + if ($driver === 'pgsql') { + if (str_starts_with($default, 'NULL::')) { + $default = null; + } + + if (preg_match("/^['(](.*)[')]::/", $default, $matches) === 1) { + return str_replace("''", "'", $matches[1]); + } + } + + if ($driver === 'sqlsrv') { + while (preg_match('/^\((.*)\)$/', $default, $matches)) { + $default = $matches[1]; + } + + if ($default === 'NULL' + || str_ends_with($default, '()')) { + return null; + } + + if (preg_match('/^\'(.*)\'$/', $default, $matches) === 1) { + return str_replace("''", "'", $matches[1]); + } + } + + if ($driver === 'sqlite') { + if ($default === 'NULL' + || str_starts_with(strtolower($default), 'current_timestamp')) { + return null; + } + + if (preg_match('/^\'(.*)\'$/s', $default, $matches) === 1) { + return str_replace("''", "'", $matches[1]); + } + } + + return $default; } } diff --git a/packages/tables/docs/11-adding-a-table-to-a-livewire-component.md b/packages/tables/docs/11-adding-a-table-to-a-livewire-component.md index 75d4e044d6f..8b225d260a1 100644 --- a/packages/tables/docs/11-adding-a-table-to-a-livewire-component.md +++ b/packages/tables/docs/11-adding-a-table-to-a-livewire-component.md @@ -135,5 +135,3 @@ Filament is also able to guess which table columns you want in the table, based ```bash php artisan make:livewire-table Products/ListProducts --generate ``` - -> If your table contains ENUM columns, the `doctrine/dbal` package we use is unable to scan your table and will crash. Hence, Filament is unable to generate the schema for your table if it contains an ENUM column. Read more about this issue [here](https://github.com/doctrine/dbal/issues/3819#issuecomment-573419808). diff --git a/packages/tables/src/Commands/Concerns/CanGenerateTables.php b/packages/tables/src/Commands/Concerns/CanGenerateTables.php index b4ea2f34a5c..113fbf68abd 100644 --- a/packages/tables/src/Commands/Concerns/CanGenerateTables.php +++ b/packages/tables/src/Commands/Concerns/CanGenerateTables.php @@ -2,7 +2,6 @@ namespace Filament\Tables\Commands\Concerns; -use Doctrine\DBAL\Types; use Filament\Tables; use Illuminate\Support\Str; @@ -16,27 +15,26 @@ protected function getResourceTableColumns(string $model): string return '//'; } + $schema = $this->getModelSchema($model); $table = $this->getModelTable($model); - if (blank($table)) { - return '//'; - } - $columns = []; - foreach ($table->getColumns() as $column) { - if ($column->getAutoincrement()) { + foreach ($schema->getColumns($table) as $column) { + if ($column['auto_increment']) { continue; } - if (in_array($column->getType()::class, [ - Types\JsonType::class, - Types\TextType::class, + $type = $this->parseColumnType($column); + + if (in_array($type['name'], [ + 'json', + 'text', ])) { continue; } - $columnName = $column->getName(); + $columnName = $column['name']; if (str($columnName)->endsWith([ '_token', @@ -51,10 +49,10 @@ protected function getResourceTableColumns(string $model): string } if (str($columnName)->endsWith('_id')) { - $guessedRelationshipName = $this->guessBelongsToRelationshipName($column, $model); + $guessedRelationshipName = $this->guessBelongsToRelationshipName($columnName, $model); if (filled($guessedRelationshipName)) { - $guessedRelationshipTitleColumnName = $this->guessBelongsToRelationshipTitleColumnName($column, app($model)->{$guessedRelationshipName}()->getModel()::class); + $guessedRelationshipTitleColumnName = $this->guessBelongsToRelationshipTitleColumnName($columnName, app($model)->{$guessedRelationshipName}()->getModel()::class); $columnName = "{$guessedRelationshipName}.{$guessedRelationshipTitleColumnName}"; } @@ -70,7 +68,7 @@ protected function getResourceTableColumns(string $model): string $columnData['label'] = [Str::upper($columnName)]; } - if ($column->getType() instanceof Types\BooleanType) { + if ($type['name'] === 'boolean') { $columnData['type'] = Tables\Columns\IconColumn::class; $columnData['boolean'] = []; } else { @@ -79,42 +77,40 @@ protected function getResourceTableColumns(string $model): string default => Tables\Columns\TextColumn::class, }; - if (in_array($column->getType()::class, [ - Types\StringType::class, + if (in_array($type['name'], [ + 'string', + 'char', ]) && ($columnData['type'] === Tables\Columns\TextColumn::class)) { $columnData['searchable'] = []; } - if (in_array($column->getType()::class, [ - Types\DateImmutableType::class, - Types\DateType::class, + if (in_array($type['name'], [ + 'date', ])) { $columnData['date'] = []; $columnData['sortable'] = []; } - if (in_array($column->getType()::class, [ - Types\DateTimeImmutableType::class, - Types\DateTimeType::class, - Types\DateTimeTzImmutableType::class, - Types\DateTimeTzType::class, + if (in_array($type['name'], [ + 'datetime', + 'timestamp', ])) { $columnData['dateTime'] = []; $columnData['sortable'] = []; } - if (in_array($column->getType()::class, [ - Types\BigIntType::class, - Types\DecimalType::class, - Types\FloatType::class, - Types\IntegerType::class, - Types\SmallIntType::class, + if (in_array($type['name'], [ + 'integer', + 'decimal', + 'float', + 'double', + 'money', ])) { $columnData[in_array($columnName, [ 'cost', 'money', 'price', - ]) ? 'money' : 'numeric'] = []; + ]) || $type['name'] === 'money' ? 'money' : 'numeric'] = []; $columnData['sortable'] = []; } }