In my controller, via service, I get from DB a list of the names of widgets (eg. chart, calendar, etc). Every widget implements WidgetInterface and may need other services as its own dependencies. The list of widgets can be different for each user, so I don't know which widgets / dependencies I will need in my controller. Generally, I put dependencies via DI, using factories, but in this case I don't know dependencies at the time of controller initialization.
I want to avoid using service locator directly in controller. How can I manage that issue? Should I get a list of the names of widgets in controller factory? And depending on widgets list get all dependencies and put them to controller?
Thanks, Tom
Solution
I solved my issue in a way that suggested Kwido and Sven Buis, it means, I built my own Plugin Manager.
Advantages: I do not need use service locator directly in controller and I have clear and extensible way to get different kinds of widgets.
Thank you.
Create your own Manager, like some sort of ServiceManager, for your widgets.
class WidgetManager extends AbstractPluginManager
Take a look at: Samsonik tutorial - pluginManager. So this way you can inject the WidgetManager and only retrieve the widgets from this manager as your function: validatePlugin, checks whether or not the fetched instance is using the WidgetInterface. Keep in mind that you can still call the parent ServiceManager.
Or keep it simple and build a plugin for your controller that maps your widget names to the service. This plugin can then use the serviceLocator/Manager to retrieve your widget(s), whether they're created by factories or invokableFactories. So you dont inject all the widget directly but only fetch them when they're requested. Something realy simplistic:
protected $map = [
// Widget name within the plugin => Name or class to call from the serviceManager
'Charts' => Widget\Charts::class,
];
public function load($name)
{
if (array_key_exists($name, $this->map)) {
return $this->getServiceManager()->get($this->map[$name]);
}
return null;
}
Injecting all the Widgets might be bad for your performance so you might consider something else, as when the list of your widgets grow so will the time to handle your request.
Hope this helped you and pushed you in some direction.
This indeed is a interesting question. You could consider using Plugins for the widgets, which can be loaded on the fly.
Depency injection is a good practise, but sometimes, with dynamic content, impossible to implement.
Another way to do this, is to make your own widget-manager. This manager then can load the specific widgets you need. The widget-manager can be injected into the controller.
Edit:
As you can see above, same idea from #kwido.
I would use a separate service and inject that into the controller.
interface UserWidgetServiceInterface
{
public function __construct(array $widgets);
public function getWidget($name);
}
The controller factory
class MyControllerFactory
{
public function __invoke(ControllerManager $controllerManager, $name, $requestedName)
{
$serviceLocator = $controllerManager->getServiceLocator();
$userWidgetService = $serviceLocator->get('UserWidgetService');
return new MyController($userWidgetService);
}
}
Then the logic to load the widgets would be moved to the UserWidgetServiceFactory.
public function UserWidgetServiceFactory
{
public function __invoke(ServiceManager $serviceLocator, $name, $requestedName)
{
$userId = 123; // Load from somewhere e.g session, auth service.
$widgetNames = $this->getWidgetNames($serviceLocator, $userId);
$widgets = $this->loadWidgets($serviceManager, $widgetNames);
return new UserWidgetService($widgets);
}
public function getWidgetNames(ServiceManager $sm, $userId)
{
return ['foo','bar'];
}
public function loadWidgets(serviceManager $sm, array $widgets)
{
$w = [];
foreach($widgets as $widgetName) {
$w[$widgetName] = $sm->get($widgetName);
}
return $w;
}
}
The call to loadWidgets() would eager load all the widgets; should you wish to optimise this you could register your widgets as LazyServices
Related
I have two questions today. This is detailed because too many other replies rely on assumptions and have not been detailed enough. I hope that this is detailed and will be able to help lots of developers.
1st. The code below points to the real question I have. How do you call a Service outside of the controller since the $this->get() method is inside of the controller only? This is not in any of the documentation or on KNP University's tutorial on Services.
2nd. From what I have read, according to some, not all, if you call to a Repository, from anywhere, it should automatically instantiate the Entity Repository. I don't think this is so. Tell me if I am right or wrong.
See the following below....
My Default Controller, it's straightforward call a class and let it do some work. As an example, I called it with a Service and a conventional OO method:
<?php
// src/AppBundle/Controller/DefaultController.php
// Here is where I am starting. There is a service
// and there is a conventional OO call.
// Both should invoke the same thing.
namespace AppBundle\Controller;
use AppBundle\Service;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
/**
* #Route("/", name="homepage")
*/
public function indexAction(Request $request)
{
// Step 1.... Do a little of this.
// Step 2.... Do some of that.
// Step 3.... Call another class to do some logic and it will
// eventually call a query...
// Invoking my service
$obj_via_service = $this->get('app.services.process_question');
$result1 = $obj_via_service->submitQuestion();
// Invoking via Namespace and Call
$obj_via_new = new Service\ProcessQuestion();
$result2 = $obj_via_new->submitQuestion();
dump($result1);
dump($result2);
die();
}
}
My Service.yml File.
# src/app/config/services.yml
parameters:
services:
app.services.process_question:
class: AppBundle\Service\ProcessQuestion
app.rep.geo_state:
class: AppBundle\Entity\GeoStateRepository
arguments: ['#doctrine.orm.entity_manager']
This is my class that is doing the work for me. I want to be able to call the second service ^^above^^ but I can't because I can't use $this->get() outside of the controller.
<?php
// src/AppBundle/Service/ProcessQuestion.php
namespace AppBundle\Service;
class ProcessQuestion
{
public function submitQuestion()
{
// Step 1.... Do this.
// Step 2.... Do that.
// Step 3.... Query for some data...
// Invoke my repository class via a Service Call....
// but I cannot do that because 'get' is a part of the
// controller...
$obj_via_service = $this->get('app.rep.geo_state');
**^^ ^^**
**^^ This is what won't work ^^**
$results = $obj_via_service->selectStates();
return $results;
}
}
My Repository Class... Keep in mind I cannot reach this class yet, but I am throwing it in here so that other new Symfony 3 developers can see this.
<?php
// src/AppBundle/Repository/GeoState.php
// My Repository Class where I want to do some queries...
namespace AppBundle\Repository;
use Doctrine\ORM\EntityRepository;
class GeoStateRepository extends EntityRepository
{
/**
* #Mapping\Column(type="string")
*/
private $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function selectStates()
{
$sql = "SELECT * FROM geo_state";
return $this->getEntityManager()->createQuery($sql)->getResult();
}
}
Why is this so hard to find an example? Also, I have followed a bunch of the Symfony 2.x documentation and the nuances are hard to port into Symfony 3 sometimes.
I think Fabian re purposed too much of the docs for 2.x to go into 3.x and there is not any good examples on coding that is between the New Developer level and the Hard Core Developer level. If you are at Sensio and reading this, please keep in mind that there is a middle ground we need to cover and most of the screencasts that out there and much of the better documentation is not in English.
You should really read more about Dependency Injection.
Symfony is very good at this .
Regarding your question about using app.rep.geo_state service in the app.services.process_question service .
In Symfony/ DI terminology it's can be termed as injecting a service into another service .
The documentation on how to do this is very good.
this is how it can be done.
services:
app.services.process_question:
class: AppBundle\Service\ProcessQuestion
arguments: ['#app.rep.geo_state']
app.rep.geo_state:
class: AppBundle\Entity\GeoStateRepository
arguments: ['#doctrine.orm.entity_manager']
And in the class
<?php
// src/AppBundle/Service/ProcessQuestion.php
namespace AppBundle\Service;
use AppBundle\Entity\GeoStateRepository;
class ProcessQuestion
{
private $geoRepository;
public function __construct(GeoStateRepository $geoRepository)
{
$this->geoRepository = $geoRepository;
}
public function submitQuestion()
{
//now you can call $this->geoRepository
}
}
Also note that $this->get() is only a shortcut function provided by the Symfony base Controller class to access the container.
To know more about DI, you can read Fabian's excellent articles about this in his blog .
I am trying to learn ZF2. I know that there are better methods to create notifications but as I said this is for learning purpose.
My idea is to save the notifications in the database. This is simple I can do it. My question is about how to show them. I have a partial for the header menu , I want to display them there.
I dont know if i am on the right track i currently have a view helper which i create via factory.
class TestMe extends AbstractHelper
{
protected $html;
public function __invoke($name = 'Unnamed')
{
// if($this->view->hasIdentity()){
// $user = $this->view->identity();
// }
//$this->testJS();
return "$name , this is Zend Framework 2 View Helper";
}
protected function htmlIt(){
}
protected function testJS($loggedIn = false){
$js = '';
$js .= <<<JS
alert('test');
JS;
$view = $this->getView();
$view->inlineScript()->prependScript($js);
}
}
I know this is nothing but I cant understand what is good an wrong. My idea is to pass ot the view helper an array with notifications for the user and display them. So I need a service for making calls to the database or there is another way?
ZF2 already has an Notification Helper Plugin. Have a Look at http://framework.zend.com/manual/current/en/modules/zend.view.helpers.flash-messenger.html
But if you really want to do it on your own, you may want to create an Controller Plugin http://framework.zend.com/manual/current/en/modules/zend.mvc.plugins.html.
There is a nice Tutorial for that here: http://lab.empirio.no/custom-controller-plugin-in-zf2.html
I am currently using DDD (Domain Driven Design) for a new Zend Framework 2 project. Everything works fine but I do have a question regarding the application services.
I understood that application services are located at the application layer and are kind of the entry point to the domain logic. They can access domain services or the repository for example.
I wonder now if it would make sense to implement the application services as controller plugins. In a classical MVC application this controller plugin could handle the results from the called domain services or repositories. Depending on these results they could generate a redirect response or pass data / a form to a ViewModel. If this logic is encapsulated within a plugin my controller only has to call the plugin and return the result of the plugin.
Am I totally wrong about this? Or would you rather keep the logic how to react on results of a domain service or a repository in a controller?
Best Regards,
Ralf
Of course it's kind of subjective and people have strong opinions about things like that... so here's mine:
Controller plugins contain code that's universal to any MVC/REST
action and business logic is not universal. Plugins are supposed to facilitate the
"controlling" of request/response and not the business logic that's
down on the model level. Linking it there would make it less
reusable, e.g. for console actions. Also it'd make it less probable
to use the business logic with some other framework.
It's awkward to test. Injecting controller plugins as controller
class constructor parameters would be kinda redundant, since they are already
available from the plugin manager injected into AbstractActionController or AbstractRestfulController. Not having the dependencies injected in a obivious/visible way (like trough contructor method) makes it harder to figure out what the controller class actually depends
on. Also since all plugins (AbstractPlugin related) depend on controller instance, switching context from
http to console (like for a phpunit test) might get problematic. Also testing logic
written/made available as controller plugin would sooner or later
escalate to including request/response objects in the tests and
that's unnecessary complexity.
It's not intuitive. When I hear plugin I think of something small.
Not a full-blown business logic code buried under such inconspicuous
name. So when I have little time to debug someones code the last
thing I need it to things be out of place like that.
Again I'd like to reiterate, that's just my opinion. I've learned that a lot of patterns can crumble under a weird-enough use case, but the above points made sense to me and my team so far.
As an example for my solution here you can see an controller action:
public function showAction()
{
$service = $this->readProductEntityCommand;
$service->setId($this->params()->fromRoute('id'));
try {
$result = $service->execute();
} catch (ProductException $e) {
$this->flashMessenger()->addMessage($e->getMessage());
return $this->redirect()->toRoute('part3/product');
}
return new ViewModel(
array(
'productEntity' => $result->getData(),
)
);
}
And here is an example of an application service build as an command object
class ReadProductEntityCommand implements CommandInterface
{
protected $productRepository;
protected $id;
public function __construct(ProductRepositoryInterface $productRepository)
{
$this->productRepository = $productRepository;
}
public function setId($id)
{
$this->id = $id;
}
public function execute()
{
if (is_null($this->id)) {
throw new ProductIdInvalidException(
'Produkt ID wurde nicht angegeben.'
);
}
try {
$product = $this->productRepository->getProduct(
new ProductIdCriterion(
new ProductId($this->id)
)
);
} catch (\Exception $e) {
throw new ProductNotFoundException(
'Es konnten kein Produkt gelesen werden.'
);
}
if ($product === false) {
throw new ProductNotFoundException('Produkt wurde nicht gefunden.');
}
$result = new Result();
$result->setValid(true);
$result->setData($product);
$result->setMessage('Produkt wurde gelesen.');
return $result;
}
}
Maybe this helps someone in the future.
According to this article: http://www.maltblue.com/tutorial/zend-framework-2-servicemanager
The ServiceManager is "in short a simple application registry that provides objects". So, I would think that it should be a singleton that we can have access anywhere in the application. But in case of ServiceManager, it isn't.
Why can't I get the service locator instance anywhere in the application?
ServiceManager basically act as container. Inside the container you satisfy various dependencies of an object you create and then return it to be used by other objects.
So in a way SM sits over the object, not goes inside the object. If you are using SM instance inside the object (probably to access other services) then you are going against the principle of Inversion of Control.
Following are the two ways
class A {
private $data;
public function __constructor($sm) {
$this->data = $sm->get('user_data'); // Service manager accessed inside the object
}
}
Other way
class B {
private $data;
public function __constructor($user_data) {
$this->data = $user_data; //$user_data getting injected from sm container
}
}
Somewhere inside Module.php:
'factories'=>
array(
'objB'=> function($sm) {
//this is the container where sm sites outside the object to satisfy its dependencies
$objB = new B($sm->get('user_data'));
return $objB;
}
)
In second example dependency ($user_data) gets injected into the object.
Here is a simple way to get ServiceLocator to instantiate objects wherever you want...is a very simple module that set ServiceLocator on application's bootstrap to a static variable in a class...you can get an idea to create something more sophisticated if it doesn't fit your needs :) Here is the module... https://github.com/fezfez/ServiceLocatorFactory
i'm looking for a larger example of dependency injection and how it can be implemented. If class A depends on class B and passes a reference of class C to B's constructor, must not class A also take a reference to class C in it's constructor? This means that the main method in the application should create all classes really, which sounds wierd?
I understand that using DI frameworks we can have it in XML files somehow, but that sounds like it could be hard to quickly see what type that really is instanciated? Especially if it a very large application.
You are correct and each DI framework has a different way of managing it.
Some use attributes on the properties etc to denote dependency and then "automagically" supply an instance of the correct type, while others (say castle windsor for .net) allow xml configuration, fluent or other methods for "wiring up" the dependency graph.
Also no, class A takes a built reference to an instance of B which was built using an instance of C. A needs to know nothing about C unless exposed via B.
public class C { }
public class B { public B(C c) { ... }}
public class A { public A(B b) { ... }}
// manual wireup
C c = new C();
B b = new B(c);
A a = new A(b);
// DI framework
InversionOfControlContainer container = new InversionOfControlContainer(... some configuration);
A a = container.ResolveInstanceOf<A>();
// container dynamically resolves the dependencies of A.
// it doesnt matter if the dependency chain on A is 100 classes long or 3.
// you need an instance of A and it will give you one.
Hope that helps.
to answer your question about classes A,B,and C, A only needs a reference to B.
Most DI frameworks do not require you to use XML for configuration. In fact, many people prefer not to use it. You can explicitly set things up in code, or use some kind of conventions or attributes for the container to infer what objects should fulfil dependencies.
Most DI frameworks have a facility for "lazy loading" to avoid the creation of every single class up front. Or you could inject your own "factory or builder" objects to create things closer to the time when they will be used
You've not said what language you are using. My example below is in C# using the Unity container. (obviously normally you would use interfaces rather than concrete types):
container = new UnityContainer();
container.RegisterType<C>();
container.RegisterType<B>();
A a = container.Resolve<A>();
here's a few examples from the PHP Language, hope this helps you understand
class Users
{
var $Database;
public function __construct(Database $DB)
{
$this->Database = $DB;
}
}
$Database = Database::getInstance();
$Users = new Users($Database);
From this example the new keyword is used in the method getInstance(), you can also do
$Users = new Users(Database::getInstance());
Or another way to tackle this is
class Users
{
/*Dependencies*/
private $database,$fileWriter;
public function addDependency($Name,$Object)
{
$this->$Name = $Object;
return $this;
}
}
$Users = new Users();
$Users->addDependency('database',new Database)->addDependency('fileWriter',new FileWriter);
Update:
to be honest, I never use Dependency Injection as all its doing is passing objects into classes to create a local scope.
I tend to create a global entity, and store objects within that so there only ever stored in 1 variable.
Let me show you a small example:
abstract class Registry
{
static $objects = array();
public function get($name)
{
return isset(self::$objects[$name]) ? self::$objects[$name] : null;
}
public function set($name,$object)
{
self::$objects[$name] = $object;
}
}
Ok the beauty of this type of class is
its very lightweight
it has a global scope
you can store anything such as resources
When your system loads up and your including and initializing all your objects you can just store them in here like so:
Registry::add('Database',new Database());
Registry::add('Reporter',new Reporter());
Where ever you are within your runtime you can just use this like a global variable:
class Users
{
public function getUserById($id)
{
$query = "SELECT * FROM users WHERE user_id = :id";
$resource = Registry::get("Database")->prepare($query);
$resource->bindParam(':id',$id,PDO::PARAM_INT);
if($resource->execute())
{
//etc
}else
{
Registry::get('Reporter')->Add("Unable to select getUserById");
}
}
}
i see this way of object passing is much cleaner
If anybody is still looking for a good example which shows DI without IoC Containers (poor man's DI) and also with IoC Container (Unity in this example) and registering the types in code and also in XML you can check this out: https://dannyvanderkraan.wordpress.com/2015/06/15/real-world-example-of-dependeny-injection/