Laravel 4 - 子构造函数通过依赖注入调用父构造函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19326855/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Laravel 4 - Child constructor call parent constructor with dependency injection
提问by ArranJacques
I'm building a CMS using Laravel 4 and I have a base admin controller for the admin pages that looks something like this:
我正在使用 Laravel 4 构建一个 CMS,并且我有一个用于管理页面的基本管理控制器,如下所示:
class AdminController extends BaseController {
public function __construct(UserAuthInterface $auth, MessagesInterface $message, ModuleManagerInterface $module)
{
$this->auth = $auth;
$this->user = $this->auth->adminLoggedIn();
$this->message = $message;
$this->module = $module;
}
}
Im using Laravel's IOC container to inject the class dependencies into the constructor. I then have various controller classes that control the different modules that make up the CMS and each class extends the admin class. For example:
我使用 Laravel 的 IOC 容器将类依赖项注入到构造函数中。然后我有各种控制器类来控制构成 CMS 的不同模块,每个类都扩展了管理类。例如:
class UsersController extends AdminController {
public function home()
{
if (!$this->user)
{
return Redirect::route('admin.login');
}
$messages = $this->message->getMessages();
return View::make('users::home', compact('messages'));
}
}
Now this works perfectly however my problem, which is less of a problem and more of a efficiency issue, occurs when I add a constructor to the UsersController
class. For example:
现在这很完美,但是当我向UsersController
类添加构造函数时,会出现我的问题,这不是问题而是效率问题。例如:
class UsersController extends AdminController {
public function __construct(UsersManager $user)
{
$this->users = $users;
}
public function home()
{
if (!$this->user)
{
return Redirect::route('admin.login');
}
$messages = $this->message->getMessages();
return View::make('users::home', compact('messages'));
}
}
Since the child class now has a constructor it means the parent's constructor isn't getting called and thus things the child class is dependant on, such as this->user
are no longer valid, causing errors. I can call the admin controller's construct function via parent::__construct()
however since I need to pass it the class dependencies I need to set these dependencies in the child constructor resulting in something that looks like this:
由于子类现在有一个构造函数,这意味着父类的构造函数没有被调用,因此子类所依赖的东西(例如this->user
不再有效)会导致错误。但是,我可以通过调用管理控制器的构造函数,parent::__construct()
因为我需要将类依赖项传递给它,因此我需要在子构造函数中设置这些依赖项,结果如下所示:
class UsersController extends AdminController {
public function __construct(UsersManager $user, UserAuthInterface $auth, MessagesInterface $message, ModuleManagerInterface $module)
{
parent::__construct($auth, $messages, $module);
$this->users = $users;
}
// Same as before
}
Now this works fine in terms of its functionality; however it doesn't seem very efficient to me to have to include the parent's dependencies in every child class that has a constructor. It also looks quite messy. Does Laravel provide a way around this, or does PHP support a way of calling both the parent and child constructor without having to call parent::__construct()
from the child?
现在这在其功能方面运行良好;但是对我来说,在每个具有构造函数的子类中都包含父类的依赖项似乎不是很有效。它看起来也很凌乱。Laravel 是否提供了解决此问题的方法,或者 PHP 是否支持调用父构造函数和子构造函数而不必parent::__construct()
从子构造函数调用的方法?
I know this is a long question for what is effectively not a problem but more me just be ocd about efficiency, but I appreciate any ideas and/or solutions.
我知道这是一个很长的问题,实际上什么不是问题,但更多的是我对效率的看法,但我感谢任何想法和/或解决方案。
Thanks in advance!
提前致谢!
回答by Luís Cruz
There isn't a perfect solution and it's important to understand that this isn't an issue with Laravel itself.
没有完美的解决方案,重要的是要了解这不是 Laravel 本身的问题。
To manage this, you can do one of three things:
要管理此问题,您可以执行以下三件事之一:
Pass the necessary dependencies to the parent (which was your issue)
// Parent public function __construct(UserAuthInterface $auth, MessagesInterface $message, ModuleManagerInterface $module) { $this->auth = $auth; $this->user = $this->auth->adminLoggedIn(); $this->message = $message; $this->module = $module; } // Child public function __construct(UsersManager $user, UserAuthInterface $auth, MessagesInterface $message, ModuleManagerInterface $module) { $this->users = $users; parent::__construct($auth, $message, $module); }
Auto resolve the dependencies in the parent construct as stated by @piotr_czin his answer
Create the instances in the parent construct instead of passing them as parameters (so you don't use Dependency Injection):
// Parent public function __construct() { $this->auth = App::make('UserAuthInterface'); $this->user = $this->auth->adminLoggedIn(); $this->message = App::make('MessagesInterface'); $this->module = App::make('ModuleManagerInterface'); } // Child public function __construct(UsersManager $user) { $this->users = $users; parent::__construct(); }
将必要的依赖项传递给父级(这是您的问题)
// Parent public function __construct(UserAuthInterface $auth, MessagesInterface $message, ModuleManagerInterface $module) { $this->auth = $auth; $this->user = $this->auth->adminLoggedIn(); $this->message = $message; $this->module = $module; } // Child public function __construct(UsersManager $user, UserAuthInterface $auth, MessagesInterface $message, ModuleManagerInterface $module) { $this->users = $users; parent::__construct($auth, $message, $module); }
在父构造中创建实例而不是将它们作为参数传递(因此您不使用依赖注入):
// Parent public function __construct() { $this->auth = App::make('UserAuthInterface'); $this->user = $this->auth->adminLoggedIn(); $this->message = App::make('MessagesInterface'); $this->module = App::make('ModuleManagerInterface'); } // Child public function __construct(UsersManager $user) { $this->users = $users; parent::__construct(); }
If you want to test your classes, the third solution will be more difficult to test. I'm unsure if you can mock the classes using the second solution, but you mock them using the first solution.
如果你想测试你的类,第三个解决方案将更难测试。我不确定您是否可以使用第二个解决方案来模拟这些类,但是您可以使用第一个解决方案来模拟它们。
回答by piotr_cz
There's a way. When BaseController autoresolves it's dependecies.
有办法。当 BaseController 自动解析它的依赖项时。
use Illuminate\Routing\Controller;
use Illuminate\Foundation\Application;
// Dependencies
use Illuminate\Auth\AuthManager;
use Prologue\Alerts\AlertsMessageBag;
class BaseController extends Controller {
protected $authManager;
protected $alerts;
public function __construct(
// Required for resolving
Application $app,
// Dependencies
AuthManager $authManager = null,
AlertsMessageBag $alerts = null
)
{
static $dependencies;
// Get parameters
if ($dependencies === null)
{
$reflector = new \ReflectionClass(__CLASS__);
$constructor = $reflector->getConstructor()
$dependencies = $constructor->getParameters();
}
foreach ($dependencies as $dependency)
{
// Process only omitted optional parameters
if (${$dependency->name} === null)
{
// Assign variable
${$dependency->name} = $app->make($dependency->getClass()->name);
}
}
$this->authManager = $authManager;
$this->alerts = $alerts;
// Test it
dd($authManager);
}
}
So in child controller you pass only Application instance:
所以在子控制器中你只传递 Application 实例:
class MyController extends BaseController {
public function __construct(
// Class dependencies resolved in BaseController
//..
// Application
Application $app
)
{
// Logic here
//..
// Invoke parent
parent::__construct($app);
}
}
Of course, we could use Facade for application
当然,我们可以使用 Facade 进行应用
回答by Chris Schmitz
I know this is a super old question, but I just finished grinding on a similar question on my current project and came to an understanding with the issue at hand.
我知道这是一个非常古老的问题,但我刚刚完成了对我当前项目的类似问题的研究,并了解了手头的问题。
The basic underlying question here is:
这里的基本问题是:
If I'm extending a parent class that has a constructor. That constructor has injected dependancies and all of it's dependencies are already documented in the parent itself. Why do I have to include the parent's dependencies again in my child class?
如果我要扩展具有构造函数的父类。该构造函数已注入依赖项,并且其所有依赖项都已记录在父级本身中。为什么我必须在我的子类中再次包含父类的依赖项?
I ran into this same issue.
我遇到了同样的问题。
My parent class requires 3 different dependencies. They're injected via the constructor:
我的父类需要 3 个不同的依赖项。它们是通过构造函数注入的:
<?php namespace CodeShare\Parser;
use CodeShare\Node\NodeRepositoryInterface as Node;
use CodeShare\Template\TemplateRepositoryInterface as Template;
use CodeShare\Placeholder\PlaceholderRepositoryInterface as Placeholder;
abstract class BaseParser {
protected $node;
protected $template;
protected $placeholder;
public function __construct(Node $node, Template $template, Placeholder $placeholder){
$this->node = $node;
$this->template = $template;
$this->placeholder = $placeholder;
}
The class is an abstract class so I can neverinstantiate it on it's own. When I extend the class, I still need to include all of those dependencies and their use
references in the child's constructor:
该类是一个抽象类,因此我永远无法单独实例化它。当我扩展类时,我仍然需要use
在子的构造函数中包含所有这些依赖项及其引用:
<?php namespace CodeShare\Parser;
// Using these so that I can pass them into the parent constructor
use CodeShare\Node\NodeRepositoryInterface as Node;
use CodeShare\Template\TemplateRepositoryInterface as Template;
use CodeShare\Placeholder\PlaceholderRepositoryInterface as Placeholder;
use CodeShare\Parser\BaseParser;
// child class dependencies
use CodeShare\Parser\PlaceholderExtractionService as Extractor;
use CodeShare\Parser\TemplateFillerService as TemplateFiller;
class ParserService extends BaseParser implements ParserServiceInterface {
protected $extractor;
protected $templateFiller;
public function __construct(Node $node, Template $template, Placeholder $placeholder, Extractor $extractor, TemplateFiller $templateFiller){
$this->extractor = $extractor;
$this->templateFiller = $templateFiller;
parent::__construct($node, $template, $placeholder);
}
Including the use
statements for the 3 parent dependencies in each class seemed like duplicate code since they're already defined in the parent constructor. My thought was to remove the parent use
statements as they'll always need to be defined in the child class that extends the parent.
use
在每个类中包含3 个父依赖项的语句似乎是重复的代码,因为它们已经在父构造函数中定义。我的想法是删除父use
语句,因为它们总是需要在扩展父类的子类中定义。
What I realized is that including the use
for the dependencies in the parent class and including the class names in the parent's constructor are ONLY needed for type hinting in the parent.
我意识到,只有use
在父类中进行类型提示时,才需要在父类中包含for 依赖项并在父类的构造函数中包含类名。
If you remove the use
statements from the parent and the type hinted class name from the parents constructor, you get:
如果use
从父构造函数中删除语句并从父构造函数中删除类型提示的类名,则会得到:
<?php namespace CodeShare\Parser;
// use statements removed
abstract class BaseParser {
protected $node;
protected $template;
protected $placeholder;
// type hinting removed for the node, template, and placeholder classes
public function __construct($node, $template, $placeholder){
$this->node = $node;
$this->template = $template;
$this->placeholder = $placeholder;
}
Without the use
statements and type hinting from the parent, it can no longer guarantee the type of class being passed to it's constructor because it has no way of knowing. You could construct from your child class with anything and the parent would accept it.
如果没有use
来自父级的语句和类型提示,它就不能再保证传递给它的构造函数的类的类型,因为它无法知道。你可以从你的子类中构造任何东西,而父类会接受它。
It does seem like double entry of code, but really in your paren't you're not constructing with the dependencies laid out in the parent, you're verifying that the child is sending in the correct types.
看起来确实像重复输入代码,但实际上在您的父级中,您没有使用父级中布置的依赖项进行构建,而是在验证子级是否以正确的类型发送。
回答by bgallagh3r
You must pass the dependencies to the parent constructor to have them available in the child. There is no way to inject the dependencies on the parent construct when you instantiate it via the child.
您必须将依赖项传递给父构造函数以使它们在子构造函数中可用。当您通过子级实例化它时,无法在父级构造上注入依赖项。
回答by Adam Moore
I came across the same issue, when extending my base Controller.
在扩展我的基本控制器时,我遇到了同样的问题。
I opted for a different approach than the other solutions shown here. Rather than rely on dependency injection, I'm using app()->make() in the parents constructor.
我选择了与此处显示的其他解决方案不同的方法。我没有依赖依赖注入,而是在父构造函数中使用 app()->make() 。
class Controller
{
public function __construct()
{
$images = app()->make(Images::class);
}
}
There may be downsides to this simpler approach - possibly making the code less testable.
这种更简单的方法可能有缺点——可能会使代码的可测试性降低。