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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 11:32:55  来源:igfitidea点击:

How To Cast Eloquent Pivot Parameters?

phplaravellaravel-5eloquent

提问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_primaryin that to a boolean? I've tried adding it to the $castsarray 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 $castsattribute won't work on either the Leador Contactmodel.

由于这是数据透视表上的一个属性,因此使用该$casts属性对LeadorContact模型都不起作用。

One thing you can try, however, is to use a custom Pivotmodel with the $castsattribute defined. Documentation on custom pivot models is here. Basically, you create a new Pivotmodel with your customizations, and then update the Leadand the Contactmodels to use this custom Pivotmodel instead of the base one.

但是,您可以尝试的一件事是使用定义Pivot$casts属性的自定义模型。关于自定义枢轴模型的文档在这里。基本上,你创建一个新的Pivot与您的自定义模型,然后更新LeadContact车型使用这个定制的Pivot,而不是基地之一的模型。

First, create your custom Pivotmodel which extends the base Pivotmodel:

首先,创建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 Leadand the Contactmodels:

现在,覆盖和模型newPivot()上的方法:LeadContact

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\Pivotclass. On this class you may define your $castsproperty.

为此,您需要创建一个类来表示您的数据透视表并让它扩展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 usingmethod on the BelongsToManyrelationship 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 $castsproperty 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/savemethods 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.

请参阅此错误报告底部的 Taylor 评论和他的提交,在此处修复问题。

回答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 setAttributesmethod 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 $castsarray 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 setAttributemethod, 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 isJsonCastablemethod call, which will return truefor any attributes you have casted as json, array, objector collectionin your whizzy pivot subclasses.

这突出了去除的isJsonCastable方法调用,这将返回true你已经铸成任何属性jsonarrayobjectcollection在您的whizzy支点的子类。

3. Create your pivot subclasses using some sort of useful naming convention(I do {PivotTable}Pivote.g. FeatureProductPivot)

3. 使用某种有用的命名约定创建您的数据透视子类{PivotTable}Pivot例如 FeatureProductPivot)

4. In your base model class, change/create your newPivotmethod 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;
    }
}