在Laravel中基于外键使列唯一

时间:2020-09-25 10:38:38

标签: laravel eloquent

我有两个模型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,则不能具有相同名称的列,但是只要它属于另一个EventLocation,我就可以创建另一个具有现有名称的EventDepartment

我已经尝试过了,但是没有用:

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

2 个答案:

答案 0 :(得分:1)

在创建迁移时,您应该通过相关字段设置唯一性。 在您的示例中,应为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的名称不会超过一个。 Docs

在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'],

Docs

这应该可行,请测试并确定是否存在错误。

答案 1 :(得分:0)

我认为,在您进行验证时,您的uniqiue属性位于错误的位置。您正在检查代码中“ 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',
    ]);