I have the following code that I put inside an entity class:
$filesystem = $this->container->get('knp_gaufrette.filesystem_map')->get('amazon');
$filesystem->write($this->file , $this->name, true);
However, this always generate an error of:
Notice: Undefined property: MySite\UserBundle\Entity\ProfilePicture::$container in /Users/Mike/Sites/MySite/src/MySite/MainBundle/Entity/Document.php line 98
Any idea why this is the case? How do I access a service container from an entity?
I've put this inside an abstract class:
abstract class Document
{
......
}
In a legacy environment i've created a doctrine eventlistener that inject automatically the container in a entity that implements ContainerAwareInterface. With it i can work with some "legacy entity" with the minimum effort.
It's like how Symfony ControllerResolver injects the container in a controller but for entity.
<?php
namespace Acme\DemoBundle\Doctrine;
use Doctrine\ORM\Event\LifecycleEventArgs;
use JMS\DiExtraBundle\Annotation as DI;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* adds some nice features to more easy entity utilization
*
* #DI\Service
* #DI\Tag("doctrine.event_listener", attributes = {"event" = "postLoad"})
*/
class ContainerAwareListener extends ContainerAware
{
/**
* #DI\InjectParams({
* "container" = #DI\Inject("service_container"),
* })
*/
public function __construct(ContainerInterface $container = null){
$this->setContainer($container);
}
/**
* After object is loaded, listener inject the container
*
* #param LifecycleEventArgs $args
*/
public function postLoad(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if($entity instanceof ContainerAwareInterface){
$entity->setContainer($this->container);
}
}
}
the entity need to implement the containerawareinterface and the required services can be obtained in setContainer() method, the service is defined with JMSDIExtrabundle but can be defined under services.yml .
As many suggest this is a bad practice because the entity must depend as little as possible from all. But as a extrema ratio (or under extreme deadlines:-) ) this is great.
Entities are data models and should contain only data.
Instead, create a listener or manager service with dependency injection.
Related
I'm new in Symfony 2.
I have a function called "addNewTarjeta" in a personalized entity respository.
<?php
namespace Elkanogroup\ClientesBundle\Repository;
/**
* ClienteRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ClienteRepository extends \Doctrine\ORM\EntityRepository {
/**
* Asigna una tarjeta a este cliente.
*/
public function addNewTarjeta(Cliente $cliente) {
$tarjeta = new \Elkanogroup\ClientesBundle\Entity\Tarjeta();
$tarjeta->setNumeroTarjeta('5555 5555 5555 5555');
$tarjeta->setCliente($cliente);
$tarjeta->setFechaExpedicion(new \DateTime());
$em = $this->getDoctrine()->getManager();
$em->persist($tarjeta);
$flush = $em->flush();
if ($flush != null) {
return false;
}
return true;
}
I have a listener waiting for a doctrine event postPersist. I would like to call to "addNewTarjeta" from a postPersist function.
I'm trying to do something like this:
<?php
namespace Elkanogroup\ClientesBundle\EventListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Elkanogroup\ClientesBundle\Entity\Cliente;
use Elkanogroup\ClientesBundle\Repository\ClienteRepository;
class ClienteListener {
public function postPersist(Cliente $cliente, LifecycleEventArgs $args) {
$cliente->addNewTarjeta($cliente);
}
But it doesnt work. Symfony says:
Attempted to call an undefined method named "addNewTarjeta" of class
"Elkanogroup\ClientesBundle\Entity\Cliente".
Can anyone help me ?? Thanks and sorry for my bad english.
Everyone here says that you need to inject the entity manager but to me it's not true: you can retrive it from LifecycleEventArgs without inject anything.
Just do
$args->getObjectManager();
and you're done.
Just a note: usually repos are used to keep custom queries (via DQL or plain SQL or query builder). A logic like this should be fitted inside a service (a manager, helper or whatever).
As #dragoste said, you need to inject the entitymanager service into your listener.
It can be done in services.yml:
name.of.your.listener:
class: AppBundle\Listener\MyListener
arguments: ["#doctrine.orm.entity_manager"]
And then, add a public function __construct(\Doctrine\ORM\EntityManager $entityManager) method in your listener:
<?php
namespace AppBundle\Listener;
class MyListener
{
/**
* #var \Doctrine\ORM\EntityManager
*/
private $entityManager;
public function __construct(\Doctrine\ORM\EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
I'd like to retrieve my module configuration from a controller in Zend Framework 3.
I searched, and it seems that the standard way to do this in ZF2 is to use
$this->getServiceLocator()
to access the configuration in module.config.php.
However, this won't work in ZF3, as there is no getServiceLocator() method.
What is the standard way to achieve this?
Don't know if you found an answer, as there are different solutions as tasmaniski wrote. Just in case, let me share one that would have helped me a lot when I started to play with ZF3:
MyControllerFactory.php
<?php
namespace My\Namespace;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
use DependencyNamespace\...\ControllerDependencyClass; // this is not a real one of course!
class MyControllerFactory implements FactoryInterface
{
/**
* #param ContainerInterface $container
* #param string $requestedName
* #param null|array $options
* #return AuthAdapter
*/
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
// Get config.
$config = $container->get('configuration');
// Get what I'm interested in config.
$myStuff = $config['the-array-i-am-interested-in']
// Do something with it.
$controllerDepency = dummyFunction($myStuff);
/*...the rest of your code here... */
// Inject dependency.
return $controllerDepency;
}
}
MyController.php
<?php
namespace My\Namespace;
use Zend\Mvc\Controller\AbstractActionController;
use DependencyNamespace\...\DependencyClass;
class MyController extends AbstractActionController
{
private $controllerDepency;
public function __construct(DependencyClass $controllerDepency)
{
$this->controllerDepency = $controllerDepency;
}
/*...the rest of your class here... */
}
You need to inject your dependencies through service manager.
Basicly you need to create 2 class Controller and ControllerFactory that will create Controller with all dependencies.
I need to inject my post repository in my post service. I have a PostController, PostEntity, PostServiceInterface and PostRepository.
My post repository contains DQL with methods like findAll(), find($id), etc...
In my PostServiceInterface I have some methods like find, findAll.
Now I want to access to repository to get results from my service. I do not want to write queries directly in service. I try to inject the service into __construct using DI but that doesn't work.
Can someone provide an example on how to do this?
I am using Zend Framework 2 with DoctrineORMModule.
The best way is writing a custom PostServiceFactory to inject PostRepository to the PostService via constructor injection.
For example:
<?php
namespace Application\Service\Factory;
use Application\Service\PostService;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class PostServiceFactory implements FactoryInterface
{
/**
* Creates and returns post service instance.
*
* #param ServiceLocatorInterface $sm
* #return PostService
*/
public function createService(ServiceLocatorInterface $sm)
{
$repository = $sm->get('doctrine.entitymanager.orm_default')->getRepository('Application\Entity\PostService');
return new PostService($repository);
}
}
You also need to change the PostService's constructor signature like below:
<?php
namespace Application\Service;
use Application\Repository\PostRepository;
class PostService
{
protected $repository;
public function __construct(PostRepository $repository)
{
$this->repository = $repository;
}
}
Finally, in your module.config.php you also need to register your factory in the service manager config:
'service_manager' => array(
'factories' => array(
'Application\Service\PostService' => 'Application\Service\Factory\PostServiceFactory',
)
)
Now, you can get the PostService via the service locator in your controller like below:
$postService = $this->getServiceLocator()->get('Application\Service\PostService');
The PostRepository will be automatically injected into the returned service instance as we coded in our factory.
I have been doing a read up on ZF2 Service Locator component and could say I understand how its being used. I have, however, a question which I think its silly but it wouldn't hurt to ask.
I want to have a namespace inside my Module called Component where I can put generic code in like say FunctionsComponent.php, MailerComponent.php or ExcelComponent.php. This would allow me to do some stuff inside my controllers.
What I would like to tryout is to have an ability to have controllers define the components they are interested to use (see below):
class SalesController extends AbstractController
{
protected $components = ['Excel'];
//In some action
public function exportAction()
{
$data = ['data to be exported'];
/**
$data : data to be exported
boolean : Whether to force download or save the file in a dedicated location
*/
$this->Excel->export($data, true);
}
}
The idea is to create a ComponentCollection that perhaps implements the FactoryInterface or ServiceLocatorInterface and then let it check each controller when the MvcEvent has been triggered inside my Module class and have the ComponentCollection inject all the controller component and make them accessible without using the service locator as shown below:
$excel = $sm->get('Application\Component\Excel');
I am well aware that this may seem like a daunting ask but I feel like the best way to learn a framework among other things is to play around with it and try to do the unimaginable.
You should create a BaseController somewhere and then extend all your Controllers from BaseController. Then you can inject your dependencies in your BaseController and use anywhere in kids. For example, I am doing this in my Controller to set head title:
<?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
class BaseController extends AbstractActionController
{
/**
* Sets the head title for every page
*
* #param string $title
*/
public function setHeadTitle($title)
{
$viewHelperManager = $this->getServiceLocator()->get('ViewHelperManager');
// Getting the headTitle helper from the view helper manager
$headTitleHelper = $viewHelperManager->get('headTitle');
// Setting a separator string for segments
$headTitleHelper->setSeparator(' - ');
// Setting the action, controller, module and site name as title segments
$siteName = 'Ribbon Cutters';
$translator = $this->getServiceLocator()->get('translator');
$title = $translator->translate($title);
$headTitleHelper->append(ucfirst($title));
$headTitleHelper->append($siteName);
}
}
Instead of defining methods, you can define properties.
public $headTitleHelper
and assign it in constructor of BaseController
$this->headTitleHelper = $this->getServiceLocator()->get('ViewHelperManager')->get('headTitle');
Now you can use $this->headTitleHelper in child controllers.
And then
<?php
namespace Application\Controller;
use Zend\View\Model\ViewModel;
use Application\Controller\BaseController;
class IndexController extends BaseController
{
/**
* Property for setting entity manager of doctrine
*/
protected $em;
/**
* landing page
*
* #return ViewModel
*/
public function indexAction()
{
$this->setHeadTitle('Welcome'); // Welcome - Ribbon Cutters
$viewModel = new ViewModel();
return $viewModel;
}
/**
* Sets and gives Doctrine Entity Manager
*
* #return Doctrine Entity Manager
*/
protected function getEntityManager()
{
if (null === $this->em) {
$this->em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
}
return $this->em;
}
}
I think this can help you.
I have trouble with dependencies in my application in service layer.
I have following class:
<?php
class UserService{
private $userRepository;
private $vocationService;
private $roleService;
public function __construct(UserRepository $userRepository, VocationService $vocationService, RoleService $roleService)
{
$this->userRepository = $userRepository;
$this->vocationService = $vocationService;
$this->roleService = $roleService;
}
}
There are only three dependencies which I'm injecting.
Assume, I want to add next dependency, for example: NextService.
My constructor will grow again.
What if I wanted to pass more dependencies within constructor ?
Maybe should I solve this problem by passing IoC container and then get desirable class?
Here is an example:
<?php
class UserService{
private $userRepository;
private $vocationService;
private $roleService;
public function __construct(ContainerInterface $container)
{
$this->userRepository = $container->get('userRepo');
$this->vocationService = $container->get('vocService');
$this->roleService = $container->get('roleService');
}
}
But now my UserService class depends on IoC container which I'm injecting.
How to solve a problem following good practices?
Regards, Adam
Injecting the container as a dependency to your service is considered as a bad practice for multiple reasons. I think the main point here is to figure out why and then try to understand the problem that leads you to think about "injecting the container" as a possible solution and how to solve this problem.
In object oriented programming, it's important to clearly define the relations between objects. When you're looking at a given object dependencies, it should be intuitive to understand how the object behaves and what are the other objects it relies on by looking at its public API.
It's also a bad idea to let your object rely on a dependency resolver, In the example you shared your object can't live without the container which is provided by the DI component.
If you want to use that object elsewhere, in an application that uses another framework for example, you'll then have to rethink the way your object get its dependencies and refactor it.
The main problem here is to understand why your service needs all these dependencies,
In object-oriented programming, the single responsibility principle
states that every context (class, function, variable, etc.) should
define a single responsibility, and that responsibility should be
entirely encapsulated by the context. All its services should be
narrowly aligned with that responsibility.
Source: Wikipedia
Based on this definition, I think you should split your UserService into services that handle only one responsability each.
A service that fetch users and save them to your dababase for example
Another service that manages roles for example
... and so on
I agree that __construct can grow fairly easy.
However, you have a Setter DI at your disposal: http://symfony.com/doc/current/components/dependency_injection/types.html#setter-injection
Morover, there is a Property DI, but I wouldn't recommed it as ti leaves your service wide-open to manipulation: http://symfony.com/doc/current/components/dependency_injection/types.html#property-injection
You can abstract some of the commonly used services in one helper service and then just inject this helper into your other services. Also you can define some useful functions in this helper service. Something like this:
<?php
namespace Saman\Library\Service;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Component\Form\FormFactory;
use Symfony\Bundle\FrameworkBundle\Translation\Translator;
use Symfony\Bundle\TwigBundle\Debug\TimedTwigEngine;
use Symfony\Component\Security\Core\SecurityContext;
use Doctrine\ORM\EntityManager;
class HelperService
{
protected $translator;
protected $securityContext;
protected $router;
protected $templating;
protected $em;
public function __construct(
Translator $translator,
SecurityContext $securityContext,
Router $router,
TimedTwigEngine $templating,
EntityManager $em
)
{
$this->translator = $translator;
$this->securityContext = $securityContext;
$this->router = $router;
$this->templating = $templating;
$this->em = $em;
}
Getters ...
public function setParametrs($parameters)
{
if (null !== $parameters) {
$this->parameters = array_merge($this->parameters, $parameters);
}
return $this;
}
/**
* Get a parameter from $parameters array
*/
public function getParameter($parameterKey, $defaultValue = null)
{
if (array_key_exists($parameterKey, $this->parameters)) {
return $this->parameters[$parameterKey];
}
return $defaultValue;
}
}
Now imagine you have a UserService then you define it like this:
<?php
namespace Saman\UserBundle\Service;
use Saman\Library\Service\HelperService;
class UserService
{
protected $helper;
public function __construct(
Helper $helper,
$parameters
)
{
$this->helper = $helper;
$this->helper->setParametrs($parameters);
}
public function getUser($userId)
{
$em = $this->helper->getEntityManager();
$param1 = $this->helper->getParameter('param1');
...
}
This example was created for Symfony 4 but the principle should work in older versions.
As others have mentioned, it's good to engineer your application to limit the functional scope of each service and reduce the number of injections on each consuming class.
The following approach will help if you truely need many injections, but it's also a nice tidy way to reduce boilerplate if you are injecting a service in many places.
Consider a service App\Services\MyService that you wish to inject into App\Controller\MyController:
Create an 'Injector' trait for your service.
<?php
// App\Services\MyService
namespace App\DependencyInjection;
use App\Services\MyService;
trait InjectsMyService
{
/** #var MyService */
protected $myService;
/**
* #param MyService $myService
* #required
*/
public function setMyService(MyService $myService): void
{
$this->myService = $myService;
}
}
Inside your controller:
<?php
namespace App\Controller;
class MyController
{
use App\DependencyInjection\InjectsMyService;
...
public myAction()
{
$this->myService->myServiceMethod();
...
}
...
}
In this way:
a single line of code will make your service available in any container managed class which is super handy if you're using a service in many places
it's easy to search for your injector class to find all usages of a service
there are no magic methods involved
your IDE will be able to auto-complete your protected service instance property and know it's type
controller method signatures become simpler, containing only arguments
If you have many injections:
<?php
namespace App\Controller;
use App\DependencyInjection as DI;
class SomeOtherController
{
use DI\InjectsMyService;
use DI\InjectsMyOtherService;
...
use DI\InjectsMyOtherOtherService;
...
}
You can also create an injector for framework provided services, e.g. the doctrine entity manager:
<?php
namespace App\DependencyInjection;
use Doctrine\ORM\EntityManagerInterface;
trait InjectsEntityManager
{
/** #var EntityManagerInterface */
protected $em;
/**
* #param EntityManagerInterface $em
* #required
*/
public function setEm(EntityManagerInterface $em): void
{
$this->em = $em;
}
}
class MyClass
{
...
use App\DependencyInjection\InjectsEntityManager;
A final note: I personally wouldn't try to make these injectors any smarter than what I've outlined. Trying to make a single polymorphic injector will probably obfuscate your code and limit your IDE's ability to auto-complete and know what type your services are.