刚开始看了laravel "系统架构"这一章后,感觉很难懂,不知道service provider、service container、Facade等等名词讲的是什么鬼。直到自己摸索写出了一个elastic search的服务扩展才恍然大悟。
其实,laravel service provider、service container、Facade 讲的都是一件事儿:你可以自由扩展laravel框架的服务。 貌似还是有点懵~
首先你要了解一点:laravel的核心其实是一个IOC容器!laravel框架提供的服务,其实都是后来才注入到容器中的!例如你熟悉的方法“DB::select()”、“Cache::put()”等等中的DB、Cache等服务都是“核心”之外的东西,都是后来才注入到laravel框架中的!如下所有的laraval框架自带的服务,都是注入到laravel核心框架后,才可以使用的!
也就是说:你可以自己定义一个服务,然后放入框架中!
下面以elasticsearch为例,注册一个elasticsearch 服务到laravel框架中。
1、“service provider” 是提供服务的东东,你想提供什么服务,就在这里面定义。
我们先在composer.json中添加elasticsearch 需要的依赖。然后使用:php artisan make:provider ElasticSearchProvider创建一个“ElasticSearchProvider”;在register中添加上你想要实例化的类,即为laravel可提供的服务。推荐使用App的singleton方法,因为很明显“单例”,或者使用其他的例如“bind”等方法,IDE上自行查阅。
/** * Register the application services. * * @return void */ public function register() { $value = config('database.elasticsearch')['hosts']; $this->app->singleton('Elasticsearch\Client',function($value) use ($value){ return ClientBuilder::create()->setHosts( [$value])->build(); }); }
然后,把所写的这个provider在config目录下的app.php配置文件中的provider 那一栏做一下注册,laravel框架中就有你的服务啦!
2、你想要达到从.env取路径或者配置的效果?就像是laravel框架原生的服务一样?
也很简单,从.env里取东西,只需调用方法"env('key')",例如你在.env文件中定义了如下内容:
#elasticsearch 配置 ElasticSearch_HOST=elasticsearch ElasticSearch_PORT=9200你想取出来,就使用:“env("ElasticSearch_HOST")”和“env(ElasticSearch_PORT)”
但是这样会出现一个问题:由于".env"文件通常是不加入版本管理的。
/vendor /node_modules /public/storage Homestead.yaml Homestead.json .env .idea _ide_helper.php storage bootstrap docker这样一来,程序就会报错。怎么办呢?细心的你可能会发现laravel自带的DB等服务,都是在config目录下的配置文件中取出.env文件中的内容的:
/* |-------------------------------------------------------------------------- | Redis Databases |-------------------------------------------------------------------------- | | Redis is an open source, fast, and advanced key-value store that also | provides a richer set of commands than a typical key-value systems | such as APC or Memcached. Laravel makes it easy to dig right in. | */ 'redis' => [ 'cluster' => false, 'default' => [ 'host' => env('REDIS_HOST', 'localhost'), 'password' => env('REDIS_PASSWORD', null), 'port' => env('REDIS_PORT', 6379), 'database' => 0, ], ],我们当然可以效仿,定义一个elasticsearch配置文件。在config目录下或者写在已有的配置文件中(本例子就是),然后在配置文件中写"return
'hosts' => env('ElasticSearch_HOST',"elasticsearch").":".env("ElasticSearch_PORT","9200")这里的env方法如果取不到“ElasticSearch_HOST”或者“ElasticSearch_PORT”,就会默认将第二个参数返回即“elasticsearch”和“9200”,这样一来,就解决了.env文件加入版本忽略而下来代码后取不到值得问题。然后想要取配置文件中的内容的时候,使用“config”方法:例如你想取elasticsearch中的"hosts"中的内容 如上图所示。你就可以写:config("elasticsearch.hosts")。本例子是将elasticsearch的配置写在 database.php中的:
/* |-------------------------------------------------------------------------- | elasticsearch 相关的配置 |-------------------------------------------------------------------------- | | */ 'elasticsearch' => [ 'hosts' => env('ElasticSearch_HOST',"elasticsearch").":".env("ElasticSearch_PORT","9200") ],所以在取值的时候是这样的:
$value = config('database.elasticsearch')['hosts'];3、好了,还有最后一步,你发现使用"DB::search()"等方式十分的简便:使用一个“代号”,来代表一个服务,然后直接使用这个服务,十分方便!下面就将ElasticSearch服务用“ES”来代替。
我原以为laraval框架既然支持大家自定义服务,会专门弄一个项目目录来存放Facade,就像controller目录来放置控制器一样,但是很遗憾,没找到!好吧,那只能自定义一个目录来放Facade了,我在App目录下自定义了一个Facade目录,然后写一个了Facade。
class ES extends Facade { protected static function getFacadeAccessor() { return 'Elasticsearch\Client'; } }写Facade也类也很简单:首先继承laravel框架的Illumiate\Support\Facades\Facade接口,然后重写"getFacadeAccessor方法,返回你在service provider “register”方法中注册的那个类的名字即可。(就是$this->app->singleton(类的名字,function(){...}),请自行看本文的步骤一 service provider的那张图)
最后,你在config目录下的app.php中的最后一行“aliases”配置项中填上"ES" => 对应的Facade目录下的ES类
'Route' => Illuminate\Support\Facades\Route::class, 'Schema' => Illuminate\Support\Facades\Schema::class, 'Session' => Illuminate\Support\Facades\Session::class, 'Storage' => Illuminate\Support\Facades\Storage::class, 'URL' => Illuminate\Support\Facades\URL::class, 'Validator' => Illuminate\Support\Facades\Validator::class, 'View' => Illuminate\Support\Facades\View::class, 'ES' => 刚才自定义的Facade的类的地址 ],
好了,大功完成!是不是很简单!laravel的扩展性真心不错!