存取器(Getter)& Mutator(塞特犬)在Laravel的数据透视表上

时间:2015-03-08 00:37:37

标签: php laravel eloquent pivot-table

我有一个将用户连接到工作区的数据透视表。在数据透视表上,我还有一个role列,它定义了该工作空间的用户角色。我可以提供Accessor(Getter)& Mutator(Setter)方法对pivot表内的角色有何影响?我一直在努力寻找,但是雄辩的数据透视表的细节非常稀少。

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

感谢。

4 个答案:

答案 0 :(得分:9)

如果你需要做的只是访问数据透视表上的其他字段,你只需要在关系定义中使用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']);

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

创建一个包含访问者/ mutator的新pivot类。此类应扩展默认的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);
    }
}

答案 1 :(得分:0)

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

我将延伸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']);

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

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

为了能够在枢轴列上使用mutator调用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。我们通过模拟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);
}

正如您所看到的,我们仍在更多地调用数据库,但我们不必担心有人使用pivot属性调用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');
    }
}

答案 2 :(得分:0)

无法使用设置器,不会影响数据透视表...而是在控制器中进行更改。

答案 3 :(得分:0)

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

您必须在 using() 关系中使用 belongsToMany,例如:

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