Symfony Passing Doctrine Paginator to a service - symfony

I want to une the class Doctrine\ORM\Tools\Pagination\Paginator in a service in symfony, so the service return an instance for this class does not exist. with best practice shoud i pass the class directly or create a service who return an instance of this class.

I suggest to create a service using Factory Method pattern to instantiate the Paginator class with required params (Query/QueryBuilder and fetchJoinCollection) and inject it into your class. Using this approach will help you to mock the Paginator object while unit testing your service.

Create a private Service as parametre
services:
your.service.name:
class: AppBundle\Services\YourClassName
arguments: ['#doctrine.services.paginator']
doctrine.services.paginator:
class: Doctrine\ORM\Tools\Pagination\Paginator
public: false

Related

Symfony: How to use configuration parameters in a service class?

I want to use the parameters in parameters.yml in my service class mailer
but I got this error while instantiate the mailer class:
$mailer = new Mailer();
knowing that the parameters are defined in parameters.yml:
Warning: Missing argument 1 for AppBundle\Service\Mailer::__construct(), called in
namespace AppBundle\Service
class Mailer
{
private $mailer_user;
private $mailer_password;
private $mailer_name;
private $mailer_host;
public function __construct($mailer_user, $mailer_password ,$mailer_name ,$mailer_host)
{
$this->mailer_name = $mailer_user;
$this->mailer_password = $mailer_password;
$this->mailer_user = $mailer_name;
$this->mailer_host = $mailer_host;
}
//.....
}
services.yml:
mailer:
class: 'AppBundle\Service\Mailer'
arguments: [%mailer_user%, %mailer_password% ,%mailer_name% ,%mailer_host%]
To learn "how can I use the parameters in parameters.yml in my service class" (and to see how a service can be used in a Symfony app) just read the Symfony docs paying attention to the Symfony version of the documentation you are reading:
Introduction to Parameters
How to Set external Parameters in the Service Container
Service Container
BTW you should never instanciate a service class directly like you did:
$mailer = new Mailer();
but retrieve the service instance from the Service Container (Symfony will take care to automatically inject all the configured dependancies) like we usually do in a Controller (the example below access the Service using the shortcode provided extending the base Controller provided by the FrameworkBundle included in the Symfony standard version):
$mailer = $this->container->get('mailer');

How to create and use a form in a custom class in Symfony 2

I need to submit data to a FormType outside of a controller in a custom class that is registered as a service. The FormType itself is also registered as as service and injected into my custom class (ApiResponseMapper). This is a simplified snippet:
class ApiResponseMapper
{
private $bestSellerListsType;
public function mapResponse($response)
{
$form = $this->bestSellerListsType;
$form->submit($response);
}
public function __construct(BestsellerListType $bestSellerListsType)
{
$this->bestSellerListsType = $bestSellerListsType;
}
}
The submit() method does not exist on the form.
How can I initialize my form properly so that I have access to the submit() method?
If the form was used in a controller, I'd have access to the createForm method and do:
$form = $this->createForm('my_form_as_a_service');
Since I'm trying to use the form in a class that does not extend the Controller, there is no such option
You need FormFactory to work with custom form types like you do in controllers. Besides your form inject form factory to your service:
arguments: [#your_form, #form.factory]
Then use create method like you do in controllers:
$this->formFactory->create(new MyType(), $data, $options);
Btw, Symfony uses facade design pattern in controllers that let you easy access to various data. You can just check implementation of controller's method and do same yourself.
I thought I'd post another in case somebody wants to handle creating a form with form.factory in the configuration of the services (services.yml in my case):
bestseller_list_form_type:
class: Air\BookishBundle\Form\Type\BestsellerListType
name:
- { name: form.type, alias: bestseller_list_form }
bestseller_list_form:
factory_service: form.factory
factory_method: create
class: Symfony\Component\Form\Form
arguments: [#bestseller_list_form_type]
api_response_mapper:
class: Air\BookishBundle\Lib\ApiResponseMapper
arguments: [#bestseller_list_form]
This way the factory method is invoked upon object instantiation. Then you have the form ready to use in api_response_mapper and there is no need to call the factory manually ($this->formFactory->create(...);).

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-How to use access a service from outside of a controller

In my Symfony2 controller, this works fine:
$uploadManager = $this->get('upload.upload_manager');
but when I move it to a custom Listener:
use Doctrine\ORM\Event\LifecycleEventArgs;
use Acme\UploadBundle\Upload\UploadManager;
class PersonChange
{
public function postRemove(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$entityManager = $args->getEntityManager();
$uploadManager = $this->get('ep_upload.upload_manager');
echo "the upload dir is " . $uploadManager->getUploadDir();
}
}
I get an error:
Fatal error: Call to undefined method Acme\MainBundle\Listener\PersonChange::get() in /home/frank/...
I know I must need a use statement but don't know what to use.
Update: Defining controllers as services is no longer officially recommended in Symfony.
The get() method in the Controller class is just a helper method to get services from the container, and it was meant to get new Symfony2 developers up to speed faster. Once people get comfortable with the framework and dependency injection, it's recommended to define controllers as services and inject each required service explicitly.
Since your PersonChange class is not a controller and doesn't extend the Controller class, you don't have that get() helper method. Instead, you need to define your class as a service and inject needed services explicitly. Read the Service Container chapter for details.
As I ran into the exact same problem maybe I can help
What Elnur said is perfectly fine and I'll just try to pop up a real life example.
In my case I wanted to access
$lucenemanager = $this->get('ivory.lucene.manager')
Even by extending the controller I couldn't get it to work while the controller does access the container (I still did not understand why)
In config.yml my listener (searchindexer.listener) is declared as follow :
services:
searchindexer.listener:
class: ripr\WfBundle\Listener\SearchIndexer
arguments:
luceneSearch: "#ivory_lucene_search"
tags:
- { name: doctrine.event_listener, event: postPersist }
A service (ivory.lucene.search) is passed as argument in my service/listener.
Then in my class
protected $lucenemanager;
public function __construct($luceneSearch)
{
$this->lucenemanager = $luceneSearch;
}
Then you can use the get method against $this
An approach that always works, despite not being the best practice in OO
global $kernel;
$assetsManager = $kernel->getContainer()->get('acme_assets.assets_manager');‏
If you need to access a Service, define it in the class constructor:
class PersonChange{
protected $uploadManager;
public function __construct(UploadManager $uploadManager){
$this->uploadManager = $uploadManager;
}
// Now you can use $this->uploadManager.
}
Now you can pass the Service as argument when calling the class (example 1) or define the clas itself as a Service (recommended, example 2)
Example 1:
use Acme\PersonChange;
class appController{
function buzzAction(){
$uploadManager = $this->get('upload.upload_manager');
$personChange = new PersonChange($uploadManager);
Example 2 (better):
Define PersonChange as a Service itself, and define the other Service as an argument in services.yml file:
# app/config/services.yml
services:
upload.upload_manager:
class: AppBundle\uploadManager
PersonChange:
class: AppBundle\PersonChange
arguments: ['#upload.upload_manager']
In this way, you don't have to bother with the upload_manager service in the Controller, since it's implicitely passed as an argument for the constructor, so your Controller can be:
class appController{
function buzzAction(){
$personChange = $this->get('PersonChange');

Resources