laravel 如何投射 Eloquent Pivot 参数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30226496/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How To Cast Eloquent Pivot Parameters?
提问by Josh Janusch
I have the following Eloquent Models with relationships:
我有以下具有关系的 Eloquent 模型:
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');
}
}
The pivot table contains an additional param (is_primary
) that marks a relationship as the primary. Currently, I see returns like this when I query for a contact:
数据透视表包含一个附加参数 ( is_primary
),用于将关系标记为主要关系。目前,当我查询联系人时,我看到这样的返回:
{
"id": 565,
"leads": [
{
"id": 349,
"pivot": {
"contact_id": "565",
"lead_id": "349",
"is_primary": "0"
}
}
]
}
Is there a way to cast the is_primary
in that to a boolean? I've tried adding it to the $casts
array of both models but that did not change anything.
有没有办法将is_primary
其中的转换为布尔值?我试过将它添加到$casts
两个模型的数组中,但这并没有改变任何东西。
采纳答案by patricus
Since this is an attribute on the pivot table, using the $casts
attribute won't work on either the Lead
or Contact
model.
由于这是数据透视表上的一个属性,因此使用该$casts
属性对Lead
orContact
模型都不起作用。
One thing you can try, however, is to use a custom Pivot
model with the $casts
attribute defined. Documentation on custom pivot models is here. Basically, you create a new Pivot
model with your customizations, and then update the Lead
and the Contact
models to use this custom Pivot
model instead of the base one.
但是,您可以尝试的一件事是使用定义Pivot
了$casts
属性的自定义模型。关于自定义枢轴模型的文档在这里。基本上,你创建一个新的Pivot
与您的自定义模型,然后更新Lead
和Contact
车型使用这个定制的Pivot
,而不是基地之一的模型。
First, create your custom Pivot
model which extends the base Pivot
model:
首先,创建Pivot
扩展基本Pivot
模型的自定义模型:
<?php namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class PrimaryPivot extends Pivot {
protected $casts = ['is_primary' => 'boolean'];
}
Now, override the newPivot()
method on the Lead
and the Contact
models:
现在,覆盖和模型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);
}
}
回答by Jonathon
In Laravel 5.4.14this issue has been resolved. You are able to define a custom pivot model and tell your relationships to use this custom model when they are defined. See the documentation, under the heading Defining Custom Intermediate Table Models.
在Laravel 5.4.14 中,这个问题已经解决。您可以定义自定义枢轴模型并告诉您的关系在定义时使用此自定义模型。请参阅“定义自定义中间表模型”标题下的文档。
To do this you need to create a class to represent your pivot table and have it extend the Illuminate\Database\Eloquent\Relations\Pivot
class. On this class you may define your $casts
property.
为此,您需要创建一个类来表示您的数据透视表并让它扩展Illuminate\Database\Eloquent\Relations\Pivot
该类。在这个类中,您可以定义您的$casts
属性。
<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\Pivot;
class CustomPivot extends Pivot
{
protected $casts = [
'is_primary' => 'boolean'
];
}
You can then use the using
method on the BelongsToMany
relationship to tell Laravel that you want your pivot to use the specified custom pivot model.
然后,您可以使用关系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');
}
}
Now, whenever you access your pivot by using ->pivot
, you should find that it is an instance of your custom pivot class and the $casts
property should be honoured.
现在,每当您使用 访问您的数据透视表时->pivot
,您应该会发现它是您自定义数据透视表类的一个实例,并且$casts
应该尊重该属性。
Update 1st June 2017
2017 年 6 月 1 日更新
The issue raised in the comments by @cdwyer regarding updating the pivot table using the usual sync
/attach
/save
methods is expected to be fixed in Laravel 5.5 which is due to be released next month (July 2017).
通过关于更新使用通常的数据透视表@cdwyer在意见中提出的问题sync
/ attach
/save
方法有望被固定Laravel 5.5这是由于下月发布(七月2017)。
See Taylor's comment at the bottom of this bug reportand his commit, fixing the issue here.
回答by Fusion
Good news! Tylor already fixed this bug:
好消息!泰勒已经修复了这个错误:
https://github.com/laravel/framework/issues/10533
https://github.com/laravel/framework/issues/10533
In Laravel 5.1 or higher you can use dot notation for pivot casts:
在 Laravel 5.1 或更高版本中,您可以使用点表示法进行枢轴转换:
protected $casts = [
'id' => 'integer',
'courses.pivot.course_id' => 'integer',
'courses.pivot.active' => 'boolean'
]
回答by simonhamp
The answer provided by @patricus above is absolutely correct, however, if like me you're looking to also benefit from casting out from JSON-encoded strings inside a pivot tablethen read on.
上面@patricus 提供的答案是绝对正确的,但是,如果像我一样,您也希望从数据透视表中的 JSON 编码字符串中剔除,然后继续阅读。
The Problem
问题
I believe that there's a bug in Laravel at this stage. The problem is that when you instantiate a pivot model, it uses the native Illuminate-Model setAttributes
method to "copy" the values of the pivot record table over to the pivot model.
我相信现阶段 Laravel 中存在一个错误。问题是当你实例化一个枢轴模型时,它使用原生的 Illuminate-ModelsetAttributes
方法将枢轴记录表的值“复制”到枢轴模型。
This is fine for most attributes, but gets sticky when it sees the $casts
array contains a JSON-style cast - it actually double-encodes the data.
这对于大多数属性来说都很好,但是当它看到$casts
数组包含 JSON 样式的转换时会变得很粘- 它实际上对数据进行了双重编码。
A Solution
一个办法
The way I overcame this is as follows:
我克服这个的方法如下:
1. Set up your own Pivot base class from which to extend your pivot subclasses(more on this in a bit)
1. 设置您自己的 Pivot 基类,从中扩展您的 Pivot 子类(稍后会详细介绍)
2. In your new Pivot base class, redefine the setAttribute
method, commenting out the lines that handle JSON-castable attributes
2. 在新的 Pivot 基类中,重新定义该setAttribute
方法,注释掉处理 JSON-castable 属性的行
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;
}
}
This highlights the removal of the isJsonCastable
method call, which will return true
for any attributes you have casted as json
, array
, object
or collection
in your whizzy pivot subclasses.
这突出了去除的isJsonCastable
方法调用,这将返回true
你已经铸成任何属性json
,array
,object
或collection
在您的whizzy支点的子类。
3. Create your pivot subclasses using some sort of useful naming convention(I do {PivotTable}Pivot
e.g. FeatureProductPivot)
3. 使用某种有用的命名约定创建您的数据透视子类({PivotTable}Pivot
例如 FeatureProductPivot)
4. In your base model class, change/create your newPivot
method override to something a little more useful
4. 在您的基础模型类中,将您的newPivot
方法覆盖更改/创建为更有用的东西
Mine looks like this:
我的看起来像这样:
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);
}
}
Then just make sure you Models extend from your base model and you create your pivot-table "models" to suit your naming convention and voilà you will have working JSON casts on pivot table columns on the way out of the DB!
然后只需确保您的模型从您的基本模型扩展,并创建您的数据透视表“模型”以适应您的命名约定,瞧,您将在离开数据库的路上对数据透视表列进行有效的 JSON 转换!
NB: This hasn't been thoroughly tested and may have problems saving back to the DB.
注意:这还没有经过彻底的测试,保存回数据库可能会出现问题。
回答by I. Koster
I had to add some extra checks to have the save and load functions working properly in Laravel 5.
我不得不添加一些额外的检查,以使保存和加载功能在 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;
}
}