ВСТАВИТЬ ИГНОР, используя Свободный Ларавель

php mysql laravel

16357 просмотра

9 ответа

Есть ли быстрый способ изменить SQL-запрос, сгенерированный Fluent от Laravel, чтобы иметь INSERT IGNOREвместо обычного INSERT?

Я пытаюсь вставить массив из пятидесяти элементов. Написание всего запроса вручную приведет к разложению кода и сделает его более восприимчивым к человеческим ошибкам.

Автор: Nyxynyx Источник Размещён: 15.07.2019 12:46

Ответы (9)


1 плюс

Решение

Для работы вам нужно создать новую грамматику, в которой будет правильная строка:

grammar.php (1)

Грамматика является общедоступной собственностью DBили в этом случае Databaseхранимого соединения. Это не совсем просто, но из-за видимости свойств вы сможете внедрить свою специальную грамматику в слой базы данных.

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


(1) Это было раньше, до даты ответа ссылка. Если вы видите это сегодня, вам нужно перейти на версию Laravel, которую вы используете, например, Grammar.php для 4.0 , в которую перешли эти классы laravel/framework.

Автор: hakre Размещён: 27.09.2012 01:31

13 плюса

Попробуйте эту магию в своей модели:

public static function insertIgnore($array){
    $a = new static();
    if($a->timestamps){
        $now = \Carbon\Carbon::now();
        $array['created_at'] = $now;
        $array['updated_at'] = $now;
    }
    DB::insert('INSERT IGNORE INTO '.$a->table.' ('.implode(',',array_keys($array)).
        ') values (?'.str_repeat(',?',count($array) - 1).')',array_values($array));
}

Используйте как это:

Shop::insertIgnore(array('name' => 'myshop'));

Это отличный способ предотвратить нарушения ограничений, которые могут возникнуть с firstOrCreate в многопользовательской среде, если это свойство name было уникальным ключом.

Автор: malhal Размещён: 24.08.2014 01:43

6 плюса

Я не мог обезьяна заплатить, как это было предложено в ответе Растислава.

Вот что сработало для меня:

  1. Переопределите compileInsertметод в пользовательском классе Query Grammar, который расширяет класс MySqlGrammar платформы.

  2. Используйте экземпляр этого пользовательского грамматического класса, вызывая setQueryGrammarметод из экземпляра соединения с БД.

Итак, код класса выглядит так:

<?php

namespace My\Namespace;

use Illuminate\Database\Query\Builder;
use Illuminate\Database\Query\Grammars\MySqlGrammar;

/**
 * Changes "INSERT" to "INSERT IGNORE"
 */
class CustomMySqlGrammar extends MySqlGrammar
{
    /**
     * Compile an insert statement into SQL.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @param  array  $values
     * @return string
     */
    public function compileInsert(Builder $query, array $values)
    {
        // Essentially we will force every insert to be treated as a batch insert which
        // simply makes creating the SQL easier for us since we can utilize the same
        // basic routine regardless of an amount of records given to us to insert.
        $table = $this->wrapTable($query->from);

        if (! is_array(reset($values))) {
            $values = [$values];
        }

        $columns = $this->columnize(array_keys(reset($values)));

        // We need to build a list of parameter place-holders of values that are bound
        // to the query. Each insert should have the exact same amount of parameter
        // bindings so we will loop through the record and parameterize them all.
        $parameters = collect($values)->map(function ($record) {
            return '('.$this->parameterize($record).')';
        })->implode(', ');

        return "insert ignore into $table ($columns) values $parameters";
    }
}

Я скопировал compileInsertметод из класса фреймворка, а затем внутри метода я только изменился insertна insert ignore. Все остальное осталось прежним.

Затем в конкретном месте кода в приложении (запланированная задача), где мне нужно было «вставить игнорирование», я просто сделал следующее:

<?php

use DB;
use My\Namespace\CustomMySqlGrammar;

class SomeClass
{
    public function someMethod()
    {
        // Changes "INSERT" to "INSERT IGNORE"
        DB::connection()->setQueryGrammar(new CustomMySqlGrammar());

        // et cetera... for example:
        ModelClass::insert($data);
    }
}
Автор: J. Bruni Размещён: 17.07.2017 06:58

4 плюса

Обновленный ответ для Laravel Eloquent в 2018 году

Это также обрабатывает несколько одновременных вставок (вместо одной записи за раз).

Предупреждение: комментарий Эрика ниже , вероятно, правильный. Этот код работал для моего прошлого проекта, но перед тем, как снова использовать этот код, я бы внимательно посмотрел на него, добавил тестовые примеры и корректировал функцию, пока она не будет всегда работать как задумано. Это может быть так же просто, как переместить линию TODO вниз за ifскобки.

Поместите это в класс вашей модели или в класс BaseModel, который расширяет ваша модель:

/**
 * @see https://stackoverflow.com/a/25472319/470749
 * 
 * @param array $arrayOfArrays
 * @return bool
 */
public static function insertIgnore($arrayOfArrays) {
    $static = new static();
    $table = with(new static)->getTable(); //https://github.com/laravel/framework/issues/1436#issuecomment-28985630
    $questionMarks = '';
    $values = [];
    foreach ($arrayOfArrays as $k => $array) {
        if ($static->timestamps) {
            $now = \Carbon\Carbon::now();
            $arrayOfArrays[$k]['created_at'] = $now;
            $arrayOfArrays[$k]['updated_at'] = $now;
            if ($k > 0) {
                $questionMarks .= ',';
            }
            $questionMarks .= '(?' . str_repeat(',?', count($array) - 1) . ')';
            $values = array_merge($values, array_values($array));//TODO
        }
    }
    $query = 'INSERT IGNORE INTO ' . $table . ' (' . implode(',', array_keys($array)) . ') VALUES ' . $questionMarks;
    return DB::insert($query, $values);
}

Используйте как это:

Shop::insertIgnore([['name' => 'myShop'], ['name' => 'otherShop']]);

Автор: Ryan Размещён: 19.07.2018 08:14

1 плюс

Не уверен, будет ли это полезным для кого-то, но недавно я адаптировал подход Хакре к Laravel 5:

Вы должны изменить следующие 3 файла, чтобы ваш Insert Ignore работал:

  1. В Builder.php (vendor / laravel / framework / src / lightingate / database / query / Builder.php) необходимо клонировать функцию insert с изменением имени на insertIgnore и изменением функции вызова грамматики на: $sql = $this->grammar->compileInsertIgnore($this, $values);)

  2. В Grammar.php (vendor / laravel / framework / src / lightingate / database / query / grammars / Grammar.php) вы должны клонировать функцию compileInsert и переименовать ее в compileInsertIgnore, где вы измените return на: return "insert ignore into $table ($columns) values $parameters";

  3. В Connection.php (vendor / laravel / framework / src / lightingate / database / Connection.php) вы должны просто клонировать функцию insert и переименовать ее в insertIgnore

Теперь все готово, соединение может распознавать функцию insertIgnore, сборщик может указать ей правильную грамматику, а грамматика включает в себя выражение «игнорировать». Пожалуйста, не для этого хорошо работает MySQL, может быть не так гладко для других баз данных.

Автор: Rastislav Molnar Размещён: 16.08.2016 04:09

1 плюс

Добавьте следующий метод insertIgnore в вашу модель

<?php

namespace App;

use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;

class User extends Model implements AuthenticatableContract,
                                    AuthorizableContract,
                                    CanResetPasswordContract
{
    use Authenticatable, Authorizable, CanResetPassword;

    /**
     * The database table used by the model.
     *
     * @var string
     */
    protected $table = 'users';

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['name', 'email', 'password'];

    /**
     * The attributes excluded from the model's JSON form.
     *
     * @var array
     */
    protected $hidden = ['password', 'remember_token'];


    public static function insertIgnore(array $attributes = [])
    {
        $model = new static($attributes);

        if ($model->usesTimestamps()) {
            $model->updateTimestamps();
        }

        $attributes = $model->getAttributes();

        $query = $model->newBaseQueryBuilder();
        $processor = $query->getProcessor();
        $grammar = $query->getGrammar();

        $table = $grammar->wrapTable($model->getTable());
        $keyName = $model->getKeyName();
        $columns = $grammar->columnize(array_keys($attributes));
        $values = $grammar->parameterize($attributes);

        $sql = "insert ignore into {$table} ({$columns}) values ({$values})";

        $id = $processor->processInsertGetId($query, $sql, array_values($attributes));

        $model->setAttribute($keyName, $id);

        return $model;
    }
}

Ты можешь использовать:

App\User::insertIgnore([
    'name' => 'Marco Pedraza',
    'email' => 'mpdrza@gmail.com'
]);

Следующий запрос будет выполнен:

insert ignore into `users` (`name`, `email`, `updated_at`, `created_at`) values (?, ?, ?, ?)

Этот метод автоматически добавляет / удаляет метки времени Eloquent, если вы включили или отключили.

Автор: Marco Pedraza Размещён: 02.11.2016 02:53

0 плюса

Я наконец нашел этот https://github.com/yadakhov/insert-on-duplicate-key, который мне очень помог

Пользователь :: insertIgnore ($ пользователей); это метод, который я использую, давая ему массив строк и возвращая его

установить его через composer: composer требуется yadakhov / insert-on-duplicate-key

Автор: Aammad Ullah Размещён: 10.05.2018 05:05

0 плюса

Возможность не писать код: https://github.com/guidocella/eloquent-insert-on-duplicate-key

Я только что проверил это - он работает с моими 5000 вставками за раз, иногда с дубликатами ...

С его помощью вы получите следующие функции:

User::insertOnDuplicateKey($data);
User::insertIgnore($data);
Автор: Gediminas Размещён: 15.07.2019 09:45

-17 плюса

$your_array = array('column' => 'value', 'second_column' => 'value');

DB::table('your_table')->insert($your_array);

Имейте в виду, я не знаю, откуда берутся ваши данные, но вы должны всегда очищать их. Если у вас более одной записи, просто переберите цикл.

Что касается INSERT IGNORE, найдите INSERTметод в свободно используемой библиотеке, создайте новый метод с именем insert_ignore точно так же, как и вставьте, и просто измените его с помощью IGNORE.

Автор: wesside Размещён: 27.09.2012 01:26
32x32