開發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)