Создание и обновление Laravel Eloquent


165

Что такое сокращение для вставки новой записи или обновления, если она существует?

<?php

$shopOwner = ShopMeta::where('shopId', '=', $theID)
    ->where('metadataKey', '=', 2001)->first();

if ($shopOwner == null) {
    // Insert new record into database
} else {
    // Update the existing record
}

Я полагаю, shopIdэто не твой первичный ключ, верно?
Серджиу Параскив

@SergiuParaschiv, да. это не
1myb

Проверьте ответ от @ErikTheDeveloper. Это показывает хороший встроенный красноречивый метод, который должен делать эту работу.
cw24

Точно такая же вещь полностью
дана

Ответы:


232

Вот полный пример того, о чем говорил lu cip:

$user = User::firstOrNew(array('name' => Input::get('name')));
$user->foo = Input::get('foo');
$user->save();

Ниже приведена обновленная ссылка на документы, которые есть в последней версии Laravel.

Документы здесь: Обновленная ссылка


1
именно! 'firstOrNew' также существует в 4.0 (не упоминается в документации)
younes0

2
Также мы можем проверить, что $ user является новым / полученным с помощью if ($ user-> существующие).
Ryu_hayabusa

1
@Ryu_hayabusa Это, вероятно, вызовет условия гонки
Крис Харрисон

новый синтаксис , кажется updateOrInsert (массив $ атрибутов массива $ значение = []) в 5.5: github.com/laravel/framework/blob/5.5/src/Illuminate/Database/...
user1204214

86

Обновлено: 27 августа 2014 г. - [ updateOrCreateВстроено в ядро ​​...]

Просто на тот случай, если люди все еще сталкиваются с этим ... Я узнал через несколько недель после написания этого, что это на самом деле часть ядра Eloquent в Laravel ...

Копаться в эквивалентных методах Eloquent. Вы можете увидеть здесь:

https://github.com/laravel/framework/blob/4.2/src/Illuminate/Database/Eloquent/Model.php#L553

на: 570 и: 553

    /**
     * Create or update a record matching the attributes, and fill it with values.
     *
     * @param  array  $attributes
     * @param  array  $values
     * @return static
     */
    public static function updateOrCreate(array $attributes, array $values = array())
    {
        $instance = static::firstOrNew($attributes);

        $instance->fill($values)->save();

        return $instance;
    }

Старый ответ ниже


Мне интересно, есть ли какая-либо встроенная функциональность L4 для этого, например:

$row = DB::table('table')->where('id', '=', $id)->first();
// Fancy field => data assignments here
$row->save();

Я создал этот метод несколько недель назад ...

// Within a Model extends Eloquent
public static function createOrUpdate($formatted_array) {
    $row = Model::find($formatted_array['id']);
    if ($row === null) {
        Model::create($formatted_array);
        Session::flash('footer_message', "CREATED");
    } else {
        $row->update($formatted_array);
        Session::flash('footer_message', "EXISITING");
    }
    $affected_row = Model::find($formatted_array['id']);
    return $affected_row;
}

Надеюсь, это поможет. Я хотел бы видеть альтернативу этому, если у кого-то есть, чем поделиться. @erikthedev_


Существует и называется firstOrNew / firstsOrCreate
malhal

@malcolmhall Я обновил ответ выше. Оказывается, в Eloquent есть много функций, которые я восстановил;) Всегда приятно потратить некоторое время на просмотр документов :)
Эрик Айбар,

Пакет 4.2.0 (стабильный 2014/6/1) не содержит updateOrCreate. Но это можно реализовать, взглянув на источник. ModelName::firstOrNew(['param' => 'condition'])->fill(Input::get())->save();должен сделать это.
bibstha

3
Просто обратите внимание, что Laravel не запускает его как транзакцию, поэтому, если у вас есть уникальные ключи, и другой пользователь создает его с тем же ключом одновременно, вы можете получить исключение. Я полагаю, что одним из преимуществ RedBeanPHP является то, что подобные вещи выполняются в транзакции для вас.
Малхал

Спасибо за указание на использование fill (), которое мне очень помогло!
Отдых на Кипре

70

2020 Обновление

Как и в Laravel> = 5.3 , если кому-то все еще интересно, как это сделать простым способом. Его можно с помощью: updateOrCreate().

Например, для заданного вопроса вы можете использовать что-то вроде:

$matchThese = ['shopId'=>$theID,'metadataKey'=>2001];
ShopMeta::updateOrCreate($matchThese,['shopOwner'=>'New One']);

Приведенный выше код проверит таблицу, представленную ShopMeta, которая, скорее всего, shop_metasбудет, если не определено иначе в самой модели

и он попытается найти запись с

колонка shopId = $theID

и

колонка metadateKey = 2001

и если он найдет то обновит столбец shopOwnerнайденной строки до New One.

Если он находит более одной совпадающей строки, он обновит самую первую строку, что означает, что у нее самый низкий основной id.

Если он вообще не найден, он вставит новую строку с:

shopId = $theID, metadateKey = 2001иshopOwner = New One

Замечание Проверьте свою модель $fillableи убедитесь, что у вас есть каждое имя столбца, определенное там, которое вы хотите вставить или обновить, а остальные столбцы имеют либо значение по умолчанию, либо значение idстолбца автоматически увеличивается.

В противном случае он выдаст ошибку при выполнении приведенного выше примера:

Illuminate\Database\QueryException with message 'SQLSTATE[HY000]: General error: 1364 Field '...' doesn't have a default value (SQL: insert into `...` (`...`,.., `updated_at`, `created_at`) values (...,.., xxxx-xx-xx xx:xx:xx, xxxx-xx-xx xx:xx:xx))'

Так как будет какое-то поле, которое будет нуждаться в значении при вставке новой строки, и это будет невозможно, так как оно не определено $fillableили не имеет значения по умолчанию.

Для получения дополнительной информации см. Документацию Laravel по адресу: https://laravel.com/docs/5.3/eloquent.

Один пример оттуда:

// If there's a flight from Oakland to San Diego, set the price to $99.
// If no matching model exists, create one.
$flight = App\Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99]
);

который в значительной степени очищает все.

Обновление Query Builder

Кто-то спросил, возможно ли использовать Query Builder в Laravel. Вот ссылка на Query Builder из документов Laravel.

Query Builder работает точно так же, как Eloquent, поэтому все, что верно для Eloquent, также верно и для Query Builder. Так что для этого конкретного случая, просто используйте ту же функцию с вашим построителем запросов, например, так:

$matchThese = array('shopId'=>$theID,'metadataKey'=>2001);
DB::table('shop_metas')::updateOrCreate($matchThese,['shopOwner'=>'New One']);

Конечно, не забудьте добавить фасад БД:

use Illuminate\Support\Facades\DB;

ИЛИ

use DB;

Я надеюсь, что это помогает


Как насчет построителя запросов?
Небо

Что насчет этого ? :)
ученик

Я хочу сделать то же самое с Query Builder. Не красноречивый. Является ли это возможным?
Небо

Обновил мой ответ, поищите раздел «Обновление Query Builder» в ответе выше.
Учащийся

Я попробовал метод DB :: table ('shop_metas') :: updateOrCreate, но это выдает мне следующую ошибку BadMethodCallException в строке 59 Macroable.php: Метод updateOrInsert не существует. Хотя я использую БД;
Свапнил Шенде

17

Функция сохранения:

$shopOwner->save()

уже делай что хочешь ...

Код Laravel:

    // If the model already exists in the database we can just update our record
    // that is already in this database using the current IDs in this "where"
    // clause to only update this model. Otherwise, we'll just insert them.
    if ($this->exists)
    {
        $saved = $this->performUpdate($query);
    }

    // If the model is brand new, we'll insert it into our database and set the
    // ID attribute on the model to the value of the newly inserted row's ID
    // which is typically an auto-increment value managed by the database.
    else
    {
        $saved = $this->performInsert($query);
    }

6
Это не похоже на атомную операцию. Если это не так, это может привести к условиям гонки.
Эмиль Викстрем

Этот код должен проверить, загружена ли модель из БД или является моделью на основе памяти. Обновление или создание требует явного определения ключевых столбцов, которые должны быть проверены и не могут быть выполнены неявно.
АМВБ

17

firstOrNewсоздаст запись, если она не существует, и обновит строку, если она уже существует. Вы также можете использовать updateOrCreateздесь полный пример

$flight = App\Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99]
); 

Если есть рейс из Окленда в Сан-Диего, установите цену в $ 99. если не существует, создайте новую строку

Справочный документ здесь: ( https://laravel.com/docs/5.5/eloquent )


7

Если вам нужна та же функциональность с помощью DB, в Laravel >= 5.5вы можете использовать:

DB::table('table_name')->updateOrInsert($attributes, $values);

или сокращенная версия, когда $attributesи $valuesто же самое:

DB::table('table_name')->updateOrInsert($values);

6
$shopOwner = ShopMeta::firstOrNew(array('shopId' => $theID,'metadataKey' => 2001));

Затем внесите изменения и сохраните. Обратите внимание, что firstOrNew не выполняет вставку, если она не найдена, если вам это нужно, тогда ее firstOrCreate.


2

Еще один вариант, если ваш идентификатор не является автоинкрементом, и вы знаете, какой из них вставить / обновить:

$object = MyModel::findOrNew($id);
//assign attributes to update...
$object->save();

2

Как и метод firstOrCreate, updateOrCreate сохраняет модель, поэтому нет необходимости вызывать save ()

// If there's a flight from Oakland to San Diego, set the price to $99.
// If no matching model exists, create one.

$flight = App\Flight::updateOrCreate(
   ['departure' => 'Oakland', 'destination' => 'San Diego'],
   ['price' => 99]
);

И для вашего вопроса

$shopOwner = ShopMeta::updateOrCreate(
   ['shopId' => $theID, 'metadataKey' => '2001'],
   ['other field' => 'val' ,'other field' => 'val', ....]
);

1

На самом деле firstOrCreate не будет обновляться в случае, если регистр уже существует в БД. Я немного улучшил решение Эрика, так как мне действительно нужно было обновить таблицу с уникальными значениями не только для столбца "id".

/**
 * If the register exists in the table, it updates it. 
 * Otherwise it creates it
 * @param array $data Data to Insert/Update
 * @param array $keys Keys to check for in the table
 * @return Object
 */
static function createOrUpdate($data, $keys) {
    $record = self::where($keys)->first();
    if (is_null($record)) {
        return self::create($data);
    } else {
        return self::where($keys)->update($data);
    }
}

Тогда вы бы использовали это так:

Model::createOrUpdate(
        array(
    'id_a' => 1,
    'foo' => 'bar'
        ), array(
    'id_a' => 1
        )
);

что было не хорошо в этом: 1. Удалить на основе ключа и 2. создать с новыми значениями. Это были еще 2 операции. это сэкономить время для индексации в случае создания и удаления?
Хафиз

1

как @JuanchoRamone, опубликованный выше (спасибо @Juancho), это очень полезно для меня, но если ваши данные являются массивом, вы должны изменить немного так:

public static function createOrUpdate($data, $keys) {
    $record = self::where($keys)->first();
    if (is_null($record)) {
        return self::create($data);
    } else {
        return $record->update($data);
    }
}

Просто быстрое замечание, что это должно быть updateOrCreate вместо createOrUpdate
Джон Шипп

Хорошо, но если будет 1000 строк, будет запущено 1000 запросов?
Марсело Агимовель,

0

Разве это не то же самое, что updateOrCreate ()?

Это похоже, но не то же самое. UpdateOrCreate () будет работать только для одной строки за раз, что не позволяет массовую вставку. InsertOnDuplicateKey будет работать на многих строках.

https://github.com/yadakhov/insert-on-duplicate-key


-2

проверьте, существует ли пользователь или нет. Если не вставить

$exist = DB::table('User')->where(['username'=>$username,'password'=>$password])->get();
if(count($exist)  >0) {
    echo "User already exist";;
}
else  {
    $data=array('username'=>$username,'password'=>$password);
    DB::table('User')->insert($data);
}
Laravel 5.4           

Добро пожаловать в SO.Посмотрите этот практический совет для обеспечения качественного ответа. ---
thewaywewere

Также пометьте фреймворк, который вы используете, версию php, базу данных.
Джейсон Джослин

1
Я использую Laravel 5.4, php7 и mysql
Сабрина Абид

Сабрина Это не идеальное решение, так как в Laravel для этого уже есть функции. Но ваше общее решение
djangodude

Его метод старой школы Laravel уже имеет функцию для этого. Посмотреть выбранный ответ
Саид Ансари
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.