i'm trying to call an utility class from a scope of a controller action.
As there will be also later another utility classes for other entites that shares some of the same dependencies, i will them extend from an abstarct class, which will be injected with entity manager.
But this doesn't work. I got for the following implementation an Exception,
""Too few arguments to function ....\Import\ImportHandle::__construct(), 0 passed in ....../Controller/ImportController.php on line 221 and exactly 1 expected""
+++++
Call from ControllerAction line 221:
$importer = new $XyImport();
+++++++
Service declaration:
...\ImportHandle:
abstract: true
public: true
arguments: ['#doctrine.orm.xy_entity_manager']
...\XyImport:
parent: ..\ImportHandle
++++++
abstract class ImportHandle
{
**
* #var EntityManager
*/
protected EntityManager $entityManager;
/**
* #param EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
}
Really, what am i doing wrong?
Regards
Related
Foreword: please disregard injecting the whole container and other unclean things, I just wanted to show not-working example, this is before refactor.
I have a service defined in YAML:
app.service.my_service:
class: App\Services\MyService
public: true
calls:
- [setContainer, ['#service_container']]
part of my service:
class MyService implements ContainerAwareInterface
{
/** #var ContainerInterface */
private $container;
/** #var EntityManagerInterface */
private $em;
public function setContainer(?ContainerInterface $container = null)
{
$this->container = $container;
$this->em = $container->get('doctrine')->getManager();
}
Then I have a controller, which have that service autowired in constructor, and not instantiaded from container:
class MyController
{
/**
* #var MyService
*/
private $my_service;
function __construct(MyService $my_service) {
$this->my_service = $my_service;
}
While it actually autowires service itself, it completely ignores the setContainer call, so I end up with empty container.
I wanted to avoid calling $this->get('app.service.my_service') everywhere and I can't simply call it once in controller's contructor, or call setContainer in the constructor on autowired service, because at that time container is empty.
Any chances I can do it in a clean way?
Since you already know injecting the container is a bad idea then I guess I'll spare you the lecture. I have not encountered this particular issue but you can try using the little known '#required' container directive and see if it helps.
/** #required */
public function setContainer(ContainerInterface $container)
{
$this->container = $container;
$this->em = $container->get('doctrine.orm.default_entity_manager);
}
This is more of a guess than an answer but it is easy to try.
Update: It worked. Cool.
I just wanted to add that, in most cases, dependencies should be injected into the constructor. This avoids having partially initialized services. On the other hand, as long as you are generating your services via the container then it is not really a problem.
I find it to be very useful in traits. For example:
trait RouterTrait
{
private RouterInterface $router;
/** #required */
public function setRouter(RouterInterface $router)
{
$this->router = isset($this->router) ? $this->router: $router;
}
// These are just copied from AbstractController
protected function generateUrl(
return $this->router->generate(...
protected function redirectToRoute(
#
class SomeService
use RouterTrait;
public function someMethod()
$this->generateUrl('''
So if a service needs to do some routing then they just need to use the router trait and the router is automatically injected. Saves adding boilerplate code to the service's constructor. I do put a guard around setRouter so it can only be effectively called once which eliminates another concern about using setters for dependencies.
Update #2:
The problem with the original code is that the MyService was defined with a service is of app.service.my_service and not the class name. Autowire actually generated a second MyService and injected it since autowire looks for service ids that match the class name. Changing the service definition to:
App\Services\MyService:
public: true
calls:
- [setContainer, ['#service_container']]
Would have also worked. Or explicitly injecting app.service.my_service into the controller.
I am trying to inject a variable in a class setter but it doesn't work.
This is my configuration :
Company\CoreBundle\Services\InspectorManager\InspectorManager:
public: true
autowire: false
App\Helpers\GraphQl\Resolver\OrderResolver:
autowire: false
calls:
- [setInspector, ['#core.inspector_manager']]
And Class
class OrderResolver
{
protected $em;
/**
* #var InspectorManager $inspectorManager
*/
protected $inspectorManager;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
/**
* #var InspectorManager $inspectorManager
* #required
*/
public function setInspector(InspectorManager $inspectorManager)
{
$this->inspectorManager = $inspectorManager;
}
}
setInspector is never called. The class InspectorManager comes from another bundle from another Symfony version(2.8) and is declared this way in it :
core.inspector_manager:
class: Company\CoreBundle\Services\InspectorManager\InspectorManager
arguments: ['#doctrine.orm.entity_manager']
calls:
- [setGeolocalisation, ['#core.geolocation_service']]
Do you have an idea why it isn't working ? I have spend way too much time in this issue and I don't see any way to solve it.
If I run debug:autowiring I get the InspectorManager in it.
I already changed the autowire setting of InspectorManager and OrderResolver to true but that doesn't change anything.
I want to create HelperController for my project. I generate a controller with doctrine:generate:controller and I need to use entity manager in it.
I enjected to services.yml but it is giving an error like this:
Argument 1 passed to CampingBundle\Controller\HelperController::__construct() must be an instance of Doctrine\ORM\EntityManager, none given ...
My Controller Code :
namespace CampingBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Doctrine\ORM\EntityManager;
class HelperController extends Controller
{
protected $manager;
public function __construct(EntityManager $manager)
{
$this->manager = $manager;
}
My Services.yml :
services:
camping.helper_controller:
class: CampingBundle\Controller\HelperController
arguments: ["#doctrine.orm.entity_manager"]
Why it doesn't work ? Shoudl I clear cache or something else or is there anything wrong in definition ?
Thanks
Try to use EntityManagerInterface and remove extends Controller.
Check this link if you need CAS (Controllers as Services).
Change protected $manager; to private $manager;
namespace CampingBundle\Controller;
use Doctrine\ORM\EntityManagerInterface;
class HelperController
{
/**
* #var EntityManagerInterface $entityManager
*/
private $entityManager;
/**
* #param $entityManager
*/
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
}
I'll leave my two cents here as I had same issue and fixed it by adding tags to service.
something.validate.some_service:
class: Path\To\Some\Validator
arguments:
- '#doctrine.orm.entity_manager'
tags:
- { name: validator.constraint_validator, alias: some_validator_alias }
How to Work with Service Tags by Symfony
I build my business logic with propel. All the models extends an abstract model, in which I want to inject the security context:
Services.yml
parameters:
Abstract.Model : 'FooBundle\Model\AbstractModel'
services:
Abstract.Model:
class: 'FooBundle\Model\AbstractModel'
calls:
- [setSecurity, ["#security.context"]]
The AbstractModel
abstract class AbstractModel extends BaseObject
{
/**
* #param $security \Symfony\Component\Security\Core\SecurityContext
*/
protected $security;
/**
* Sets the security context
*
* #param $security \Symfony\Component\Security\Core\SecurityContext
*/
public function setSecurity($security)
{
$this->security = $security;
}
/**
* Code to be run before persisting the object
*
* #param PropelPDO $con
* #return boolean
*/
public function preSave(\PropelPDO $con = null)
{
$token = $this->security->getToken();
}
}
If propel runs the preSave method, it responses with a 500:
FatalErrorException: Error: Call to a member function getToken() on a non-object
Anyone know whats going wrong here?
Greetings
Update_01:
public function save(PropelPDO $con = null, $skipReload = false)
{
...
$ret = $this->preSave($con);
}
An abstract class cannot be instantiated.
Extend your Abstract class:
class MyModel extends AbstractModel
{
}
and use this class as service:
services:
My.Model:
class: 'FooBundle\Model\MyModel'
calls:
- [setSecurity, ["#security.context"]]
What's the difference between Doctrine\Common\Persistence\ObjectManager and Doctrine\ORM\EntityManager when using it in a custom form type?
I can get the respository using both $this->em->getRepository() and $this->om->getRepository().
class MyFormType extends \Symfony\Component\Form\AbstractType
{
/**
* #var Doctrine\ORM\EntityManager
*/
protected $em;
public function __construct(Doctrine\ORM\EntityManager $em)
{
$this->em = $em;
}
}
Instead of:
class MyFormType extends \Symfony\Component\Form\AbstractType
{
/**
* #var Doctrine\Common\Persistence\ObjectManager
*/
protected $om;
public function __construct(Doctrine\Common\Persistence\ObjectManager $om)
{
$this->om = $om;
}
}
ObjectManager is an interface and EntityManager is its ORM implementation. It's not the only implementation; for example, DocumentManager from MongoDB ODM implements it as well. ObjectManager provides only the common subset of all its implementations.
If you want your form type to work with any ObjectManager implementation, then use it. This way you could switch from ORM to ODM and your type would still work the same. But if you need something specific that only EntityManager provides and aren't planning to switch to ODM, use it instead.