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

109

У меня есть модель 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> - это код, который я ищу.

  • 2
    Офигенный вопрос спасибо! И отличные ответы ребятам ниже. Сэкономил мне время на моем проекте.
Теги:
eloquent
laravel-5
laravel-4

8 ответов

137
Лучший ответ

В 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 - это встроенная функция php, что здорово.
  • 0
    Разве Collection уже не имеет функции count() ? Таким образом, вы могли бы просто сказать $model->relation->count() ?
Показать ещё 17 комментариев
53

A Объект привязки передает неизвестные вызовы методов через Запрос запросов, который настроен только для выбора связанных объектов. Этот 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, который для вас создал Largvel (получение связанного объекта/объекта).

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

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

  • 3
    Спасибо, это должен быть принятый ответ!
  • 2
    Да, это должно быть !!!
Показать ещё 3 комментария
14

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

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

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

  • 1
    +1; count ($ модель-> отношение) возвращал для меня истину в Laravel 5.2, хотя в таблице отношений не было элемента. -> Существует () делает свое дело.
  • 0
    Работает только на многих отношениях.
Показать ещё 1 комментарий
6

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

Поскольку в зависимости от типа отношения, Eloquent возвращает Collection, 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) помогло.

  • 0
    Я полагаю, что это отношение $data->relation без $ (редактировать нельзя, из-за ограничения в 6 символов)
  • 2
    Ах, $relation будет названием вашего отношения, например, $data->posts или тому подобное. Извините, если это сбивает с толку, я хотел прояснить, что relation не было конкретным свойством модели: P
Показать ещё 2 комментария
2

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

1

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

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

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

$model = RepairItem::find($id);
$model->option()
      ->updateOrCreate(['repair_item_id' => $model->id],['option' => 'A']);

Ещё вопросы

Сообщество Overcoder
Наверх
Меню