Search code examples
mysqllaraveloptimizationeloquentinner-join

Check if relation exists in database via pivot table


situation

User can belong to multiple organizations, linked via a pivot table called employees

Models at play: User, Employee & Organizations

Relevant database columns:

users
- id

employees
- user_id
- organization_id

organizations
- id

goal

An efficient way to check if user 1 and user 2 share at least one organization_id in the employees table

usecase

Api endpoint /api/v1/user/# returns additional metadata regarding the user.

Using a policy, it checks if the current user and the user id from the url are the same, or that they are both employee in at least one organization, the organization_id is not known at this stage, all that matters is that it matches.

example A

user A (1) is employee of organization foo (1)

user B (2) is employee of organization bar (2)

employee table thus has the following records:

+-----------------+---------+
| organization_id | user_id |
+-----------------+---------+
|               1 |       1 |
|               2 |       2 |
+-----------------+---------+

in this example the query should return a false result, since there is no shared organization_id between user A and B

example B

user A (1) is employee of organization foo (1)

user A (1) is employee of organization foobar (3)

user B (2) is employee of organization bar (2)

user B (2) is employee of organization foobar (3)

employee table thus has the following records:

+-----------------+---------+
| organization_id | user_id |
+-----------------+---------+
|               1 |       1 |
|               2 |       2 |
|               3 |       1 |
|               3 |       2 |
+-----------------+---------+

in this example the query should return a true result, since there is a shared organization_id between user A and B

policy code

/**
 * Determine whether the user can view the model.
 *
 * @param  \App\User  $user
 * @param  \App\User  $model
 * @return mixed
 */
public function view(User $user, User $model)
{
    if ($user->is($model)) {
        return true;
    } else {
        // check if users share at least one organization 
    }
}

code that works but does not look efficient

foreach ($user->organizations()->with('users')->get() as $organization) {

    if ($organization->users->where('id', $model->id)->first()) {
        return true;
    }

}

return false;

experimental code with joins instead of something done with laravel models

        \Illuminate\Support\Facades\DB::table('employees as auth_employee')
        ->join('employees as other_employee', 'other_employee.organization_id', '=', 'auth_employee.organization_id')
//        ->join('organizations', 'organizations.id', '=', 'organizations.id')
        ->where('auth_employee.id', 1)
        ->where('other_employee.id', 2)
        ->get()

requested solution

An efficient query to get a (castable to) boolean result result wether or not 2 users share at least one organization_id on the employees table, 'bonus points' for using the laravel models / query builder.

footer

Thanks for reading, here is a potato: 🥔


Solution

  • Assuming you have a users relationship set up in your Organization model, you could use the whereHas method:

    $user->organizations()->whereHas('users', function ($query) use($model) {
        $query->where('users.id', $model->id);
    })->exists();