在咱們的項目中,使用到了大量的attribute函數,來改善咱們在面向業務時的調用方便和代碼層面的優雅,可是一樣也帶來的必定的問題,會產生大量的重複計算和SQL調用,在應用上的性能上形成了一擊。php
咱們來演示一下這個過程,首先定義出一個attribute,這樣咱們能夠方便經過的$user->posts_count,拿到用戶發表的動態總數,像比原來的寫法更加具備語義化,也更優雅了不少。緩存
public function getPostsCountAttribute() { return $this->posts()->count(); }
可是遇到下面這種狀況,就會糟糕了不少,會產生屢次的SQL查詢,來形成服務端響應的緩慢app
if($user->posts_count > 30){ //處理一 } if($user->posts_count > 50){ //處理二 } if($user->posts_count > 100){ //處理三 } if($user->posts_count > 200){ //處理四 }
像上面這樣的寫法的,咱們可能會形成4次SQL聚合運算,從而影響到咱們的響應速度,固然咱們也能夠嘗試改進代碼規範來解決這個問題,可是仍然不能保證,這個屬性被第二次、三次持續調用,那麼方便、快捷的辦法就是添加一層屬性緩存,將獲取後的值緩存到模型屬性上。框架
使用屬性緩存的優點就是簡單、快捷,無需藉助第三方擴展,採用的是面嚮對象語言的原生優點,下面來實現一下:函數
在咱們Larvel框架的項目中,model都是繼承於Eloquent\Model
這個基類,咱們從新複寫該基類操做比較繁瑣,因此能夠採用php爲了實現多繼承的trait來實現。post
首先實現一個AttributeCacheHelper
的trait性能
<?php namespace App\Traits; trait AttributeCacheHelper { private $cachedAttributes = []; public function getCachedAttribute(string $key, callable $callable) { if (!array_key_exists($key, $this->cachedAttributes)) { $this->setCachedAttribute($key, call_user_func($callable)); } return $this->cachedAttributes[$key]; } public function setCachedAttribute(string $key, $value) { return $this->cachedAttributes[$key] = $value; } public function refresh() { unset($this->cachedAttributes); return parent::refresh(); } }
主要是實現了三個函數,get和set用於獲取和設置屬性緩存,refresh重寫了父類的刷新函數,每次刷新後都會清理掉對象緩存。this
而後咱們開始從新修改一下咱們的attribute,來添加對象緩存,首先是須要在Model上使用AttributeCacheHelper
這個traitspa
public function getPostsCountAttribute() { $method = 'postsCount'; $callable = [$this, $method]; return $this->getCachedAttribute($method, $callable); } public function postsCount() { return $this->posts()->count(); }
修改完成後,咱們再從新應用到場景中,就算執行一萬次,也只會產生1次SQL查詢,極大的提升了查詢效率和響應速度。代碼規範
for($i=0;$i<10000;$i++){ // 僅產生一次真實查詢,其他所有讀取對象緩存數據 dump($user->posts_count); }
最後 happy coding 。