如何投射雄辩的枢轴参数?

问题描述 投票:0回答:6

我有以下具有关系的雄辩模型:

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
数组中,但这并没有改变任何东西。

php laravel laravel-5 eloquent
6个回答
53
投票

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 月)发布.

请参阅泰勒在此错误报告底部的评论以及他的提交,修复问题此处


16
投票

由于这是数据透视表上的属性,因此使用

$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);
    }
}

6
投票

好消息! Tylor 已经修复了这个错误:

https://github.com/laravel/framework/issues/10533

在 Laravel 5.1 或更高版本中,您可以使用点表示法进行枢轴转换:

protected $casts = [
    'id' => 'integer',
    'courses.pivot.course_id' => 'integer',
    'courses.pivot.active' => 'boolean'
]

3
投票

上面 @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 转换!

注意:这尚未经过彻底测试,保存回数据库时可能会出现问题。


1
投票

我必须添加一些额外的检查才能使保存和加载功能在 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;
    }
}

0
投票

实现此目的的更简单方法是在枢轴关系函数上标记

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,
        ]);
}
© www.soinside.com 2019 - 2024. All rights reserved.