I did write a simple Interface under app\Libs
. I registered the namespace in 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 ;)"
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
<?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
<?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
<?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.
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.
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. 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.
With that in mind, let's create a 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.
'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.
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.
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.
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 :)