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


У меня есть красноречивая модель, которая имеет связанную модель:

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> - код, который я ищу.

Author: Jarek Tkaczyk, 2014-05-28

8 answers

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

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

Универсальное решение, работающее со всеми типами отношений ( до 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
 123
Author: Jarek Tkaczyk, 2018-07-02 11:36:27

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

Это означает, что вы можете использовать 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) (если только отношение уже не было загружено с нетерпением), так как он выполняет запрос count, а не извлекает все данные для любых связанных объектов из базы данных, просто для их подсчета. Аналогично, при использовании exists не нужно тянуть данные модели также.

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

 50
Author: tremby, 2017-09-22 19:15:07

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

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

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

 14
Author: Hafez Divandari, 2017-11-12 23:04:24

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

Потому что в зависимости от типа отношения, Красноречивый вернет Collection, Model или Null. И в Php 7.1 count(null) бросит error.

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

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

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

Для множественных отношений: Например: hasMany и belongsToMany

if ($model->relation->isNotEmpty()) {
   ....
}
 4
Author: Hemerson Varela, 2018-03-21 14:24:56

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

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

 3
Author: Dave Stewart, 2016-03-01 14:57:59

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

 2
Author: Anthony, 2016-10-26 15:52:25

Как уже сказал Хемерсон Варела в 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 = $user->expertise()->create();
   }

   $option->someAttribute = temp;
   $option->save();
};
 1
Author: Adam, 2018-04-26 12:29:11

Вы сказали, что хотите проверить, существует ли уже связь, поэтому вы можете сделать update или create. Однако в этом нет необходимости из-за метода updateOrCreate.

Просто сделай это:

$model = RepairItem::find($id);
$model->option()
      ->updateOrCreate(['repair_item_id' => $model->id],['option' => 'A']);
 0
Author: Adam, 2018-08-02 08:41:14