diff --git a/.github/ISSUE_TEMPLATE/report-a-bug.md b/.github/ISSUE_TEMPLATE/report-a-bug.md index 8232dfcf..3064d31b 100644 --- a/.github/ISSUE_TEMPLATE/report-a-bug.md +++ b/.github/ISSUE_TEMPLATE/report-a-bug.md @@ -19,3 +19,9 @@ assignees: '' ### Steps To Reproduce: + + +### draft.yaml: +```yaml +# PLEASE PASTE YOUR YAML HERE +``` diff --git a/src/Generators/FactoryGenerator.php b/src/Generators/FactoryGenerator.php index 8a12f13f..eb0c9b22 100644 --- a/src/Generators/FactoryGenerator.php +++ b/src/Generators/FactoryGenerator.php @@ -14,6 +14,8 @@ class FactoryGenerator implements Generator /** @var \Illuminate\Contracts\Filesystem\Filesystem */ private $files; + private $imports = []; + public function __construct($files) { $this->files = $files; @@ -27,6 +29,9 @@ public function output(array $tree): array /** @var \Blueprint\Models\Model $model */ foreach ($tree['models'] as $model) { + $this->addImport($model, 'Faker\Generator as Faker'); + $this->addImport($model, $model->fullyQualifiedClassName()); + $path = $this->getPath($model); if (!$this->files->exists(dirname($path))) { @@ -53,9 +58,9 @@ protected function getPath(Model $model) protected function populateStub(string $stub, Model $model) { - $stub = str_replace('DummyModel', $model->fullyQualifiedClassName(), $stub); $stub = str_replace('DummyClass', $model->name(), $stub); $stub = str_replace('// definition...', $this->buildDefinition($model), $stub); + $stub = str_replace('// imports...', $this->buildImports($model), $stub); return $stub; } @@ -72,15 +77,41 @@ protected function buildDefinition(Model $model) continue; } - if (in_array($column->dataType(), ['id', 'uuid'])) { - $foreign = $column->isForeignKey(); + $foreign = $column->isForeignKey(); + if ($foreign) { + $table = Str::beforeLast($column->name(), '_id'); + $key = 'id'; + + if (Str::contains($foreign, '.')) { + [$table, $key] = explode('.', $foreign); + } elseif ($foreign !== 'foreign') { + $table = $foreign; + + if (Str::startsWith($column->name(), $foreign . '_')) { + $key = Str::after($column->name(), $foreign . '_'); + } elseif (Str::startsWith($column->name(), Str::snake(Str::singular($foreign)) . '_')) { + $key = Str::after($column->name(), Str::snake(Str::singular($foreign)) . '_'); + } elseif (!Str::endsWith($column->name(), '_id')) { + $key = $column->name(); + } + } + + $class = Str::studly(Str::singular($table)); - if ($foreign && $foreign !== 'foreign') { - $class = Str::studly(Str::singular($foreign)); + if ($key === 'id') { + $definition .= self::INDENT . "'{$column->name()}' => "; + $definition .= sprintf('factory(%s::class)', '\\' . $model->fullyQualifiedNamespace() . '\\' . $class); + $definition .= ',' . PHP_EOL; } else { - $name = Str::beforeLast($column->name(), '_id'); - $class = Str::studly($column->attributes()[0] ?? $name); + $definition .= self::INDENT . "'{$column->name()}' => function () {"; + $definition .= PHP_EOL; + $definition .= self::INDENT . ' ' . sprintf('return factory(%s::class)->create()->%s;', '\\' . $model->fullyQualifiedNamespace() . '\\' . $class, $key); + $definition .= PHP_EOL; + $definition .= self::INDENT . '},' . PHP_EOL; } + } elseif (in_array($column->dataType(), ['id', 'uuid'])) { + $name = Str::beforeLast($column->name(), '_id'); + $class = Str::studly($column->attributes()[0] ?? $name); $definition .= self::INDENT . "'{$column->name()}' => "; $definition .= sprintf('factory(%s::class)', '\\' . $model->fullyQualifiedNamespace() . '\\' . $class); @@ -118,6 +149,11 @@ protected function buildDefinition(Model $model) } $definition .= sprintf('%s%s => $faker->%s,%s', self::INDENT, "'{$column->name()}_id'", self::fakerDataType('id'), PHP_EOL); $definition .= sprintf('%s%s => $faker->%s,%s', self::INDENT, "'{$column->name()}_type'", self::fakerDataType('string'), PHP_EOL); + } elseif ($column->dataType() === 'rememberToken') { + $this->addImport($model, 'Illuminate\Support\Str'); + $definition .= self::INDENT . "'{$column->name()}' => "; + $definition .= 'Str::random(10)'; + $definition .= ',' . PHP_EOL; } else { $definition .= self::INDENT . "'{$column->name()}' => "; $faker = self::fakerData($column->name()) ?? self::fakerDataType($column->dataType()); @@ -129,6 +165,21 @@ protected function buildDefinition(Model $model) return trim($definition); } + private function addImport(Model $model, $class) + { + $this->imports[$model->name()][] = $class; + } + + private function buildImports(Model $model) + { + $imports = array_unique($this->imports[$model->name()]); + sort($imports); + + return implode(PHP_EOL, array_map(function ($class) { + return 'use ' . $class . ';'; + }, $imports)); + } + private function fillableColumns(array $columns): array { if (config('blueprint.fake_nullables')) { diff --git a/src/Generators/MigrationGenerator.php b/src/Generators/MigrationGenerator.php index 1673b118..0d093b8d 100644 --- a/src/Generators/MigrationGenerator.php +++ b/src/Generators/MigrationGenerator.php @@ -111,6 +111,8 @@ protected function buildDefinition(Model $model) $column_definition = self::INDENT; if ($dataType === 'bigIncrements' && $this->isLaravel7orNewer()) { $column_definition .= '$table->id('; + } elseif ($dataType === 'rememberToken') { + $column_definition .= '$table->rememberToken('; } else { $column_definition .= '$table->' . $dataType . "('{$column->name()}'"; } @@ -176,6 +178,11 @@ protected function buildDefinition(Model $model) $definition .= self::INDENT . '$table->' . $model->softDeletesDataType() . '();' . PHP_EOL; } + if ($model->morphTo()) { + $definition .= self::INDENT . sprintf('$table->unsignedBigInteger(\'%s\');', Str::lower($model->morphTo() . "_id")) . PHP_EOL; + $definition .= self::INDENT . sprintf('$table->string(\'%s\');', Str::lower($model->morphTo() . "_type")) . PHP_EOL; + } + if ($model->usesTimestamps()) { $definition .= self::INDENT . '$table->' . $model->timestampsDataType() . '();' . PHP_EOL; } @@ -192,15 +199,19 @@ protected function buildPivotTableDefinition(array $segments) $references = 'id'; $on = Str::plural($column); $foreign = Str::singular($column) . '_' . $references; + $table = $foreign; + if (Str::contains($foreign, ':')) { + [, $table] = explode(':', $foreign); + } if (!$this->isLaravel7orNewer()) { - $definition .= self::INDENT . '$table->unsignedBigInteger(\'' . $foreign . '\');' . PHP_EOL; + $definition .= self::INDENT . '$table->unsignedBigInteger(\'' . $table . '\');' . PHP_EOL; } if (config('blueprint.use_constraints')) { - $definition .= $this->buildForeignKey($foreign, $on, 'id') . ';' . PHP_EOL; + $definition .= $this->buildForeignKey($table, $on, 'id') . ';' . PHP_EOL; } elseif ($this->isLaravel7orNewer()) { - $definition .= self::INDENT . '$table->foreignId(\'' . $foreign . '\');' . PHP_EOL; + $definition .= self::INDENT . '$table->foreignId(\'' . $table . '\');' . PHP_EOL; } } @@ -270,9 +281,16 @@ protected function getPivotClassName(array $segments) protected function getPivotTableName(array $segments) { $segments = array_map(function ($name) { - return Str::snake($name); + $table = $name; + + if (Str::contains($name, ':')) { + [,$table] = explode(':', $name); + } + + return Str::snake($table); }, $segments); sort($segments); + return strtolower(implode('_', $segments)); } diff --git a/src/Generators/ModelGenerator.php b/src/Generators/ModelGenerator.php index ccf59a2a..9983b7d0 100644 --- a/src/Generators/ModelGenerator.php +++ b/src/Generators/ModelGenerator.php @@ -137,18 +137,49 @@ private function buildRelationships(Model $model) foreach ($model->relationships() as $type => $references) { foreach ($references as $reference) { + $key = null; + $class = null; + + $column_name = $reference; + $method_name = Str::beforeLast($reference, '_id'); + if (Str::contains($reference, ':')) { - [$class, $name] = explode(':', $reference); - } else { - $name = $reference; - $class = null; + [$foreign_reference, $column_name] = explode(':', $reference); + $method_name = Str::beforeLast($column_name, '_id'); + + if (Str::contains($foreign_reference, '.')) { + [$class, $key] = explode('.', $foreign_reference); + + if ($key === 'id') { + $key = null; + } else { + $method_name = Str::lower($class); + } + } else { + $class = $foreign_reference; + } } - $name = Str::beforeLast($name, '_id'); - $class = Str::studly($class ?? $name); - $relationship = sprintf("\$this->%s(%s::class)", $type, '\\' . $model->fullyQualifiedNamespace() . '\\' . $class); + $relationshipClass = Str::studly($class ?? $method_name); + + if ($type === 'morphTo') { + $relationship = sprintf('$this->%s()', $type); + } elseif ($type === 'morphMany' || $type === 'morphOne') { + $relation = Str::lower(Str::singular($column_name)) . 'able'; + $relationship = sprintf('$this->%s(%s::class, \'%s\')', $type, '\\' . $model->fullyQualifiedNamespace() . '\\' . $relationshipClass, $relation); + } elseif (!is_null($key)) { + $relationship = sprintf('$this->%s(%s::class, \'%s\', \'%s\')', $type, '\\' . $model->fullyQualifiedNamespace() . '\\' . $relationshipClass, $column_name, $key); + } elseif (!is_null($class) && $type === 'belongsToMany') { + $relationship = sprintf('$this->%s(%s::class, \'%s\')', $type, '\\' . $model->fullyQualifiedNamespace() . '\\' . $relationshipClass, $method_name); + } else { + $relationship = sprintf('$this->%s(%s::class)', $type, '\\' . $model->fullyQualifiedNamespace() . '\\' . $relationshipClass); + } - $method_name = $type === 'hasMany' || $type === 'belongsToMany' ? Str::plural($name) : $name; + if ($type === 'morphTo') { + $method_name = Str::lower($relationshipClass); + } elseif (in_array($type, ['hasMany', 'belongsToMany', 'morphMany'])) { + $method_name = Str::plural($column_name); + } $method = str_replace('DummyName', Str::camel($method_name), $template); $method = str_replace('null', $relationship, $method); @@ -175,6 +206,7 @@ private function fillableColumns(array $columns) 'deleted_at', 'created_at', 'updated_at', + 'remember_token', ]); } diff --git a/src/Generators/RouteGenerator.php b/src/Generators/RouteGenerator.php index 89ddc83a..8c50e967 100644 --- a/src/Generators/RouteGenerator.php +++ b/src/Generators/RouteGenerator.php @@ -22,21 +22,22 @@ public function output(array $tree): array return []; } - $routes = ''; - $path = 'routes/web.php'; + $routes = ['api' => '', 'web' => '']; + /** @var \Blueprint\Models\Controller $controller */ foreach ($tree['controllers'] as $controller) { - $routes .= PHP_EOL . PHP_EOL . $this->buildRoutes($controller); - - if ($controller->isApiResource()) { - $path = 'routes/api.php'; - } + $type = $controller->isApiResource() ? 'api' : 'web'; + $routes[$type] .= PHP_EOL . PHP_EOL . $this->buildRoutes($controller); } - $routes .= PHP_EOL; - $this->files->append($path, $routes); + $paths = []; + foreach (array_filter($routes) as $type => $definitions) { + $path = 'routes/' . $type . '.php'; + $this->files->append($path, $definitions . PHP_EOL); + $paths[] = $path; + } - return ['updated' => [$path]]; + return ['updated' => $paths]; } protected function buildRoutes(Controller $controller) diff --git a/src/Lexers/ControllerLexer.php b/src/Lexers/ControllerLexer.php index 99071a10..d18e1c60 100644 --- a/src/Lexers/ControllerLexer.php +++ b/src/Lexers/ControllerLexer.php @@ -29,14 +29,16 @@ public function analyze(array $tokens): array foreach ($tokens['controllers'] as $name => $definition) { $controller = new Controller($name); - if ($this->isResource($definition)) { - $original = $definition; - $definition = $this->generateResourceTokens($controller, $this->methodsForResource($definition['resource'])); - // unset shorthand - unset($original['resource']); - // this gives the ability to both use a shorthand and override some methods - $definition = array_merge($definition, $original); - $controller->setApiResource(true); + if (isset($definition['resource'])) { + $resource_definition = $this->generateResourceTokens($controller, $this->methodsForResource($definition['resource'])); + + if ($definition['resource'] === 'api') { + $controller->setApiResource(true); + } + + unset($definition['resource']); + + $definition = array_merge($resource_definition, $definition); } foreach ($definition as $method => $body) { @@ -49,11 +51,6 @@ public function analyze(array $tokens): array return $registry; } - private function isResource(array $definition) - { - return isset($definition['resource']) && is_string($definition['resource']); - } - private function generateResourceTokens(Controller $controller, array $methods) { return collect($this->resourceTokens()) diff --git a/src/Lexers/ModelLexer.php b/src/Lexers/ModelLexer.php index 333cda7f..6393e702 100644 --- a/src/Lexers/ModelLexer.php +++ b/src/Lexers/ModelLexer.php @@ -5,6 +5,7 @@ use Blueprint\Contracts\Lexer; use Blueprint\Models\Column; use Blueprint\Models\Model; +use Illuminate\Support\Str; class ModelLexer implements Lexer { @@ -12,7 +13,10 @@ class ModelLexer implements Lexer 'belongsto' => 'belongsTo', 'hasone' => 'hasOne', 'hasmany' => 'hasMany', - 'belongstomany' => 'belongsToMany' + 'belongstomany' => 'belongsToMany', + 'morphone' => 'morphOne', + 'morphmany' => 'morphMany', + 'morphto' => 'morphTo', ]; private static $dataTypes = [ @@ -149,6 +153,10 @@ private function buildModel(string $name, array $columns) foreach ($columns['relationships'] as $type => $relationships) { foreach (explode(',', $relationships) as $reference) { $model->addRelationship(self::$relationships[strtolower($type)], trim($reference)); + + if ($type === 'morphTo') { + $model->setMorphTo(trim($reference)); + } } } } @@ -165,12 +173,27 @@ private function buildModel(string $name, array $columns) $column = $this->buildColumn($name, $definition); $model->addColumn($column); - if ($column->name() !== 'id' && in_array($column->dataType(), ['id', 'uuid'])) { - if ($column->attributes()) { - $model->addRelationship('belongsTo', $column->attributes()[0] . ':' . $column->name()); - } else { - $model->addRelationship('belongsTo', $column->name()); + $foreign = collect($column->modifiers())->filter(function ($modifier) { + return (is_array($modifier) && key($modifier) === 'foreign') || $modifier === 'foreign'; + })->flatten()->first(); + + if ($column->name() !== 'id' && (in_array($column->dataType(), ['id', 'uuid']) || $foreign)) { + $reference = $column->name(); + + if ($foreign && $foreign !== 'foreign') { + $table = $foreign; + $key = 'id'; + + if (Str::contains($foreign, '.')) { + [$table, $key] = explode('.', $foreign); + } + + $reference = Str::singular($table) . ($key === 'id' ? '' : '.' . $key) . ':' . $column->name(); + } elseif ($column->attributes()) { + $reference = $column->attributes()[0] . ':' . $column->name(); } + + $model->addRelationship('belongsTo', $reference); } } @@ -179,10 +202,10 @@ private function buildModel(string $name, array $columns) private function buildColumn(string $name, string $definition) { - $data_type = 'string'; + $data_type = null; $modifiers = []; - $tokens = preg_split('#".*?"(*SKIP)(*F)|\s+#', $definition); + $tokens = preg_split('#".*?"(*SKIP)(*FAIL)|\s+#', $definition); foreach ($tokens as $token) { $parts = explode(':', $token); $value = $parts[0]; @@ -210,6 +233,14 @@ private function buildColumn(string $name, string $definition) } } + if (is_null($data_type)) { + $is_foreign_key = collect($modifiers)->contains(function ($modifier) { + return (is_array($modifier) && key($modifier) === 'foreign') || $modifier === 'foreign'; + }); + + $data_type = $is_foreign_key ? 'id' : 'string'; + } + return new Column($name, $data_type, $modifiers, $attributes ?? []); } } diff --git a/src/Models/Model.php b/src/Models/Model.php index 4ff3005d..d40f3210 100644 --- a/src/Models/Model.php +++ b/src/Models/Model.php @@ -11,6 +11,7 @@ class Model private $primaryKey = 'id'; private $timestamps = 'timestamps'; private $softDeletes = false; + private $morphTo; private $columns = []; private $relationships = []; private $pivotTables = []; @@ -162,4 +163,14 @@ public function pivotTables(): array { return $this->pivotTables; } + + public function setMorphTo(string $reference) + { + $this->morphTo = $reference; + } + + public function morphTo(): ?string + { + return $this->morphTo; + } } diff --git a/stubs/factory.stub b/stubs/factory.stub index cb4f3803..607596eb 100644 --- a/stubs/factory.stub +++ b/stubs/factory.stub @@ -2,8 +2,7 @@ /** @var \Illuminate\Database\Eloquent\Factory $factory */ -use DummyModel; -use Faker\Generator as Faker; +// imports... $factory->define(DummyClass::class, function (Faker $faker) { return [ diff --git a/tests/Feature/Generator/FactoryGeneratorTest.php b/tests/Feature/Generator/FactoryGeneratorTest.php index 680062af..b231bf09 100644 --- a/tests/Feature/Generator/FactoryGeneratorTest.php +++ b/tests/Feature/Generator/FactoryGeneratorTest.php @@ -48,7 +48,7 @@ public function output_writes_nothing_for_empty_tree() * @test * @dataProvider modelTreeDataProvider */ - public function output_writes_migration_for_model_tree($definition, $path, $migration) + public function output_writes_factory_for_model_tree($definition, $path, $factory) { $this->files->expects('stub') ->with('factory.stub') @@ -59,7 +59,7 @@ public function output_writes_migration_for_model_tree($definition, $path, $migr ->andReturnTrue(); $this->files->expects('put') - ->with($path, $this->fixture($migration)); + ->with($path, $this->fixture($factory)); $tokens = $this->blueprint->parse($this->fixture($definition)); $tree = $this->blueprint->analyze($tokens); @@ -149,7 +149,9 @@ public function modelTreeDataProvider() ['definitions/unconventional.bp', 'database/factories/TeamFactory.php', 'factories/unconventional.php'], ['definitions/model-modifiers.bp', 'database/factories/ModifierFactory.php', 'factories/model-modifiers.php'], ['definitions/model-key-constraints.bp', 'database/factories/OrderFactory.php', 'factories/model-key-constraints.php'], -// ['definitions/unconventional-foreign-key.bp', 'database/factories/StateFactory.php', 'factories/unconventional-foreign-key.php'], + ['definitions/unconventional-foreign-key.bp', 'database/factories/StateFactory.php', 'factories/unconventional-foreign-key.php'], + ['definitions/foreign-key-shorthand.bp', 'database/factories/CommentFactory.php', 'factories/foreign-key-shorthand.php'], + ['definitions/resource-statements.bp', 'database/factories/UserFactory.php', 'factories/resource-statements.php'], ]; } } diff --git a/tests/Feature/Generator/MigrationGeneratorTest.php b/tests/Feature/Generator/MigrationGeneratorTest.php index 51168a77..a70dbb0f 100644 --- a/tests/Feature/Generator/MigrationGeneratorTest.php +++ b/tests/Feature/Generator/MigrationGeneratorTest.php @@ -70,6 +70,29 @@ public function output_writes_migration_for_model_tree($definition, $path, $migr $this->assertEquals(['created' => [$timestamp_path]], $this->subject->output($tree)); } + /** + * @test + */ + public function output_writes_migration_for_foreign_shorthand() + { + $this->files->expects('stub') + ->with('migration.stub') + ->andReturn(file_get_contents('stubs/migration.stub')); + + $now = Carbon::now(); + Carbon::setTestNow($now); + + $timestamp_path = str_replace('timestamp', $now->format('Y_m_d_His'), 'database/migrations/timestamp_create_comments_table.php'); + + $this->files->expects('put') + ->with($timestamp_path, $this->fixture('migrations/foreign-key-shorthand.php')); + + $tokens = $this->blueprint->parse($this->fixture('definitions/foreign-key-shorthand.bp')); + $tree = $this->blueprint->analyze($tokens); + + $this->assertEquals(['created' => [$timestamp_path]], $this->subject->output($tree)); + } + /** * @test */ @@ -269,7 +292,6 @@ public function output_also_creates_constraints_for_pivot_table_migration() $this->assertEquals(['created' => [$model_migration, $pivot_migration]], $this->subject->output($tree)); } - /** * @test */ @@ -305,8 +327,8 @@ public function output_also_creates_constraints_for_pivot_table_migration_larave } /** - * @test - */ + * @test + */ public function output_does_not_duplicate_pivot_table_migration() { $this->files->expects('stub') @@ -334,8 +356,8 @@ public function output_does_not_duplicate_pivot_table_migration() } /** - * @test - */ + * @test + */ public function output_does_not_duplicate_pivot_table_migration_laravel6() { $app = \Mockery::mock(); @@ -368,6 +390,64 @@ public function output_does_not_duplicate_pivot_table_migration_laravel6() $this->assertEquals(['created' => [$company_migration, $people_migration, $pivot_migration]], $this->subject->output($tree)); } + /** + * @test + */ + public function output_also_creates_pivot_table_migration_with_custom_name() + { + $app = \Mockery::mock(); + $app->shouldReceive('version') + ->withNoArgs() + ->andReturn('6.0.0'); + App::swap($app); + + $this->files->expects('stub') + ->with('migration.stub') + ->andReturn(file_get_contents('stubs/migration.stub')); + + $now = Carbon::now(); + Carbon::setTestNow($now); + + $model_migration = str_replace('timestamp', $now->format('Y_m_d_His'), 'database/migrations/timestamp_create_users_table.php'); + $pivot_migration = str_replace('timestamp', $now->format('Y_m_d_His'), 'database/migrations/timestamp_create_test_user_table.php'); + + $this->files->expects('put') + ->with($model_migration, $this->fixture('migrations/custom-pivot-column-user-laravel6.php')); + $this->files->expects('put') + ->with($pivot_migration, $this->fixture('migrations/custom-pivot-column-test-laravel6.php')); + + $tokens = $this->blueprint->parse($this->fixture('definitions/custom-pivot-column.bp')); + $tree = $this->blueprint->analyze($tokens); + + $this->assertEquals(['created' => [$model_migration, $pivot_migration]], $this->subject->output($tree)); + } + + /** + * @test + */ + public function output_also_creates_pivot_table_migration_with_custom_name_laravel6() + { + $this->files->expects('stub') + ->with('migration.stub') + ->andReturn(file_get_contents('stubs/migration.stub')); + + $now = Carbon::now(); + Carbon::setTestNow($now); + + $model_migration = str_replace('timestamp', $now->format('Y_m_d_His'), 'database/migrations/timestamp_create_users_table.php'); + $pivot_migration = str_replace('timestamp', $now->format('Y_m_d_His'), 'database/migrations/timestamp_create_test_user_table.php'); + + $this->files->expects('put') + ->with($model_migration, $this->fixture('migrations/custom-pivot-column-user.php')); + $this->files->expects('put') + ->with($pivot_migration, $this->fixture('migrations/custom-pivot-column-test.php')); + + $tokens = $this->blueprint->parse($this->fixture('definitions/custom-pivot-column.bp')); + $tree = $this->blueprint->analyze($tokens); + + $this->assertEquals(['created' => [$model_migration, $pivot_migration]], $this->subject->output($tree)); + } + /** * @test */ @@ -422,6 +502,70 @@ public function output_creates_foreign_keys_with_nullable_chained_correctly_lara $this->assertEquals(['created' => [$model_migration]], $this->subject->output($tree)); } + /** + * @test + */ + public function output_works_with_polymorphic_relationships() + { + $this->files->expects('stub') + ->with('migration.stub') + ->andReturn(file_get_contents('stubs/migration.stub')); + + $now = Carbon::now(); + Carbon::setTestNow($now); + + $post_migration = str_replace('timestamp', $now->copy()->subSeconds(2)->format('Y_m_d_His'), 'database/migrations/timestamp_create_posts_table.php'); + $user_migration = str_replace('timestamp', $now->copy()->subSecond()->format('Y_m_d_His'), 'database/migrations/timestamp_create_users_table.php'); + $image_migration = str_replace('timestamp', $now->format('Y_m_d_His'), 'database/migrations/timestamp_create_images_table.php'); + + $this->files->expects('put') + ->with($post_migration, $this->fixture('migrations/polymorphic_relationships_posts_table.php')); + $this->files->expects('put') + ->with($user_migration, $this->fixture('migrations/polymorphic_relationships_users_table.php')); + $this->files->expects('put') + ->with($image_migration, $this->fixture('migrations/polymorphic_relationships_images_table.php')); + + $tokens = $this->blueprint->parse($this->fixture('definitions/polymorphic-relationships.bp')); + $tree = $this->blueprint->analyze($tokens); + + $this->assertEquals(['created' => [$post_migration, $user_migration, $image_migration]], $this->subject->output($tree)); + } + + /** + * @test + */ + public function output_works_with_polymorphic_relationships_laravel6() + { + $app = \Mockery::mock(); + $app->shouldReceive('version') + ->withNoArgs() + ->andReturn('6.0.0'); + App::swap($app); + + $this->files->expects('stub') + ->with('migration.stub') + ->andReturn(file_get_contents('stubs/migration.stub')); + + $now = Carbon::now(); + Carbon::setTestNow($now); + + $post_migration = str_replace('timestamp', $now->copy()->subSeconds(2)->format('Y_m_d_His'), 'database/migrations/timestamp_create_posts_table.php'); + $user_migration = str_replace('timestamp', $now->copy()->subSecond()->format('Y_m_d_His'), 'database/migrations/timestamp_create_users_table.php'); + $image_migration = str_replace('timestamp', $now->format('Y_m_d_His'), 'database/migrations/timestamp_create_images_table.php'); + + $this->files->expects('put') + ->with($post_migration, $this->fixture('migrations/polymorphic_relationships_posts_table_laravel6.php')); + $this->files->expects('put') + ->with($user_migration, $this->fixture('migrations/polymorphic_relationships_users_table_laravel6.php')); + $this->files->expects('put') + ->with($image_migration, $this->fixture('migrations/polymorphic_relationships_images_table_laravel6.php')); + + $tokens = $this->blueprint->parse($this->fixture('definitions/polymorphic-relationships.bp')); + $tree = $this->blueprint->analyze($tokens); + + $this->assertEquals(['created' => [$post_migration, $user_migration, $image_migration]], $this->subject->output($tree)); + } + public function modelTreeDataProvider() { return [ @@ -437,6 +581,7 @@ public function modelTreeDataProvider() ['definitions/disable-auto-columns.bp', 'database/migrations/timestamp_create_states_table.php', 'migrations/disable-auto-columns.php'], ['definitions/uuid-shorthand.bp', 'database/migrations/timestamp_create_people_table.php', 'migrations/uuid-shorthand.php'], ['definitions/unconventional-foreign-key.bp', 'database/migrations/timestamp_create_states_table.php', 'migrations/unconventional-foreign-key.php'], + ['definitions/resource-statements.bp', 'database/migrations/timestamp_create_users_table.php', 'migrations/resource-statements.php'], ]; } } diff --git a/tests/Feature/Generator/ModelGeneratorTest.php b/tests/Feature/Generator/ModelGeneratorTest.php index 98193cb2..fd62401f 100644 --- a/tests/Feature/Generator/ModelGeneratorTest.php +++ b/tests/Feature/Generator/ModelGeneratorTest.php @@ -55,7 +55,7 @@ public function output_generates_models($definition, $path, $model) ->with('model/fillable.stub') ->andReturn(file_get_contents('stubs/model/fillable.stub')); - if ($definition === 'definitions/nested-components.bp') { + if (in_array($definition, ['definitions/nested-components.bp','definitions/resource-statements.bp'])) { $this->files->expects('stub') ->with('model/hidden.stub') ->andReturn(file_get_contents('stubs/model/hidden.stub')); @@ -93,8 +93,8 @@ public function output_generates_models($definition, $path, $model) } /** - * @test - */ + * @test + */ public function output_works_for_pascal_case_definition() { $this->files->expects('stub') @@ -164,6 +164,51 @@ public function output_generates_relationships() $this->assertEquals(['created' => ['app/Subscription.php']], $this->subject->output($tree)); } + /** + * @test + */ + public function output_generates_polymorphic_relationships() + { + $this->files->expects('stub') + ->with('model/class.stub') + ->andReturn(file_get_contents('stubs/model/class.stub')); + $this->files->expects('stub') + ->times(3) + ->with('model/fillable.stub') + ->andReturn(file_get_contents('stubs/model/fillable.stub')); + $this->files->expects('stub') + ->times(3) + ->with('model/casts.stub') + ->andReturn(file_get_contents('stubs/model/casts.stub')); + $this->files->expects('stub') + ->times(3) + ->with('model/method.stub') + ->andReturn(file_get_contents('stubs/model/method.stub')); + + $this->files->expects('exists') + ->with('app') + ->andReturnTrue(); + $this->files->expects('put') + ->with('app/Post.php', $this->fixture('models/post-polymorphic-relationship.php')); + + $this->files->expects('exists') + ->with('app') + ->andReturnTrue(); + $this->files->expects('put') + ->with('app/User.php', $this->fixture('models/user-polymorphic-relationship.php')); + + $this->files->expects('exists') + ->with('app') + ->andReturnTrue(); + $this->files->expects('put') + ->with('app/Image.php', $this->fixture('models/image-polymorphic-relationship.php')); + + $tokens = $this->blueprint->parse($this->fixture('definitions/polymorphic-relationships.bp')); + $tree = $this->blueprint->analyze($tokens); + + $this->assertEquals(['created' => ['app/Post.php', 'app/User.php', 'app/Image.php']], $this->subject->output($tree)); + } + /** * @test */ @@ -326,8 +371,8 @@ public function output_generates_models_with_guarded_property_when_config_option } /** - * @test - */ + * @test + */ public function output_generates_models_with_custom_namespace_correctly() { $definition = 'definitions/custom-models-namespace.bp'; @@ -361,6 +406,39 @@ public function output_generates_models_with_custom_namespace_correctly() $this->assertEquals(['created' => [$path]], $this->subject->output($tree)); } + /** + * @test + */ + public function output_generates_models_with_custom_pivot_columns() + { + $this->files->expects('stub') + ->with('model/class.stub') + ->andReturn(file_get_contents('stubs/model/class.stub')); + $this->files->expects('stub') + ->with('model/fillable.stub') + ->andReturn(file_get_contents('stubs/model/fillable.stub')); + $this->files->expects('stub') + ->with('model/casts.stub') + ->andReturn(file_get_contents('stubs/model/casts.stub')); + $this->files->expects('stub') + ->with('model/method.stub') + ->andReturn(file_get_contents('stubs/model/method.stub')); + $this->files->expects('stub') + ->with('model/hidden.stub') + ->andReturn(file_get_contents('stubs/model/hidden.stub')); + + $this->files->expects('exists') + ->with('app') + ->andReturnTrue(); + $this->files->expects('put') + ->with('app/User.php', $this->fixture('models/custom-pivot-column.php')); + + $tokens = $this->blueprint->parse($this->fixture('definitions/custom-pivot-column.bp')); + $tree = $this->blueprint->analyze($tokens); + + $this->assertEquals(['created' => ['app/User.php']], $this->subject->output($tree)); + } + public function modelTreeDataProvider() { return [ @@ -370,6 +448,7 @@ public function modelTreeDataProvider() ['definitions/relationships.bp', 'app/Comment.php', 'models/relationships.php'], ['definitions/unconventional.bp', 'app/Team.php', 'models/unconventional.php'], ['definitions/nested-components.bp', 'app/Admin/User.php', 'models/nested-components.php'], + ['definitions/resource-statements.bp', 'app/User.php', 'models/resource-statements.php'], ]; } @@ -380,6 +459,7 @@ public function docBlockModelsDataProvider() ['definitions/soft-deletes.bp', 'app/Comment.php', 'models/soft-deletes-phpdoc.php'], ['definitions/relationships.bp', 'app/Comment.php', 'models/relationships-phpdoc.php'], ['definitions/disable-auto-columns.bp', 'app/State.php', 'models/disable-auto-columns-phpdoc.php'], + ['definitions/foreign-key-shorthand.bp', 'app/Comment.php', 'models/foreign-key-shorthand-phpdoc.php'], ]; } } diff --git a/tests/Feature/Generator/RouteGeneratorTest.php b/tests/Feature/Generator/RouteGeneratorTest.php index c18da46c..9cc61ef7 100644 --- a/tests/Feature/Generator/RouteGeneratorTest.php +++ b/tests/Feature/Generator/RouteGeneratorTest.php @@ -34,7 +34,7 @@ protected function setUp(): void /** * @test */ - public function output_writes_nothing_for_empty_tree() + public function output_generates_nothing_for_empty_tree() { $this->files->shouldNotHaveReceived('append'); @@ -45,7 +45,7 @@ public function output_writes_nothing_for_empty_tree() * @test * @dataProvider controllerTreeDataProvider */ - public function output_writes_migration_for_route_tree($definition, $routes) + public function output_generates_web_routes($definition, $routes) { $path = 'routes/web.php'; $this->files->expects('append') @@ -60,19 +60,31 @@ public function output_writes_migration_for_route_tree($definition, $routes) /** * @test */ - public function output_writes_migration_for_route_tree_api_routes() + public function output_generates_api_routes() { - $definition = "definitions/api-routes-example.bp"; - $routes = "routes/api-routes.php"; - $path = 'routes/api.php'; + $this->files->expects('append') + ->with('routes/api.php', $this->fixture('routes/api-routes.php')); + + $tokens = $this->blueprint->parse($this->fixture('definitions/api-routes-example.bp')); + $tree = $this->blueprint->analyze($tokens); + $this->assertEquals(['updated' => ['routes/api.php']], $this->subject->output($tree)); + } + + /** + * @test + */ + public function output_generates_routes_for_mixed_resources() + { $this->files->expects('append') - ->with($path, $this->fixture($routes)); + ->with('routes/api.php', $this->fixture('routes/multiple-resource-controllers-api.php')); + $this->files->expects('append') + ->with('routes/web.php', $this->fixture('routes/multiple-resource-controllers-web.php')); - $tokens = $this->blueprint->parse($this->fixture($definition)); + $tokens = $this->blueprint->parse($this->fixture('definitions/multiple-resource-controllers.bp')); $tree = $this->blueprint->analyze($tokens); - $this->assertEquals(['updated' => [$path]], $this->subject->output($tree)); + $this->assertEquals(['updated' => ['routes/api.php', 'routes/web.php']], $this->subject->output($tree)); } public function controllerTreeDataProvider() diff --git a/tests/Feature/Lexers/ControllerLexerTest.php b/tests/Feature/Lexers/ControllerLexerTest.php index 16cbd0d1..2a1a1c40 100644 --- a/tests/Feature/Lexers/ControllerLexerTest.php +++ b/tests/Feature/Lexers/ControllerLexerTest.php @@ -380,4 +380,53 @@ public function it_returns_a_resource_controller_with_overrides() $this->assertCount(1, $methods['custom']); $this->assertEquals('custom-statements', $methods['custom'][0]); } + + /** + * @test + */ + public function it_returns_a_resource_controllers_with_api_flag_set() + { + $tokens = [ + 'controllers' => [ + 'Page' => [ + 'resource' => 'web', + ], + 'File' => [ + 'resource' => 'api', + ], + 'Category' => [ + 'resource' => 'web', + ], + 'Gallery' => [ + 'resource' => 'api', + ], + ] + ]; + + $this->statementLexer->shouldReceive('analyze'); + + $actual = $this->subject->analyze($tokens); + + $this->assertCount(4, $actual['controllers']); + + $controller = $actual['controllers']['Page']; + $this->assertEquals('PageController', $controller->className()); + $this->assertCount(7, $controller->methods()); + $this->assertFalse($controller->isApiResource()); + + $controller = $actual['controllers']['File']; + $this->assertEquals('FileController', $controller->className()); + $this->assertCount(5, $controller->methods()); + $this->assertTrue($controller->isApiResource()); + + $controller = $actual['controllers']['Category']; + $this->assertEquals('CategoryController', $controller->className()); + $this->assertCount(7, $controller->methods()); + $this->assertFalse($controller->isApiResource()); + + $controller = $actual['controllers']['Gallery']; + $this->assertEquals('GalleryController', $controller->className()); + $this->assertCount(5, $controller->methods()); + $this->assertTrue($controller->isApiResource()); + } } diff --git a/tests/Feature/Lexers/ModelLexerTest.php b/tests/Feature/Lexers/ModelLexerTest.php index cfd386f1..02dd018c 100644 --- a/tests/Feature/Lexers/ModelLexerTest.php +++ b/tests/Feature/Lexers/ModelLexerTest.php @@ -381,6 +381,113 @@ public function it_enables_soft_deletes() $this->assertEquals([], $columns['id']->modifiers()); } + /** + * @test + */ + public function it_converts_foreign_shorthand_to_id() + { + $tokens = [ + 'models' => [ + 'Model' => [ + 'post_id' => 'foreign', + 'author_id' => 'foreign:user', + ], + ], + ]; + + $actual = $this->subject->analyze($tokens); + + $this->assertIsArray($actual['models']); + $this->assertCount(1, $actual['models']); + + $model = $actual['models']['Model']; + $this->assertEquals('Model', $model->name()); + $this->assertTrue($model->usesTimestamps()); + $this->assertFalse($model->usesSoftDeletes()); + + $columns = $model->columns(); + $this->assertCount(3, $columns); + $this->assertEquals('id', $columns['id']->name()); + $this->assertEquals('id', $columns['id']->dataType()); + $this->assertEquals([], $columns['id']->modifiers()); + $this->assertEquals('post_id', $columns['post_id']->name()); + $this->assertEquals('id', $columns['post_id']->dataType()); + $this->assertEquals(['foreign'], $columns['post_id']->modifiers()); + $this->assertEquals('author_id', $columns['author_id']->name()); + $this->assertEquals('id', $columns['author_id']->dataType()); + $this->assertEquals([['foreign' => 'user']], $columns['author_id']->modifiers()); + } + + /** + * @test + */ + public function it_sets_belongs_to_with_foreign_attributes() + { + $tokens = [ + 'models' => [ + 'Model' => [ + 'post_id' => 'id foreign', + 'author_id' => 'id foreign:users', + 'uid' => 'id:user foreign:users.id', + 'cntry_id' => 'foreign:countries', + 'ccid' => 'foreign:countries.code', + ], + ], + ]; + + $actual = $this->subject->analyze($tokens); + + $this->assertIsArray($actual['models']); + $this->assertCount(1, $actual['models']); + + $model = $actual['models']['Model']; + $this->assertEquals('Model', $model->name()); + $this->assertTrue($model->usesTimestamps()); + $this->assertFalse($model->usesSoftDeletes()); + + $columns = $model->columns(); + $this->assertCount(6, $columns); + $this->assertEquals('id', $columns['id']->name()); + $this->assertEquals('id', $columns['id']->dataType()); + $this->assertEquals([], $columns['id']->attributes()); + $this->assertEquals([], $columns['id']->modifiers()); + + $this->assertEquals('post_id', $columns['post_id']->name()); + $this->assertEquals('id', $columns['post_id']->dataType()); + $this->assertEquals([], $columns['post_id']->attributes()); + $this->assertEquals(['foreign'], $columns['post_id']->modifiers()); + + $this->assertEquals('author_id', $columns['author_id']->name()); + $this->assertEquals('id', $columns['author_id']->dataType()); + $this->assertEquals([], $columns['author_id']->attributes()); + $this->assertEquals([['foreign' => 'users']], $columns['author_id']->modifiers()); + + $this->assertEquals('uid', $columns['uid']->name()); + $this->assertEquals('id', $columns['uid']->dataType()); + $this->assertEquals(['user'], $columns['uid']->attributes()); + $this->assertEquals([['foreign' => 'users.id']], $columns['uid']->modifiers()); + + $this->assertEquals('cntry_id', $columns['cntry_id']->name()); + $this->assertEquals('id', $columns['cntry_id']->dataType()); + $this->assertEquals([], $columns['cntry_id']->attributes()); + $this->assertEquals([['foreign' => 'countries']], $columns['cntry_id']->modifiers()); + + $this->assertEquals('ccid', $columns['ccid']->name()); + $this->assertEquals('id', $columns['ccid']->dataType()); + $this->assertEquals([], $columns['ccid']->attributes()); + $this->assertEquals([['foreign' => 'countries.code']], $columns['ccid']->modifiers()); + + $relationships = $model->relationships(); + $this->assertCount(1, $relationships); + $this->assertEquals([ + 'post_id', + 'user:author_id', + 'user:uid', + 'country:cntry_id', + 'country.code:ccid', + ], $relationships['belongsTo']); + } + /** * @test */ @@ -499,6 +606,42 @@ public function it_stores_relationships() $this->assertEquals(['Duration', 'Transaction:tid'], $relationships['hasOne']); } + /** + * @test + */ + public function it_enables_morphable_and_set_its_reference() + { + $tokens = [ + 'models' => [ + 'Model' => [ + 'relationships' => [ + 'morphTo' => 'Morphable', + ] + ], + ], + ]; + + $actual = $this->subject->analyze($tokens); + + $this->assertIsArray($actual['models']); + $this->assertCount(1, $actual['models']); + + $model = $actual['models']['Model']; + $this->assertEquals('Model', $model->name()); + $this->assertEquals('Morphable', $model->morphTo()); + $this->assertTrue($model->usesTimestamps()); + + $columns = $model->columns(); + $this->assertCount(1, $columns); + $this->assertEquals('id', $columns['id']->name()); + $this->assertEquals('id', $columns['id']->dataType()); + $this->assertEquals([], $columns['id']->modifiers()); + + $relationships = $model->relationships(); + $this->assertCount(1, $relationships); + $this->assertEquals(['Morphable'], $relationships['morphTo']); + } + public function dataTypeAttributesDataProvider() { return [ diff --git a/tests/fixtures/definitions/custom-pivot-column.bp b/tests/fixtures/definitions/custom-pivot-column.bp new file mode 100644 index 00000000..b470bd6b --- /dev/null +++ b/tests/fixtures/definitions/custom-pivot-column.bp @@ -0,0 +1,6 @@ +models: + User: + name: string + remember_token: remembertoken + relationships: + belongsToMany: Account:test diff --git a/tests/fixtures/definitions/foreign-key-shorthand.bp b/tests/fixtures/definitions/foreign-key-shorthand.bp new file mode 100644 index 00000000..e588c4e4 --- /dev/null +++ b/tests/fixtures/definitions/foreign-key-shorthand.bp @@ -0,0 +1,5 @@ +models: + Comment: + post_id: foreign + author_id: foreign:user + ccid: foreign:countries.code diff --git a/tests/fixtures/definitions/multiple-resource-controllers.bp b/tests/fixtures/definitions/multiple-resource-controllers.bp new file mode 100644 index 00000000..c7ffe1b8 --- /dev/null +++ b/tests/fixtures/definitions/multiple-resource-controllers.bp @@ -0,0 +1,35 @@ +models: + Page: + page_name: string + page_slug: string + page_content: longtext + order: integer nullable + + File: + file_name: string + file_path: text + relationships: + belongsToMany: Category + + Category: + category_name: string + relationships: + belongsToMany: File + + Gallery: + gallery_name: string + images: longtext + order: integer nullable + +controllers: + Page: + resource + + File: + resource: api + + Category: + resource + + Gallery: + resource: api diff --git a/tests/fixtures/definitions/polymorphic-relationships.bp b/tests/fixtures/definitions/polymorphic-relationships.bp new file mode 100644 index 00000000..ac4b9143 --- /dev/null +++ b/tests/fixtures/definitions/polymorphic-relationships.bp @@ -0,0 +1,15 @@ +models: + Post: + name: string:400 + relationships: + morphMany: Image + + User: + name: string:400 + relationships: + morphMany: Image + + Image: + url: string:400 + relationships: + morphTo: Imageable diff --git a/tests/fixtures/definitions/unconventional-foreign-key.bp b/tests/fixtures/definitions/unconventional-foreign-key.bp index 00a8af9d..7cfd1f2a 100644 --- a/tests/fixtures/definitions/unconventional-foreign-key.bp +++ b/tests/fixtures/definitions/unconventional-foreign-key.bp @@ -1,4 +1,7 @@ models: State: name: string - country_code: string foreign:countries.code + countries_id: id foreign:countries + country_code: string foreign:countries + ccid: string foreign:countries + c_code: string foreign:countries.code diff --git a/tests/fixtures/factories/foreign-key-shorthand.php b/tests/fixtures/factories/foreign-key-shorthand.php new file mode 100644 index 00000000..978b000d --- /dev/null +++ b/tests/fixtures/factories/foreign-key-shorthand.php @@ -0,0 +1,16 @@ +define(Comment::class, function (Faker $faker) { + return [ + 'post_id' => factory(\App\Post::class), + 'author_id' => factory(\App\User::class), + 'ccid' => function () { + return factory(\App\Country::class)->create()->code; + }, + ]; +}); diff --git a/tests/fixtures/factories/post-configured.php b/tests/fixtures/factories/post-configured.php index 2215210f..a49a99ed 100644 --- a/tests/fixtures/factories/post-configured.php +++ b/tests/fixtures/factories/post-configured.php @@ -2,8 +2,8 @@ /** @var \Illuminate\Database\Eloquent\Factory $factory */ -use Some\App\Models\Post; use Faker\Generator as Faker; +use Some\App\Models\Post; $factory->define(Post::class, function (Faker $faker) { return [ diff --git a/tests/fixtures/factories/resource-statements.php b/tests/fixtures/factories/resource-statements.php new file mode 100644 index 00000000..567f5d05 --- /dev/null +++ b/tests/fixtures/factories/resource-statements.php @@ -0,0 +1,16 @@ +define(User::class, function (Faker $faker) { + return [ + 'name' => $faker->name, + 'email' => $faker->safeEmail, + 'password' => $faker->password, + 'remember_token' => Str::random(10), + ]; +}); diff --git a/tests/fixtures/factories/unconventional-foreign-key.php b/tests/fixtures/factories/unconventional-foreign-key.php index 890f750c..248406e3 100644 --- a/tests/fixtures/factories/unconventional-foreign-key.php +++ b/tests/fixtures/factories/unconventional-foreign-key.php @@ -8,8 +8,15 @@ $factory->define(State::class, function (Faker $faker) { return [ 'name' => $faker->name, + 'countries_id' => factory(\App\Country::class), 'country_code' => function () { return factory(\App\Country::class)->create()->code; }, + 'ccid' => function () { + return factory(\App\Country::class)->create()->ccid; + }, + 'c_code' => function () { + return factory(\App\Country::class)->create()->code; + }, ]; }); diff --git a/tests/fixtures/migrations/custom-pivot-column-test-laravel6.php b/tests/fixtures/migrations/custom-pivot-column-test-laravel6.php new file mode 100644 index 00000000..c4b1e489 --- /dev/null +++ b/tests/fixtures/migrations/custom-pivot-column-test-laravel6.php @@ -0,0 +1,31 @@ +unsignedBigInteger('test_id'); + $table->unsignedBigInteger('user_id'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('test_user'); + } +} diff --git a/tests/fixtures/migrations/custom-pivot-column-test.php b/tests/fixtures/migrations/custom-pivot-column-test.php new file mode 100644 index 00000000..f508aac3 --- /dev/null +++ b/tests/fixtures/migrations/custom-pivot-column-test.php @@ -0,0 +1,31 @@ +foreignId('test_id'); + $table->foreignId('user_id'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('test_user'); + } +} diff --git a/tests/fixtures/migrations/custom-pivot-column-user-laravel6.php b/tests/fixtures/migrations/custom-pivot-column-user-laravel6.php new file mode 100644 index 00000000..badfc789 --- /dev/null +++ b/tests/fixtures/migrations/custom-pivot-column-user-laravel6.php @@ -0,0 +1,33 @@ +bigIncrements('id'); + $table->string('name'); + $table->rememberToken(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('users'); + } +} diff --git a/tests/fixtures/migrations/custom-pivot-column-user.php b/tests/fixtures/migrations/custom-pivot-column-user.php new file mode 100644 index 00000000..40ae3ec7 --- /dev/null +++ b/tests/fixtures/migrations/custom-pivot-column-user.php @@ -0,0 +1,33 @@ +id(); + $table->string('name'); + $table->rememberToken(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('users'); + } +} diff --git a/tests/fixtures/migrations/foreign-key-shorthand.php b/tests/fixtures/migrations/foreign-key-shorthand.php new file mode 100644 index 00000000..f4d0586a --- /dev/null +++ b/tests/fixtures/migrations/foreign-key-shorthand.php @@ -0,0 +1,34 @@ +id(); + $table->foreignId('post_id')->constrained()->cascadeOnDelete(); + $table->foreignId('author_id')->constrained('users')->cascadeOnDelete(); + $table->foreignId('ccid')->constrained('countries', 'code')->cascadeOnDelete(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('comments'); + } +} diff --git a/tests/fixtures/migrations/polymorphic_relationships_images_table.php b/tests/fixtures/migrations/polymorphic_relationships_images_table.php new file mode 100644 index 00000000..22138a77 --- /dev/null +++ b/tests/fixtures/migrations/polymorphic_relationships_images_table.php @@ -0,0 +1,34 @@ +id(); + $table->string('url', 400); + $table->unsignedBigInteger('imageable_id'); + $table->string('imageable_type'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('images'); + } +} diff --git a/tests/fixtures/migrations/polymorphic_relationships_images_table_laravel6.php b/tests/fixtures/migrations/polymorphic_relationships_images_table_laravel6.php new file mode 100644 index 00000000..ec0cf69e --- /dev/null +++ b/tests/fixtures/migrations/polymorphic_relationships_images_table_laravel6.php @@ -0,0 +1,34 @@ +bigIncrements('id'); + $table->string('url', 400); + $table->unsignedBigInteger('imageable_id'); + $table->string('imageable_type'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('images'); + } +} diff --git a/tests/fixtures/migrations/polymorphic_relationships_posts_table.php b/tests/fixtures/migrations/polymorphic_relationships_posts_table.php new file mode 100644 index 00000000..daa77b34 --- /dev/null +++ b/tests/fixtures/migrations/polymorphic_relationships_posts_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('name', 400); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('posts'); + } +} diff --git a/tests/fixtures/migrations/polymorphic_relationships_posts_table_laravel6.php b/tests/fixtures/migrations/polymorphic_relationships_posts_table_laravel6.php new file mode 100644 index 00000000..9f45d2bb --- /dev/null +++ b/tests/fixtures/migrations/polymorphic_relationships_posts_table_laravel6.php @@ -0,0 +1,32 @@ +bigIncrements('id'); + $table->string('name', 400); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('posts'); + } +} diff --git a/tests/fixtures/migrations/polymorphic_relationships_users_table.php b/tests/fixtures/migrations/polymorphic_relationships_users_table.php new file mode 100644 index 00000000..b9c84d54 --- /dev/null +++ b/tests/fixtures/migrations/polymorphic_relationships_users_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('name', 400); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('users'); + } +} diff --git a/tests/fixtures/migrations/polymorphic_relationships_users_table_laravel6.php b/tests/fixtures/migrations/polymorphic_relationships_users_table_laravel6.php new file mode 100644 index 00000000..bcd71a5c --- /dev/null +++ b/tests/fixtures/migrations/polymorphic_relationships_users_table_laravel6.php @@ -0,0 +1,32 @@ +bigIncrements('id'); + $table->string('name', 400); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('users'); + } +} diff --git a/tests/fixtures/migrations/resource-statements.php b/tests/fixtures/migrations/resource-statements.php new file mode 100644 index 00000000..220be996 --- /dev/null +++ b/tests/fixtures/migrations/resource-statements.php @@ -0,0 +1,35 @@ +id(); + $table->string('name'); + $table->string('email'); + $table->string('password'); + $table->rememberToken(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('users'); + } +} diff --git a/tests/fixtures/migrations/unconventional-foreign-key.php b/tests/fixtures/migrations/unconventional-foreign-key.php index 1276c028..2aa754b3 100644 --- a/tests/fixtures/migrations/unconventional-foreign-key.php +++ b/tests/fixtures/migrations/unconventional-foreign-key.php @@ -16,8 +16,13 @@ public function up() Schema::create('states', function (Blueprint $table) { $table->id(); $table->string('name'); + $table->foreignId('countries_id')->constrained('countries')->cascadeOnDelete(); $table->string('country_code'); $table->foreign('country_code')->references('code')->on('countries')->onDelete('cascade'); + $table->string('ccid'); + $table->foreign('ccid')->references('ccid')->on('countries')->onDelete('cascade'); + $table->string('c_code'); + $table->foreign('c_code')->references('code')->on('countries')->onDelete('cascade'); $table->timestamps(); }); } diff --git a/tests/fixtures/models/custom-pivot-column.php b/tests/fixtures/models/custom-pivot-column.php new file mode 100644 index 00000000..81f6486a --- /dev/null +++ b/tests/fixtures/models/custom-pivot-column.php @@ -0,0 +1,41 @@ + 'integer', + ]; + + + public function tests() + { + return $this->belongsToMany(\App\Account::class, 'test'); + } +} diff --git a/tests/fixtures/models/foreign-key-shorthand-phpdoc.php b/tests/fixtures/models/foreign-key-shorthand-phpdoc.php new file mode 100644 index 00000000..be2874c7 --- /dev/null +++ b/tests/fixtures/models/foreign-key-shorthand-phpdoc.php @@ -0,0 +1,64 @@ + 'integer', + 'post_id' => 'integer', + 'author_id' => 'integer', + 'ccid' => 'integer', + ]; + + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function post() + { + return $this->belongsTo(\App\Post::class); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function author() + { + return $this->belongsTo(\App\User::class); + } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function country() + { + return $this->belongsTo(\App\Country::class, 'ccid', 'code'); + } +} diff --git a/tests/fixtures/models/image-polymorphic-relationship.php b/tests/fixtures/models/image-polymorphic-relationship.php new file mode 100644 index 00000000..8aa3b034 --- /dev/null +++ b/tests/fixtures/models/image-polymorphic-relationship.php @@ -0,0 +1,32 @@ + 'integer', + ]; + + + public function imageable() + { + return $this->morphTo(); + } +} diff --git a/tests/fixtures/models/post-polymorphic-relationship.php b/tests/fixtures/models/post-polymorphic-relationship.php new file mode 100644 index 00000000..a985d00d --- /dev/null +++ b/tests/fixtures/models/post-polymorphic-relationship.php @@ -0,0 +1,32 @@ + 'integer', + ]; + + + public function images() + { + return $this->morphMany(\App\Image::class, 'imageable'); + } +} diff --git a/tests/fixtures/models/resource-statements.php b/tests/fixtures/models/resource-statements.php new file mode 100644 index 00000000..ff7aa920 --- /dev/null +++ b/tests/fixtures/models/resource-statements.php @@ -0,0 +1,38 @@ + 'integer', + ]; +} diff --git a/tests/fixtures/models/user-polymorphic-relationship.php b/tests/fixtures/models/user-polymorphic-relationship.php new file mode 100644 index 00000000..dd5f75e4 --- /dev/null +++ b/tests/fixtures/models/user-polymorphic-relationship.php @@ -0,0 +1,32 @@ + 'integer', + ]; + + + public function images() + { + return $this->morphMany(\App\Image::class, 'imageable'); + } +} diff --git a/tests/fixtures/routes/multiple-resource-controllers-api.php b/tests/fixtures/routes/multiple-resource-controllers-api.php new file mode 100644 index 00000000..b94d068e --- /dev/null +++ b/tests/fixtures/routes/multiple-resource-controllers-api.php @@ -0,0 +1,5 @@ + + +Route::apiResource('file', 'FileController'); + +Route::apiResource('gallery', 'GalleryController'); diff --git a/tests/fixtures/routes/multiple-resource-controllers-web.php b/tests/fixtures/routes/multiple-resource-controllers-web.php new file mode 100644 index 00000000..bc851a39 --- /dev/null +++ b/tests/fixtures/routes/multiple-resource-controllers-web.php @@ -0,0 +1,5 @@ + + +Route::resource('page', 'PageController'); + +Route::resource('category', 'CategoryController');