Сделать столбец уникальным на основе внешнего ключа в Laravel

#laravel #eloquent

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

Вопрос:

У меня есть две модели EventLocation и EventDepartment . Модели и отношения показаны ниже:

 class EventLocation extends Model
{
    public function event_departments()
    {
        return $this->hasMany(EventDepartment::class);
    }

}
  
 class EventDepartment extends Model
{
    public function event_location()
    {
        return $this->belongsTo(EventLocation::class, 'location_id');
    }
}
  

Миграция для EventDepartment содержит в location_id качестве внешнего ключа, как показано:

 Schema::create('event_departments', function (Blueprint $table) {
      $table->id();

      $table->string('name');
      $table->foreignId('location_id')->constrained('event_locations');

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

Проблема, с которой я сталкиваюсь сейчас, заключается в том, как мне сделать столбец уникальным в EventDepartment таблице при создании нового отделения на основе внешнего ключа, который есть location_id ?

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

Я пробовал с этим, но не работает:

 public function store(Request $request)
    {
        $this->validate($request, [
            'name' => 'required|min:3|unique:event_departments,location_id',
            'location_id' => 'required',
        ]);

        $department = EventDepartment::create([
            'name' => $request->name,
            'location_id' => $request->location_id,
        ]);

        return new EventDepartmentResource($department);
    }
  

Ответ №1:

При создании миграции вы должны установить уникальность по связанным полям. В вашем примере это должно быть по name и location_id

 public function up()
{
    Schema::create('event_departments', function (Blueprint $table) {
        $table->id();

        $table->string('name');
        $table->foreignId('location_id')->constrained('event_locations');

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

        $table->unique(['name', 'location_id'], 'unique_name_location');
    });
}

public function down()
{
    Schema::enableForeignKeyConstraints();
    Schema::table('event_departments', function (Blueprint $table) {
        $table->dropForeign(['location_id']);
        $table->dropUnique('unique_name_location');
    })
    Schema::disableForeignKeyConstraints();

    Schema::drop('event_departments');
}
  

Это обеспечит уровень БД, установленные правила таблицы, и не будет более одного одинакового имени с одинаковым location_id. Документы.

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

-Создать класс правил

 php artisan make:rule UniqueNameLocationRule
  

-Код класса правил

 public function __construct(string $name, int $locationId)
{
    $this->name = $name;
    $this->locationId = $locationId;
}

public function passes($attribute, $value)
{
    return !EventDepartment::where([
        'name' => $this->name,
        'location_id' => $this->locationId,
    ])->exists();
}
  

-Код проверки

 'name' => ['bail', 'required', 'min:3', new UniqueNameLocationRule((string)$request->name, (int)$request->location_id)],
'location_id' => ['required', 'exists:event_locations,id'],
  

Документы.

Это должно сработать, пожалуйста, протестируйте и сообщите, есть ли какая-то ошибка.

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

1. Привет, @Tpojka. Похоже, действительно хороший и хорошо продуманный подход, поэтому я поддержал это. Тем не менее, я постараюсь реализовать в будние дни и принять в качестве ответа, если это сработает для меня. Спасибо

2. Имея в виду ваш запрос из вопроса, я представил, что бы я сделал в этой ситуации.

Ответ №2:

Я думаю, что при вашей проверке ваше свойство uniqiue находится не в том месте. Вы проверяете, что ‘является ли переменная «name» уникальной в таблице event_departments в столбце location_id’ в вашем коде, но вы не хотите проверять name. Вы хотите проверить location_id. Если я неправильно понял ваш вопрос, извините.

 $this->validate($request, [
        'name' => 'required|min:3',
        'location_id' => 'required|unique:event_departments,location_id',
    ]);
  

Можете ли вы попробовать это? Или, если вы хотите проверить имя, попробуйте это;

 $this->validate($request, [
        'name' => 'required|min:3|unique:event_departments,name',
        'location_id' => 'required',
    ]);
  

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

1. Я проверяю имя. EventDepartment Имя должно быть уникальным для каждого EventLocation примера EventDepartment , которое может быть названо «ИТ-отдел», и это имя может появляться в таблице дважды, если они принадлежат разным EventLocation , т.е. location_id в EventDepartment таблице не совпадают.