Расширение поведения класса в Laravel с помощью макросов


В этой статье я собираюсь обсудить функцию в Laravel, с помощью которой вы можете расширить функциональность определенных базовых классов Laravel, даже не касаясь исходной кодовой базы. Или, другими словами, добавление методов в класс динамически.

Macroable Классы

Laravel корабли с этой чертой называется Осветить Support TRAITS Macroable единственная цель которого – сделать класс «Macroable», в котором он используется. Класс является макрообъемным, то есть он позволяет вам добавлять дополнительные методы в класс во время выполнения.

Таким образом, эта черта позволяет разработчикам добавлять в класс дополнительное поведение без изменения исходного исходного кода класса. Вам конкретно нужно проверить, должен ли класс, к которому вы хотите добавить поведение, использовать эту черту или нет, чтобы использовать макросы. Вот несколько классов Laravel, которые используют Macroable черта характера.

  • Осветить Support Коллекция
  • Осветить Support Str
  • Осветить Http UploadedFile
  • Осветить Http RedirectResponse
  • Осветить Http Запрос
  • Осветить Routing ResponseFactory
  • Осветить Routing UrlGenerator
  • Осветить Routing Router

Объявление макросов

Например, у нас есть IlluminateSupportCollection класс в Laravel, который является «макро» классом. Теперь, чтобы добавить дополнительный метод, скажем, дополнительный метод с именем makeKebab к этому классу, который преобразует каждый элемент коллекции в кебаб, вы можете использовать статический ::macro метод вроде так.

namespace AppProviders;

use IlluminateSupportCollection;
use IlluminateSupportStr;

class AppServiceProvider
{
    public function boot()
    {
        Collection::macro('makeKebab', function () {
            return $this->map(function ($value) {
                return Str::kebab($value);
            });
        });
    }
}

Как вы можете видеть, вам, как правило, нужно объявлять макросы в boot метод. В нашем случае это AppProvidersAppServiceProvider, Здесь мы назвали ::macro метод на Collection класс, который принимает два аргумента. Первый аргумент – это имя метода, который мы собираемся использовать, а второй – замыкание, которое реализует реальную функциональность.

Использование макросов

Теперь мы можем позвонить makeKebab() метод для коллекции, который в нашем случае преобразует все элементы коллекции в кебаб. Вот как это использовать.

$collection = collect(('laravel is awesome', 'foo bar'));

$upper = $collection->makeKebab();

// ('laravel-is-awesome', 'foo-bar')

Это оно! Таким образом вы можете добавить поведение к классу, не нарушая его первоначальную реализацию.

За кулисами

Если вы посмотрите на IlluminateSupportTraitsMacroable черта, которую вы узнаете, используя эту черту, ваш класс будет унаследовать статический $macros свойство массива. И используя статический macro Метод это назначит закрытие как callable к $name в качестве индекса $macros вот так.

public static function macro($name, $macro)
{
    static::$macros($name) = $macro;
}

Теперь, когда метод вызывается в этом классе, скажем makeKebab в нашем предыдущем примере, поскольку метод недоступен в классе, он вызовет PHP __вызов() магический метод. Вот воплощение __call() магический метод в Macroable черта характера.

public function __call($method, $parameters)
{
    if (! static::hasMacro($method)) {
        throw new BadMethodCallException(sprintf(
            'Method %s::%s does not exist.', static::class, $method
        ));
    }

    $macro = static::$macros($method);
    if ($macro instanceof Closure) {
        return call_user_func_array($macro->bindTo($this, static::class), $parameters);
    }
    
    return $macro(...$parameters);
}

То, что происходит здесь, сначала проверит, существует ли указанный метод в $macros свойство. Если нет, то выкинет BadMethodCallException и если он существует, он будет продолжать вызывать Closure(который является вторым параметром ::macro метод) с использованием call_user_func_array метод наряду с указанными параметрами.

В заключение

Макросы Laravel хороши, если вы хотите быстро раскрутить повторяющуюся логику в основных классах Laravel всякий раз, когда вы сталкиваетесь с дилеммой повторного использования вашего кода.