我对Laravel 4很陌生。到目前为止,我仍在努力了解外观,依赖注入和IoC究竟是什么。 (我一直在阅读很多关于他们的内容,我仍然在那里"试图弄清楚" 阶段。)
我相信如果有人可以向我解释为什么我们可以写(类似于红宝石类的方法),我会突然意识到我是多么愚蠢并且理解一切。
$post = Post::where('slug', '=', $slug)->first();
像这样
$post = $this->post->where('slug', '=', $slug)->first();
之后
protected $post;
public function __construct(Post $post)
{
parent::__construct();
$this->post = $post;
}
让我感到困惑的是,我们正在访问静态方法Post::where('slug', '=', $slug)->first()
,但在一些实现/魔术之后,我们可以像使用->
的实例方法一样访问它。
这就像我们刚刚将一个类放入一个对象中。
但我觉得他们不是真正的静态方法(经过一些我尚未完全理解的文章之后)。
这个外观和/或一些IoC技巧在起作用吗?这是怎么回事?
答案 0 :(得分:3)
这很简单: Facade ,实际上,服务定位器。它在IoC容器内定位实例化服务(对象),并在动态调用对象方法时转换对Facade的静态调用。所以,你是对的,它们并不是静态的,实际上它们是静态调用,但随后它们被转发为动态调用。我们来看看这个过程:
这是由服务提供商完成的,但这只是一个约定,因为您可以在需要时在IoC容器中实例化和填充对象。这是一个基本的服务提供商:
class MailerServiceProvider extends ServiceProvider {
public function register()
{
$this->app->bindShared('mailer-ioc-alias', function($app)
{
return new Mailer();
});
}
}
如您所见,register方法只是将一个闭包绑定到名为mailer-ioc-alias
的IoC容器中。一旦我们的应用程序需要访问我们的邮件程序,就会运行该关闭,实例化我们的邮件程序并将其处理回调用者。此调用与以下内容完全相同:
App::bindShared('mailer-ioc-alias', function($app)
{
return new Mailer();
});
或者,因为我们的Mailer没有依赖关系并且IoC容器足够聪明,可以按类名实例化对象,甚至
App::bindShared('mailer-ioc-alias', 'Mailer');
但是,如果你的类有依赖关系,你可能会被迫使用那个闭包:
App::bindShared('mailer-ioc-alias', function($app)
{
$mailService = new Mailgun();
return new Mailer($mailService);
});
因此,例如,如果您将其中一行放在routes.php文件中,它将完全相同。你甚至不需要服务提供商。设计不好,但也许有助于理解服务提供商所做的事情非常简单。服务提供者和您的服务一样复杂,如果它有很多依赖关系,您的服务提供者可能会有很多实例化调用的方法。
如上所述,Facade几乎不执行任何操作,它基本上采用服务别名(mailer-ioc-alias),向IoC Container询问与之相关的实例,并在其收到的实例上调用动态方法。这是一个完整的Facade扩展:
class MailerFacade extends Facade {
protected static function getFacadeAccessor()
{
return 'mailer-ioc-alias';
}
}
正如您所看到的,它只有一种方法,可以返回您的服务的IoC别名。这个类是Illuminate\Support\Facades\Facade
的扩展,它有这个PHP魔术方法,负责将静态调用转换为动态调用:
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();
switch (count($args))
{
case 0:
return $instance->$method();
case 1:
return $instance->$method($args[0]);
case 2:
return $instance->$method($args[0], $args[1]);
case 3:
return $instance->$method($args[0], $args[1], $args[2]);
case 4:
return $instance->$method($args[0], $args[1], $args[2], $args[3]);
default:
return call_user_func_array(array($instance, $method), $args);
}
}
它通过getFacadeRoot()
获取您的服务实例,该实例将在内部调用您的getFacadeAccessor()
,并在其上执行动态方法。
所以,这个静态调用:
Mailer::send($user, 'myview');
实际上是一个动态的电话,非常伪装成静态。
但您可以在系统中的任何位置动态调用您的邮件程序:
App::make('mailer-ioc-alias')->send($user, 'myView');
或者使用app()
全局函数,该函数返回应用程序的实例:
app()->make('mailer-ioc-alias')->send($user, 'myView');
在服务提供商和其他一些Laravel课程中,您还可以通过以下方式访问它:
$this->app->make('mailer-ioc-alias')->send($user, 'myView');
所有这些动态调用正是Facade为您内部所做的事情。
Eloquent模型的“问题”是它们不使用Facades,它们是不使用IoC容器的完整静态类。当你打电话:
$post = Post::where('slug', '=', $slug);
它在内部实例化:
$instance = new static;
与
相同$instance = new Post;
并自行返回,to->provide->that->nesting->we->all->love
:
return $this;
但是如果你看一下代码,你就会看到这个神奇的方法:
public static function __callStatic($method, $parameters)
{
$instance = new static;
return call_user_func_array(array($instance, $method), $parameters);
}
这一行
return call_user_func_array(array($instance, $method), $parameters);
只有一个参数,可以翻译成
$instance->{$method}($parameters[0]);
return $instance;
它以这种方式做事,所以你进一步:
$post->where('user_id', $user->id);
因为它总是回归自己,所以你可以嵌套来电:
$post->where('user_id', $user->id)->where('region', 'US');
它是有状态的,所以你可以继续这样做:
$post->orderBy('region');
或
foreach(Input::all() as $property => $value)
{
$post->where($property, 'LIKE', "%{$value}%");
}
直到你:
$result = $post->get();
或者
$result = $post->first();
当它构建并执行查询时,返回集合或模型。
因此,第一次调用它实际上是静态的,但所有后续调用都被发送到实例化对象。