簡體   English   中英

Laravel 4 - 子構造函數通過依賴注入調用父構造函數

[英]Laravel 4 - Child constructor call parent constructor with dependency injection

我正在使用 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;
    }
}

我使用 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'));
    }
}

現在這很完美,但是當我向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'));
    }
}

由於子類現在有一個構造函數,這意味着父類的構造函數不會被調用,因此子類所依賴的東西,例如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
}

現在這在其功能方面工作正常; 但是對我來說,在每個具有構造函數的子類中都包含父類的依賴項似乎不是很有效。 它看起來也很凌亂。 Laravel 是否提供了解決此問題的方法,或者 PHP 是否支持調用父構造函數和子構造函數而不必從子進程調用parent::__construct()的方法?

我知道這是一個很長的問題,實際上什么不是問題,但更多的是我對效率的看法,但我感謝任何想法和/或解決方案。

提前致謝!

沒有完美的解決方案,重要的是要了解這不是 Laravel 本身的問題。

要管理此問題,您可以執行以下三件事之一:

  1. 將必要的依賴項傳遞給父級(這是您的問題)

     // 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); }
  2. 自動解析父結構中的依賴關系,如@piotr_cz在他的回答中所述

  3. 在父構造中創建實例而不是將它們作為參數傳遞(因此您不使用依賴注入):

     // 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(); }

如果你想測試你的類,第三個解決方案將更難測試。 我不確定您是否可以使用第二個解決方案來模擬這些類,但是您可以使用第一個解決方案來模擬它們。

我知道這是一個非常古老的問題,但我剛剛在我當前的項目中完成了一個類似的問題,並了解了手頭的問題。

這里的基本問題是:

如果我要擴展具有構造函數的父類。 該構造函數已注入依賴項,並且其所有依賴項都已記錄在父級本身中。 為什么我必須在我的子類中再次包含父類的依賴項

我遇到了同樣的問題。

我的父類需要 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;
    }

該類是一個抽象類,因此我永遠無法單獨實例化它。 當我擴展類時,我仍然需要在子的構造函數中包含所有這些依賴項及其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);
    }

在每個類中包含 3 個父依賴項的use語句似乎是重復的代碼,因為它們已經在父構造函數中定義。 我的想法是刪除父use語句,因為它們總是需要在擴展父類的子類中定義。

我意識到,只有在父類中進行類型提示時,才需要在父類中use依賴項並在父類的構造函數中包含類名。

如果從父級中刪除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;
    }

如果沒有來自父級的use語句和類型提示,它就不能再保證傳遞給它的構造函數的類的類型,因為它無法知道。 你可以從你的子類中構造任何東西,而父類會接受它。

看起來確實像重復輸入代碼,但實際上在您的父級中,您沒有使用父級中布置的依賴項進行構建,而是在驗證子級是否以正確的類型發送。

有辦法。 當 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);
    }
}

所以在子控制器中你只傳遞 Application 實例:

class MyController extends BaseController {

    public function __construct(
        // Class dependencies resolved in BaseController
        //..

        // Application
        Application $app
    )
    {
        // Logic here
        //..


        // Invoke parent
        parent::__construct($app);
    }
}

當然,我們可以使用 Facade 進行應用

您必須將依賴項傳遞給父構造函數才能在子構造函數中使用它們。 當您通過子級實例化它時,無法在父級構造上注入依賴項。

在擴展我的基本控制器時,我遇到了同樣的問題。

我選擇了與此處顯示的其他解決方案不同的方法。 我沒有依賴依賴注入,而是在父構造函數中使用 app()->make() 。

class Controller
{
    public function __construct()
    {
        $images = app()->make(Images::class);
    }
}

這種更簡單的方法可能有缺點——可能會使代碼的可測試性降低。

我也解決了這個問題,並通過不在子類中調用構造函數並在函數參數中使用額外需要的依賴項來解決這個問題。

它將與控制器一起使用,因為您不需要手動調用這些函數,並且可以在那里注入所有內容。 所以常見的依賴關系到父級,不需要的將被添加到方法本身。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM