Laravel深刻学习7 - 框架的扩展

声明:本文并不是博主原创,而是来自对《Laravel 4 From Apprentice to Artisan》阅读的翻译和理解,固然也不是原汁原味的翻译,能保证90%的原汁性,另外由于是理解翻译,确定会有错误的地方,欢迎指正。php

欢迎转载,转载请注明出处,谢谢!数据库

框架的扩展

介绍

Laravel为咱们提供了不少自定义系统组件的扩展点,你甚至能够彻底的替换掉他们。好比,哈希结构是由HasherInterface接口约定的,你能够根据你的应用来实现本身的需求。你也能够扩展本身的Request对象,并添加本身的“辅助”方法。甚至咱们彻底添加本身的认证、缓存、SESSION驱动。bootstrap

Laravel组件扩展一般有两种方法:向IoC容器中绑定本身的接口实现;痛过使用“工厂模式”实现的Manager类注册本身的扩展。本章将探索多种扩展框架的方法,以及必要的测试代码。数组

扩展方式缓存

牢记Laravel组件扩展的两种方式:IoC容器绑定和使用Manager类注册。类库管理类以工厂模式实现,负责诸如缓存、session等驱动的实例化。session

类库管理类和工厂方法

Laravel有诸多Manager类库来管理建立那些基于驱动的组件。它包括缓存、session、认证、队列组件。管理类的职责是根据应用的配置来建立特殊的驱动实例。例如,CacheManager能够建立APC、Memcached、Native、以及其余缓存驱动系统的实现。闭包

全部这些管理器都有个extend方法,能够轻易的将新的驱动功能注入到其中。对于上面全部的管理器,咱们经过实例来演示如何注入咱们自定义的那些服务驱动。app

学习咱们本身的管理器框架

请花一些事件来熟悉下Laravel提供的Manager类库,如CacheManagerSessionManager。通读这些代码可让你加深理解。全部的管理器类都继承自IlluminateSupportManager基类,他为每一个具体的管理器提供了不少常见使用的方法。ide

缓存 Cache

咱们经过在CacheManager类中的extend方法绑定自定义的驱动解析器来扩展Laravel的缓存机制。好比,注册一个“mongo”驱动到缓存驱动中,代码以下:

Cache::extend('mongo', function($app)
{
    // Return IlluminateCacheRepository instance...
});

传入method方法的第一个参数是驱动名称。一般和app/config/app.php配置文件中的driver选项匹配。第二个参数是返回IlluminateCacheRepository实例的闭包。闭包需要传入继承自IlluminateFoundationApplication和IoC容器的实例化对象$app

对于咱们自定义的缓存驱动,要实现IlluminateCacheStoreInterface接口的约定。所以,咱们的MongoDB缓存实现代码为这样:

class MongoStore implements IlluminateCacheStoreInterface {

    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 IlluminateCacheRepository;

Cache::extend('mongo', function($app)
{
    return new Repository(new MongoStore);
}

如上可见,咱们可以使用IlluminateCacheRepository来直接建立咱们自定义的缓存驱动,而无需建立本身的(Repository)仓库类。

若是不知道该把代码放在哪里,能够考虑放到Packagist!或者在应用主目录下建立一个Extensions命名空间的目录存放。例如你的应用起名叫个Snappy,能够把这个扩展放到app/Snappy/Extensions/MongoStore.php。Laravel建立的应用没有死板的各类结构的要求,根据你的喜爱本身来组织就好。

何处引入扩展

若是你还在考虑该在何处引入扩展,不妨继续使用服务提供器。咱们已经讨论过这是组织咱们代码的一个利器,要善用利器。

Session 会话

扩展Session机制其实像扩展缓存机制同样简单。一样,使用extend方法来注册咱们本身的驱动:

Session::extend('mongo', function($app)
{
    // Return implementation of SessionHandlerInterface
});

注意,咱们自定义的驱动需要实现SessionHandlerInterface接口。此接口是被包含在PHP5.4+核心中的。若是你在使用PHP5.3,Laravel也是向前兼容的,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基于以PHP自身的本地存储实现了native会话驱动方式,你基本上不须要在本方法中添加任何东西了。你能够把他留空。PHP这种接口设计很显然是一种很差的设计方式(咱们后续讨论)。

  • close方法同open方法,一般不用理会。大多数驱动都不须要他。

  • read方法返回根据$sessionId变量关联session数据的字符串。这里不须要进行序列化或者其余类型的转义,Laravel已经帮你进行了处理。

  • write方法根据$sessionId关联session数据将$data数据写入持久化存储系统中,如MongoDB、Dynamo等。

  • gc方法将根据$lifetime指定UNIX时间戳销毁以前全部的session数据。对于能够自动删除过时数据的系统好比Memcached或者Redis,该方法留空便可。

当实现SessionHandlerInterface以后,咱们就能够向Session管理器中注册他了:

Session::extend('mongo', function($app)
{
    return new MongoHandler;
});

当会话驱动注册以后,在配置文件app/config/session.php中指定mongo配置就能使用咱们自定义的session驱动了。

分享你的成果

记住,若是你实现了本身的session驱动,能够在Packagist上分享给你们!

认证

相似缓存和会话扩展,咱们继续从method方法开始:

Session::extend('mongo', function($app)
{
    return new MongoHandler;
});

接口UserProviderInterface的实现指责是从各类持久化存储系统如MySQL、Riak等中获取数据,并返回接口UserInterface实现的对象。这两个接口可让Laravel专一于验证自己,而无需关心用户数据的存储实现,以及用户对象是那种类来表示的问题。

让咱们先看一下UserProviderInterface接口:

interface UserProviderInterface {

    public function retrieveById($identifier);
    public function retrieveByCredentials(array $credentials);
    public function validateCredentials(UserInterface $user, array $credentials);

}

retrieveById方法一般接收的参数是一个惟一标识符,例如MySQL数据库中的主键自增索引ID。该方法将获取$identifier对应的数据,并返回接口UserInterface实现类的实例化对象。

当用户试图登陆系统时,retrieveByCredentials方法接收参数是一个验证数组,同时他也是传入Auth::attempt的参数。该方法会“查询”给定的用户验证数据,一般,它会根据$credentials['username']运行一个“查询”条件语句来匹配数据。请不要用本方法进行任何密码的校验或验证。

方法validateCredentials会经过对比$user$credentials来验证用户。例如,方法中会对$user->getAuthPassword();获得的字符串和$credentials['password']经过Hash::make加密后的结果进行对比。

上面咱们探索了UserProviderInterface中的各个方法,接下来看一看UserInterface接口。记住,上面提供器中的retrieveByIdretrieveByCredentials方法返回的就是本接口实现类的实例化对象:

interface UserInterface {

    public function getAuthIdentifier();
    public function getAuthPassword();

}

接口很简单。getAuthIdentifier返回用户的“主键索引”。在MySQL后台存储系统中,这里就指代用户表的主键自增索引。getAuthPassword返回用户密码的哈希值。这种接口的实现方式使认证系统和User类彻底分离,并能在各类类型的实现下正常工做,而不用关心咱们用的是ORM方式仍是其余存储层实现的方式。在app/models下,Laravel已经实现了该接口的User类,你能够参考下这个类。

实现接口UserProviderInterface后,咱们就作好了将咱们的扩展注册进Auth门面的准备:

Auth::extend('riak', function($app)
{
    return new RiakUserProvider($app['riak.connection']);
});

method注册以后,咱们就能在app/config/auth.php中切换新的验证驱动了。

基于IoC容器的扩展

几乎全部Laravel框架包含的服务提供器都是做为对象绑定到IoC容器中的。在配置文件app/config/app.php中有详细的列表。有时间的话,最好过一遍源码。这样,你能了解框架都加载了哪些服务,也就是容器中绑定的各式的服务。

好比,PaginationServiceProviderpaginator绑定到容器中,他对应的是IlluminatePaginationEnvironment的实例。你能够很容易的经过扩展重写这些类并从新绑定到容器中。又如,咱们来扩展下基础的Environment类:

namespace SnappyExtensionsPagination;

class Environment extends IlluminatePaginationEnvironment {

    //

}

扩展完成以后,在建立一个新的SnappyPaginationProvider服务提供器,并在boot方法中替换掉原有的paginator:

class SnappyPaginationProvider extends PaginationServiceProvider {

    public function boot()
    {
        App::bind('paginator', function()
        {
            return new SnappyExtensionsPaginationEnvironment;
        }

        parent::boot();
    }

}

注意,本类继承的PaginationServiceProvider,并非默认的ServiceProvider。当完成扩展以后要记住app/config/app.php中替换成本身的扩展名称。

这就是对容器中已经绑定的核心类库进行扩展的方法。实际上,全部核心类库都能用这种方式进行重写。深刻阅读源码,能帮你熟练知晓Laravel是怎么将各个部分组织在一块儿运转的。

请求的扩展

由于在每一个请求的生命周期中,请求是很早就会被实例化的一个最基础的部分,因此Request的扩展会有稍许不一样。

首先,仍是像往常同样实现一个子类:

namespace QuickBillExtensions;

class Request extends IlluminateHttpRequest {

    // Custom, helpful methods here...

}

而后,打开bootstrap/start.php,它是请求到达应用时最先被包含的文件。注意这里被执行的第一个动做就是建立Laravel的实例化对象$app

$app = new IlluminateFoundationApplication;

当对象被建立,同时也会建立IlluminateHttpRequest实例,并以request键绑定到IoC容器中。这里,咱们应该另辟途径实现一个类来做为“默认的”请求类型,对不对?还好,requestClass方法就能帮咱们那实现它!因此咱们须要在bootstrap/start.php文件开头加上这样一行:

use IlluminateFoundationApplication;

Application::requestClass('QuickBillExtensionsRequest');

当添加玩自定义的请求类后,Laravel在任何地方都能用到咱们的这个自定义的Request实例,这能使咱们实现的定义请求类的实例可用,甚至在单元测试中也能使用。

相关文章
相关标签/搜索