Перевірка Laravel, якщо пов'язана модель існує


151

У мене красномовна модель, яка має пов'язану модель:

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

загальне рішення, що працює на всіх типах відношень ( до 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()методу.
Jarek Tkaczyk

7
@CurvianVynes Ні, це не так. Collectionмає власний метод isEmpty, але загальна emptyфункція повертає false для об'єкта (таким чином, не працює для порожньої колекції).
Ярек Ткачик

1
count($model->relation)не працював, morphToколи стосунки ще не були встановлені асоціаціями. Іноземний ідентифікатор і тип є нульовими, а db-запит, побудований Laravel, є хибним і збільшує виняток. Я використовував $model->relation()->getOtherKey()як вирішення.
Джоселін

1
@Jocelyn Так, це красномовна помилка. На жаль, є принаймні декілька з них для поліморфних відносин, так що очевидно, ви не можете покластися на них жодним чином.
Jarek Tkaczyk

2
Він зламається на PHP 7.2, повертаючи:count(): Parameter must be an array or an object that implements Countable
CodeGodie

81

Об'єкт зв'язку проходить невідомі виклики методів через до красномовного запитом Builder , який встановлюється тільки вибрати відповідні об'єкти. Цей Builder, у свою чергу, передає невідомий метод викликів до його базового запиту 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.


існує, немає в просвіті, не знаю чому.
briankip

@briankip - так і повинно. Ви впевнені, що отримуєте об'єкт відношення (викликаючи метод), а не колекцію (використовуючи магічну властивість)?
тремтіння

18

Я вважаю за краще використовувати existsметод:

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

перевірити, чи існує відповідна модель чи ні. Це добре працює на Laravel 5.2


1
+1; count ($ model-> відношення) повертався справжнім для мене в Laravel 5.2, хоча в таблиці відношень не було жодного елемента. -> існує () робить трюк.
Бен Вілсон

9

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

Оскільки залежно від типу відносини, Красномовний поверне 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. Будь-яка ідея, чому або як це виправити? Тепер це стає причиною завантаження реляційних даних з якоїсь причини.
Ентоні

Я додав відповідь, яка має для цього виправлення.
Ентоні


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 () -> повідомлення-> знайти (6); (перевірте, чи є у користувача посаду id = 6, використовуючи -> find ())

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

$ x = Auth :: user () -> профіль-> відділи; (перевірте, чи в профілі є кілька відділів, чи може бути багато відділів)

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

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

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

Сподіваюся, що це може допомогти, навіть через 5 років після того, як було задано питання, цей пост stackoverflow дуже допоміг мені!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.