diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 442ddb1..addbb4b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: true matrix: - php: [ '7.4', '8.0', '8.1', '8.2', '8.3', '8.4' ] + php: [ '7.4', '8.0', '8.1', '8.2', '8.3' ] name: PHP ${{ matrix.php }} env: diff --git a/composer.json b/composer.json index 5068ea3..6f32704 100644 --- a/composer.json +++ b/composer.json @@ -20,18 +20,18 @@ } }, "require": { - "doctrine/dbal": "^3.5", - "illuminate/collections": "^8.0|^9.0|^10.0", - "illuminate/database": "^8.0|^9.0|^10.0", + "illuminate/collections": "^8.0|^9.0|^10.0|^11.0", + "illuminate/database": "^8.0|^9.0|^10.0|^11.0", "nikic/php-parser": "^4.15" }, "require-dev": { + "doctrine/dbal": "^3.5|^4.0", "guzzlehttp/guzzle": "^7.5", "mockery/mockery": "^1.5", - "orchestra/testbench": "^6.25|^7.13|^8.0", - "phpunit/phpunit": "^9.5", - "spatie/laravel-permission": "^5.7", - "spatie/phpunit-snapshot-assertions": "^4.2" + "orchestra/testbench": "^6.25|^7.13|^8.0|^9.0", + "phpunit/phpunit": "^9.5|^10.0|^11.0", + "spatie/laravel-permission": "^5.7|^6.0", + "spatie/phpunit-snapshot-assertions": "^4.2|^5.1.6" }, "scripts": { "post-autoload-dump": [ diff --git a/database/migrations/2014_10_12_000000_testbench_create_users_table.php b/database/migrations/2014_10_12_000000_testbench_create_users_table.php new file mode 100644 index 0000000..f69ce2d --- /dev/null +++ b/database/migrations/2014_10_12_000000_testbench_create_users_table.php @@ -0,0 +1,36 @@ +id(); + $table->string('name'); + $table->string('email')->unique(); + $table->timestamp('email_verified_at')->nullable(); + $table->string('password'); + $table->rememberToken(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('users'); + } +}; diff --git a/database/migrations/2014_10_12_100000_testbench_create_password_reset_tokens_table.php b/database/migrations/2014_10_12_100000_testbench_create_password_reset_tokens_table.php new file mode 100644 index 0000000..3366d60 --- /dev/null +++ b/database/migrations/2014_10_12_100000_testbench_create_password_reset_tokens_table.php @@ -0,0 +1,32 @@ +string('email')->primary(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('password_reset_tokens'); + } +}; diff --git a/database/migrations/2019_08_19_000000_testbench_create_failed_jobs_table.php b/database/migrations/2019_08_19_000000_testbench_create_failed_jobs_table.php new file mode 100644 index 0000000..7ef0ed0 --- /dev/null +++ b/database/migrations/2019_08_19_000000_testbench_create_failed_jobs_table.php @@ -0,0 +1,36 @@ +id(); + $table->string('uuid')->unique(); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('failed_jobs'); + } +}; diff --git a/phpunit.xml b/phpunit.xml index 48306e3..839c210 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,27 +1,13 @@ - - - - tests - - - - - - src - - + + + + tests + + + + + src + + diff --git a/src/Adapter/Contracts/Column.php b/src/Adapter/Contracts/Column.php new file mode 100644 index 0000000..eab44d9 --- /dev/null +++ b/src/Adapter/Contracts/Column.php @@ -0,0 +1,20 @@ + + */ + public function getColumns(): Collection; + + /** + * @return Collection + */ + public function getPrimaryKey(): Collection; +} diff --git a/src/Adapter/Column.php b/src/Adapter/DBAL/Column.php similarity index 87% rename from src/Adapter/Column.php rename to src/Adapter/DBAL/Column.php index 6a50573..b30d9cf 100644 --- a/src/Adapter/Column.php +++ b/src/Adapter/DBAL/Column.php @@ -1,12 +1,13 @@ column->getPrecision(); } - public function getColumnType(): string - { - try { - return Type::getTypeRegistry()->lookupName($this->column->getType()); - } catch (Exception $e) { - return 'unknown'; - } - } - public function getDefault() { return $this->column->getDefault(); @@ -53,4 +45,13 @@ public function getAutoincrement(): bool { return $this->column->getAutoincrement(); } + + public function getColumnType(): string + { + try { + return Type::getTypeRegistry()->lookupName($this->column->getType()); + } catch (Exception $e) { + return 'unknown'; + } + } } diff --git a/src/Adapter/DBAL/SchemaManager.php b/src/Adapter/DBAL/SchemaManager.php new file mode 100644 index 0000000..0db3f0c --- /dev/null +++ b/src/Adapter/DBAL/SchemaManager.php @@ -0,0 +1,23 @@ +schemaManager = $connection->getDoctrineSchemaManager(); + } + + public function introspectTable(string $name): TableContract + { + return new Table($this->schemaManager->introspectTable($name)); + } +} diff --git a/src/Adapter/Table.php b/src/Adapter/DBAL/Table.php similarity index 84% rename from src/Adapter/Table.php rename to src/Adapter/DBAL/Table.php index 3248042..d5222c9 100644 --- a/src/Adapter/Table.php +++ b/src/Adapter/DBAL/Table.php @@ -1,12 +1,13 @@ - */ public function getPrimaryKey(): Collection { $primaryKey = $this->table->getPrimaryKey(); diff --git a/src/Adapter/Laravel/Column.php b/src/Adapter/Laravel/Column.php new file mode 100644 index 0000000..bfaeb45 --- /dev/null +++ b/src/Adapter/Laravel/Column.php @@ -0,0 +1,54 @@ +column = $column; + } + + public function getName(): string + { + return $this->column['name']; + } + + public function getNotnull(): bool + { + return ! $this->column['nullable']; + } + + public function getPrecision(): int + { + return $this->column['precision'] ?? 10; + } + + public function getColumnType(): string + { + $type = $this->column['type']; + + return $type === 'varchar' ? 'string' : $type; + } + + public function getDefault() + { + $default = $this->column['default']; + + return $default ? trim($default, "'") : $default; + } + + public function getComment(): ?string + { + return $this->column['comment'] ?? null; + } + + public function getAutoincrement(): bool + { + return $this->column['auto_increment']; + } +} diff --git a/src/Adapter/Laravel/SchemaManager.php b/src/Adapter/Laravel/SchemaManager.php new file mode 100644 index 0000000..1c12a4d --- /dev/null +++ b/src/Adapter/Laravel/SchemaManager.php @@ -0,0 +1,23 @@ +schemaBuilder = $connection->getSchemaBuilder(); + } + + public function introspectTable(string $name): TableContract + { + return new Table($this->schemaBuilder, $name); + } +} diff --git a/src/Adapter/Laravel/Table.php b/src/Adapter/Laravel/Table.php new file mode 100644 index 0000000..c3d3529 --- /dev/null +++ b/src/Adapter/Laravel/Table.php @@ -0,0 +1,46 @@ +builder = $builder; + $this->name = $name; + } + + public function getName(): string + { + return $this->name; + } + + /** + * @return Collection + */ + public function getColumns(): Collection + { + return collect($this->builder->getColumns($this->name))->map(function (array $column) { + return new Column($column); + }); + } + + /** + * @return Collection + */ + public function getPrimaryKey(): Collection + { + return collect($this->builder->getIndexes($this->name)) + ->filter(fn (array $column) => $column['primary'] === true) + ->map(fn (array $column) => $column['columns']) + ->collapse(); + } +} diff --git a/src/Adapter/SchemaManager.php b/src/Adapter/SchemaManager.php deleted file mode 100644 index 98623a0..0000000 --- a/src/Adapter/SchemaManager.php +++ /dev/null @@ -1,21 +0,0 @@ -schemaManager = $container->make('db')->getDoctrineSchemaManager(); - } - - public function introspectTable(string $name) - { - return new Table($this->schemaManager->introspectTable($name)); - } -} diff --git a/src/ErdFinder.php b/src/ErdFinder.php index ad40750..756ec4c 100644 --- a/src/ErdFinder.php +++ b/src/ErdFinder.php @@ -3,11 +3,11 @@ namespace Recca0120\LaravelErd; use Illuminate\Support\Collection; -use Recca0120\LaravelErd\Adapter\SchemaManager as SchemaManagerAdapter; +use Recca0120\LaravelErd\Adapter\Contracts\SchemaManager as SchemaManagerContract; class ErdFinder { - private SchemaManagerAdapter $schemaManager; + private SchemaManagerContract $schemaManager; private ModelFinder $modelFinder; @@ -16,7 +16,7 @@ class ErdFinder private string $directory; public function __construct( - SchemaManagerAdapter $schemaManager, + SchemaManagerContract $schemaManager, ModelFinder $modelFinder, RelationFinder $relationFinder ) { @@ -86,7 +86,7 @@ private function findByModels(Collection $models, array $excludes = []): Collect ->unique(fn (Relation $relation) => $this->uniqueRelation($relation)) ->groupBy(fn (Relation $relation) => $relation->table()) ->sortBy(fn (Collection $relations, string $table) => $table) - ->map(function (Collection $relations, $table) { + ->map(function (Collection $relations, string $table) { return new Table($this->schemaManager->introspectTable($table), $relations); }); } diff --git a/src/LaravelErdServiceProvider.php b/src/LaravelErdServiceProvider.php index 73509dd..032413e 100644 --- a/src/LaravelErdServiceProvider.php +++ b/src/LaravelErdServiceProvider.php @@ -3,6 +3,9 @@ namespace Recca0120\LaravelErd; use Illuminate\Support\ServiceProvider; +use Recca0120\LaravelErd\Adapter\Contracts\SchemaManager as SchemaManagerContract; +use Recca0120\LaravelErd\Adapter\DBAL\SchemaManager as DBALSchemaManager; +use Recca0120\LaravelErd\Adapter\Laravel\SchemaManager as LaravelSchemaManager; use Recca0120\LaravelErd\Console\Commands\LaravelErdCommand; use Recca0120\LaravelErd\Console\Commands\LaravelErdInitCommand; use Recca0120\LaravelErd\Templates\Factory; @@ -35,9 +38,14 @@ public function register() $this->app->singleton(Factory::class, Factory::class); $this->app->singleton(ErdFinder::class, ErdFinder::class); - $this->commands([ - LaravelErdInitCommand::class, - LaravelErdCommand::class, - ]); + $this->app->singleton(SchemaManagerContract::class, function () { + $connection = $this->app['db']->connection(); + + return method_exists($connection, 'getDoctrineSchemaManager') + ? new DBALSchemaManager($connection) + : new LaravelSchemaManager($connection); + }); + + $this->commands([LaravelErdInitCommand::class, LaravelErdCommand::class]); } } diff --git a/src/Table.php b/src/Table.php index da5a077..d03891d 100644 --- a/src/Table.php +++ b/src/Table.php @@ -2,20 +2,20 @@ namespace Recca0120\LaravelErd; -use Doctrine\DBAL\Schema\Column as DBALColumn; use Illuminate\Support\Collection; -use Recca0120\LaravelErd\Adapter\Table as TableAdapter; +use Recca0120\LaravelErd\Adapter\Contracts\Column as ColumnContract; +use Recca0120\LaravelErd\Adapter\Contracts\Table as TableContract; class Table { - private TableAdapter $table; + private TableContract $table; /** * @var Collection */ private Collection $relations; - public function __construct(TableAdapter $table, Collection $relations) + public function __construct(TableContract $table, Collection $relations) { $this->table = $table; $this->relations = $relations; @@ -27,7 +27,7 @@ public function name(): string } /** - * @return Collection + * @return Collection */ public function columns(): Collection { diff --git a/src/Templates/DDL.php b/src/Templates/DDL.php index 3ed1f4c..8d6b00f 100644 --- a/src/Templates/DDL.php +++ b/src/Templates/DDL.php @@ -4,7 +4,7 @@ use Illuminate\Support\Collection; use Illuminate\Support\Facades\File; -use Recca0120\LaravelErd\Adapter\Column; +use Recca0120\LaravelErd\Adapter\Contracts\Column as ColumnContract; use Recca0120\LaravelErd\Helpers; use Recca0120\LaravelErd\Relation; use Recca0120\LaravelErd\Table; @@ -31,7 +31,7 @@ public function save(string $output, string $path, array $options = []): int private function renderColumn(Table $table): string { return $table->columns() - ->map(function (Column $column) { + ->map(function (ColumnContract $column) { $type = $this->getColumnType($column); $precision = $column->getPrecision(); $default = $column->getDefault(); @@ -92,7 +92,7 @@ private function renderRelation(Relation $relation): string ); } - private function getColumnType(Column $column): string + private function getColumnType(ColumnContract $column): string { $type = $column->getColumnType(); diff --git a/src/Templates/Er.php b/src/Templates/Er.php index f935e57..6cf09ee 100644 --- a/src/Templates/Er.php +++ b/src/Templates/Er.php @@ -10,7 +10,7 @@ use Illuminate\Database\Eloquent\Relations\MorphOne; use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Support\Collection; -use Recca0120\LaravelErd\Adapter\Column; +use Recca0120\LaravelErd\Adapter\Contracts\Column as ColumnContract; use Recca0120\LaravelErd\Helpers; use Recca0120\LaravelErd\Relation; use Recca0120\LaravelErd\Table; @@ -85,13 +85,13 @@ private function renderTable(Table $table): string $result = sprintf("[%s] {}\n", $table->name()); $result .= $table->columns() - ->map(fn (Column $column) => $this->renderColumn($column, $primaryKeys, $indexes)) - ->implode("\n")."\n"; + ->map(fn (ColumnContract $column) => $this->renderColumn($column, $primaryKeys, $indexes)) + ->implode("\n")."\n"; return $result; } - private function renderColumn(Column $column, Collection $primaryKeys, array $indexes): string + private function renderColumn(ColumnContract $column, Collection $primaryKeys, array $indexes): string { return sprintf( '%s%s%s {label: "%s, %s"}', diff --git a/tests/ErdFinderTest.php b/tests/ErdFinderTest.php index 2d0eda7..20689fc 100644 --- a/tests/ErdFinderTest.php +++ b/tests/ErdFinderTest.php @@ -2,7 +2,6 @@ namespace Recca0120\LaravelErd\Tests; -use Doctrine\DBAL\Exception; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Collection; use Recca0120\LaravelErd\ErdFinder; @@ -24,9 +23,6 @@ protected function setUp(): void $this->template = new Er(); } - /** - * @throws Exception - */ public function test_find_er_model_in_directory(): void { $finder = $this->givenFinder(); @@ -36,9 +32,6 @@ public function test_find_er_model_in_directory(): void ); } - /** - * @throws Exception - */ public function test_find_er_model_by_file(): void { $finder = $this->givenFinder(); @@ -48,9 +41,6 @@ public function test_find_er_model_by_file(): void ); } - /** - * @throws Exception - */ public function test_find_er_model_by_model(): void { $finder = $this->givenFinder(); @@ -60,9 +50,6 @@ public function test_find_er_model_by_model(): void ); } - /** - * @throws Exception - */ public function test_find_er_model_exclude_owner(): void { $finder = $this->givenFinder();