Как Laravel находит и отображает динамические атрибуты моделей Eloquent при использовании интерфейса командной строки tinker?

Когда мы используем artisan tinker и ссылаемся на объект модели Eloquent, REPL автоматически печатает атрибуты модели так же, как он печатает общедоступные свойства любого стандартного объекта, на который мы ссылаемся:

>>> (object) ['hello' => 'world']
 => {
      +"hello": "world",
    }

>>> App\User::first()
 => App\User {
      id: 1,
      name: "...",
   }

Что менее очевидно для меня, так это как эти виртуальные атрибуты могут появиться здесь, как если бы они уже были определены как общедоступные свойства класса. Я понимаю, что большая часть назначения атрибутов для модели обрабатывается трейтом HasAttributes внутри, но даже глядя туда, я все еще не понимаю, как авторам Laravel удалось добиться такого поведения.

Я пробовал построить такой класс:

class Bunch implements Arrayable, ArrayAccess, Jsonable, JsonSerializable { ... }

но даже с рабочим доступом к массиву и методом toArray, когда я ссылаюсь на него непосредственно из artisan tinker:

>>> $b = new Bunch()
 => Bunch {}
>>> $b->one = 1
 => 1
>>> $b['one']
 => 1
>>> $b
 => Bunch {}

Как мы можем изменить представление, которое REPL использует при печати такого объекта?


person Chris Whalen    schedule 30.07.2020    source источник


Ответы (2)


Код, который определяет, как отображать атрибуты модели в Tinker, находится в коде Tinker, а не в модели:

Кастеры являются частью компонента Symfony VarDumper, который используется PsySH для отображения классов. Tinker построен на основе PsySH.

Tinker сопоставляет ролики с классами в своем классе команд консоли:

Это возвращает сопоставление классов их заклинателям:

/**
     * Get an array of Laravel tailored casters.
     *
     * @return array
     */
    protected function getCasters()
    {
        $casters = [
            'Illuminate\Support\Collection' => 'Laravel\Tinker\TinkerCaster::castCollection',
            'Illuminate\Support\HtmlString' => 'Laravel\Tinker\TinkerCaster::castHtmlString',
            'Illuminate\Support\Stringable' => 'Laravel\Tinker\TinkerCaster::castStringable',
        ];

        if (class_exists('Illuminate\Database\Eloquent\Model')) {
            $casters['Illuminate\Database\Eloquent\Model'] = 'Laravel\Tinker\TinkerCaster::castModel';
        }

        if (class_exists('Illuminate\Foundation\Application')) {
            $casters['Illuminate\Foundation\Application'] = 'Laravel\Tinker\TinkerCaster::castApplication';
        }

        return $casters;
    }

Это устанавливает ролики на VarDumper:

        $config->getPresenter()->addCasters(
            $this->getCasters()
        );

Если вы хотите добавить дополнительные свойства из модели для отображения в Tinker, вы можете использовать $добавляет свойство к модели:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The accessors to append to the model's array form.
     *
     * @var array
     */
    protected $appends = ['is_admin'];
}
person Brion Finlay    schedule 01.04.2021
comment
Да! Это полностью то, что я искал. Добавление $casters['App\Bunch'] = 'App\Bunch::castBunch'; позволяет мне определить пользовательский метод для обработки REPL-представления моего пользовательского класса. Большое спасибо! - person Chris Whalen; 03.04.2021

Eloquent автоматически заполняет атрибуты модели, используя настройку подключения к базе данных.

person Pierre Label    schedule 30.07.2020