diff --git a/tests/database/migrations/create_team_user_table.php b/tests/database/migrations/create_team_user_table.php new file mode 100644 index 0000000000..40347a989b --- /dev/null +++ b/tests/database/migrations/create_team_user_table.php @@ -0,0 +1,24 @@ +id(); + $table->foreignId('team_id')->constrained(); + $table->foreignId('user_id')->constrained(); + $table->string('role')->nullable(); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('team_user'); + } +}; diff --git a/tests/src/Models/User.php b/tests/src/Models/User.php index 82e98a060b..184f85e375 100644 --- a/tests/src/Models/User.php +++ b/tests/src/Models/User.php @@ -9,6 +9,7 @@ use Filament\Tests\Database\Factories\UserFactory; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; @@ -36,6 +37,11 @@ class User extends Authenticatable implements FilamentUser, HasTenants, MustVeri return $this->hasMany(Post::class, 'author_id'); } + public function teams(): BelongsToMany + { + return $this->belongsToMany(Team::class); + } + protected static function newFactory() { return UserFactory::new(); diff --git a/tests/src/Support/Services/RelationshipJoinerTest.php b/tests/src/Support/Services/RelationshipJoinerTest.php new file mode 100644 index 0000000000..88d9321b93 --- /dev/null +++ b/tests/src/Support/Services/RelationshipJoinerTest.php @@ -0,0 +1,88 @@ +create(); + + expect($user->teams()->toBase()) + ->distinct->toBeFalse() + ->getColumns()->toBe([]) + ->orders->toBeNull(); + + $query = app(RelationshipJoiner::class)->prepareQueryForNoConstraints($user->teams()); + + expect($query->toBase()) + ->distinct->toBeTrue() + ->getColumns()->toBe(['teams.*']) + ->orders->toBeNull(); + + $query = app(RelationshipJoiner::class)->prepareQueryForNoConstraints( + $user + ->teams() + ->orderBy('id') + ->orderBy((new Team)->qualifyColumn('name')) + ->orderBy('team_user.role') + ); + + expect($query->toBase()) + ->distinct->toBeTrue() + ->getColumns()->toBe([ + (new Team)->qualifyColumn('*'), // Default select... + 'id', // Select without a qualified table also included just to be sure... + // Select for `team.name` not included as that is already included in the `team.*`... + 'team_user.role', // Select for a qualitified other table included... + ]) + ->orders->toBe([ + [ + 'column' => 'id', + 'direction' => 'asc', + ], + [ + 'column' => 'teams.name', + 'direction' => 'asc', + ], + [ + 'column' => 'team_user.role', + 'direction' => 'asc', + ], + ]); + + $query = app(RelationshipJoiner::class)->prepareQueryForNoConstraints( + $user->teams()->orderByRaw("CASE WHEN role = 'admin' THEN 1 ELSE 2 END") + ); + + expect($query->toBase()) + ->distinct->toBeTrue() + ->getColumns()->toBe([ + (new Team)->qualifyColumn('*'), + "CASE WHEN role = 'admin' THEN 1 ELSE 2 END", // Select added from `orderByRaw`... + ]) + ->orders->toBe([ + [ + 'type' => 'Raw', + 'sql' => "CASE WHEN role = 'admin' THEN 1 ELSE 2 END", + ], + ]); + + $query = app(RelationshipJoiner::class)->prepareQueryForNoConstraints( + $user->teams()->orderBy(new Expression("CASE WHEN role = 'some_other_role' THEN 1 ELSE 2 END")) + ); + + expect($query->toBase()) + ->distinct->toBeTrue() + ->getColumns()->toBe([ + (new Team)->qualifyColumn('*'), + "CASE WHEN role = 'user' THEN 1 ELSE 2 END", // Select added from `orderByRaw`... + ]) + ->orders->toHaveCount(1) + ->and($query->toBase()->orders[0]) + ->column->getValue($user->teams()->getGrammar())->toBe("CASE WHEN role = 'some_other_role' THEN 1 ELSE 2 END") + ->direction->toBe('asc'); +});