Laravel eloquent: 关系如何从查询生成器继承函数?

Laravel eloquent: How does relation inherit functions from query builder?

他们如何实现 Illuminate\Database\Eloquent\Relations\Relation 可以访问所有查询构建器功能?

我看到他们有一个 $query 属性,但没有解释它的所有方法如何在关系中可用

如果您知道 php magic methods then you will know __call method 当您初始化 php 对象并尝试调用 class 中不可用的方法时,将调用此方法。通过使用 Illuminate\Database\Eloquent\Relations\Relation class 的 __call 方法 ,他们将呼叫转发给 Illuminate\Database\Eloquent\Builder。我会通过指出代码来解释得很清楚。

在 laravel 框架中有一个名为 ForwardsCalls 的特征。许多 classes 中使用此特征来处理呼叫转移到另一个 class.

下面是来自 Relation class 的呼叫如何转发到 Builder class。在启动新关系时 class Builder class 将是 initialized. So when you try to call a method from reltion class which is not available it will call __call method. After that it will look for a available macros . So when a macros method is not found. Then it will use forwardDecoratedCallTo from ForwardsCalls Trait.

因此 forwardDecoratedCallTo 将接受 3 个参数,即 $object, $method and $parameters。而

  1. $object 将是 $this->query,它有一个 Illuminate\Database\Eloquent\Builder 实例。
  2. $method 将是您尝试从 Builder Method 访问的方法。
  3. $parameters 将是传递给该方法的所有参数。

我将尝试演示没有 laravel

中的特征和助手的示例
class ClassTwo {
  
public function classTwoMethodOne()
    {
      dd(__FUNCTION__.' has been called');
    }  
  
  public function classTwoMethodTwo()
    {
      dd(__FUNCTION__.' has been called');
    }  

  public function classTwoMethodThree()
    {
    //you cannot call this method dynamically 
      dd(__FUNCTION__.' has been called');
    }  

}


class ClassOne {
  
public function classOneMethodOne()
    {
      dd(__FUNCTION__.' has been called');
    }  
  
  public function classOneMethodTwo()
    {
      dd(__FUNCTION__.' has been called');
    }  

  public function __call($methodName, $arguments)
    {
    
    $methodsTobeForwarededtoClassTwo = [
        'classTwoMethodOne',
        'classTwoMethodTwo',
      // 'classTwoMethodThree' 
      //i have commented this method so you cannot access it 
      //from dynamic calls
    ];
    if(in_array($methodName,$methodsTobeForwarededtoClassTwo))
    {
      return (new ClassTwo)->{$methodName}($arguments);
    }
             dd(sprintf(
            'Call to undefined method ClassTwo::%s()',  $methodName
        ));
    }
}

所以测试部分来了。

$classOneobj = new ClassOne;

Basic Test

  1. dump($classOneobj->classOneMethodOne()); 将输出为 classOneMethodOne 已被调用
  2. dump($classOneobj->classOneMethodTwo()); 将输出为 classOneMethodTwo 已被调用

Dynamic Call Test

  1. dump($classOneobj->classTwoMethodOne()); 将输出为 classTwoMethodOne 已被调用
  2. dump($classOneobj->classTwoMethodTwo()); 将输出为 classOneMethodTwo 已被调用
  3. dump($classOneobj->classTwoMethodThree()); 将输出为 Call to undefined method ClassTwo::classTwoMethodThree() 因为我在 __call 函数中评论了那个方法 ClassOne.

如果您还需要澄清,请post发表评论