Php имеет магический метод под названием "__call". Когда вы сначала вызываете метод внутри класса, он вызывает метод вызова. Laravel использует этот метод для загрузки другого класса в классе. Таким образом, вы можете смоделировать вызов метода, как если бы вы помещали в него другие методы классов (что-то вроде наследования) Это сложно сделать, если у вас нет надежного способа реализации методов в классах.
В более ранних версиях Laravel этот метод использовался в красноречивом абстрактном классе:
public function __call($method, $parameters)
{
if (in_array($method, ['increment', 'decrement'])) {
return $this->$method(...$parameters);
}
return $this->newQuery()->$method(...$parameters);
}
Видите ли, когда вы вызываете метод гидрата, он копается в классе, а когда метод не существует, он загружает класс построителя запросов и вызывает метод внутри этого класса.
После 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. Вы хотите дать возможность разработчикам переопределять метод, а не другие методы, но вы не хотите делать метод закрытым.
Модель, например, Модель пользователя расширяется от 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 (). Потому что через Model вызывается только метод гидратов.
Не поймите меня неправильно, я не говорю, что невозможно переопределить getModels () в модели User. Я просто говорю, что вы не можете изменить это через красноречивый процесс извлечения данных. Если вы попытаетесь переопределить метод getModels () в модели User, а после этого, если вы создадите новый экземпляр модели User и попытаетесь вызвать метод getModels () через модель , он будет переопределен. Но он не будет переопределен в модели и не в классе Builder. Вы можете переопределить метод только в Builder, если этот метод вызывается из Model;