Как отличить красноречивые параметры?
У меня есть следующие Eloquent Models с отношениями:
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
обеих моделей, но это ничего не изменило.
Ответы
Ответ 1
Поскольку это атрибут в сводной таблице, использование атрибута $casts
не будет работать ни в модели Lead
, ни в Contact
.
Однако вы можете попытаться использовать пользовательскую модель Pivot
с определенным атрибутом $casts
. Документация по пользовательским моделям pivot здесь. В основном вы создаете новую модель 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);
}
}
Ответ 2
В 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
, вы должны обнаружить, что это экземпляр вашего настраиваемого класса pivot и свойство $casts
должно быть выполнено.
Обновление 1 июня 2017 года
Вопрос, поднятый в комментариях @cdwyer относительно обновления сводной таблицы с использованием обычных методов sync
/attach
/save
, как ожидается, будет исправлен в Laravel 5.5, который должен быть выпущен в следующем месяце (июль 2017).
См. комментарий Тейлора в нижней части этот отчет об ошибке и его фиксация, исправление проблемы здесь.
Ответ 3
Хорошие новости! Tylor уже исправил эту ошибку:
https://github.com/laravel/framework/issues/10533
В Laravel 5.1 или более поздней версии вы можете использовать точечную нотацию для поворотных отбрасываний:
protected $casts = [
'id' => 'integer',
'courses.pivot.course_id' => 'integer',
'courses.pivot.active' => 'boolean'
]
Ответ 4
Ответ, предоставленный @patricus выше, является абсолютно правильным, однако, если я, как я, , вы также хотите извлечь из строк, закодированных JSON внутри сводной таблицы, затем прочитать.
Проблема
Я считаю, что на этом этапе есть ошибка в Laravel. Проблема заключается в том, что при создании экземпляра модели поворота, он использует родную ILLUMINATE-модель setAttributes
метод "копировать" значение сводной таблицы перезаписать модели вращения.
Это отлично подходит для большинства атрибутов, но становится липким, когда он видит массив $casts
, содержащий приведение в стиле JSON - он фактически дважды кодирует данные.
Решение
То, как я преодолел это, выглядит следующим образом:
1. Настройте свой собственный базовый класс Pivot, из которого можно расширить сводные подклассы (подробнее об этом немного)
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;
}
}
Это подчеркивает удаление вызова метода isJsonCastable
, который возвращает 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);
}
}
Затем просто убедитесь, что модели расширяются от базовой модели, и вы создаете "модели" сводных таблиц в соответствии с вашим соглашением об именах и voilà, у вас будет работающий JSON-бросок на столбцах сводной таблицы на выходе из базы данных!
Примечание. Это не было тщательно протестировано и может возникнуть проблемы с возвратом в БД.
Ответ 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 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;
}
}