開發application常常要對資料庫做各種不同條件的查詢。
這類的查詢code很容易到處重複。
以 Laravel Eloquent來說,controller很容易出現這樣的code:
$users = User::where('votes', '>', 100) ->whereGender('W') ->orderBy('created_at') ->get();
這段「取出受歡迎的女性使用者」的code,在中小型application還可以接受,
一旦規模稍大就會在不同controller到處重複(duplicate)。
利用內建的query scope功能,雖然能夠稍微改善,但還是不夠:
$users = User::popular()->women()->orderBy('created_at')->get();
不只如此,還會讓model裡面多出定義query scope的程式碼,導致model變胖:
class User extends Eloquent { public function scopePopular($query) { return $query->where('votes', '>', 100); } public function scopeWomen($query) { return $query->whereGender('W'); } }
這種時候,適合寫一個Repository進行封裝:
class UserRepository { public function getPopularWomen() { return User::where('votes', '>', 100)->whereGender('W')->orderBy('created_at')->get(); } }
它會讓controller內的code簡潔、可讀許多:
$repository = new UserRepository(); $users = $repository->getPopularWomen();
如果您是 Laravel 使用者,這種搭配Automatic Resolution的寫法一定經常看到:
class UserController extends BaseController { protected $users; public function __construct(UserRepository $repository) { parent::__construct(); $this->users = $repository; } public function getIndex() { $women = $this->users->getPopularWomen(); } }
甚至可以寫一個共用的abstract class減少duplicate code:
model = $model; } public function getById($id) { return $this->model->find($id); } public function getAll() { return $this->model->all(); } public function save($data) { if ($data instanceOf Model) { return $this->storeEloquentModel($data); } } public function saveMany($collection) { foreach($collection as $model) { $this->storeEloquentModel($model); } } public function delete($model) { return $model->delete(); } protected function storeEloquentModel($model) { if ($model->getDirty()) { return $model->save(); } else { return $model->touch(); } } }
透過Repository封裝還有一個好處:
測試controller或是service的時候可以抽換掉Repository,讓測試時不用碰到資料庫!
下次你的application包含大量query logic時,不妨試試看這個作法。
(Photo via Susanne Nilsson, CC licensed)