这篇文章应该算是心得体会一类的吧,因为并没有什么卵用(笑
就像前两篇文章说的一样,在我把项目框架改为 Laravel 后,自然要最大限度地利用 Laravel 的特性来提升我们应用的性能(虽然使用 Laravel 本身就是在降低性能了),并且让我们的代码看起来更优雅 装逼。
其中我们可以最方便地利用的一个特性就是 Laravel 的服务容器了。在这里我不多赘述 Service Container 是个啥,想了解的可以自行搜索。不想了解的就只要大致知道它是个可以 绑定/取出 实例的东西就好了(当然服务容器可不止这么点功能)。
相信很多 Web 应用都会在数据库建立一个 options
表来储存一些用户的配置信息,而为了方便,我们通常会封装一个 Option
类来方便地进行数据的 get
、set
操作。而通常的做法是把这些操作做成类内静态方法来调用。
但是这样的弊端就是每一次的操作都要去查询数据库,这对于性能还是有挺大影响的,尤其是在一次响应中要使用很多 options 的情况下。
那么在 Laravel 下我们可以怎么优化呢?
蛤?你说 Eloquent?你 TM 长点脑子啊 用了 ORM 那 tm 还叫优化?你们呀,不要听的风是得雨,看到 Laravel 就想到 Eloquent!
好吧好吧,再强行 +1s 是要出事的,我们回到正题。没错,我们正是要把 options
放到 Laravel 的服务容器里去。
这样的话我们只需要在应用启动的时候实例化一个 OptionRepository
类,在构造函数里读入所有的 options
并放到类内属性上,get
方法直接从该属性里取值,而调用 set
操作的时候则对该属性进行修改,同时 push 修改过的 key
到一个 $items_modified
数组里去,在对象析构的时候再真正执行数据库操作,写入所有修改过的 options
。
下面我们来定义 OptionRepository
类:
<?php
namespace App\Services;
use DB;
use ArrayAccess;
use Illuminate\Support\Arr;
use Illuminate\Contracts\Config\Repository as ConfigContract;
class OptionRepository implements ArrayAccess, ConfigContract
{
/**
* All of the option items.
*
* @var array
*/
protected $items = [];
protected $items_modified = [];
/**
* Create a new option repository.
*
* @return void
*/
public function __construct()
{
$options = DB::table('options')->get();
foreach ($options as $option) {
$this->items[$option->option_name] = $option->option_value;
}
}
/**
* Determine if the given option value exists.
*
* @param string $key
* @return bool
*/
public function has($key)
{
return Arr::has($this->items, $key);
}
/**
* Get the specified option value.
*
* @param string $key
* @param mixed $default
* @return mixed
*/
public function get($key, $default = null)
{
return Arr::get($this->items, $key, $default);
}
/**
* Set a given option value.
*
* @param array|string $key
* @param mixed $value
* @return void
*/
public function set($key, $value = null)
{
if (is_array($key)) {
foreach ($key as $innerKey => $innerValue) {
Arr::set($this->items, $innerKey, $innerValue);
$this->items_modified[] = $innerKey;
}
} else {
Arr::set($this->items, $key, $value);
$this->items_modified[] = $key;
}
}
protected function save()
{
$this->items_modified = array_unique($this->items_modified);
foreach ($this->items_modified as $key) {
if (!DB::table('options')->where('option_name', $key)->first()) {
DB::table('options')
->insert(['option_name' => $key, 'option_value' => $this[$key]]);
} else {
DB::table('options')
->where('option_name', $key)
->update(['option_value' => $this[$key]]);
}
}
}
/**
* Prepend a value onto an array option value.
*
* @param string $key
* @param mixed $value
* @return void
*/
public function prepend($key, $value)
{
$array = $this->get($key);
array_unshift($array, $value);
$this->set($key, $array);
}
/**
* Push a value onto an array option value.
*
* @param string $key
* @param mixed $value
* @return void
*/
public function push($key, $value)
{
$array = $this->get($key);
$array[] = $value;
$this->set($key, $array);
}
/**
* Get all of the option items for the application.
*
* @return array
*/
public function all()
{
return $this->items;
}
/**
* Determine if the given option option exists.
*
* @param string $key
* @return bool
*/
public function offsetExists($key)
{
return $this->has($key);
}
/**
* Get a option option.
*
* @param string $key
* @return mixed
*/
public function offsetGet($key)
{
return $this->get($key);
}
/**
* Set a option option.
*
* @param string $key
* @param mixed $value
* @return void
*/
public function offsetSet($key, $value)
{
$this->set($key, $value);
}
/**
* Unset a option option.
*
* @param string $key
* @return void
*/
public function offsetUnset($key)
{
$this->set($key, null);
}
/**
* Save all modified options into database
*/
public function __destruct()
{
$this->save();
}
}
可以看到我们顺便实现了 ArrayAccess
接口,所以我们在拿到 OptionRepository
的实例后就可以使用类似于 $option['fuck']
的形式来获取数据了(其实我后来一次也没用到)。
不过光是实现了一个 Repository
还是不够的,我们还需要把它绑定到服务容器里,同时注册个 Facade
给它,让我们能够更优雅地调用仓库类的相关方法:
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
// 绑定单例到服务容器上
$this->app->singleton('option', \App\Services\OptionRepository::class);
}
}
新建一个 Option
类并集成 Laravel 的 Facade
基类:
<?php
namespace App\Services\Facades;
use \Illuminate\Support\Facades\Facade;
class Option extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'option';
}
}
然后我们在 config/app.php
中加入我们 Facade 的别名:
<?php
return [
'aliases' => [
'Option' => App\Services\Facades\Option::class
],
];
大功告成!我们现在又可以像以前那样方便地使用类似于 Option::get('shit')
的方法获取数据,而且又大幅减少了数据库操作,是不是很棒呢~ (=゚ω゚)=
好吧其实本文也没讲啥有用的东西,不过希望对从其他框架迁移到 Laravel 的开发者们有所启示~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。