我有以下具有关系的雄辩模型:
class Lead extends Model
{
public function contacts()
{
return $this->belongsToMany('App\Contact')
->withPivot('is_primary');
}
}
class Contact extends Model
{
public function leads()
{
return $this->belongsToMany('App\Lead')
->withPivot('is_primary');
}
}
数据透视表包含一个附加参数 (
is_primary
),将关系标记为主要关系。目前,当我查询联系人时,我会看到这样的返回:
{
"id": 565,
"leads": [
{
"id": 349,
"pivot": {
"contact_id": "565",
"lead_id": "349",
"is_primary": "0"
}
}
]
}
有没有办法将其中的
is_primary
转换为布尔值?我尝试将其添加到两个模型的 $casts
数组中,但这并没有改变任何东西。
在 Laravel 5.4.14 中此问题已得到解决。您可以定义自定义数据透视模型,并告诉您的关系在定义时使用此自定义模型。请参阅标题 定义自定义中间表模型下的文档。
为此,您需要创建一个类来表示数据透视表并让它扩展
Illuminate\Database\Eloquent\Relations\Pivot
类。在这个类中,您可以定义您的 $casts
属性。
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class CustomPivot extends Pivot
{
protected $casts = [
'is_primary' => 'boolean'
];
}
然后,您可以在
using
关系上使用 BelongsToMany
方法来告诉 Laravel 您希望您的数据透视表使用指定的自定义数据透视表模型。
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Lead extends Model
{
public function contacts()
{
return $this->belongsToMany('App\Contact')->using('App\CustomPivot');
}
}
现在,每当您使用
->pivot
访问数据透视表时,您都应该发现它是自定义数据透视表类的实例,并且应该尊重 $casts
属性。
2017 年 6 月 1 日更新
@cdwyer 在评论中提出的有关使用常用
sync
/attach
/save
方法更新数据透视表的问题预计将在 Laravel 5.5 中得到修复,该版本将于下个月(2017 年 7 月)发布.
由于这是数据透视表上的属性,因此使用
$casts
属性不适用于 Lead
或 Contact
模型。
但是,您可以尝试的一件事是使用定义了
Pivot
属性的自定义 $casts
模型。有关自定义枢轴模型的文档位于此处。基本上,您可以使用自定义项创建一个新的 Pivot
模型,然后更新 Lead
和 Contact
模型以使用此自定义 Pivot
模型而不是基础模型。
首先,创建扩展基本
Pivot
模型的自定义 Pivot
模型:
<?php namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class PrimaryPivot extends Pivot {
protected $casts = ['is_primary' => 'boolean'];
}
现在,重写
newPivot()
和 Lead
模型上的 Contact
方法:
class Lead extends Model {
public function newPivot(Model $parent, array $attributes, $table, $exists) {
return new \App\PrimaryPivot($parent, $attributes, $table, $exists);
}
}
class Contact extends Model {
public function newPivot(Model $parent, array $attributes, $table, $exists) {
return new \App\PrimaryPivot($parent, $attributes, $table, $exists);
}
}
好消息! Tylor 已经修复了这个错误:
https://github.com/laravel/framework/issues/10533
在 Laravel 5.1 或更高版本中,您可以使用点表示法进行枢轴转换:
protected $casts = [
'id' => 'integer',
'courses.pivot.course_id' => 'integer',
'courses.pivot.active' => 'boolean'
]
上面 @patricus 提供的答案是绝对正确的,但是,如果像我一样 您也希望从数据透视表内的 JSON 编码字符串中转换中受益然后继续阅读。
我相信现阶段 Laravel 存在一个错误。问题是,当您实例化数据透视模型时,它使用本机 Illuminate-Model
setAttributes
方法将数据透视记录表的值“复制”到数据透视模型。
这对于大多数属性来说都很好,但是当它看到
$casts
数组包含 JSON 样式的转换时就会变得棘手 - 它实际上对数据进行了双重编码。
我克服这个问题的方法如下:
1。设置您自己的 Pivot 基类,从中扩展您的 Pivot 子类(稍后会详细介绍)
2。在新的 Pivot 基类中,重新定义
setAttribute
方法,注释掉处理 JSON 可转换属性的行
class MyPivot extends Pivot {
public function setAttribute($key, $value)
{
if ($this->hasSetMutator($key))
{
$method = 'set'.studly_case($key).'Attribute';
return $this->{$method}($value);
}
elseif (in_array($key, $this->getDates()) && $value)
{
$value = $this->fromDateTime($value);
}
/*
if ($this->isJsonCastable($key))
{
$value = json_encode($value);
}
*/
$this->attributes[$key] = $value;
}
}
这突出显示了
isJsonCastable
方法调用的删除,该方法调用将为您在 Whizzy 枢轴子类中转换为 true
、json
、array
或 object
的任何属性返回 collection
。
3.使用某种有用的命名约定创建您的数据透视子类(我这样做
{PivotTable}Pivot
,例如FeatureProductPivot)
4。在您的基础模型类中,将您的
newPivot
方法重写更改/创建为更有用的东西
我的看起来像这样:
public function newPivot(Model $parent, array $attributes, $table, $exists)
{
$class = 'App\Models\\' . studly_case($table) . 'Pivot';
if ( class_exists( $class ) )
{
return new $class($parent, $attributes, $table, $exists);
}
else
{
return parent::newPivot($parent, $attributes, $table, $exists);
}
}
然后只需确保模型从基本模型扩展,并创建数据透视表“模型”以适合您的命名约定,瞧,您将在数据透视表列上进行有效的 JSON 转换!
注意:这尚未经过彻底测试,保存回数据库时可能会出现问题。
我必须添加一些额外的检查才能使保存和加载功能在 Laravel 5 中正常工作。
class BasePivot extends Pivot
{
private $loading = false;
public function __construct(Model $parent, array $attributes, $table, $exists)
{
$this->loading = true;
parent::__construct($parent, $attributes, $table, $exists);
$this->loading = false;
}
public function setAttribute($key, $value)
{
// First we will check for the presence of a mutator for the set operation
// which simply lets the developers tweak the attribute as it is set on
// the model, such as "json_encoding" an listing of data for storage.
if ($this->hasSetMutator($key)) {
$method = 'set'.Str::studly($key).'Attribute';
return $this->{$method}($value);
}
// If an attribute is listed as a "date", we'll convert it from a DateTime
// instance into a form proper for storage on the database tables using
// the connection grammar's date format. We will auto set the values.
elseif ($value && (in_array($key, $this->getDates()) || $this->isDateCastable($key))) {
$value = $this->fromDateTime($value);
}
/**
* @bug
* BUG, double casting
*/
if (!$this->loading && $this->isJsonCastable($key) && ! is_null($value)) {
$value = $this->asJson($value);
}
$this->attributes[$key] = $value;
return $this;
}
}
实现此目的的更简单方法是在枢轴关系函数上标记
withCasts
:
public function teachers(): BelongsToMany
{
return $this->belongsToMany(Teacher::class, 'section_teacher', 'section_id', 'teacher_id')
->withPivot(['id', 'role', 'created_at', 'updated_at'])
->withCasts([
'role' => TeacherSectionRoleEnum::class,
]);
}