Cannot autowire service - php

I am trying to implement UserManager from FOSUserBundle (Symfony3.4).
Service/Register.php
<?php
namespace AppBundle\Service;
use FOS\UserBundle\Model\UserManager;
class Register
{
private $userManager;
public function __construct(UserManager $userManager)
{
$this->userManager = $userManager;
}
public function register() {
$user = $this->userManager->findUserByUsernameOrEmail('aaa#gmail.clom');
if($user){
return false;
}
return true;
}
}
When I try call this method I get:
Cannot autowire service "AppBundle\Service\Register": argument "$userManager" of method "__construct()" references class "FOS\UserBundle\Model\UserManager" but no such service exists. You should maybe alias this class to the existing "fos_user.user_manager.default" service.
What should I do now?

I had a similar problem (in Symfony 4, but the principles should apply to 3.4) with a different service and managed to find the answer on the Symfony doc page Defining Services Dependencies Automatically (Autowiring).
Here's an extract from that page:
The main way to configure autowiring is to create a service whose id exactly matches its class. In the previous example, the service's id is AppBundle\Util\Rot13Transformer, which allows us to autowire this type automatically.
This can also be accomplished using an alias.
You need an alias because the service ID doesn't match the classname. So do this:
# app/config/services.yml
services:
# ...
# the `fos_user.user_manager.default` service will be injected when
# a `FOS\UserBundle\Model\UserManager` type-hint is detected
FOS\UserBundle\Model\UserManager: '#fos_user.user_manager.default'

Related

How to use entity manager in a service past symfony 3.4? [duplicate]

I am developing a Symfony 3 application. Symfony profiler logs tell me:
Relying on service auto-registration for type "App\Entity\SubDir\Category"
is deprecated since version 3.4 and won't be supported in 4.0.
Create a service named "App\Entity\SubDir\Category" instead.
Yet, this is a simple ORM bean:
/**
* #ORM\Entity
* #ORM\Table(name="category")
*/
class Category
{
...
How should I get rid of this issue? Do I really need to declare ORM entities as services in services.yaml? If yes, how?
Update
In fact, my entity is in a sub directory. I have amended my question.
In my service.yaml, I have tried:
App\:
resource: '../src/*'
exclude: '../src/{Entity,Repository,Tests,Entity/SubDir}'
...but to no avail.
Do you have any Classes under Service-auto registration which use an Entity as constructor argument?
That's where your problem comes from.
You need to ask yourself if the concerning class really is a service or just a plain object of which you always create the instance yourself.
If it is not used as a service through the container you have 2 options:
You can exclude this class also through the glob pattern like for example
AppBundle\:
resource: '...'
# you can exclude directories or files
# but if a service is unused, it's removed anyway
exclude: '../../{Entity,PathToYourNotService}'
or you can set the following parameter in your config
parameters:
container.autowiring.strict_mode: true
with this option the container won't try to create a service class with arguments that are not available as services and you will get a decisive error. This is the default setting for sf4
A good example for a class that triggers exactly this error would be a custom event class that takes an entity as payload in the constructor:
namespace AppBundle\Event;
use AppBundle\Entity\Item;
use Symfony\Component\EventDispatcher\Event;
class ItemUpdateEvent extends Event
{
const NAME = 'item.update';
protected $item;
public function __construct(Item $item)
{
$this->item = $item;
}
public function getItem()
{
return $this->item;
}
}
Now if this file isn't excluded specifically the container will try to auto register it as service. And because the Entity is excluded it can't autowire it. But in 3.4 there's this fallback which triggers this warning.
Once the strict_mode is activated the event just won't be available as service and if you tried using it as one an error would rise.

How to add a constructor argument to Symfony Service

I create a service that uses FilesystemCache, I don't want to make a new FilesystemCache each time I call the service so I have an argument in the service constructor where I can give an instance.
What I have so far:
Service class:
class MyService
{
private $cache;
private $url;
/**
* MyService constructor.
* #param FilesystemCache $cache
*/
public function __construct(FilesystemCache $cache)
{
$this->cache = $cache;
}
private function data()
{
if ($this->cache->has('data')) {
$data = $this->cache->get('data');
} else {
$data = file_get_contents("my/url/to/data");
}
return $data;
}
}
Config:
services:
# Aliases
Symfony\Component\Cache\Adapter\FilesystemAdapter: '#cache.adapter.filesystem'
# Services
services.myservice:
class: AppBundle\Services\MyService
arguments:
- '#cache.adapter.filesystem'
Where I use the Service:
$myService = $this->container->get('services.myservice');
But what I get is an error:
The definition "services.myservice" has a reference to an abstract definition "cache.adapter.filesystem". Abstract definitions cannot be the target of references.
So, my question is how I have to modify my service or my declaration, or whatever, to be able to do what I want to do: not create an instance each time I call the service.
I highly recommand you to use the cache.app service instead of your own filesystem.cache. Also, you could create your own Adapter.
To make that posible I had to register a new service with the class I want to use in my service constructor. So, my services.yml would be like:
services:
filesystem.cache:
class: Symfony\Component\Cache\Simple\FilesystemCache
services.myservice:
class: AppBundle\Services\MyService
arguments:
- '#filesystem.cache'
and now I'm able to use my service without getting an error.
Using the standard S3.3 autowire setup, this worked for me:
// services.yml
// This basically gives autowire a concrete cache implementation
// No additional parameters are needed
Symfony\Component\Cache\Simple\FilesystemCache:
// And there is no need for any entry for MyService
....
// MyService.php
use Psr\SimpleCache\CacheInterface;
public function __construct(CacheInterface $cache)
This of course will only work if you only have one concrete implementation of CacheInterface in your container.

Disable SonataUserBundle sonata.user.admin.group service

I'm working with SonataAdminBundle and SonataUserBundle.
SonataUserBundle registers a service sonata.user.admin.group which is automatically detected by SonataAdminBundle to set links in the admin dashboard to group CRUD operations.
How can I disable sonata.user.admin.group? I've been following that recipes in Symfony2 documentation:
How to Override any Part of a Bundle - Services and Configuration
Compiling the Container - Creating a Compiler Pass
Working with Container Parameters and Definitions
So far, I have the following code in my bundle definition to add a compiler pass:
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new CompilerPass());
}
And here it is the compiler pass:
<?php
namespace NS\Service\CompilerPass;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class CompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$container->removeDefinition('sonata.user.admin.group');
}
}
I thought that this should work but no. Symfony is throwing an exception telling me that sonata.user.admin.group service does not exist. But it exists, and if I do $container->getDefinition('sonata.user.admin.group') the actual definition is return.
Thanks
Try marking the service as abstract and set its public property to false e.g.
#in any services.yml
services:
sonata.user.admin.group:
abstract: true
public: false
#...
Addition to completeness:
And add to the CompilerPass:
$container->getDefinition('sonata.user.admin.group')->setSynthetic(true);
You've removed the service definition but it's still used on the dashboard. That's why Symfony complains (dashboard tries to access it). It's not an optional service.
You could try to overwrite the dashboard template and avoid using the service? This way service wouldn't be called and you wouldn't have to remove it. If service is not used it's never created.
Alternative would be overloading the service with your implementation.

Symfony2: get Doctrine in a generic PHP class

In a Symfony2 project, when you use a Controller, you can access Doctrine by calling getDoctrine() on this, i.e.:
$this->getDoctrine();
In this way, I can access the repository of such a Doctrine Entity.
Suppose to have a generic PHP class in a Symfony2 project. How can I retrieve Doctrine ?
I suppose that there is such a service to get it, but I don't know which one.
You can register this class as a service and inject whatever other services into it. Suppose you have GenericClass.php as follows:
class GenericClass
{
public function __construct()
{
// some cool stuff
}
}
You can register it as service (in your bundle's Resources/config/service.yml|xml usually) and inject Doctrine's entity manager into it:
services:
my_mailer:
class: Path/To/GenericClass
arguments: [doctrine.orm.entity_manager]
And it'll try to inject entity manager to (by default) constructor of GenericClass. So you just have to add argument for it:
public function __construct($entityManager)
{
// do something awesome with entity manager
}
If you are not sure what services are available in your application's DI container, you can find out by using command line tool: php app/console container:debug and it'll list all available services along with their aliases and classes.
After checking the symfony2 docs i figured out how to pass your service
in a custom method to break the default behavior.
Rewrite your configs like this:
services:
my_mailer:
class: Path/To/GenericClass
calls:
- [anotherMethodName, [doctrine.orm.entity_manager]]
So, the Service is now available in your other method.
public function anotherMethodName($entityManager)
{
// your magic
}
The Answer from Ondrej is absolutely correct, I just wanted to add this piece of the puzzle to this thread.

Symfony2 global functions

For example i have algorithmic function, which calculates specific hash-code. Function itself is 300+ lines of code. I need to use that functions many times in many different controllers in my bundle. Where can i store my calculate_hash() to use it in my bundle ? Can i access it from other bundles ?
Can i also write global calculate_hash() which have access to entity manager ?
Didn't find my answer here.
In the Symfony2 world, this is clearly belonging to a service. Services are in fact normal classes that are tied to the dependency injection container. You can inject them the dependencies you need. For example, say your class where the function calculate_hash is located is AlgorithmicHelper. The service holds "global" functions. You define your class something like this:
namespace Acme\AcmeBundle\Helper;
// Correct use statements here ...
class AlgorithmicHelper {
private $entityManager;
public function __construct(EntityManager $entityManager) {
$this->entityManager = $entityManager;
}
public function calculate_hash() {
// Do what you need, $this->entityManager holds a reference to your entity manager
}
}
This class then needs to be made aware to symfony dependecy container. For this, you define you service in the app/config/config.yml files by adding a service section like this:
services:
acme.helper.algorithmic:
class: Acme\AcmeBundle\Helper\AlgorithmicHelper
arguments:
entityManager: "#doctrine.orm.entity_manager"
Just below the service, is the service id. It is used to retrieve your service in the controllers for example. After, you specify the class of the service and then, the arguments to pass to the constructor of the class. The # notation means pass a reference to the service with id doctrine.orm.entity_manager.
Then, in your controller, you do something like this to retrieve the service and used it:
$helper = $this->get('acme.helper.algorithmic');
$helper-> calculate_hash();
Note that the result of the call to $this->get('acme.helper.algorithmic') will always return the same instance of the helper. This means that, by default, service are unique. It is like having a singleton class.
For further details, I invite you to read the Symfony2 book. Check those links also
The service container section from Symfony2 book.
An answer I gave on accesing service outside controllers, here.
Hope it helps.
Regards,
Matt
Braian in comment asked for Symfony 3 answer, so here is one Symfony 3.3 (released May 2017):
1. The original class remains the same
namespace Acme\AcmeBundle\Helper;
use Doctrine\ORM\EntityManager;
final class AlgorithmicHelper
{
/**
* #var EntityManager
*/
private $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
public function calculateHash()
{
// Do what you need, $this->entityManager holds a reference to your entity manager
}
}
2. Service registration is much simpler
# app/config/services.yml
services:
_defaults: autowire # this enabled constructor autowiring for all registered services
Acme\AcmeBundle\Helper\AlgorithmicHelper: ~
3. Use constructor injection to get the service
use Acme\AcmeBundle\Helper\AlgorithmicHelper;
class SomeController
{
/**
* #var AlgorithmicHelper
*/
private $algorithmicHelper;
public function __construct(AlgorithmicHelper $algorithmicHelper)
{
$this->algorithmicHelper = $algorithmicHelper;
}
public function someAction()
{
// some code
$hash = $this->algorithmicHelper->calculateHash();
// some code
}
}
You can read about Symfony 3.3 dependency injection (in this case registering services in config and using it in controller) news in these 2 posts:
https://www.tomasvotruba.cz/blog/2017/05/07/how-to-refactor-to-new-dependency-injection-features-in-symfony-3-3/
https://symfony.com/blog/the-new-symfony-3-3-service-configuration-changes-explained

Categories