diff --git a/src/Traits/FixturesTrait.php b/src/Traits/FixturesTrait.php index 5ce121b9..a3446930 100644 --- a/src/Traits/FixturesTrait.php +++ b/src/Traits/FixturesTrait.php @@ -9,6 +9,7 @@ trait FixturesTrait { protected static $tables; + protected static $sequences; protected $postgisTables = [ 'tiger.addrfeat', 'tiger.edges', @@ -61,9 +62,8 @@ protected function loadTestDump(): void } $databaseTables = $this->getTables(); - $scheme = config('database.default'); - $this->clearDatabase($scheme, $databaseTables, array_merge($this->postgisTables, $this->truncateExceptTables)); + $this->clearDatabase($databaseTables, array_merge($this->postgisTables, $this->truncateExceptTables)); app('db.connection')->unprepared($dump); } @@ -117,8 +117,10 @@ public function exportJson($fixture, $data): void $this->exportContent(json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), $fixture); } - public function clearDatabase(string $scheme, array $tables, array $except): void + public function clearDatabase(array $tables, array $except): void { + $scheme = config('database.default'); + if ($scheme === 'pgsql') { $query = $this->getClearPsqlDatabaseQuery($tables, $except); } elseif ($scheme === 'mysql') { @@ -156,15 +158,19 @@ public function getClearMySQLDatabaseQuery(array $tables, array $except = ['migr return "{$query} SET FOREIGN_KEY_CHECKS = 1;\n"; } - public function prepareSequences(array $tables, array $except = []): void + public function prepareSequences(array $except = []): void { $except = array_merge($this->postgisTables, $this->prepareSequencesExceptTables, $except); - $query = array_concat($tables, function ($table) use ($except) { - if (in_array($table, $except)) { + $query = array_concat($this->getSequences(), function ($item) use ($except) { + if (in_array($item->table_name, $except)) { return ''; } else { - return "SELECT setval('{$table}_id_seq', (select coalesce(max(id), 1) from {$table}), (case when (select max(id) from {$table}) is NULL then false else true end));\n"; + $sequenceName = str_replace(["nextval('", "'::regclass)"], '', $item->column_default); + + return "SELECT setval('{$sequenceName}', (select coalesce(max({$item->column_name}), 1) from " . + "{$item->table_name}), (case when (select max({$item->column_name}) from {$item->table_name}) " . + "is NULL then false else true end));\n"; } }); @@ -190,6 +196,20 @@ protected function getTables(): array return self::$tables; } + protected function getSequences() + { + if (empty(self::$sequences)) { + self::$sequences = app('db.connection') + ->table('information_schema.columns') + ->select('table_name', 'column_name', 'column_default') + ->where('column_default', 'LIKE', 'nextval%') + ->get() + ->toArray(); + } + + return self::$sequences; + } + protected function exportContent($content, string $fixture): void { if (env('FAIL_EXPORT_JSON', true)) { diff --git a/src/Traits/SearchTrait.php b/src/Traits/SearchTrait.php index d68773d2..0812eb4a 100644 --- a/src/Traits/SearchTrait.php +++ b/src/Traits/SearchTrait.php @@ -176,7 +176,7 @@ public function wrapPaginatedData(Collection $data): LengthAwarePaginator $perPage = $this->calculatePerPage($total); - $paginator = new LengthAwarePaginator($data, count($data), $perPage, 1, [ + $paginator = new LengthAwarePaginator($data, $total, $perPage, 1, [ 'path' => Paginator::resolveCurrentPath(), 'pageName' => 'page' ]); @@ -370,11 +370,9 @@ protected function calculatePerPage(int $total): int return $total; } - if (!empty($this->filter['per_page'])) { - return $this->filter['per_page']; - } + $defaultPerPage = config('defaults.items_per_page', 1); - return config('defaults.items_per_page', 1); + return Arr::get($this->filter, 'per_page', $defaultPerPage); } protected function postQueryHook(): void diff --git a/tests/EntityControlTraitTest.php b/tests/EntityControlTraitTest.php index d7a9579f..50e36280 100644 --- a/tests/EntityControlTraitTest.php +++ b/tests/EntityControlTraitTest.php @@ -83,7 +83,7 @@ public function testWith() $this->assertEquals(['relation'], $attachedRelations); } - public function withCount() + public function testWithCount() { $this->testRepositoryClass->withCount('relation'); @@ -100,7 +100,7 @@ public function testAll() ->withTrashed() ->onlyTrashed() ->with('relation') - ->withCount('relation') + ->withCount(['relation', 'relation.child_relation']) ->force() ->all(); diff --git a/tests/FixturesTraitTest.php b/tests/FixturesTraitTest.php index f436943d..ad0c0f7c 100644 --- a/tests/FixturesTraitTest.php +++ b/tests/FixturesTraitTest.php @@ -2,8 +2,10 @@ namespace RonasIT\Support\Tests; -use Doctrine\DBAL\Schema\PostgreSQLSchemaManager; +use Doctrine\DBAL\Schema\MySQLSchemaManager; use Illuminate\Database\Connection; +use Illuminate\Database\Query\Grammars\Grammar; +use Illuminate\Database\Query\Processors\Processor; use Illuminate\Http\Response; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Storage; @@ -151,20 +153,42 @@ public function testLoadTestDumpForPgsql() $this->loadTestDump(); } - public function testPrepareSequences() + public function testGetTables() { - $mock = $this->mockClass(PostgreSQLSchemaManager::class, [ - $this->functionCall('listTableNames', [], $this->getJsonFixture('prepare_sequences/tables.json')), + $mock = $this->mockClass(MySQLSchemaManager::class, [ + $this->functionCall('listTableNames', [], $this->getJsonFixture('get_tables/tables.json')), ], true); $connection = $this->mockClass(Connection::class, [ $this->functionCall('getDoctrineSchemaManager', [], $mock), + ], true); + + $this->app->instance('db.connection', $connection); + + Config::set('database.default', 'mysql'); + + $this->getTables(); + + $this->assertEqualsFixture('get_tables/tables.json', self::$tables); + } + + public function testPrepareSequences() + { + $sequences = collect($this->getJsonFixture('prepare_sequences/information_schema.json')) + ->map(fn($item) => (object) $item); + + $connection = $this->mockClass(Connection::class, [ + $this->functionCall('getQueryGrammar', [], new Grammar), + $this->functionCall('getPostProcessor', [], new Processor), + $this->functionCall('select', [], $sequences), $this->functionCall('unprepared', [$this->getFixture('prepare_sequences/sequences.sql')]), ], true); $this->app->instance('db.connection', $connection); - $this->prepareSequences($this->getTables()); + Config::set('database.default', 'pgsql'); + + $this->prepareSequences(['roles']); } public function testGetFixtureWithoutGlobalExportMode() diff --git a/tests/SearchTraitTest.php b/tests/SearchTraitTest.php index cfbcec9d..77b80bbc 100644 --- a/tests/SearchTraitTest.php +++ b/tests/SearchTraitTest.php @@ -21,7 +21,6 @@ class SearchTraitTest extends HelpersTestCase protected ReflectionProperty $forceModeProperty; protected ReflectionProperty $attachedRelationsProperty; protected ReflectionProperty $attachedRelationsCountProperty; - protected ReflectionProperty $queryProperty; protected ReflectionProperty $shouldSettablePropertiesBeResetProperty; protected ReflectionMethod $setAdditionalReservedFiltersMethod; @@ -58,9 +57,6 @@ public function setUp(): void ); $this->shouldSettablePropertiesBeResetProperty->setAccessible(true); - $this->queryProperty = new ReflectionProperty(TestRepository::class, 'query'); - $this->queryProperty->setAccessible(true); - $reflectionClass = new ReflectionClass(TestRepository::class); $this->setAdditionalReservedFiltersMethod = $reflectionClass->getMethod('setAdditionalReservedFilters'); $this->setAdditionalReservedFiltersMethod->setAccessible(true); @@ -79,7 +75,7 @@ public function testSearchQuery() 'with_count' => ['relation'] ]); - $sql = $this->queryProperty->getValue($this->testRepositoryClass)->toSql(); + $sql = $this->testRepositoryClass->getSearchQuery()->toSql(); $onlyTrashed = $this->onlyTrashedProperty->getValue($this->testRepositoryClass); $withTrashed = $this->withTrashedProperty->getValue($this->testRepositoryClass); @@ -98,11 +94,25 @@ public function testSearchQuery() public function testGetSearchResultWithAll() { - $this->mockSelect('select * from `test_models` where `test_models`.`deleted_at` is null order by `id` asc'); + $this->mockSelect('select * from `test_models` where `test_models`.`deleted_at` is null order by `id` asc', [ + 1, 2, 3 + ]); $this->testRepositoryClass->searchQuery(['all' => true])->getSearchResults(); } + public function testGetSearchResultWithAllAndParams() + { + $this->mockSelect('select * from `test_models` where `test_models`.`deleted_at` is null order by `id` asc', []); + + $this->testRepositoryClass + ->searchQuery([ + 'all' => true, + 'per_page' => 20, + ]) + ->getSearchResults(); + } + public function testGetSearchResult() { $this->mockGetSearchResult($this->selectResult); @@ -218,6 +228,7 @@ public function testSearchQueryWithRelations() ]) ->filterByQuery(['query_field', 'relation.another_query_field']) ->filterBy('relation.name', 'relation_name') + ->filterBy('relation.another_name') ->getSearchResults(); } diff --git a/tests/TestCase.php b/tests/TestCase.php index ce7913d6..35cf9198 100755 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -42,7 +42,7 @@ public function setUp(): void } if (config('database.default') === 'pgsql') { - $this->prepareSequences($this->getTables()); + $this->prepareSequences(); } Carbon::setTestNow(Carbon::parse($this->testNow)); diff --git a/tests/fixtures/FixturesTraitTest/clear_database/tables.json b/tests/fixtures/FixturesTraitTest/clear_database/tables.json index 087b20bc..48556aa5 100644 --- a/tests/fixtures/FixturesTraitTest/clear_database/tables.json +++ b/tests/fixtures/FixturesTraitTest/clear_database/tables.json @@ -1,5 +1,5 @@ [ - "users", - "migrations", - "groups" + "users", + "migrations", + "groups" ] diff --git a/tests/fixtures/FixturesTraitTest/get_tables/tables.json b/tests/fixtures/FixturesTraitTest/get_tables/tables.json new file mode 100644 index 00000000..48556aa5 --- /dev/null +++ b/tests/fixtures/FixturesTraitTest/get_tables/tables.json @@ -0,0 +1,5 @@ +[ + "users", + "migrations", + "groups" +] diff --git a/tests/fixtures/FixturesTraitTest/prepare_sequences/information_schema.json b/tests/fixtures/FixturesTraitTest/prepare_sequences/information_schema.json new file mode 100644 index 00000000..26c78230 --- /dev/null +++ b/tests/fixtures/FixturesTraitTest/prepare_sequences/information_schema.json @@ -0,0 +1,37 @@ +[ + { + "table_name": "articles", + "column_name": "id", + "column_default": "nextval('articles_id_seq'::regclass)" + }, + { + "table_name": "workers", + "column_name": "id", + "column_default": "nextval('workers_id_seq'::regclass)" + }, + { + "table_name": "groups", + "column_name": "id", + "column_default": "nextval('groups_id_seq'::regclass)" + }, + { + "table_name": "homes", + "column_name": "id", + "column_default": "nextval('homes_id_seq'::regclass)" + }, + { + "table_name": "users", + "column_name": "id", + "column_default": "nextval('users_id_seq'::regclass)" + }, + { + "table_name": "roles", + "column_name": "id", + "column_default": "nextval('roles_id_seq'::regclass)" + }, + { + "table_name": "telescope_entries", + "column_name": "sequence", + "column_default": "nextval('telescope_entries_sequence_seq'::regclass)" + } +] \ No newline at end of file diff --git a/tests/fixtures/FixturesTraitTest/prepare_sequences/sequences.sql b/tests/fixtures/FixturesTraitTest/prepare_sequences/sequences.sql index 0b9721b0..b81b3c8f 100644 --- a/tests/fixtures/FixturesTraitTest/prepare_sequences/sequences.sql +++ b/tests/fixtures/FixturesTraitTest/prepare_sequences/sequences.sql @@ -1,3 +1,6 @@ -SELECT setval('users_id_seq', (select coalesce(max(id), 1) from users), (case when (select max(id) from users) is NULL then false else true end)); +SELECT setval('articles_id_seq', (select coalesce(max(id), 1) from articles), (case when (select max(id) from articles) is NULL then false else true end)); +SELECT setval('workers_id_seq', (select coalesce(max(id), 1) from workers), (case when (select max(id) from workers) is NULL then false else true end)); SELECT setval('groups_id_seq', (select coalesce(max(id), 1) from groups), (case when (select max(id) from groups) is NULL then false else true end)); -SELECT setval('projects_id_seq', (select coalesce(max(id), 1) from projects), (case when (select max(id) from projects) is NULL then false else true end)); +SELECT setval('homes_id_seq', (select coalesce(max(id), 1) from homes), (case when (select max(id) from homes) is NULL then false else true end)); +SELECT setval('users_id_seq', (select coalesce(max(id), 1) from users), (case when (select max(id) from users) is NULL then false else true end)); +SELECT setval('telescope_entries_sequence_seq', (select coalesce(max(sequence), 1) from telescope_entries), (case when (select max(sequence) from telescope_entries) is NULL then false else true end)); diff --git a/tests/fixtures/FixturesTraitTest/prepare_sequences/tables.json b/tests/fixtures/FixturesTraitTest/prepare_sequences/tables.json deleted file mode 100644 index f85ced8b..00000000 --- a/tests/fixtures/FixturesTraitTest/prepare_sequences/tables.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - "users", - "migrations", - "groups", - "tiger.state", - "projects" -] diff --git a/tests/support/Mock/ChildRelationModel.php b/tests/support/Mock/ChildRelationModel.php new file mode 100644 index 00000000..f46b0360 --- /dev/null +++ b/tests/support/Mock/ChildRelationModel.php @@ -0,0 +1,9 @@ +hasMany(ChildRelationModel::class); + } } \ No newline at end of file diff --git a/tests/support/Traits/SqlMockTrait.php b/tests/support/Traits/SqlMockTrait.php index 1ce4c8c1..15f18464 100644 --- a/tests/support/Traits/SqlMockTrait.php +++ b/tests/support/Traits/SqlMockTrait.php @@ -20,7 +20,9 @@ protected function mockAll(array $selectResult): void ); $this->mockSelect( - 'select * from `relation_models` where `relation_models`.`test_model_id` in (1)' + 'select `relation_models`.*, (select count(*) from `child_relation_models` ' + . 'where `relation_models`.`id` = `child_relation_models`.`relation_model_id`) as `child_relation_count` ' + . 'from `relation_models` where `relation_models`.`test_model_id` in (1)', ); }