{{TOC}} {{DOCVER 5.0=f6e862ba51a97f2bf9865f35fd5c522c14af93a1 31.07.2015 15:36:24}} .(alert) Данная статья документации актуальна только для версии 5.0 и была удалена в версии 5.1. == Управляющие и фабрики == Laravel содержит несколько классов %%Manager%%, которые управляют созданием компонентов, основанных на драйверах. Эти компоненты включают в себя кэш, сессии, авторизацию и ((docs/v5/queues очереди)). Класс-управляющий ответственен за создание конкретной реализации драйвера в зависимости от настроек приложения. Например, класс %%CacheManager%% может создавать объекты-реализации APC, Memcached, File и различных других драйверов кэша. Каждый из этих управляющих классов имеет метод %%extend()%%, который может использоваться для простого добавления новой реализации драйвера. Мы поговорим об этих управляющих классах ниже, с примерами того, как добавить собственный драйвер в каждый из них. .(alert) **Внимание:** Уделите несколько минут изучению различных классов %%Manager%%, которые поставляются с Laravel, таких как %%CacheManager%% и %%SessionManager%%. Знакомство с их кодом поможет вам лучше понять внутреннюю работу Laravel. Все классы-управляющие наследуют базовый класс %%Illuminate\Support\Manager%%, который реализует общую полезную функциональность для каждого из них. == Кэш == Для расширения подсистемы ((docs/v5/cache кэширования)) мы используем метод %%extend()%% класса %%CacheManager%%, который используется для привязки стороннего драйвера к управляющему классу и является общим для всех таких классов. Например, для регистрации нового драйвера кэша с именем %%(t)mongo%% мы бы сделали следующее: %% Cache::extend('mongo', function($app) { return Cache::repository(new MongoStore); }); %% Первый параметр, передаваемый методу %%extend()%% - имя драйвера. Это имя соответствует значению параметра %%(t)driver%% файла настроек %%(t)config/cache.php%%. Второй параметр - функция-замыкание, которая должна вернуть объект типа %%Illuminate\Cache\Repository%%. Замыкание получит параметр %%$app%% - объект %%Illuminate\Foundation\Application%% и сервис-контейнер. Вызов %%Cache::extend%% может быть сделан в методе %%boot()%% в провайдере по умолчанию %%(t)App\Providers\AppServiceProvider%%, который входит в новые приложения Laravel. Или вы можете создать свой сервис-провайдер для размещения расширения - только не забудьте зарегистрировать провайдер в массиве провайдеров в %%(t)config/app.php%%. Для создания стороннего драйвера для кэша мы начнём с реализации контракта %%Illuminate\Contracts\Cache\Store%%. Итак, наша реализация MongoDB будет выглядеть примерно так: %% class MongoStore implements Illuminate\Contracts\Cache\Store { 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. Как только мы это сделали, можно закончить регистрацию нового драйвера: %% Cache::extend('mongo', function($app) { return Cache::repository(new MongoStore); }); %% Если вы задумались о том, куда поместить код вашего нового драйвера кэша - подумайте о публикации его через ((http://packagist.org/ Packagist))!. Либо вы можете создать пространство имён %%Extensions%% в вашей папке %%(t)app%%. Однако держите в уме то, что Laravel не имеет жёсткой структуры папок и вы можете организовать свои файлы, как вам удобно. == Сессии == Расширение системы ((docs/v5/session сессий)) Laravel собственным драйвером так же просто, как и расширение драйвером ((#кэш+))а. Мы вновь используем метод %%extend()%% для регистрации собственного кода: %% Session::extend('mongo', function($app) { // Вернуть объект, реализующий SessionHandlerInterface }); %% === Где расширять сессии === Код расширения системы сессий следует поместить в метод %%boot()%% вашего %%(t)AppServiceProvider%%. === Написание расширения сессий === Заметьте, что наш драйвер сессии должен реализовывать интерфейс %%SessionHandlerInterface%%. Этот интерфейс содержит несколько простых методов, которые нам нужно написать. Заглушка драйвера 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 этот метод может быть пустым. Когда %%(t)SessionHandlerInterface%% реализован, мы можем зарегистрировать драйвер в управляющем классе сессий: %% Session::extend('mongo', function($app) { return new MongoHandler; }); %% Когда драйвер сессий зарегистрирован, мы можем использовать драйвер %%(t)mongo%% в нашем файле настроек %%(t)config/session.php%%. .(alert) **Внимание:** Помните, если вы написали новый драйвер сессии, поделитесь им на ((http://packagist.org/ Packagist))! == Авторизация == Механизм авторизации может быть расширен тем же способом, что и кэш и сессии. Мы используем метод %%extend()%%, с которым вы уже знакомы: %% Auth::extend('riak', function($app) { // Вернуть объект, реализующий Illuminate\Contracts\Auth\UserProvider }); %% Реализации %%(t)UserProvider%% ответственны только за то, чтобы получать реализацию %%(t)Illuminate\Contracts\Auth\Authenticatable%% из постоянного хранилища, такого как MySQL, Riak и др. Эти два интерфейса позволяют работать механизму авторизации Laravel вне зависимости от того, как хранятся пользовательские данные и какой класс используется для их представления. Давайте посмотрим на контракт %%(t)UserProvider%%: %% interface UserProvider { public function retrieveById($identifier); public function retrieveByToken($identifier, $token); public function updateRememberToken(Authenticatable $user, $token); public function retrieveByCredentials(array $credentials); public function validateCredentials(Authenticatable $user, array $credentials); } %% Метод %%retrieveById()%% обычно получает числовой ключ, идентифицирующий пользователя - такой, как автоинкрементное числовое поле ID в СУБД MySQL. Метод должен возвращать объект-реализацию%%Authenticatable%%, соответствующий переданному ID. Метод %%retrieveByToken()%% получает пользователя по его уникальному %%$identifier%% и токену "запомнить меня" - %%$token%%, который хранится в поле %%remember_token%%. Как и предыдущий метод, он должен возвращать объект-реализацию %%Authenticatable%%. Метод %%updateRememberToken()%% записывает новое значение %%$token%% в поле %%remember_token%% для указанного %%$user%%. Новый токен может быть как свежим, назначенным при удачном входе с опцией "запомнить меня", так и нулевым, когда пользователь выходит из приложения. Метод %%retrieveByCredentials()%% получает массив данных, которые были переданы методу %%Auth::attempt()%% при попытке входа в систему. Этот метод должен запросить своё постоянное хранилище на наличие пользователя с совпадающими данными. Обычно этот метод выполнит SQL-запрос с проверкой на %%$credentials['username']%%. А затем он должен вернуть объект-реализацию %%(t)UserInterface%%. **Этот метод не должен производить сравнение паролей или выполнять вход.** Метод %%validateCredentials()%% должен сравнить переданный объект пользователя %%$user%% с данными для входа %%$credentials%% для того, чтобы его авторизовать. К примеру, этот метод может сравнивать строку %%$user->getAuthPassword%% с результатом вызова %%Hash::make()%% на строке %%$credentials['password']%%. Этот метод должен только проверять данные пользователя и возвращать значение типа %%(t)boolean%%. Теперь, когда мы узнали о каждом методе интерфейса %%UserProviderInterface%% давайте посмотрим на интерфейс %%Authenticatable%%. Как вы помните, провайдер должен вернуть реализацию этого интерфейса из своих методов %%retrieveById()%% и %%retrieveByCredentials()%%: %% interface Authenticatable { public function getAuthIdentifier(); public function getAuthPassword(); public function getRememberToken(); public function setRememberToken($value); public function getRememberTokenName(); } %% Это простой интерфейс. Метод %%getAuthIdentifier()%% должен просто вернуть "первичный ключ" пользователя. Если используется хранилище MySQL, то это будет автоинкрементное числовое поле-первичный ключ. Метод %%getAuthPassword()%% должен вернуть хэшированный пароль. Этот интерфейс позволяет системе авторизации работать с любым классом пользователя, вне зависимости от используемой ((ВП:ORM)) или хранилища данных. Изначально Laravel содержит класс %%(t)User%% в папке %%(t)app%%, который реализует этот интерфейс, поэтому вы можете обратиться к этому классу, чтобы увидеть пример реализации. Наконец, когда мы написали класс-реализацию %%UserProvider%%, мы готовы зарегистрировать наше расширение в ((docs/v5/facades фасаде)) %%Auth%%: %% Auth::extend('riak', function($app) { return new RiakUserProvider($app['riak.connection']); }); %% Когда вы зарегистрировали драйвер методом %%extend()%% вы можете активировать его в вашем файле настроек %%(t)config/auth.php%%. == Расширения на основе сервис-контейнера == Почти все сервис-провайдеры Laravel получают свои объекты из сервис-контейнера. Вы можете увидеть список провайдеров вашего приложения в файле %%(t)config/app.php%%. Вам стоит пробежаться по коду каждого из поставщиков в свободное время - сделав это вы получите намного более чёткое представление о том, какую именно функциональность каждый из них добавляет к фреймворку, а также какие ключи используются для привязки различных услуг в сервис-контейнере. Например, %%HashServiceProvider%% использует привязку ключа %%(t)hash%% для получения экземпляра %%Illuminate\Hashing\BcryptHasher%% из сервис-контейнера. Вы можете легко расширить и перекрыть этот класс в вашем приложении перекрыв эту привязку. Например: %% app->bindShared('hash', function() { return new \Snappy\Hashing\ScryptHasher; }); } } %% Заметьте, что этот класс наследует %%HashServiceProvider%%, а не базовый класс по умолчанию %%ServiceProvider%%. Когда вы расширили провайдер, измените %%HashServiceProvider%% в файле настроек %%(t)config/app.php%% на имя вашего нового поставщика услуг. Это общий подход к расширению любого класса ядра, который привязан к контейнеру. Фактически каждый класс так или иначе привязан к нему, и с его помощью может быть перекрыт. Опять же, прочитав код включённых в фреймворк сервис-провайдеров, вы познакомитесь с тем, где различные классы привязываются к контейнеру и какие ключи для этого используются. Это отличный способ узнать, как работает Laravel.