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