Цель: Я хочу «украсить» Laravel Query Builder дополнительными функциями (без непосредственного изменения).
Пример задачиЯ постараюсь сделать это очень кратким. Я реализовал get
Метод на моем декораторе:
public function get($columns = ['*'])
{
return $this->cache->get(implode('.', $columns), function () use ($columns) {
return $this->queryBuilder->get($columns);
});
}
Я также делегирую все вызовы методов, не реализованных в декораторе, в Query Builder.
public function __call($method, $parameters)
{
return call_user_func_array([$this->queryBuilder, $method], $parameters);
}
Работает нормально при вызове непосредственно на декоратор, как и следовало ожидать. Но почти все привыкли объединять методы в цепочки при использовании Query Builder.
$queryBuilder = (new CachingDecorator( new QueryBuilder , $app['cache.store'] ));
// get all users
$queryBuilder->from('users')->get();
// get one user
$queryBuilder->from('users')->first(); // <-- delegates to get() internally
проблема: Результаты вызова выше не кэшируются. Очевидно, потому что from
Метод возвращает экземпляр Laravel Query Builder, а не мой декоратор.
Вопрос: Есть ли полезная модель, которая поможет решить эту проблему? Или это ограничение шаблона декоратора?
Моей первой мыслью было попытаться связать $ this с другим объектом, как вы можете это сделать в Javascript. Я не думаю, что PHP позволяет это.
Лучшее решение, которое я могу придумать, включает в себя класс для сопоставления объекта построителя запросов с его декоратором (-ами) и / или своего рода базовый декоратор, который повторно реализует почти каждый метод в объекте построителя запросов (не фанат этого один, поскольку он полностью выбрасывает принцип СУХОЙ в окно).
Дополнительные примечанияЯ знаю, что могу обойти проблему, просто не объединяя вызовы методов. Ежу понятно, верно? За исключением того, что нет смысла просить каждого разработчика в команде избегать цепочки своих вызовов вместе. Я бы предпочел решать эта проблема, чем обойти его.
Вы должны вернуть свой декоратор из __call
метод:
public function __call($method, $parameters)
{
$result = call_user_func_array([$this->queryBuilder, $method], $parameters);
return $result === $this->queryBuilder ? $this : $result;
}
Если вы используете PHP 5.6+, вы можете использовать оператор распространения, чтобы немного это исправить:
public function __call($method, $parameters)
{
$result = $this->queryBuilder->$method(...$parameters);
return $result === $this->queryBuilder ? $this : $result;
}
Других решений пока нет …