laravel如何调用不在类和父类中的方法

问题描述 投票:1回答:1

我想知道laravel如何调用父类中既不存在于类中的方法。

例如,我查看了雄辩的模型类,从未在其中看到过水合物方法。模型类没有父级,但是当你调用水合物方法时,它会填充模型的属性属性。在挖掘源代码后,我在Builder.php类中找到了hydrate类。那么laravel如何设法在另一个类中调用该方法?它是某种方法注射吗?

php laravel lumen function-call method-call
1个回答
5
投票

Php有一种叫做“__call”的神奇方法。当您首先在类内部调用方法时,它会触发调用方法。 Laravel使用该方法在类中加载另一个类。这样你可以模拟方法调用,就像你将另一个类方法放入该类(类似继承)。如果你没有在类中实现方法的可靠方法,这是一件很棘手的事情。

在早期版本的Laravel中,它在雄辩的抽象类中使用此方法:

 public function __call($method, $parameters)
 {
        if (in_array($method, ['increment', 'decrement'])) {
            return $this->$method(...$parameters);
        }

        return $this->newQuery()->$method(...$parameters);
  }

你看到当你调用hydrate方法时,它会深入到类中,当方法不存在时,它会加载查询构建器类并调用该类中的方法。

在Laravel 5.5之后,他们改变了这个策略并添加了一个名为“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);
}

在解释之后,我想指出这种编码方式的好处。假设你有一个从雄辩/模型中扩展出来的模型。您希望让开发人员能够覆盖方法而不是其他方法,但您不希望将该方法设为私有。例如用户模型的模型从Eloquent / Model扩展,对吧?所以它可以覆盖所有受保护和公共方法。但是这样开发人员只能访问hydrate()方法,而不是说getModels()方法。因为在构建器类中我们有这段代码:

public function getModels($columns = ['*'])
{
    return $this->model->hydrate(
        $this->query->get($columns)->all()
    )->all();
}

在这个方法中,Laravel调用了hydrate()方法抛出模型类。但是hydrate()方法实际上是Builder.php中的一种方法,而不是Model.php中的方法。总而言之,在我之前解释过的那个过程之后,开发人员可以覆盖水合物方法而不是例如getModels()方法。因为只通过模型调用水合物方法。

不要误解我的意思,我不是说在User模型中覆盖getModels()是不可能的。我只是说你不能通过雄辩的数据处理来改变它。如果您尝试覆盖User模型中的getModels()方法,之后,如果您创建User模型的新实例并尝试通过模型调用getModels()方法,则将覆盖它。但它不会在模型中被覆盖,也不会在Builder类中被覆盖。如果通过Model调用该方法,则只能覆盖Builder中的方法;

© www.soinside.com 2019 - 2024. All rights reserved.