首页 > 解决方案 > 在 Laravel 中根据外键使列唯一

问题描述

我有两个模型EventLocationEventDepartment. 模型及关系如下图所示:

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);
    }

标签: laraveleloquent

解决方案


在创建迁移时,您应该通过相关字段设置唯一性。在您的示例中,它应该是namelocation_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'],

文档

这应该可以,请测试并判断是否有错误。


推荐阅读