Данная статья документации актуальна только для версии 5.0 и была удалена в версии 5.1.
Управляющие и фабрики
Laravel содержит несколько классов PHPManager
, которые управляют созданием компонентов, основанных на драйверах. Эти компоненты включают в себя кэш, сессии, авторизацию и очереди. Класс-управляющий ответственен за создание конкретной реализации драйвера в зависимости от настроек приложения. Например, класс PHPCacheManager
может создавать объекты-реализации APC, Memcached, File и различных других драйверов кэша.
Каждый из этих управляющих классов имеет метод PHPextend()
, который может использоваться для простого добавления новой реализации драйвера. Мы поговорим об этих управляющих классах ниже, с примерами того, как добавить собственный драйвер в каждый из них.
Внимание: Уделите несколько минут изучению различных классов PHPManager
, которые поставляются с Laravel, таких как PHPCacheManager
и PHPSessionManager
. Знакомство с их кодом поможет вам лучше понять внутреннюю работу Laravel. Все классы-управляющие наследуют базовый класс PHPIlluminate\Support\Manager
, который реализует общую полезную функциональность для каждого из них.
Кэш
Для расширения подсистемы кэширования мы используем метод PHPextend()
класса PHPCacheManager
, который используется для привязки стороннего драйвера к управляющему классу и является общим для всех таких классов. Например, для регистрации нового драйвера кэша с именем mongo мы бы сделали следующее:
Cache::extend('mongo', function($app)
{
return Cache::repository(new MongoStore);
});
Первый параметр, передаваемый методу PHPextend()
— имя драйвера. Это имя соответствует значению параметра driver файла настроек config/cache.php. Второй параметр — функция-замыкание, которая должна вернуть объект типа PHPIlluminate\Cache\Repository
. Замыкание получит параметр PHP$app
— объект PHPIlluminate\Foundation\Application
и сервис-контейнер.
Вызов PHPCache::extend
может быть сделан в методе PHPboot()
в провайдере по умолчанию App\Providers\AppServiceProvider, который входит в новые приложения Laravel. Или вы можете создать свой сервис-провайдер для размещения расширения — только не забудьте зарегистрировать провайдер в массиве провайдеров в config/app.php.
Для создания стороннего драйвера для кэша мы начнём с реализации контракта PHPIlluminate\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);
});
Если вы задумались о том, куда поместить код вашего нового драйвера кэша — подумайте о публикации его через Packagist!. Либо вы можете создать пространство имён PHPExtensions
в вашей папке app. Однако держите в уме то, что Laravel не имеет жёсткой структуры папок и вы можете организовать свои файлы, как вам удобно.
Сессии
Расширение системы сессий Laravel собственным драйвером так же просто, как и расширение драйвером кэша. Мы вновь используем метод PHPextend()
для регистрации собственного кода:
Session::extend('mongo', function($app)
{
// Вернуть объект, реализующий SessionHandlerInterface
});
Где расширять сессии
Код расширения системы сессий следует поместить в метод PHPboot()
вашего AppServiceProvider.
Написание расширения сессий
Заметьте, что наш драйвер сессии должен реализовывать интерфейс PHPSessionHandlerInterface
. Этот интерфейс содержит несколько простых методов, которые нам нужно написать. Заглушка драйвера 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) {}
}
Эти методы не так легки в понимании, как методы драйвера кэша (PHPStoreInterface
), поэтому давайте пробежимся по каждому из них подробнее:
- Метод open обычно используется при открытии системы сессий, основанной на файлах. Так как Laravel поставляется с драйвером сессий file, вам почти никогда не понадобиться добавлять что-либо в этот метод. Вы можете оставить его пустым. Фактически, это просто плохое решение PHP, из-за которого мы должны написать этот метод (мы обсудим это ниже).
- Метод close(), аналогично методу
PHPopen()
, обычно также игнорируется. Для большей части драйверов он не требуется. - Метод read() должен вернуть строку — данные сессии, связанные с переданным
PHP$sessionId
. Нет необходимости сериализовать объекты или делать какие-то другие преобразования при чтении или записи данных сессии в вашем драйвере — Laravel делает это автоматически. - Метод write() должен связать строку
PHP$data
с данными сессии с переданным идентификаторомPHP$sessionId
, сохранив её в каком-либо постоянном хранилище, таком как MongoDB, Dynamo и др. - Метод destroy() должен удалить все данные, связанные с переданным
PHP$sessionId
, из постоянного хранилища. - Метод gc() должен удалить все данные, которые старее переданного
PHP$lifetime
(отпечатка времени Unix). Для самоочищающихся систем вроде Memcached и Redis этот метод может быть пустым.
Когда SessionHandlerInterface реализован, мы можем зарегистрировать драйвер в управляющем классе сессий:
Session::extend('mongo', function($app)
{
return new MongoHandler;
});
Когда драйвер сессий зарегистрирован, мы можем использовать драйвер mongo в нашем файле настроек config/session.php.
Внимание: Помните, если вы написали новый драйвер сессии, поделитесь им на Packagist!
Авторизация
Механизм авторизации может быть расширен тем же способом, что и кэш и сессии. Мы используем метод PHPextend()
, с которым вы уже знакомы:
Auth::extend('riak', function($app)
{
// Вернуть объект, реализующий Illuminate\Contracts\Auth\UserProvider
});
Реализации UserProvider ответственны только за то, чтобы получать реализацию Illuminate\Contracts\Auth\Authenticatable из постоянного хранилища, такого как MySQL, Riak и др. Эти два интерфейса позволяют работать механизму авторизации Laravel вне зависимости от того, как хранятся пользовательские данные и какой класс используется для их представления.
Давайте посмотрим на контракт 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);
}
Метод PHPretrieveById()
обычно получает числовой ключ, идентифицирующий пользователя — такой, как автоинкрементное числовое поле ID в СУБД MySQL. Метод должен возвращать объект-реализациюPHPAuthenticatable
, соответствующий переданному ID.
Метод PHPretrieveByToken()
получает пользователя по его уникальному PHP$identifier
и токену «запомнить меня» — PHP$token
, который хранится в поле PHPremember_token
. Как и предыдущий метод, он должен возвращать объект-реализацию PHPAuthenticatable
.
Метод PHPupdateRememberToken()
записывает новое значение PHP$token
в поле PHPremember_token
для указанного PHP$user
. Новый токен может быть как свежим, назначенным при удачном входе с опцией «запомнить меня», так и нулевым, когда пользователь выходит из приложения.
Метод PHPretrieveByCredentials()
получает массив данных, которые были переданы методу PHPAuth::attempt()
при попытке входа в систему. Этот метод должен запросить своё постоянное хранилище на наличие пользователя с совпадающими данными. Обычно этот метод выполнит SQL-запрос с проверкой на PHP$credentials['username']
. А затем он должен вернуть объект-реализацию UserInterface. Этот метод не должен производить сравнение паролей или выполнять вход.
Метод PHPvalidateCredentials()
должен сравнить переданный объект пользователя PHP$user
с данными для входа PHP$credentials
для того, чтобы его авторизовать. К примеру, этот метод может сравнивать строку PHP$user->getAuthPassword
с результатом вызова PHPHash::make()
на строке PHP$credentials['password']
. Этот метод должен только проверять данные пользователя и возвращать значение типа boolean.
Теперь, когда мы узнали о каждом методе интерфейса PHPUserProviderInterface
давайте посмотрим на интерфейс PHPAuthenticatable
. Как вы помните, провайдер должен вернуть реализацию этого интерфейса из своих методов PHPretrieveById()
и PHPretrieveByCredentials()
:
interface Authenticatable {
public function getAuthIdentifier();
public function getAuthPassword();
public function getRememberToken();
public function setRememberToken($value);
public function getRememberTokenName();
}
Это простой интерфейс. Метод PHPgetAuthIdentifier()
должен просто вернуть «первичный ключ» пользователя. Если используется хранилище MySQL, то это будет автоинкрементное числовое поле-первичный ключ. Метод PHPgetAuthPassword()
должен вернуть хэшированный пароль. Этот интерфейс позволяет системе авторизации работать с любым классом пользователя, вне зависимости от используемой ORM или хранилища данных. Изначально Laravel содержит класс User в папке app, который реализует этот интерфейс, поэтому вы можете обратиться к этому классу, чтобы увидеть пример реализации.
Наконец, когда мы написали класс-реализацию PHPUserProvider
, мы готовы зарегистрировать наше расширение в фасаде PHPAuth
:
Auth::extend('riak', function($app)
{
return new RiakUserProvider($app['riak.connection']);
});
Когда вы зарегистрировали драйвер методом PHPextend()
вы можете активировать его в вашем файле настроек config/auth.php.
Расширения на основе сервис-контейнера
Почти все сервис-провайдеры Laravel получают свои объекты из сервис-контейнера. Вы можете увидеть список провайдеров вашего приложения в файле config/app.php. Вам стоит пробежаться по коду каждого из поставщиков в свободное время — сделав это вы получите намного более чёткое представление о том, какую именно функциональность каждый из них добавляет к фреймворку, а также какие ключи используются для привязки различных услуг в сервис-контейнере.
Например, PHPHashServiceProvider
использует привязку ключа hash для получения экземпляра PHPIlluminate\Hashing\BcryptHasher
из сервис-контейнера. Вы можете легко расширить и перекрыть этот класс в вашем приложении перекрыв эту привязку. Например:
<?php namespace App\Providers;
class SnappyHashProvider extends \Illuminate\Hashing\HashServiceProvider {
public function boot()
{
parent::boot();
$this->app->bindShared('hash', function()
{
return new \Snappy\Hashing\ScryptHasher;
});
}
}
Заметьте, что этот класс наследует PHPHashServiceProvider
, а не базовый класс по умолчанию PHPServiceProvider
. Когда вы расширили провайдер, измените PHPHashServiceProvider
в файле настроек config/app.php на имя вашего нового поставщика услуг.
Это общий подход к расширению любого класса ядра, который привязан к контейнеру. Фактически каждый класс так или иначе привязан к нему, и с его помощью может быть перекрыт. Опять же, прочитав код включённых в фреймворк сервис-провайдеров, вы познакомитесь с тем, где различные классы привязываются к контейнеру и какие ключи для этого используются. Это отличный способ узнать, как работает Laravel.