简体   繁体   English

Laravel 5.5:我是否在正确的模式下使用依赖注入?

[英]Laravel 5.5: Am I using Dependency Injection in right mode?

I did write a simple Interface under app\\Libs . 我确实在app\\Libs下编写了一个简单的Interface。 I registered the namespace in composer.json 我在composer.json中注册了名称空间

"autoload": {
        "classmap": [
            "database/seeds",
            "database/factories",
            "app/Libs"
        ],

DomoticControllerInterface.php (the name "Controller" because this is a controller for domotic setup, not the "Laravel Controller ;)" DomoticControllerInterface.php (名称为“ Controller”,因为它是用于domotic设置的控制器,而不是“ Laravel Controller;”)

namespace App\Libs;

interface DomoticControllerInterface
{
    /**
     * Get the current temperature.
     */
    public function getCurrentTemperature();

    /**
     * Get the current status of general heating (ON/OFF)
     */
    public function getCurrentStatusOfGeneralHeating();

}

And a class Domoticz.php that implements it 还有一个实现它的类Domoticz.php

<?php
/**
 * Domoticz instance.
 *
 * @see www.domoticz.com
 */

namespace App\Libs;


class Domoticz implements DomoticControllerInterface
{

    public function getCurrentTemperature()
    {
        // TODO: Implement getCurrentTemperature() method.
        return "27.4";
    }

    public function getCurrentStatusOfGeneralHeating()
    {
        // TODO: Implement getCurrentStatusOfGeneralHeating() method.
    }
}

I wrote a HeaterService.php (model? Provider?) under app\\Libs 我在app \\ Libs下编写了HeaterService.php (模型?提供程序?)

<?php
/**
 * Heater Service Class.
 * This class perform work on Heater
 *
 * @since 3.0.0
 * @author sineverba
 */

namespace App\Libs;


/**
 * Class HeaterService
 * @package App\Libs
 */
class HeaterService
{

    /**
     * The domotic controller.
     *
     * @var object
     */
    private $domotic_controller;

    public function __construct($domotic_controller)
    {
        $this->setDomoticController($domotic_controller);
    }

    /**
     * Get the current temperature
     *
     * @return string the current temperature
     */
    public function getCurrentTemperature()
    {
        return $this->getDomoticController()->getCurrentTemperature();
    }

    /**
     * Set the domotic controller.
     *
     * @param object the domotic controller to use
     * @since 1.0.0
     * @author sineverba
     */
    private function setDomoticController($domoticController)
    {
        $this->domotic_controller = $domoticController;
    }

    /**
     * Get the istance of domotic controller
     *
     * @return object the domotic controller
     * @since 3.0.0
     * @author sineverba
     */
    private function getDomoticController()
    {
        return $this->domotic_controller;
    }

}

Finally, in my BaseController.php under app\\Http\\Controllers 最后,在我的BaseController.php中的 app \\ Http \\ Controllers下

<?php

/**
 * Base Controller that redirect to right controller/model,
 * based on the URL params.
 *
 * @author sineverba
 * @since 1.0.0
 */

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class BaseController extends Controller
{
    //
    /**
     * Get the param from the URL and redirect to right controller.
     *
     */
    public function getTheScriptFromUrl(Request $request)
    {

        $domotic_controller = env("DOMOTIC_CONTROLLER", "Domoticz");
        if ($domotic_controller=="Domoticz") {
            $dom_controller = new \App\Libs\Domoticz();
        }
        $heater = new \App\Libs\HeaterService($dom_controller);

        echo $heater->getCurrentTemperature();
    }
}

It works. 有用。 I got the 27.4 hardcoded. 我得到了27.4硬编码。

But, is this the right mode to use? 但是,这是正确的使用模式吗?

Another one question, I would use the Type-Hinting in HeaterService.php constructor, but don't know how to do. 另一个问题是,我将在HeaterService.php构造函数中使用Type- Hinting ,但不知道该怎么做。

Finally, my app works (for the moment), without binding. 最终,我的应用程序(暂时)可以正常工作,而没有绑定。 Is it possible? 可能吗?

Thank you very much 非常感谢你

There are several things wrong here. 这里有几处错误。 So let's take them one at a time. 因此,让我们一次带他们一个。

First of all, there's no need to autoload the class if you are adding it under the app namespace. 首先,如果要在app名称空间下添加类,则无需自动加载该类。 It should already be auto-loaded. 它应该已经自动加载了。

"autoload": {
    "psr-4": {
        "App\\": "app/"
    }
}

A very powerful feature of the service container is its ability to bind an interface to a given implementation. 服务容器的一个非常强大的功能是其将接口绑定到给定实现的能力。

In OOP, you will often hear the phrase "code to an interface", and that's what's happening here. 在OOP中,您经常会听到“向接口编码”一词,这就是这里发生的情况。

With that in mind, let's create a HeaterServiceProvider . 考虑到这一点,让我们创建一个HeaterServiceProvider Its responsibility will be to register our dependency into the container so that we can use it later. 它的责任是将我们的依赖项注册到容器中,以便以后使用。

<?php

namespace App\Libs;

use Illuminate\Support\ServiceProvider;

class HeaterServiceProvider extends ServiceProvider
{
    /**
    * Register bindings in the container.
    *
    * @return void
    */
    public function register()
    {
        $this->app->bind(
            'App\Libs\DomoticControllerInterface',
            'App\Libs\Domoticz
        );
    }
}

... and then you would register this service provider in your config/app.php 's providers array. ...然后您将在config/app.php的providers数组中注册此服务提供商。

'providers' => [
    // 
    //
    App\Libs\HeaterServiceProvider::class
]

This will register the bindings into your application's container. 这会将绑定注册到应用程序的容器中。 Wherever you need to resolve App\\Libs\\DomoticControllerInterface , an instance of App\\Libs\\Domoticz will be returned. 无论你需要解决App\\Libs\\DomoticControllerInterface ,实例App\\Libs\\Domoticz将被退回。

So, to use it, you could just type-hint the interface in your methods, such as: 因此,要使用它,您可以仅在方法中键入该接口的提示,例如:

<?php

/**
* Base Controller that redirect to right controller/model,
* based on the URL params.
*
* @author sineverba
* @since 1.0.0
*/

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Libs\DomoticControllerInterface;

class BaseController extends Controller
{
    //
    /**
    * Get the param from the URL and redirect to right controller.
    *
    */
    public function getTheScriptFromUrl(DomoticControllerInterface $heater, Request $request)
    {
        // Laravel will automatically resolve an instance of 
        // App\Libs\Domoticz here, so you could just do the following
        echo $heater->getCurrentTemperature();
    }
}

In your particular case, by "newing" up your implementation inside the getTheScriptFromUrl() , you are actually "coding to an implementation" rather than an interface. 在您的特定情况下,通过“更新” getTheScriptFromUrl() ,您实际上是在“编码为实现”,而不是接口。

You are also defeating the purpose of using binding things to the container if you are not actually going to resolve your dependencies from it. 如果您实际上并不想从容器中解决依赖关系,那么您将无法实现将内容绑定到容器的目的。

Your implementations may change in the future. 您的实现将来可能会更改。 If you code to an interface, the only thing you'd need to do if your implementations change would be to bind the new implementation into the container and your application should be good to go. 如果您对接口进行编码,则如果您的实现发生更改,您唯一需要做的就是将新的实现绑定到容器中,并且您的应用程序应该可以使用。

Also, it's much easier to perform testing if you are using Dependency Injection. 另外,如果使用依赖注入,则执行测试要容易得多。 You could easily swap/mock those implementations in a test environment. 您可以在测试环境中轻松交换/模拟那些实现。

I would suggest you to take a deeper look at the Service Container documentation. 我建议您更深入地了解服务容器文档。 You can also take a look at this article . 您也可以看一下这篇文章 The author does a really good job in explaining Laravel's Service Container. 作者在解释Laravel的服务容器方面做得非常好。

Additionally, going through the Service Provider 's documentation would also help. 此外,仔细阅读服务提供商的文档也将有所帮助。

Edit: Based on our discussion in the comments, here's how you would do it in your scenario. 编辑:根据我们在评论中的讨论,这是您在方案中的处理方法。

<?php

namespace App\Libs;

use Illuminate\Support\ServiceProvider;

class HeaterServiceProvider extends ServiceProvider
{
    /**
    * Register bindings in the container.
    *
    * @return void
    */
    public function register()
    {
        $implementation = 'Homeassistant'; // You could be pulling this from anywhere
        $this->app->bind(
            'App\Libs\DomoticControllerInterface',
            "App\Libs\{$implementation}"
        );
    }
} 

The point is, you don't actually need to create an instance of your implementation anywhere since you are already binding it in your controller. 关键是,您实际上不需要在任何地方创建实现的实例,因为您已经在控制器中绑定了它。

In our particular case, the following: 在我们的特定情况下,以下内容:

<?php

/**
* Base Controller that redirect to right controller/model,
* based on the URL params.
*
* @author sineverba
* @since 1.0.0
*/

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Libs\DomoticControllerInterface;

class BaseController extends Controller
{
    //
    /**
    * Get the param from the URL and redirect to right controller.
    *
    */
    public function getTheScriptFromUrl(DomoticControllerInterface $heater, Request $request)
    {
        // Laravel will automatically resolve an instance of 
        // App\Libs\Homeassistant here, so you could just do the following
        echo $heater->getCurrentTemperature();
    }
}

I hope that makes sense :) 我希望这是有道理的 :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM