Merge pull request #11429 from hafezdivandari/laravel-11-remove-doctrine-dbal

Laravel 11: Remove Doctrine DBAL
This commit is contained in:
Dan Harrin
2024-02-23 13:28:36 +00:00
committed by GitHub
13 changed files with 245 additions and 528 deletions
-1
View File
@@ -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",
Generated
+40 -387
View File
@@ -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",
@@ -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:
@@ -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:
@@ -14,17 +14,12 @@ trait CanGenerateExporterColumns
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 @@ trait CanGenerateExporterColumns
}
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}";
}
@@ -2,7 +2,6 @@
namespace Filament\Actions\Commands\Concerns;
use Doctrine\DBAL\Types;
use Illuminate\Support\Str;
trait CanGenerateImporterColumns
@@ -15,20 +14,17 @@ trait CanGenerateImporterColumns
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 @@ trait CanGenerateImporterColumns
$columnData['label'] = [Str::upper($columnName)];
}
if ($column->getNotnull()) {
if (! $column['nullable']) {
$columnData['rules'][0][] = 'required';
$columnData['requiredMapping'] = [];
}
@@ -59,44 +55,39 @@ trait CanGenerateImporterColumns
$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,
} elseif (in_array($type['name'], [
'integer',
'decimal',
'float',
'double',
'money',
])) {
$columnData['rules'][0][] = 'integer';
$columnData['numeric'] = [];
} elseif (in_array($column->getType()::class, [
Types\DecimalType::class,
Types\FloatType::class,
])) {
$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.
@@ -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).
@@ -2,7 +2,6 @@
namespace Filament\Forms\Commands\Concerns;
use Doctrine\DBAL\Types;
use Filament\Forms;
use Illuminate\Support\Str;
@@ -16,20 +15,17 @@ trait CanGenerateForms
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 @@ trait CanGenerateForms
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 @@ trait CanGenerateForms
$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)];
}
}
@@ -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:
-1
View File
@@ -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",
@@ -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 @@ trait CanReadModelSchemas
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 null;
}
return app($model)
->getConnection()
->getSchemaBuilder();
}
protected function guessBelongsToRelationshipName(AbstractAsset $column, string $model): ?string
protected function getModelTable(string $model): string
{
return app($model)->getTable();
}
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 @@ trait CanReadModelSchemas
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 @@ trait CanReadModelSchemas
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 @@ trait CanReadModelSchemas
return 'title';
}
return $schema->getPrimaryKey()->getColumns()[0];
return collect($schema->getIndexes($table))->firstWhere('primary')['columns'][0] ?? 'id';
}
/**
* @param array<string, mixed> $column
* @return array<string, mixed>
*/
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<string, mixed> $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;
}
}
@@ -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).
@@ -2,7 +2,6 @@
namespace Filament\Tables\Commands\Concerns;
use Doctrine\DBAL\Types;
use Filament\Tables;
use Illuminate\Support\Str;
@@ -16,27 +15,26 @@ trait CanGenerateTables
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 @@ trait CanGenerateTables
}
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 @@ trait CanGenerateTables
$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 @@ trait CanGenerateTables
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'] = [];
}
}