繁体   English   中英

Laravel 数据透视表上的访问器(Getter)和修改器(Setter)

[英]Accessors (Getter) & Mutators (Setter) On a Pivot Table in Laravel

我有一个将用户连接到工作区的数据透视表。 在数据透视表上,我还有一个角色列,它定义了该工作区的用户角色。 我可以为数据透视表中的角色提供访问器(Getter)和修改器(Setter)方法吗? 我一直在尝试查看所有内容,但有关 eloquent 数据透视表的详细信息非常稀少。

我不确定是否必须设置自定义枢轴模型? 如果我这样做,一个例子会很棒,因为关于枢轴模型的文档非常基础。

谢谢。

如果您需要做的只是访问数据透视表上的其他字段,则只需在关系定义上使用withPivot()方法:

class User extends Model {
    public function workspaces() {
        return $this->belongsToMany('App\Models\Workspace')->withPivot('role');
    }
}

class Workspace extends Model {
    public function users() {
        return $this->belongsToMany('App\Models\User')->withPivot('role');
    }
}

现在您的角色字段将在数据透视表中可用:

$user = User::first();

// get data
foreach($user->workspaces as $workspace) {
    var_dump($workspace->pivot->role);
}

// set data
$workspaceId = $user->workspaces->first()->id;
$user->workspaces()->updateExistingPivot($workspaceId, ['role' => 'new role value']);

如果您确实需要为数据透视表创建访问器/修改器,则需要创建自定义数据透视表类。 我以前没有这样做过,所以我不知道这是否真的有效,但看起来你会这样做:

创建一个包含访问器/修改器的新枢轴类。 这个类应该扩展默认的 Pivot 类。 这个新类是将在 User 或 Workspace 创建 Pivot 模型实例时实例化的类。

namespace App\Models;
use Illuminate\Database\Eloquent\Relations\Pivot;
class UserWorkspacePivot extends Pivot {
    getRoleAttribute() {
        ...
    }
    setRoleAttribute() {
        ...
    }
}

现在,更新您的用户和工作区模型以创建这个新的数据透视表类,而不是默认的。 这是通过覆盖 Model 类提供的newPivot()方法来完成的。 您希望覆盖此方法,以便创建新 UserWorkspacePivot 类的实例,而不是默认的 Pivot 类。

class User extends Model {
    // normal many-to-many relationship to workspaces
    public function workspaces() {
        // don't forget to add in additional fields using withPivot()
        return $this->belongsToMany('App\Models\Workspace')->withPivot('role');
    }

    // method override to instantiate custom pivot class
    public function newPivot(Model $parent, array $attributes, $table, $exists) {
        return new UserWorkspacePivot($parent, $attributes, $table, $exists);
    }
}

class Workspace extends Model {
    // normal many-to-many relationship to users
    public function users() {
        // don't forget to add in additional fields using withPivot()
        return $this->belongsToMany('App\Models\User')->withPivot('role');
    }

    // method override to instantiate custom pivot class
    public function newPivot(Model $parent, array $attributes, $table, $exists) {
        return new UserWorkspacePivot($parent, $attributes, $table, $exists);
    }
}

这是一个很难的问题。 我能想到的解决方案都很臭,以后可能会引起一些问题。

我将扩展 Patricus 的回答以使其发挥作用。

我打算对帕特里克斯的回答发表评论,但要解释的实在太多了。 为了让他的解决方案与attachsync一起工作,我们必须做一些丑陋的事情。

问题

首先让我们找出他的解决方案的问题。 他的 getter 和 setter 可以工作,但是当运行syncattachdetach时,belongsToMany 关系不使用 Pivot 模型。 这意味着每次我们使用$attributes参数调用其中之一时,非变异数据将被放入数据库列中。

// This will skip the mutator on our extended Pivot class
$user->workspaces()->attach($workspace, ['role' => 'new role value']);

我们可以试着记住,每次我们调用其中一个时,我们不能使用第二个参数来附加变异的数据,而只能使用必须变异的数据调用updateExistingPivot 所以attach就是 Patricus 所说的:

$user->workspaces()->attach($workspace);
$user->workspaces()->updateExistingPivot($workspaceId, ['role' => 'new role value']);

并且我们永远无法使用正确的方式将枢轴属性作为第一个示例中显示的attach方法的第二个参数传递。 这将导致更多的数据库语句和代码腐烂,因为您必须始终记住不要按正常方式进行。 如果您假设每个开发人员,甚至您自己,都知道不要像预期的那样使用带有第二个参数的attach方法,那么您以后可能会遇到严重的问题。

解决方案(未经测试且不完美)

为了能够在枢轴列上使用增变器调用attach ,您必须进行一些疯狂的扩展。 我还没有测试过这个,但如果你想尝试一下,它可能会让你走上正确的道路。 我们必须首先创建我们自己的关系类来扩展BelongsToMany并实现我们的自定义attach方法:

use Illuminate\Database\Eloquent\Relations\BelongsToMany;

class UserWorkspaceBelongsToMany extends BelongsToMany {
    public function attach($id, array $attributes = [], $touch = true)
    {
        $role = $attributes['role'];
        unset($attributes['role']);
        parent::attach($id, $attributes, $touch);
        $this->updateExistingPivot($id, ['role' => $role], $touch);
    }
    // You will need sync here too
}

现在,我们必须使每个Model::belongsToMany使用我们的新UserWorkspaceBelongsToMany类,而不是正常的BelongsToMany 我们通过belongsToMany User 和 Workspace 类中的belongsToMany来做到这一点:

// put this in the User and Workspace Class
public function userWorkspaceBelongsToMany($related, $table = null, $foreignKey = null, $otherKey = null, $relation = null)
{
    if (is_null($relation)) {
        $relation = $this->getBelongsToManyCaller();
    }

    $foreignKey = $foreignKey ?: $this->getForeignKey();

    $instance = new $related;

    $otherKey = $otherKey ?: $instance->getForeignKey();

    if (is_null($table)) {
        $table = $this->joiningTable($related);
    }

    $query = $instance->newQuery();

    return new UserWorkspaceBelongsToMany($query, $this, $table, $foreignKey, $otherKey, $relation);
}

如您所见,我们仍在更多地调用数据库,但我们不必担心有人使用枢轴属性调用attach并且它们不会发生变异。

现在在你的模型中使用它而不是普通的belongsToMany:

class User extends Model {
    public function workspaces() {
        return $this->userWorkspaceBelongsToMany('App\Models\Workspace')->withPivot('role');
    }
}

class Workspace extends Model {
    public function users() {
        return $this->userWorkspaceBelongsToMany('App\Models\User')->withPivot('role');
    }
}

不可能使用设置器,不会影响数据透视表......改为在控制器中进行更改。

我如何使用AccessorsMutators上的数据透视表(我使用Laravel 5.8)

你必须在你的belongsToMany关系上使用using() ,例如:

namespace App;
use Illuminate\Database\Eloquent\Model;

class User extends Model {
    public function workspaces() {
        return $this->belongsToMany('App\Workspace')->using('App\UserWorkspace');
    }
}
namespace App;
use Illuminate\Database\Eloquent\Model;

class Workspace extends Model {
    public function users() {
        return $this->belongsToMany('App\User')->using('App\UserWorkspace');
    }
}

因此,使用您的 Pivot 模型:

namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;

class UserWorkspace extends Pivot {
    public function getRoleAttribute() {
        // your code to getter here

    }
    public function setRoleAttribute($value) {
        // your code to setter here
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM