通过数据透视表检查数据库中是否存在关系

时间:2018-09-07 10:47:40

标签: mysql laravel optimization eloquent inner-join

情况

用户可以属于多个组织,并通过名为employees的数据透视表进行链接

正在使用的模型:UserEmployeeOrganizations

相关数据库列:

users
- id

employees
- user_id
- organization_id

organizations
- id

目标

一种有效的方法来检查用户1和用户2是否在organization_id表中共享至少一个employees

用例

Api端点/api/v1/user/#返回有关用户的其他元数据。

使用policy,它检查当前用户和url中的用户ID是否相同,或者它们都是至少一个组织的雇员,organization_id在在这个阶段,重要的是它是否匹配。

示例A

用户A(1)是组织foo(1)的雇员

用户B(2)是组织栏(2)的员工

员工表因此具有以下记录:

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

在此示例中,查询应返回错误结果,因为用户A和B之间没有共享的organization_id

示例B

用户A(1)是组织foo(1)的雇员

用户A(1)是组织foobar(3)的雇员

用户B(2)是组织栏(2)的员工

用户B(2)是组织foobar(3)的雇员

员工表因此具有以下记录:

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

在此示例中,查询应该返回真实结果,因为用户A和B之间共享organization_id

政策代码

/**
 * 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 
    }
}

有效但效率不高的代码

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

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

}

return false;

带有连接的实验代码,而不是使用laravel模型完成的

        \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()

请求的解决方案

一个有效的查询,以获取(可广播)布尔结果结果,无论是否有2个用户在organization_id表上共享至少一个employees,为使用laravel模型/查询提供“加分”建造者。

页脚

感谢阅读,这里有个土豆:

4 个答案:

答案 0 :(得分:3)

假设您在Organization模型中建立了用户关系,则可以使用whereHas方法:

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

答案 1 :(得分:1)

作为原始查询,我可能会在这里使用EXISTS,但是由于您需要将任何查询移植到Laravel / PHP代码,因此我建议使用自连接:

SELECT DISTINCT
    e1.user_id, e2.user_id
FROM employees e1
INNER JOIN employees e2
    ON e1.organization_id = e2.organization_id AND e1.user_id = 2
WHERE
    e1.user_id = 1;

这只会返回user_id对值(1, 2)对。如果希望查询返回至少共享一个组织的所有成对的不同用户,则可以将此查询重写为:

SELECT DISTINCT
    e1.user_id, e2.user_id
FROM employees e1
INNER JOIN employees e2
    ON e1.organization_id = e2.organization_id AND e1.user_id <> e2.user_id;

答案 2 :(得分:1)

我能想到的最易读的示例是在用户模型中放置以下内容:

public function isColleagueWith(User $user): bool
{
    return $this->organizations->intersectByKey($user->organizations)->count() > 0;
}

用法易于阅读和理解:

$userA->isColleagueWith($userB);

如果要使用较少的数据库查询,则可以直接查询数据透视表。在这里,您将获得雇用两个用户的所有组织,并检查列表中是否包含重复的组织ID。

use Illuminate\Database\Eloquent\Relations\Pivot;

class Employees extends Pivot
{
    public function areColleagues(int $userIdA, int $userIdB): bool
    {
        $employments = $this->where('user_id', $userIdA)
            ->orWhere('user_id', $userIdB)
            ->get('organization_id');

        return $employments->count() > $employments->unique()->count();
    }
}

用法:

Employees::areColleagues($userIdA, $userIdB);

答案 3 :(得分:1)

要检查用户1和2是否共享一个或多个组织,请执行以下操作:

SELECT EXISTS (
          SELECT 1 FROM employees AS a
                   JOIN employees AS b  USING(organization_id)
                   WHERE  a.user_id = 1
                     AND  b.user_id = 2;

同时具有以下两个索引:

(user_id, organization_id)
(organization_id, user_id)