装饰器和链接方法

时间:2015-11-09 21:52:59

标签: php laravel decorator method-chaining

目标:我想用其他功能“装饰”Laravel的查询生成器(不直接修改它)。

示例问题:我会尽量保持简短。我在装饰器上实现了get方法:

public function get($columns = ['*'])
{
    return $this->cache->get(implode('.', $columns), function () use ($columns) {
        return $this->queryBuilder->get($columns);
    });
}

我也将对未在装饰器上实现的方法的所有调用委托给查询生成器。

public function __call($method, $parameters)
{
    return call_user_func_array([$this->queryBuilder, $method], $parameters);
}

直接在装饰器上调用时工作正常,正如您所期望的那样。但是,在使用查询生成器时,几乎每个人都习惯将方法链接在一起。

$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查询生成器的实例,而不是我的装饰器。

问题:是否有一些有用的模式可以帮助解决这个问题?或者这是装饰模式的限制?

我的第一个想法是尝试将$ this绑定到另一个对象,就像在Javascript中一样。我认为PHP不允许这样做。

我能提出的最佳解决方案涉及一个类,用于将查询构建器对象映射到其装饰器,和/或某种基本装饰器,它几乎重新实现了查询构建器对象中的每个方法(不是这一个的粉丝,因为它完全抛出了DRY原则窗外)。

附加说明:我知道我可以通过不联系方法调用来解决问题。没问题吧?除了要求团队中的每个开发人员避免将他们的呼叫链接在一起是不合理的。我宁愿解决这个问题而不是支持它。

1 个答案:

答案 0 :(得分:2)

您应该从__call方法返回装饰器:

public function __call($method, $parameters)
{
    $result = call_user_func_array([$this->queryBuilder, $method], $parameters);

    return $result === $this->queryBuilder ? $this : $result;
}

如果你正在使用PHP 5.6+,你可以使用spread运算符来清理它:

public function __call($method, $parameters)
{
    $result = $this->queryBuilder->$method(...$parameters);

    return $result === $this->queryBuilder ? $this : $result;
}