我想知道laravel如何调用一个在类中和父类中都不存在的方法。例如,我查看了雄辩模型类,但从未在其中看到hydrate方法。模型类没有父类,但是当你调用hydrate方法时,它会填充模型的attribute属性。在挖掘源代码后,我在Builder.phpclass中找到了水合物类。那么laravel是如何设法在另一个类中调用这个方法的呢?是某种方法注射吗?
njthzxwz1#
PHP提供了一个神奇的方法,名为__call。如果调用了类中不存在的方法,则__call方法被触发。Laravel利用这种机制来模拟从另一个类调用方法,就好像它们是当前类的方法一样,这可以比作继承,但在实现上是不同的。这是一种微妙的方法,需要清楚地理解类中的方法实现。在以前的Laravel版本中,这是在Eloquent抽象类中使用的:
__call
public function __call($method, $parameters) { if (in_array($method, ['increment', 'decrement'])) { return $this->$method(...$parameters); } return $this->newQuery()->$method(...$parameters); }
在上面的代码中,如果调用hydrate方法,并且在当前类中找不到它,Laravel会求助于查询构建器类并在那里调用该方法。从Laravel 5.5版本开始,这个策略被修改了。引入了一个名为Illuminate\Support\Traits\ForwardsCalls的特性。这个trait包括forwardCallTo()方法:
hydrate
Illuminate\Support\Traits\ForwardsCalls
forwardCallTo()
protected function forwardCallTo($object, $method, $parameters) { try { return $object->{$method}(...$parameters); } catch (Error | BadMethodCallException $e) { $pattern = '~^Call to undefined method (?P<class>[^:]+)::(?P<method>[^\(]+)\(\)$~'; if (!preg_match($pattern, $e->getMessage(), $matches)) { throw $e; } if ($matches['class'] != get_class($object) || $matches['method'] != $method) { throw $e; } static::throwBadMethodCallException($method); } }
forwardCallTo()方法本质上是将一个类的方法调用委托给另一个类。因此,__call方法适用于:
public function __call($method, $parameters) { if (in_array($method, ['increment', 'decrement'])) { return $this->$method(...$parameters); } return $this->forwardCallTo($this->newQuery(), $method, $parameters); }
以下是这种架构选择的一个关键优势:假设您正在使用一个模型,比如User模型,它是从Eloquent\Model扩展而来的。虽然您可能希望允许开发人员重写某些方法而不将其变为私有,但此结构对可以重写的内容提供了一定程度的控制。举例来说,虽然User模型可以覆盖Eloquent\Model中的任何受保护或公共方法,但开发人员实际上只能访问和覆盖hydrate()方法,而不是getModels()方法,因为builder类中有以下代码:
User
Eloquent\Model
hydrate()
getModels()
public function getModels($columns = ['*']) { return $this->model->hydrate( $this->query->get($columns)->all() )->all(); }
虽然hydrate()方法主要位于Builder.php而不是Model.php中,但由于__call的魔力,它可以通过Model类访问。澄清一下,这并不意味着您不能在User模型中重写getModels()方法。可以,而且如果您直接通过User模型示例调用它,它将遵守您的重写。然而,在Eloquent的数据获取过程中,它不会在构建器类中被覆盖,因为它没有通过模型路由。简单地说,只有通过模型类调用构建器方法时,才可以重写模型中的构建器方法。
Builder.php
Model.php
Model
1条答案
按热度按时间njthzxwz1#
PHP提供了一个神奇的方法,名为
__call
。如果调用了类中不存在的方法,则__call
方法被触发。Laravel利用这种机制来模拟从另一个类调用方法,就好像它们是当前类的方法一样,这可以比作继承,但在实现上是不同的。这是一种微妙的方法,需要清楚地理解类中的方法实现。在以前的Laravel版本中,这是在Eloquent抽象类中使用的:
在上面的代码中,如果调用
hydrate
方法,并且在当前类中找不到它,Laravel会求助于查询构建器类并在那里调用该方法。从Laravel 5.5版本开始,这个策略被修改了。引入了一个名为
Illuminate\Support\Traits\ForwardsCalls
的特性。这个trait包括forwardCallTo()
方法:forwardCallTo()
方法本质上是将一个类的方法调用委托给另一个类。因此,__call
方法适用于:以下是这种架构选择的一个关键优势:假设您正在使用一个模型,比如
User
模型,它是从Eloquent\Model
扩展而来的。虽然您可能希望允许开发人员重写某些方法而不将其变为私有,但此结构对可以重写的内容提供了一定程度的控制。举例来说,虽然
User
模型可以覆盖Eloquent\Model
中的任何受保护或公共方法,但开发人员实际上只能访问和覆盖hydrate()
方法,而不是getModels()
方法,因为builder类中有以下代码:虽然
hydrate()
方法主要位于Builder.php
而不是Model.php
中,但由于__call
的魔力,它可以通过Model
类访问。澄清一下,这并不意味着您不能在
User
模型中重写getModels()
方法。可以,而且如果您直接通过User
模型示例调用它,它将遵守您的重写。然而,在Eloquent的数据获取过程中,它不会在构建器类中被覆盖,因为它没有通过模型路由。简单地说,只有通过模型类调用构建器方法时,才可以重写模型中的构建器方法。