Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

MattB's avatar
Level 2

Laravel Testing reading many to many relationships

Ok so Im new to testing but I am trying to test if I can read from a db table which shares a many to many with another but only where certain conditions are met, IE Can I get all users who have role ID 1 or 2 .

My setup: -Roles and Users are the 2 tables which share a many to many connection.

  • Roles is being filled through the use of a RoleSeeder:
public function run()
    {
        DB::table('roles')->delete();

        $roles = [
            ['id' => 1, 'name' => 'superuser'],
            ['id' => 2, 'name' => 'staff'],
            ['id' => 3, 'name' => 'venueadmin'],
            ['id' => 4, 'name' => 'venuestaff'],
        ];

        Role::insert($roles);
    }
  • Users is being filled through this seeder:
public function run()
    {
        DB::table('users')->delete();
        DB::table('role_user')->delete();

        User::factory()
            ->count(100)
            ->create();

        $roles = Role::all();

        User::all()->each(function ($user) use ($roles) { 
            $user->roles()->attach(
                $roles->random(rand(1, 2))->pluck('id')->toArray()
            ); 
        });
    }
  • the UsersController is being called via API
  • The index function of UsersController looks like this:
public function index()
    {
        $users = User::whereHas('roles', function ($query) {
            $query->where('role_id', '1')
                  ->orWhere('role_id','2');
        })->with('roles')->get();
        return $users;
    }

Now, stop me if I'm wrong but here are my thought processes/assumptions:

  1. Before I can read anything from Users, I need to enter some test data. This means calling the User factory.
  2. This will only bring back users, not their relationships with roles
  3. The users factory (I think) is not able to establish the relationship I need, only the seeder will
  4. I can't call seeder from the test (Or can I?).

So, how can I enter a test user, with one or two roles and read it back testing that only users with certain roles are grabbed? Here is what I had so far in the test:

$this->withoutExceptionHandling();

        $this->postJson('/api/users', [
            'firstname' => 'Fake',
            'lastname' => 'Person',
            'email' => '[email protected]',
            'password' => 'yIXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi',
            'remember_token' => Str::random(10),
            'roles' => '1','2'
        ]);
        
        $user = $this->getJson('/api/users');

        dd($user);

That DD returned null

0 likes
2 replies
kevinbui's avatar

Before get to your questions. I think your controller action got to be slightly revised:

public function index()
{
    return User::whereHas('roles', function ($query) {
        $query->whereKey([1, 2]);
    })->with('roles')->get();
}

About your questions:

  1. Yes.
  2. Yes. If you want to create relationships, be explicit about that.
  3. Nope. You can do so in both.
  4. Yes. You can do so.

You might want to set up your test cases like this for that API:

class ApiUsersTest extends TestCase
{
    use RefreshDatabase;

    public function test_api_users()
    {
        $this->seed(RolesSeeder::class);
        $firstUser = User::factory()->create()->attach(1);
        $secondUser = User::factory()->create()->attach(2);
        $thirdUser = User::factory()->create()->attach(3);

        $this->getJson('/api/users')
            ->assertJson($firstUser->only('id', 'firstname', 'lastname', 'email'))
            ->assertJson($secondUser->only('id', 'firstname', 'lastname', 'email'))
            ->assertJsonMissing($thirdUser->only('id', 'firstname', 'lastname', 'email'));
    }
}

Please or to participate in this conversation.