Подтвердить что ты не робот

Проверьте, существует ли отношение toToMany - Laravel

Две мои таблицы (клиенты и продукты) имеют отношение ManyToMany, используя Laravel blongToMany и сводную таблицу. Теперь я хочу проверить, есть ли у определенного клиента определенный продукт.

Я мог бы создать модель для проверки в сводной таблице, но поскольку Laravel не требует этой модели для метода belongsToMany, мне было интересно, есть ли другой способ проверить, существует ли определенная связь без модели для сводной таблицы.

4b9b3361

Ответ 1

Я думаю, что официальный способ сделать это:

$client = Client::find(1);
$exists = $client->products->contains($product_id);

Это несколько расточительно, потому что он выполнит запрос SELECT, получит все результаты в Collection, а затем, наконец, выполнит foreach над Collection, чтобы найти модель с идентификатором, который вы проходите. Однако он не требует моделирования сводной таблицы.

Если вам не нравится расточительность этого, вы можете сделать это самостоятельно в SQL/Query Builder, что также не потребует моделирования таблицы (и не потребовалось бы получить модель Client, если вы не уже имеют его для других целей:

$exists = DB::table('client_product')
    ->whereClientId($client_id)
    ->whereProductId($product_id)
    ->count() > 0;

Ответ 2

Обновление: Я не принимал во внимание полезность проверки нескольких отношений, если это так, то @deczo имеет лучший ответ на этот вопрос. Требуется только один запрос для проверки всех отношений.

    /**
     * Determine if a Client has a specific Product
     * @param $clientId
     * @param $productId
     * @return bool
     */
    public function clientHasProduct($clientId, $productId)
    {
        return ! is_null(
            DB::table('client_product')
              ->where('client_id', $clientId)
              ->where('product_id', $productId)
              ->first()
        );
    }

Вы можете поместить это в свою модель User/Client, или вы можете получить ее в ClientRepository и использовать ее там, где вам это нужно.

if ($this->clientRepository->clientHasProduct($clientId, $productId)
{
    return 'Awesome';
}

Но если вы уже определили отношение belongsToMany на модели клиента Eloquent, вы можете сделать это внутри своей модели клиента:

    return ! is_null(
        $this->products()
             ->where('product_id', $productId)
             ->first()
    );

Ответ 3

@nielsiano будут работать, но они будут запрашивать DB для каждой пары пользователь/продукт, что, по-моему, является отходами.

Если вы не хотите загружать все данные связанных моделей, то это то, что я сделал бы для одного пользователя:

// User model
protected $productIds = null;

public function getProductsIdsAttribute()
{
    if (is_null($this->productsIds) $this->loadProductsIds();

    return $this->productsIds;
}

public function loadProductsIds()
{
    $this->productsIds = DB::table($this->products()->getTable())
          ->where($this->products()->getForeignKey(), $this->getKey())
          ->lists($this->products()->getOtherKey());

    return $this;
}

public function hasProduct($id)
{
    return in_array($id, $this->productsIds);
}

Тогда вы можете просто сделать это:

$user = User::first();
$user->hasProduct($someId); // true / false

// or
Auth::user()->hasProduct($someId);

Выполняется только 1 запрос, затем вы работаете с массивом.


Самый простой способ - использовать contains, как предложил @alexrussell.

Я думаю, что это вопрос предпочтения, поэтому, если ваше приложение довольно велико и требует большой оптимизации, вы можете выбрать, с чем вам легче работать.

Ответ 4

Вопрос довольно старый, но это может помочь другим, кто ищет решение:

$client = Client::find(1);
$exists = $client->products()->where('products.id', $productId)->exists();

Нет "расточительности", как в решении @alexrussell, и запрос также более эффективен.

Ответ 5

Привет всем) Мое решение для этой проблемы: я создал собственный класс, расширенный от Eloquent, и расширил все мои модели от него. В этом классе я написал эту простую функцию:

function have($relation_name, $id) {
    return (bool) $this->$relation_name()->where('id','=',$id)->count();
}

Для проверки существующего отношения вы должны написать что-то вроде:

if ($user->have('subscribes', 15)) {
    // do some things
}

Этот способ генерирует только запрос SELECT count (...) без получения реальных данных из таблиц.

Ответ 6

Решение Alex работает, но оно загрузит модель Client и все связанные модели Product из БД в память, и только после этого он проверит, существует ли связь.

Лучше всего это сделать, чтобы использовать whereHas().

1. Вам не нужно загружать клиентскую модель, вы можете просто использовать свой идентификатор.

2. Вам также не нужно загружать все продукты, связанные с этим клиентом, в память, как это делает Алекс.

3. Один SQL-запрос к БД.

$doesClientHaveProduct = Product::where('id', $productId)
    ->whereHas('clients', function($q) use($clientId) {
        $q->where('id', $clientId);
    })
    ->count();

Ответ 7

Использовать признак:

trait hasPivotTrait
{
    public function hasPivot($relation, $model)
    {
        return (bool) $this->{$relation}()->wherePivot($model->getForeignKey(), $model->{$model->getKeyName()})->count();
    }
}

.

if ($user->hasPivot('tags', $tag)){
    // do some things...
}