How to decorate final class Sonata\PageBundle\Admin\BlockAdmin - symfony

Since Sonata\PageBundle\Admin\BlockAdmin is #final i should not extend from it. In UPGRADE-3x.md i can see that i should use decoration.
I am trying to this by decorating this final class with App\Sonata\PageBundle\Admin\PageAdmin.
services.yaml configuration:
parameters:
sonata.page.admin.page.class: App\Sonata\PageBundle\Admin\PageAdmin
services:
Sonata\PageBundle\Admin\PageAdmin:
alias: sonata.page.admin.page
App\Sonata\PageBundle\Admin\PageAdmin:
decorates: Sonata\PageBundle\Admin\PageAdmin
Decorator App\Sonata\PageBundle\Admin\PageAdmin body looks like this (i just invoke all the methods from final class in my decorator): https://gist.github.com/AVAW/910604534684eedb228f71df1d7deb40
But i get error:
Is there any possible way to decorate this final service or I am doing something wrong?
I am using:
sonata-project/admin-bundle: 3.107.3
sonata-project/page-bundle: 3.26.0
symfony: 4.4.41

Did you try to remove the alias and directly use the sonata service name in the "decorate" ? like this :
services:
App\Sonata\PageBundle\Admin\PageAdmin:
decorates: 'sonata.page.admin.page'

Related

How to use libphonenumber.phone_number_util in Symfony 4

To parse phone number I need to use libphonenumber.phone_number_util in my controller ( Symfony 4) as like as :
$parsed = $this->get('libphonenumber.phone_number_util')->parse($phoneNo);
as we have libphonenumber.phone_number_util in private I wanted to make it public by adding this helper in service as below:
services:
libphonenumber\PhoneNumberUtil:
alias: libphonenumber.phone_number_util
public: true
But this returns Exception and message:
"message": "The \"libphonenumber.phone_number_util\" service or alias has been removed or inlined when the container was compiled. You should either make it public, or stop using the container directly and use dependency injection instead.",
"class": "Symfony\\Component\\DependencyInjection\\Exception\\ServiceNotFoundException",
If you are using this in a controller method (which I presume you do based on $this->get(...)), you need to
1) Declare your controller as a service and tag it with controller.service_arguments tag
2) Make sure your util service id matches the class name (I suppose it does already). You don't need it to be public - that's and ancient approach
3) Require the util as a parameter to your controller's action method.
E.g.
services:
libphonenumber\PhoneNumberUtil:
alias: libphonenumber.phone_number_util
AppBundle\Controller\MyController:
tags: ['controller.service_arguments']
and
public function validatePhoneAction(Request $request, PhoneNumberUtil $phoneNumberUtil)
{
...
$phoneNumberUtil->parse($request->request->get('phone_number');
...
}
There is a nice Symfony blog post about these changes in dependency management: https://symfony.com/blog/new-in-symfony-3-4-services-are-private-by-default

monolog.logger.db service has been removed

I'm trying to refactor some Symfony 3 code to Symfony 4.
I am getting the following error when attempting to log:
The "monolog.logger.db" service or alias has been removed or inlined
when the container was compiled. You should either make it public, or
stop using the conta iner directly and use dependency injection
instead.
My logging code:
$logger = $container->get('monolog.logger.db');
$logger->info('Import command triggered');
Monolog config:
monolog:
channels: ['db']
handlers:
db:
channels: ['db']
type: service
id: app.monolog.db_handler
app.monolog.db_handler config (Note, I tried public: true here and it had no affect:
app.monolog.db_handler:
class: App\Util\MonologDBHandler
arguments: ['#doctrine.orm.entity_manager']
How can I get this wired up correctly in Symfony 4?
By default all services in Symfony 4 are private (and is the recommended pratice) so you need to "inject" in each Controller each needed service (personally I use a custom CommonControllerServiceClass).
You can also create a public service "alias" to continue accessing the service as you did, but it's not the best pratice to follow (also because I guess you will have many other services to fix).
mylogger.db:
alias: monolog.logger.db
public: true
then you can get the service from the container:
$logger = $container->get('mylogger.db');
Alister's answer is a good start, but you can utilise service arguments binding instead of creating a new service for each logger:
services:
_defaults:
autowire: true
bind:
$databaseLogger: '#monolog.logger.db'
Then just change the argument name in your class:
// in App\Util\MonologDBHandler.php
use Psr\Log\LoggerInterface;
public function __construct(LoggerInterface $databaseLogger = null) {...}
It appears that App\Util\MonologDBHandler may be the only thing that is actively using monolog.logger.db - via a container->get('...') call. (If not, you will want to use this technique to tag the specific sort of logger into more services).
You would be better to allow the framework to build the app.monolog.db_handler service itself, and use the container to help to build it. Normally, to inject a logger service, you will just need to type-hint it:
// in App\Util\MonologDBHandler.php
use Psr\Log\LoggerInterface;
public function __construct(LoggerInterface $logger = null) {...}
However, that will, by default, setup with the default #logger, so you need to add an extra hint in the service definition of the handler that you want a different type of logger:
services:
App\Log\CustomLogger:
arguments: ['#logger']
tags:
- { name: monolog.logger, channel: db }
Now, the logger in CustomLogger should be what you had previously known as monolog.logger.db.
You can also alias a different interface (similar to how the LoggerInterface is aliased to inject '#logger') to the allow for the tagging.

get container parameter in my custom classes Symfony2

I have the class, it declare as service. When I get() my service I run some method and this method require two params what I want to let user configure in config.yml. How I can get these parameters in this class? Maybe exist some way to do this in my service definition? Or I need extend my class from ContainerAware (if I am right its bad practice)? Thanks!
You can inject parameters into your service using %param_name% syntax
services.yml
services:
your_service:
class: Acme\DemoBundle\YourClass
arguments: [#some.other.service, %my_parameter%]
parameters.yml
parameters:
my_parameter: my_value
You can use call them using the constructor
acme.your_service:
class: Acme\DemoBundle\YourService
arguments: [%param1%]
in the class
class YourService {
protected $param1;
public function __construct($param1) {
$this->param1 = $param1;
}
}

Injecting service with JMS\DiExtraBundle

I have service PgHistService in subdirectory Service in DbExtensionBundle:
namespace Iba\DbExtensionBundle\Service;
class PgHistService { ...}
This service is defined in bundles's services.yml and can be sucessfully included in a controller via $this->get('pghist.service'):
parameters:
pghist.service.class: Iba\DbExtensionBundle\Service\PgHistService
services:
pghist.service:
class: %pghist.service.class%
arguments:
entityManager: "#doctrine.orm.entity_manager"
Now I want to inject it with JMS\DIExtraBundle in doctrine entity listener:
namespace Iba\DbExtensionBundle\Entity;
use JMS\DiExtraBundle\Annotation as DI;
class BaseEntityListener {
/** #DI\Inject("pghist.service") */
public $pgHist;
}
Variable pgHist is always null. What am I doing wrong, please? I tried to set this in config.yml but it doesn't work either:
jms_di_extra:
locations:
all_bundles: false
bundles: [DbExtensionBundle]
directories: ["%kernel.root_dir%/../vendor/iba/db-extension-bundle/Iba/DbExtensionBundle/Service"]
Jason Roman is right, thank you.
If you want to use JMS\DiExtraBunde together with entity listener, you have to use DIExtraBundle own system of invoking listener via annotation #DoctrineListener in listener instead of Doctrine standard one #EntityListeners in the entity.

symfony2 custom repository extending EntityRepository

I am trying to implement a custom repository class in symfony2, and I want it to extend EntityRepository class. I am having trouble with passing the getting arguments to the parent (i.e. EntityRepository) constructor. This is the signiture of parent constructor:
public function __construct($em, Mapping\ClassMetadata $class)
So I had to add this to my services.yml file, in order to get the arguments:
parameters:
user_provider.class: Untitled\F5Bundle\Security\UserRepository
services:
user_meta_data:
class: Doctrine\ORM\Mapping\ClassMetaData
arguments:
name: "Untitled\F5Bundle\Entity\User"
user_provider:
class: "%user_provider.class%"
arguments:
entityManager: "#doctrine.orm.entity_manager"
meta_data: "#user_meta_data"
And I also added the annotation tag to my User class (which I'm not sure if it was neccessary)
Now when I run it, it raises an error. the message says:
FatalErrorException: Error: Class 'Doctrine\ORM\Mapping\ClassMetaData' not found
in /mnt/data/Projects/F5/app/cache/dev/appDevDebugProjectContainer.php line 2749
(/mnt/data/Projects/F5/ is where I keep the code)
I don't get it. What's wrong here? What am I doing wrong?
Metadata is obtained with the MetadataFactory. As an example you can see how it works in EntityManager.
public function getClassMetadata($className)
{
return $this->metadataFactory->getMetadataFor($className);
}
You can retrieve you repository as service as well. Look at this question.
You don't need to inject these constructor arguments yourself, just specify which repository class you want to use:
/**
* #Entity(repositoryClass="MyProject\UserRepository")
*/
class User
{
...
}
See also http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html#entity
You miss typed classname "ClassMetaData" should be ClassMetadata
class: Doctrine\ORM\Mapping\ClassMetadata

Resources