这段代码的工作原理和原因是什么?

时间:2017-02-20 16:45:48

标签: php laravel vue.js traits

我的问题基于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;
}

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

在控制器中他执行类似

的操作
$model = App\Customer::paginateAndOrder();

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

  1. 为什么我可以使用 :: 以静态方法方式调用此非静态方法?
  2. 为什么我可以从方法名称中省略范围
  3. 我不必将Query Builder对象作为参数传递。那么特质如何知道"我希望在哪个模型上分页/订购
  4. 感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

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

Laravel利用magic functions

所知的内容
  
      
  1. 为什么我可以使用::?
  2. 以静态方法方式调用这个非静态方法   

我只会在这里列出两个__call()__callStatic(),当你分别调用不存在的非静态静态函数时会调用这些函数。

所以在Illuminate\Database\Eloquent\Model这是所有模型的超类都这样做,请检查link

  
      
  1. 为什么我可以省略方法名称中的范围?'
  2.   

Simply Laravel允许您省略表示其功能的函数名称的前缀。例如。在您的情况下scope前缀..并继续按特定顺序添加前缀,直到找到它为止。 如果您需要仔细查看,请阅读code

  
      
  1. 我不必将Query Builder对象作为参数传递。那么特质如何知道"我希望在哪个模型上分页/订购
  2.   

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

注意:Q3需要更多的研究才能确定&如何注射。

答案 1 :(得分:1)

Laravel充分利用了PHP的一些神奇方法,特别是在这种情况下__call()__callStatic

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

使用callStatic,如果调用的静态方法不存在或不可访问,则调用将委托给该类中的__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

希望这有帮助!