Проверьте, существует ли связь в базе данных через сводную таблицу - PullRequest
0 голосов
/ 07 сентября 2018

положение

Пользователь может принадлежать нескольким организациям, связанным через сводную таблицу с именем employees

Модели в игре: User, Employee & Organizations

Соответствующие столбцы базы данных:

users
- id

employees
- user_id
- organization_id

organizations
- id

цель

Эффективный способ проверить, если пользователь 1 и пользователь 2 совместно используют хотя бы один organization_id в таблице employees

UseCase

Конечная точка API /api/v1/user/# возвращает дополнительные метаданные о пользователе.

Используя policy, он проверяет, являются ли текущий пользователь и идентификатор пользователя из URL одинаковыми или они оба являются сотрудниками хотя бы в одной организации, organization_id на данном этапе неизвестно, все важно то, что оно соответствует.

пример A

пользователь A (1) является сотрудником организации foo (1)

пользователь B (2) является сотрудником организационной панели (2)

Таким образом, таблица

employee имеет следующие записи:

+-----------------+---------+
| 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 |
+-----------------+---------+

в этом примере запрос должен возвращать истинный результат, поскольку существует общий organization_id между пользователем A и B

код политики

/**
 * 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()

запрошенное решение

Эффективный запрос для получения (преобразуемого) логического результата, независимо от того, используют ли два пользователя по крайней мере один organization_id в таблице employees, «бонусные баллы» за использование моделей / построителя запросов laravel.

сноска

Спасибо за чтение, вот картошка: 110

Ответы [ 4 ]

0 голосов
/ 25 сентября 2018

Чтобы проверить, используют ли пользователи 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)
0 голосов
/ 07 сентября 2018

Предполагая, что в вашей модели Organization настроены отношения пользователей, вы можете использовать метод whereHas :

$user->organizations()->whereHas('users', function ($query) use($model) {
    $query->where('users.id', $model->id);
})->exists();
0 голосов
/ 07 сентября 2018

Самый читаемый пример, который я могу придумать, это поместить что-то подобное в пользовательскую модель:

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

Использование легко читать и понимать:

$userA->isColleagueWith($userB);

Если вы хотите использовать меньше запросов к БД, вы можете вместо этого запросить сводную таблицу напрямую. Здесь вы получаете все организации, в которых работают два пользователя, и проверяете, не содержит ли список дублирующиеся идентификаторы организации.

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);
0 голосов
/ 07 сентября 2018

В качестве необработанного запроса я, вероятно, использовал бы здесь 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...