Laravel Проверьте, существует ли родственная модель

151

У меня есть модель Eloquent, у которой есть связанная модель:

public function option() {
    return $this->hasOne('RepairOption', 'repair_item_id');
}

public function setOptionArrayAttribute($values)
{
    $this->option->update($values);
}

Когда я создаю модель, она не обязательно имеет связанную модель. Когда я обновляю его, я могу добавить опцию или нет.

Поэтому мне нужно проверить, существует ли связанная модель, обновить или создать ее соответственно:

$model = RepairItem::find($id);
if (Input::has('option')) {
    if (<related_model_exists>) {
        $option = new RepairOption(Input::get('option'));
        $option->repairItem()->associate($model);
        $option->save();
        $model->fill(Input::except('option');
    } else {
       $model->update(Input::all());
    }
};

Где <related_model_exists>код, который я ищу.

Том Макдональд
источник
3
Офигенный вопрос спасибо! И отличные ответы ребятам ниже. Сэкономил мне время на моем проекте.
Рафаэль

Ответы:

197

В php 7.2+ вы не можете использовать countобъект отношения, поэтому не существует универсального метода для всех отношений. Вместо этого используйте метод запроса как @tremby, представленный ниже:

$model->relation()->exists()

универсальное решение, работающее со всеми типами отношений ( pre php 7.2 ):

if (count($model->relation))
{
  // exists
}

Это будет работать для каждого отношения, так как динамические свойства возвращают Modelили Collection. Оба реализуют ArrayAccess.

Итак, это выглядит так:

одиночные отношения: hasOne / belongsTo/ morphTo/morphOne

// no related model
$model->relation; // null
count($model->relation); // 0 evaluates to false

// there is one
$model->relation; // Eloquent Model
count($model->relation); // 1 evaluates to true

отношения ко многим: hasMany / belongsToMany/ morphMany/ morphToMany/morphedByMany

// no related collection
$model->relation; // Collection with 0 items evaluates to true
count($model->relation); // 0 evaluates to false

// there are related models
$model->relation; // Collection with 1 or more items, evaluates to true as well
count($model->relation); // int > 0 that evaluates to true
Ярек Ткачик
источник
1
Прочитайте все это. count($relation)это общее решение для всех отношений. Это будет работать Modelи Collection, пока Modelне имеет ->count()метода.
Ярек Ткачик
7
@CurvianVynes Нет, это не так. Collectionимеет свой собственный метод isEmpty, но универсальная emptyфункция возвращает false для объекта (поэтому не будет работать для пустой коллекции).
Ярек Ткачик
1
count($model->relation)не работал, morphToкогда отношения еще не были установлены. Иностранный идентификатор и тип являются нулевыми, и запрос БД, созданный Laravel, является поддельным и вызывает исключение. Я использовал $model->relation()->getOtherKey()в качестве обходного пути.
Джоселин
1
@Jocelyn Да, это красноречивая ошибка. К сожалению, есть по крайней мере несколько из них для полиморфных отношений, поэтому, очевидно, вы не можете на них полагаться.
Ярек Ткачик
2
Он сломается на PHP 7.2, возвращая:count(): Parameter must be an array or an object that implements Countable
CodeGodie
81

Объект Relation передает вызовы неизвестного метода в построитель запросов Eloquent , который настроен на выбор только связанных объектов. Этот Builder, в свою очередь, передает вызовы неизвестных методов своему базовому построителю запросов .

Это означает, что вы можете использовать методы exists()или count()напрямую из объекта отношения:

$model->relation()->exists(); // bool: true if there is at least one row
$model->relation()->count(); // int: number of related rows

Обратите внимание на круглые скобки после relation: ->relation()это вызов функции (получение объекта отношения), в отличие от ->relationкоторого Laravel установил для вас средство получения магического свойства (получение связанного объекта / объектов).

Использование countметода для объекта отношения (то есть использование скобок) будет намного быстрее, чем выполнение $model->relation->count()или count($model->relation)(если отношение не было уже загружено), поскольку он выполняет запрос подсчета, а не извлекает все данные для любых связанных объектов из базы данных, просто посчитать их. Аналогично, использование existsне требует извлечения данных модели.

Как exists()и count()работа на всех типах реляционных я пробовал, так что, по крайней мере belongsTo, hasOne, hasManyи belongsToMany.

tremby
источник
существует не доступно в люмен, не знаю почему.
briankip
@briankip - так и должно быть. Вы уверены, что получаете объект отношения (вызывая метод), а не коллекцию (используя магическое свойство)?
Тремби
18

Я предпочитаю использовать existsметод:

RepairItem::find($id)->option()->exists()

проверить, существует ли связанная модель или нет. На Laravel 5.2 работает нормально

Хафез Дивандари
источник
1
+1; count ($ модель-> отношение) возвращал для меня истину в Laravel 5.2, хотя в таблице отношений не было элемента. -> Существует () делает свое дело.
Бен Уилсон
9

После Php 7.1 принятый ответ не будет работать для всех типов отношений.

Поскольку в зависимости от типа отношения, Eloquent возвращает a Collection, a Modelили Null. И в Php 7.1 count(null) добавлю error.

Итак, чтобы проверить, существует ли отношение, вы можете использовать:

Для одиноких отношений: например hasOneиbelongsTo

if(!is_null($model->relation)) {
   ....
}

Для отношений несколько: например: hasManyиbelongsToMany

if ($model->relation->isNotEmpty()) {
   ....
}
Хемерсон Варела
источник
4

Не уверен, что это изменилось в Laravel 5, но использование принятого ответа count($data->$relation)не сработало для меня, так как сам доступ к свойству отношения вызвал его загрузку.

В конце концов, простой isset($data->$relation)сделал свое дело для меня.

Дейв Стюарт
источник
Я считаю, что это $data->relationбез $(не может редактировать, из-за ограничения в 6 символов)
Zanshin13
2
Ах, $relationбудет имя вашего отношения, например, $data->postsили как , как. Извините, если это сбивает с толку, я хотел прояснить, что relationэто не конкретное свойство модели: P
Дейв Стюарт,
Это работало некоторое время, но перестало работать после того, как я обновил Laravel с 5.2.29 до 5.2.45. Есть идеи, почему или как это исправить? Теперь это вызывает загрузку реляционных данных по некоторым причинам.
Энтони
Я добавил ответ, в котором есть исправление.
Энтони
3

Вы можете использовать relationLoaded метод на модели объекта. Это спасло мой бекон, надеюсь, это поможет кому-то еще. Мне дали это предложение, когда я задал тот же вопрос на Laracasts.

Энтони
источник
2

Как уже сказал Хемерсон Варела в Php 7.1, count(null)он выдает errorи hasOneвозвращает, nullесли строки не существует. Поскольку у вас есть hasOneотношение, я бы использовал emptyметод для проверки:

$model = RepairItem::find($id);
if (!empty($temp = $request->input('option'))) {
   $option = $model->option;

   if(empty($option)){
      $option = $model->option()->create();
   }

   $option->someAttribute = temp;
   $option->save();
};

Но это лишнее. Нет необходимости проверять, существует ли эта связь, чтобы определить, следует ли вам позвонить updateили createпозвонить. Просто используйте метод updateOrCreate . Это эквивалентно приведенному выше:

$model = RepairItem::find($id);
if (!empty($temp = $request->input('option'))) {  
   $model->option()
         ->updateOrCreate(['repair_item_id' => $model->id],
                          ['option' => $temp]);
}
Адам
источник
0

Мне пришлось полностью реорганизовать мой код, когда я обновил свою версию PHP до 7.2+ из-за неправильного использования функции count ($ x). Это настоящая боль, и это также очень страшно, потому что есть сотни способов использования в разных сценариях, и нет единого правила, подходящего для всех ..

Правила, которым я следовал, чтобы изменить все, примеры:

$ x = Auth :: user () -> posts-> find (6); (проверьте, есть ли у пользователя идентификатор сообщения = 6, используя -> find ())

[FAILS] if(count($x)) { return 'Found'; } 
[GOOD] if($x) { return 'Found'; }

$ x = Auth :: user () -> profile-> департаменты; (проверьте, есть ли в профиле несколько отделов, может быть много отделов)

[FAILS] if(count($x)) { return 'Found'; }
[GOOD] if($x->count()) { return 'Found'; }

$ x = Auth :: user () -> profile-> get (); (проверьте, есть ли у пользователя профиль после использования -> get ())

[FAILS] if(count($x)) { return 'Found'; }
[GOOD] if($x->count()) { return 'Found'; }

Надеюсь, что это может помочь, даже через 5 лет после того, как вопрос был задан, эта статья о переполнении стека очень мне помогла!

raphjutras
источник