Автоинкрементный столбец с условиями

#laravel #eloquent

#laravel #красноречивый

Вопрос:

Я хотел бы реализовать столбец порядка (который будет использоваться для определения иерархии для объектов, как в бэкэнде, так и в интерфейсе). Есть ли хороший способ сделать это на уровне миграции?

Так что при сохранении новой модели будет найден самый высокий номер столбца заказа — но только для моделей, которые используют shop_id. Итак, если я добавлю такие модели, как:

 // First model should get order = 1
    $priceExceptionA = new PriceException();
    $priceExceptionA->shop_id = 1;
    $priceExceptionA->amount = 100;
    $priceExceptionA->save();

// Second model should get order = 2
    $priceExceptionB = new PriceException();
    $priceExceptionB->shop_id = 1;
    $priceExceptionB->amount = 100;
    $priceExceptionB->save();
 

Я хотел бы найти способ реализовать это поведение уже через миграцию:

 public function up()
{
    Schema::create('price_exceptions', function (Blueprint $table) {
        $table->id();
        $table->integer('shop_id')->unsigned();
        $table->integer('amount')->nullable();

        // Find the highest order value for rows with the same shop_id and increment this by one
        $table->integer('order')->default(////)

        $table->timestamps();
        $table->softDeletes();

        $table->foreign('shop_id')->references('id')->on('shops')->onDelete('cascade');
    });
}
 

Комментарии:

1. Помогло ли мое решение вашей проблеме или вы столкнулись с другими проблемами?

Ответ №1:

Я не верю, что SQL может делать то, что вы хотите, с функциями по умолчанию.

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

 public static function boot()
{
    static::creating(function(PriceException $priceException)
    {
        $priceException->order = ($priceException->shop->priceExceptions()->max('order') ?? 0)   1;
    });
}
 

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

 static::creating(function(PriceException $priceException)
{
    Cache::lock($priceException->shop_id)->get(function () use ($priceException) {   
        $priceException->order = ($priceException->shop->priceExceptions()->max('order') ?? 0)   1;
    });
});
 

Комментарии:

1. Спасибо, чувак. Оказывается, мне нужно было больше логики, и вместо этого я реализовал функцию в контроллере. Но об этом мы поговорим позже, и мне действительно нравится элегантность вашего решения. Я также могу увидеть другие варианты использования для меня с событием создания. Итак, я узнал здесь кое-что новое. Поддержано и проверено ;-). Спасибо.