Подтвердить что ты не робот

Как отличить красноречивые параметры?

У меня есть следующие 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 обеих моделей, но это ничего не изменило.

4b9b3361

Ответ 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;
    }
}