{{TOC}} {{DOCVER 4.0=0da300f6445bec5a70d007f503834fce957b065b 16.10.2014 5:19:26, 4.1=efd541a0b218b1c6aafb73f0051c18ed150e3c24 25.05.2014 6:21:03, 4.2=d7b13440c003218ed79e9d508706eca01990122f 4.12.2014 5:01:15}} == Введение == Laravel предоставляет вам множество точек для настройки поведения различных частей ядра библиотеки или даже полной замены. К примеру, ((docs/v4/security хэширующие функции)) определены контрактом (интерфейсом) %%HasherInterface%%, который вы можете реализовать в зависимости от требований вашего приложения. Вы также можете расширить объект %%Request%%, добавив собственные удобные вспомогательные методы (//helpers//). Вы даже можете добавить новый драйвер авторизации, ((docs/v4/cache кэширования)) и ((docs/v4/session сессии))! Расширение компонентов Laravel происходит двумя основными способами: привязка новой реализации через ((docs/v4/ioc контейнер IoC)) и регистрация расширения через класс %%Manager%%, который реализует шаблон проектирования "Фабрика". В этом разделе мы изучим различные методы расширения фреймворка и код, который для этого необходим. .(alert) **Подсказка:** компоненты Laravel обычно расширяются через ((docs/v4/ioc IoC)) или классы %%Manager%% - они служат реализацией "фабрик" и ответственны за работу подсистем, основанных на драйверах, таких как кэширование и сессии. == Управляющие и фабрики == Laravel содержит несколько классов %%Manager%%, которые управляют созданием компонентов, основанных на драйверах. Эти компоненты включают в себя кэш, сессии, авторизацию и ((docs/v4/queues очереди)). Класс-управляющий ответственнен за создание конкретной реализации драйвера в зависимости от настроек приложения. Например, класс %%CacheManager%% может создавать объекты-реализации APC, Memcached, **file** (!!(tl_note)вместо **native** в 4.0 - //прим. пер.//!!) и различных других драйверов. Каждый из этих управляющих классов имеет метод %%extend()%%, который может использоваться для простого добавления новой реализации драйвера. Мы поговорим об этих управляющих классах ниже, с примерами о том, как добавить собственный драйвер в каждый из них. .(alert) **Подсказка:** посвятите несколько минут изучению различных классов %%Manager%%, которые поставляются с Laravel, таких как %%CacheManager%% и %%SessionManager%%. Знакомство с их кодом поможет вам лучше понять внутреннюю работу Laravel. Все классы-управляющие наследуют базовый класс %%Illuminate\Support\Manager%%, который реализует общую полезную функциональность для каждого из них. == Где расширять == Эта документация описывает, как расширять различные компоненты Laravel, но вы можете задуматься - //где// разместить код расширения. Как и большую часть остального кода начальной загрузки, вы можете поместить некоторые расширения в ваши старт-файлы. Расширения //Кэша// и //Авторизации// являются хорошими кандидатами для такого подхода. Другие расширения, такие как //Сессии//, должны быть размещены в методе %%register()%% поставщика услуг, так как они необходимы в самом начале обработки запроса. == Кэш == Для расширения подсистемы ((docs/v4/cache кэширования)) мы используем метод %%extend()%% класса %%CacheManager%%, который используется для привязки стороннего драйвера к управляющему классу и является общим для всех таких классов. Например, для регистрации нового драйвера кэша с именем %%(t)mongo%% мы бы сделали следующее: %% Cache::extend('mongo', function ($app) { // Вернуть объект типа Illuminate\Cache\Repository... }); %% Первый параметр, передаваемый методу %%extend()%% - имя драйвера. Это имя соответствует значению параметра %%(t)driver%% файла настроек %%(t)app/config/cache.php%%. Второй параметр - функция-замыкание, которая должна вернуть объект типа %%Illuminate\Cache\Repository%%. Замыкание получит параметр %%$app%% - объект %%Illuminate\Foundation\Application%% и ((docs/v4/ioc IoC-контейнер)). Для создания стороннего драйвера для кэша мы начнём с реализации контракта (интерфейса) %%Illuminate\Cache\StoreInterface%%. Итак, наша реализация MongoDB будет выглядеть примерно так: %% class MongoStore implements Illuminate\Cache\StoreInterface { public function get($key) {} public function put($key, $value, $minutes) {} public function increment($key, $value = 1) {} public function decrement($key, $value = 1) {} public function forever($key, $value) {} public function forget($key) {} public function flush() {} } %% Нам только нужно реализовать каждый из этих методов с использованием подключения к MongoDB. Как только мы это сделали, можно закончить регистрацию нового драйвера: %% use Illuminate\Cache\Repository; Cache::extend('mongo', function ($app) { return new Repository(new MongoStore); }); %% Как вы видите в примере выше, можно использовать базовый класс %%Illuminate\Cache\Repository%% при создании нового драйвера для кэша. Обычно не требуется создавать собственный класс хранилища. Если вы задумались о том, куда поместить ваш новый драйвер - подумайте о публикации его через ((http://packagist.org/ Packagist)). Либо вы можете создать пространство имён %%Extensions%% в основной папке вашего приложения. Например, если оно называется "Snappy", вы можете поместить драйвер кэша в %%(е)app/Snappy/Extensions/MongoStore.php%%. Однако держите в уме то, что Laravel не имеет жёсткой структуры папок и вы можете организовать свои файлы, как вам удобно. .(alert) **Подсказка:** если у вас стоит вопрос о том, где должен располагаться определённый код, в первую очередь вспомните о поставщиках услуг. Как мы уже говорили, их использование для упорядочивания расширений библиотеки очень здорово упорядочивает их код. == Сессии == Расширение системы ((docs/v4/session сессий)) Laravel собственным драйвером так же просто, как и расширение драйвером ((#кэш+))а. Мы вновь используем метод %%extend()%% для регистрации собственного кода: %% Session::extend('mongo', function ($app) { // Вернуть объект, реализующий SessionHandlerInterface... }); %% === Где расширять сессии == Расширения сессий должны быть зарегистрированы иначе, чем другие расширения, такие как кэш и авторизация. Поскольку сессии используются на самых ранних этапах обработки запроса, регистрация расширений в старт-файле произойдет слишком поздно. Вместо этого,будет необходим ((/docs/v4/ioc#поставщики поставщик услуг)). Вы должны поместить ваш код расширения сессий в метод %%register()%% поставщика услуг, а поставщик должен быть помещен **ниже** поставщика по умолчанию %%(t)Illuminate\Session\SessionServiceProvider%% в массиве настроек %%(t)providers%%. === Написание расширения сессий == Заметьте, что наш драйвер сессии должен реализовывать интерфейс %%SessionHandlerInterface%%. Он включен в ядро PHP 5.4+. Если вы используете PHP 5.3, то Laravel создаст его для вас, что позволит поддерживать совместимость будущих версий. Этот интерфейс содержит несколько простых методов, которые нам нужно написать. Заглушка драйвера MongoDB выглядит так: %% class MongoHandler implements SessionHandlerInterface { public function open($savePath, $sessionName) {} public function close() {} public function read($sessionId) {} public function write($sessionId, $data) {} public function destroy($sessionId) {} public function gc($lifetime) {} } %% Эти методы не так легки в понимании, как методы драйвера кэша (%%StoreInterface%%), поэтому давайте пробежимся по каждому из них подробнее: * Метод **open** обычно используется при открытии системы сессий, основанной на файлах. Так как Laravel поставляется с драйвером сессий %%(t)file%%, вам почти никогда не понадобиться добавлять что-либо в этот метод. Вы можете всегда оставить его пустым. Фактически, это просто плохое решение PHP, из-за которого мы должны написать этот метод (мы обсудим это ниже). * Метод **close()**, аналогично методу %%open()%%, обычно также игнорируется. Для большей части драйверов он не требуется. * Метод **read()** должен вернуть строку - данные сессии, связанные с переданным %%$sessionId%%. Нет необходимости сериализовать объекты или делать какие-то другие преобразования при чтении или записи данных сессии в вашем драйвере - Laravel делает это автоматически. * Метод **write()** должен связать строку %%$data%% с данными сессии с переданным идентификатором %%$sessionId%%, сохранив её в каком-либо постоянном хранилище, таком как MongoDB, Dynamo и др. * Метод **destroy()** должен удалить все данные, связанные с переданным %%$sessionId%%, из постоянного хранилища. * Метод **gc()** должен удалить все данные, которые старее переданного %%$lifetime%% (отпечатка времени Unix). Для самоочищающихся систем вроде Memcached и Redis этот метод может быть пустым. Как только интерфейс %%SessionHandlerInterface%% был реализован мы готовы к тому, чтобы зарегистрировать новый драйвер в управляющем классе %%Session%%: %% Session::extend('mongo', function ($app) { return new MongoHandler; }); %% Как только драйвер сессии зарегистрирован мы можем использовать его имя %%(t)mongo%% в нашем файле настроек %%(t)app/config/session.php%%. .(alert) **Подсказка:** если вы написали новый драйвер сессии, поделитесь им на ((http://packagist.org/ Packagist))! == Авторизация == Механизм авторизации может быть расширен тем же способом, что и кэш и сессии. Мы используем метод %%extend()%%, с которым вы уже знакомы: %% Auth::extend('riak', function ($app) { // Вернуть объект, реализующий Illuminate\Auth\UserProviderInterface... }); %% Реализация %%UserProviderInterface%% ответственна только за то, чтобы получать нужный объект %%UserInterface%% из постоянного хранилища, такого как MySQL, Riak и др. Эти два интерфейса позволяют работать механизму авторизации Laravel вне зависимости от того, как хранятся пользовательские данные и какой класс используется для их представления. Давайте посмотрим на определение %%UserProviderInterface%%: %% interface UserProviderInterface { public function retrieveById($identifier); public function retrieveByToken($identifier, $token); public function updateRememberToken(UserInterface $user, $token); public function retrieveByCredentials(array $credentials); public function validateCredentials(UserInterface $user, array $credentials); } %% Метод %%retrieveById()%% обычно получает числовой ключ, идентифицирующий пользователя - такой, как автоувеличивающееся числовое поле ID в СУБД MySQL. Метод должен возвращать объект %%UserInterface%%, соответствующий переданному ID. %%(DOCNEW 4.2=d7b13440c003218ed79e9d508706eca01990122f 4.12.2014 5:01:15) **Добавлены %%retrieveByToken%% и %%updateRememberToken%%.** Метод %%retrieveByToken()%% получает пользователя по его уникальному %%$identifier%% и токену "запомнить меня" - %%$token%%, который хранится в поле %%remember_token%%. Как и предыдущий метод, он должен возвращать объект %%UserInterface%%. Метод %%updateRememberToken()%% записывает новое значение %%$token%% в поле %%remember_token%% для указанного %%$user%%. Новый токен может быть как свежим, назначенным при удачном входе с опцией "запомнить меня", так и нулевым, когда пользователь выходит из приложения. %% Метод %%retrieveByCredentials()%% получает массив данных, которые были переданы методу %%Auth::attempt()%% при попытке входа в систему. Этот метод должен запросить своё постоянное хранилище на наличие пользователя с совпадающими данными. Обычно этот метод выполнит SQL-запрос с проверкой на %%$credentials['username']%%. **Этот метод не должен производить сравнение паролей или выполнять вход.** Метод %%validateCredentials()%% должен сравнить переданный объект пользователя %%$user%% с данными для входа %%$credentials%% для того, чтобы его авторизовать. К примеру, этот метод может сравнивать строку %%$user->getAuthPassword%% с результатом вызова %%Hash::make()%% на строке %%$credentials['password']%%. Теперь, когда мы узнали о каждом методе интерфейса %%UserProviderInterface%% давайте посмотрим на интерфейс %%UserInterface%%. Как вы помните, поставщик должен вернуть реализацию этого интерфейса из своих методов %%retrieveById()%% и %%retrieveByCredentials()%%. %% interface UserInterface { public function getAuthIdentifier(); public function getAuthPassword(); } %% Это простой интерфейс. Метод %%getAuthIdentifier()%% должен просто вернуть "первичный ключ" пользователя. Если используется хранилище MySQL, то это будет автоматическое числовое поле-первичный ключ. Метод %%getAuthPassword()%% должен вернуть хэшированный пароль. Этот интерфейс позволяет системе авторизации работать с любым классом пользователя, вне зависимости от используемой ((ВП:ORM)) или хранилища данных. Изначально Laravel содержит класс %%User%% в папке %%(t)app/models%%, который реализует этот интерфейс, поэтому вы можете обратиться к этому классу, чтобы увидеть пример реализации. Наконец, как только мы написали класс-реализацию %%UserProviderInterface%%, у нас готово для регистрации расширения в ((docs/v4/facades фасаде)) %%Auth%%: %% Auth::extend('riak', function ($app) { return new RiakUserProvider($app['riak.connection']); }); %% Когда вы зарегистрировали драйвер методом %%extend()%% вы можете активировать его в вашем файле настроек %%(t)app/config/auth.php%%. == Расширения посредством IoC == Почти каждый поставщик услуг Laravel получает свои объекты из ((docs/v4/ioc контейнера IoC)). Вы можете увидеть список поставщиков в вашем приложения в файле %%(t)app/config/app.php%%. Вам стоит пробежаться по коду каждого из поставщиков в свободное время - сделав это вы получите намного более чёткое представление о том, какую именно функциональность каждый из них добавляет к фреймворку, а также какие ключи используются для регистрации различных услуг в контейнере IoC. Например, %%HashServiceProvider%% использует ключ %%(t)hash%% для получения экземпляра %%Illuminate\Hashing\BcryptHasher%% из контейнера IoC. Вы можете легко расширить и перекрыть этот класс в вашем приложении перекрыв эту привязку. Например: %% class SnappyHashProvider extends Illuminate\Hashing\HashServiceProvider { public function boot() { App::bindShared('hash', function() { return new Snappy\Hashing\ScryptHasher; }); parent::boot(); } %% Заметьте, что этот класс расширяет %%HashServiceProvider%%, а не класс %%ServiceProvider%% по умолчанию. Как только вы расширили этого поставщика, измените %%HashServiceProvider%% в файле настроек %%(t)app/config/app.php%% на имя вашего нового поставщика услуг. Это общий подход к расширению любого класса ядра, который привязан к контейнеру. Фактически каждый класс так или иначе привязан к нему, и с его помощью может быть перекрыт. Опять же, прочитав код включённых в библиотеку поставщиков услуг вы познакомитись с тем, где различные классы привязываются к контейнеру и какие ключи для этого используются. Это отличный способ понять глубинную работу Laravel. == Расширение запроса == Расширение класса %%Request%% происходит немного иначе в отличии от описанных ранее из-за того, что это основополагающая часть фреймворка и создаётся в самом начале обработки запроса. Для начала расширьте класс, как это обычно делается: %%