这段代码如何以及为何起作用?

How and why does this code work?

我的问题是基于Data Viewer with Laravel 5.3 and Vue.js

这个家伙创建了一个特征 DataViewer 并将其编码为类似于以下内容:

trait DataViewer 
{
    // $query should be a Query Builder
    public function scopePaginateAndOrder($query)
    {
        // Validation ...

        // Where, Pagination, Order etc ...
        return $query->where('foo', 'like', 'bar');
    }
}

现在您可以在任何 Eloquent 模型中使用此特征来添加搜索功能。

class Customer extends Model
{
    use DataViewer;
}

一切都很平常,没什么特别的... 但是还有一些 "magic" 我还没有在 PHP 中看到。

在控制器中他做了类似

的事情
$model = App\Customer::paginateAndOrder();

最后一个代码片段确实有很多我无法理解的方面。

  1. 为什么我可以使用 :: 以静态方法的方式调用这个非静态方法?
  2. 为什么我可以从方法名称中省略 scope
  3. 我不必将查询生成器对象作为参数传递。那么我想要 paginate/order
  4. 的特征 "know" 如何

感谢大家的帮助!

这就是 Laravel 的魔力.. 根本不是 php。

Laravel 利用了 magic functions

的已知信息
  1. Why can i call this non-static-method in a static-method way using ::?

我这里只列出两个__call()__callStatic(),这两个函数分别在调用non-existentnon-static静态函数时被调用..

所以在 Illuminate\Database\Eloquent\Model 中,它是所有模型的超级 class,检查这个 link

  1. Why can i omit the scope from the method name?'

简单地 Laravel 允许您省略代表其功能的函数名称的前缀。例如。 scope 前缀在你的情况下..并继续以特定顺序添加前缀直到找到它。 如果您想进一步了解,请阅读 code

  1. I don't have to pass a Query Builder object as parameter. So how does the trait "know" on which model i want to paginate/order

我认为这是由于 Laravel IoC 容器和依赖注入..

注意:第 3 季度需要更多的研究来确定注射的时间和方式。

Laravel 很好地利用了 PHP 的一些神奇方法,特别是在本例中 __call()__callStatic.

http://php.net/manual/en/language.oop5.overloading.php#object.callstatic

使用 callStatic,如果调用的静态方法不存在或不可访问,则调用将委托给 class 中的 __callStatic() 方法(如果一个存在)。 __call()和实例方法也是如此。

如果您查看 Illuminate\Database\Eloquent\Model,您会发现:

/**
 * Handle dynamic method calls into the model.
 *
 * @param  string  $method
 * @param  array  $parameters
 * @return mixed
 */
public function __call($method, $parameters)
{
    if (in_array($method, ['increment', 'decrement'])) {
        return call_user_func_array([$this, $method], $parameters);
    }

    $query = $this->newQuery();

    return call_user_func_array([$query, $method], $parameters);
}

/**
 * Handle dynamic static method calls into the method.
 *
 * @param  string  $method
 * @param  array  $parameters
 * @return mixed
 */
public static function __callStatic($method, $parameters)
{
    $instance = new static;

    return call_user_func_array([$instance, $method], $parameters);
}

在上面,如果 __callStatic 被调用,那么它将创建模型的 new 实例并尝试调用该实例的方法,所以当你调用 App\Customer::paginateAndOrder()然后它将尝试调用 paginateAndOrder() 作为实例方法。

(不包括 incrementdecrement)模型上的 __call() 方法将尝试在 Illuminate\Database\Eloquent\Builder 上调用该方法。 Builder 然后有它自己的 __call() 方法,其中包含以下内容:

if (method_exists($this->model, $scope = 'scope' . ucfirst($method))) {
    return $this->callScope([$this->model, $scope], $parameters);
}

callScope 然后调用实际的 scopePaginateAndOrder 并传递 Builder

希望对您有所帮助!