Можно ли в Laravel обновить записи модели с помощью массива за одну операцию?

Предположим, у вас есть массив объектов, содержащий поля id и date:

```php
$arr=[
(object)['id'=>1,'date'=>'2024-12-11'],
(object)['id'=>2,'date'=>'2024-03-28'],
(object)['id'=>3,'date'=>'2024-04-01']
//...etc
];
```

Можно ли обновлять записи в базе данных, используя цикл `foreach`, с помощью метода `find(id)`, изменяя поле `date` и сохраняя модель через `save()`? Однако, существует ли встроенный способ сделать это в Eloquent? Я знаю о методе `whereIn`, но он подходит только для обновления одного значения для всех записей. Имеет ли это смысл с точки зрения оптимизации, или существует альтернативный подход для более эффективного выполнения подобного запроса?
  • 27 августа 2024 г. 9:04
Ответы на вопрос 2
Да, в Laravel действительно возможно обновить несколько записей модели за одну операцию, и это может быть более эффективно, чем использовать цикл `foreach` с `find()` и `save()` для каждой записи. Вы правильно заметили, что метод `whereIn` подходит только для обновления одного значения для всех записей, однако существует более удобный способ сделать это с помощью метода `update()`.

Для обновления разных значений для разных записей можно использовать:

1. **DB::table()**: Это базовая сущность работы с базой данных. Вы можете использовать ее для массового обновления:

```php
use Illuminate\Support\Facades\DB;

$arr = [
    (object)['id'=>1, 'date'=>'2024-12-11'],
    (object)['id'=>2, 'date'=>'2024-03-28'],
    (object)['id'=>3, 'date'=>'2024-04-01']
];

foreach ($arr as $item) {
    DB::table('your_table_name')->where('id', $item->id)->update(['date' => $item->date]);
}
```

Это просто и эффективно, но не самое оптимальное с точки зрения количества запросов.

2. **Batch Update**: Более эффективный способ - это группировать данные и использовать один SQL-запрос с помощью выражения CASE. Например:

```php
$caseStatements = [];
$ids = [];
foreach ($arr as $item) {
    $caseStatements[] = "WHEN id = {$item->id} THEN '{$item->date}'";
    $ids[] = $item->id;
}

$idsList = implode(',', $ids);
$caseString = implode(' ', $caseStatements);

$sql = "UPDATE your_table_name SET date = CASE $caseString END WHERE id IN ($idsList)";
DB::update($sql);
```

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

3. **Использование Eloquent**: К сожалению, Eloquent не поддерживает обновление нескольких записей с разными значениями в одном вызове нативно, но вы можете использовать подход, аналогичный вышеуказанному, через моделирование запросов, если это имеет смысл в вашем контексте.

В заключение, если у вас есть множество записей для обновления, лучший подход будет использовать SQL `UPDATE` с выражениями `CASE` для оптимизации и уменьшения нагрузки на базу данных.
Если не принципиален вызов событий и всякие $casts, и есть гарантия, что все данные существуют (или это не принципиально), то можно воспользоваться upsert .
Похожие вопросы