使用 Laravel 的服務容器來優化讀寫數據庫中的 options

原文地址:https://prinzeugen.net/optimi...php

這篇文章應該算是心得體會一類的吧,由於並無什麼卵用(笑laravel

就像前兩篇文章說的同樣,在我把項目框架改成 Laravel 後,天然要最大限度地利用 Laravel 的特性來提高咱們應用的性能(雖然使用 Laravel 自己就是在下降性能了),而且讓咱們的代碼看起來更優雅 裝逼數據庫

其中咱們能夠最方便地利用的一個特性就是 Laravel 的服務容器了。在這裏我很少贅述 Service Container 是個啥,想了解的能夠自行搜索。不想了解的就只要大體知道它是個能夠 綁定/取出 實例的東西就行了(固然服務容器可不止這麼點功能)。數組

相信不少 Web 應用都會在數據庫創建一個 options 表來儲存一些用戶的配置信息,而爲了方便,咱們一般會封裝一個 Option 類來方便地進行數據的 getset 操做。而一般的作法是把這些操做作成類內靜態方法來調用。app

可是這樣的弊端就是每一次的操做都要去查詢數據庫,這對於性能仍是有挺大影響的,尤爲是在一次響應中要使用不少 options 的狀況下。框架

那麼在 Laravel 下咱們能夠怎麼優化呢?ide

蛤?你說 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 的開發者們有所啓示~

相關文章
相關標籤/搜索