У меня есть следующие 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
таблица содержит дополнительный параметр ( is_primary
), который обозначает отношения как первичные. В настоящее время я вижу, что возвращается, когда я запрашиваю контакт:
{ "id": 565, "leads": [ { "id": 349, "pivot": { "contact_id": "565", "lead_id": "349", "is_primary": "0" } } ] }
Есть ли способ is_primary
к этому логическому? Я попытался добавить его в массив $casts
обеих моделей, но это ничего не изменило.
Поскольку это атрибут в сводной таблице, использование атрибута $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); } }
В 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 года).
См. Комментарий Тейлора в нижней части этого отчета об ошибке и его фиксации, исправляя проблему здесь .
Хорошие новости! 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 есть ошибка. Проблема заключается в том, что при создании экземпляра модели поворота, он использует родной Осветите-модель setAttributes
метод «скопировать» значения сводной таблицы звукозаписывающей к модели поворота.
Это отлично подходит для большинства атрибутов, но становится липким, когда он видит, что массив $casts
содержит приведение в стиле JSON – он фактически выполняет двойное кодирование данных.
То, как я преодолел это, выглядит следующим образом:
1. Настройте свой собственный базовый класс Pivot, из которого можно расширить свои подклассы (подробнее об этом немного)
2. В новом базовом классе Pivot переопределите метод setAttribute
, 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
метода 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); } }
Затем просто убедитесь, что модели расширяются от базовой модели, и вы создаете «модели» сводной таблицы в соответствии с вашим соглашением об именах, и вы будете работать с JSON на столбцах сводной таблицы на выходе из БД!
NB: Это не было тщательно протестировано и может возникнуть проблемы с возвратом в БД.
Мне пришлось добавить дополнительные проверки, чтобы функции сохранения и загрузки работали правильно в 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; } }