I'm trying to create a manager to handle basic requests of a controller (list, new, edit, delete). I need to inject the form factory within the constructor of this service. By what name should I call?
I need something like this:
lp_ExpedienteManager:
class: AppBundle\Services\ExpedienteManager\ExpedienteManager
arguments: [ "#doctrine.orm.entity_manager", "#security.token_storage", "#form_factory" ]
Thanks for your time!
For future references, since Symfony 3.3 this service is available as Symfony\Component\Form\FormFactoryInterface. So you can inject in your services like
use Symfony\Component\Form\FormFactoryInterface;
class AccountBridge
{
private $formFactory;
public function __construct(FormFactoryInterface $formFactory)
{
$this->formFactory = $formFactory;
}
public function accountCreateAction(Account $account)
{
$form = $this->formFactory->create(AccountType::class, $account);
}
}
Related
Aware that there is a lot of information around the net regarding this, I am still having a lot of trouble getting this to work.
I have created a custom service:
<?php
namespace App\Service;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\AccommodationType;
use App\Entity\Night;
class AvailabilityChecks {
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function nightAvailable(string $RoomCode, string $NightDate) {
$GetRoom = $this->em->getDoctrine()->getRepository(AccommodationType::class)->findOneBy([
'RoomCode' => $RoomCode
]);
$RoomQnt = $GetRoom->getNightlyQnt();
$GetNight = $this->em->getDoctrine()->getRepository(Night::class)->findOneBy([
'RoomCode' => $RoomCode,
'NightDate' => $NightDate
]);
$NumberOfNights = $GetNight->count();
if($NumberOfNights<$RoomQnt) {
return true;
}
else {
return false;
}
}
}
and have put this in services.yaml:
AvailabilityChecks.service:
class: App\Service\AvailabilityChecks
arguments: ['#doctrine.orm.entity_manager']
So when I try and use this in my controller, I get this error:
Too few arguments to function App\Service\AvailabilityChecks::__construct(), 0 passed in /mypath/src/Controller/BookController.php on line 40 and exactly 1 expected
I just can't figure out why it's not injecting the ORM stuff into the constructor! Any help greatly appreciated
The problem is in your BookController. Even though you didn't posted its code I can assume you create new AvailabilityChecks in it (on line 40).
In Symfony every service is intantiated by service container. You should never intantiate service objects by yourself. Instead BookController must ask service container for AvailabilityChecks service. How should it do it ?
In Symfony <3.3 we used generally :
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class MyController extends Controller
{
public function myAction()
{
$em = $this->get('doctrine.orm.entity_manager');
// ...
}
}
Nowadays services can be injected in controllers using autowiring which is way easier:
use Doctrine\ORM\EntityManagerInterface;
class MyController extends Controller
{
public function myAction(EntityManagerInterface $em)
{
// ...
}
}
You are using the wrong service for what you want to do. The alias doctrine that is used, e.g. in the AbstractController when you call getDoctrine() is bound to the service Doctrine\Common\Persistence\ManagerRegistry.
So the code you wrote fits better with that and you should either add #doctrine or #Doctrine\Common\Persistence\ManagerRegistry to the service definition.
Both with your current configuration or the changed one, you don't have to call $this->em->getDoctrine(), because $this->em is already equivalent to $this->getDoctrine() from your controller. Instead you could create a (private) method to make it look more like that code, e.g.:
private function getDoctrine()
{
return $this->em;
}
Then you can call $this->getDoctrine()->getRepository(...) or use $this->em->getRepository(...) directly.
In Symfony 4, you dont need to create it as services. This is automatically now. Just inject the dependencies what you need in the constructor. Be sure that you have autowire property with true value in services.yml (it is by default)
Remove this from services.yml:
AvailabilityChecks.service:
class: App\Service\AvailabilityChecks
arguments: ['#doctrine.orm.entity_manager']
You dont need EntityManagerInterface because you are not persisting anything, so inject repositories only.
<?php
namespace App\Service;
use App\Entity\AccommodationType;
use App\Entity\Night;
use App\Repository\AccommodationTypeRepository;
use App\Repository\NightRepository;
class AvailabilityChecks {
private $accommodationTypeRepository;
private $nightRepository
public function __construct(
AcommodationTypeRepository $acommodationTypeRepository,
NightRepository $nightRepository
)
{
$this->acommodationTypeRepository = $acommodationTypeRepository;
$this->nightRepository = $nightRepository;
}
public function nightAvailable(string $RoomCode, string $NightDate) {
$GetRoom = $this->acommodationTypeRepository->findOneBy([
'RoomCode' => $RoomCode
]);
$RoomQnt = $GetRoom->getNightlyQnt();
$GetNight = $this->nightRepository->findOneBy([
'RoomCode' => $RoomCode,
'NightDate' => $NightDate
]);
$NumberOfNights = $GetNight->count();
if($NumberOfNights<$RoomQnt) {
return true;
}
else {
return false;
}
}
}
In SF4, you no longer need to specify dependencies required by your custom service in the service.yaml file. All you have to do is to use dependency injection.
So remove config lines, and call your service directly in the controller method :
<?php
namespace App\Controller;
use App\Service\AvailabilityChecks ;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class AppController extends AbstractController
{
public function index(AvailabilityChecks $service)
{
...
}
}
Having said that, i think you don't need custom service to do simple operations on database. Use repository instead.
Is it a good practice to have a service getter for frequently used services in a controller? For example I mean:
class SomeController Extends Contorller {
private function getSomethingManager()
{
return $this->get('myvendorname.something.manager');
}
}
Your example is a bit confusing because you can use the Doctrine service directly with your controller. You can inject it in your Action if you use the Autowire function.
public function test(EntityManagerInterface $em) {
}
Then you have the entity manager injected or you can load it over the controller with:
$this->getDoctrine()->getManager()
So this is not a real good example. When you use autowire all classes are registered as service and you can use it.
For database queries you have to use entities and repositories.
https://symfony.com/doc/current/doctrine.html
If you are above Symfony 3.3 you can use a Service Locater. You list all common services in Service Locator class. When you need to fetch a specific service from anywhere (from example, Controller, Command, Service so on), all you have to do is, inject ServiceLocator class and fetch required service via ServiceLocator:locate.
It is pretty simple and useful. It helps you to reduce dependency injection as well. Have a look at the full example in the link above.
class ServiceLocator implements ServiceLocatorInterface, ServiceSubscriberInterface
{
private $locator;
public function __construct(ContainerInterface $locator)
{
$this->locator = $locator;
}
public static function getSubscribedServices()
{
return [
ModelFactoryInterface::class,
CalculatorUtilInterface::class,
EntityManagerInterface::class,
AnotherClass::class,
AndAnother::class,
];
}
public function get(string $id)
{
if (!$this->locator->has($id)) {
throw new ServiceLocatorException(sprintf(
'The entry for the given "%s" identifier was not found.',
$id
));
}
try {
return $this->locator->get($id);
} catch (ContainerExceptionInterface $e) {
throw new ServiceLocatorException(sprintf(
'Failed to fetch the entry for the given "%s" identifier.',
$id
));
}
}
}
And this is how you use it: ServiceLocator->locate(AnotherClass::class);
Calling member function of other controller in zend framework3?
You should write a mailer class and inject it in to action and send mails on it. Probably you will need mailer class in few actions so an aware trait would be nice so you will not have to inject it on every action on __construct method. I think something like that can solve problem, so you can use your mailer service in anywhere you want. Just don't forget to inject it.
interface MailServiceInterface
{
public function send(string $to, string $from, string $subject, string $body, array $headers = []);
}
trait MailServiceAwareTrait
{
/**
* #var \Infrastructure\Mailer\MailServiceInterface
*/
protected $mailService;
public function setMailService(MailServiceInterface $mailService)
{
$this->mailService = $mailService;
}
public function getMailService(): MailServiceInterface
{
return $this->mailService;
}
}
class myAction extends AbstractActionControl
{
use MailServiceAwareTrait;
public function processAction()
{
$this->getMailService()->send($to, $from, $subject, $body);
}
}
"Sending emails" is a service, so typically it should be in a separate model file (aka service file), not in the controller. While you actually can put it in a controller as a function, but that will simply means you are completely misusing the MVC concept itself.
Anyway, I'll answer how to do it but I strongly do NOT recommend it. In your controller (for example, IndexController), this is what you can do:
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class IndexController extends AbstractActionController {
public function indexAction() {
// This below line will call FooController's barAction()
$otherViewModel = $this->forward()->dispatch(\Application\Controller\FooController::class, ['action'=>'bar']);
$otherViewModel->setTemplate('application/foo/bar');// you must set which template does this view use
return $otherViewModel;
}
}
I have a form event subscriber which needs an entity repository.
I would like to inject this repository dependency ideally without having to use the constructors of the subscriber and its parents because this subscriber is needed in many different forms.
So basically I have the following chain :
Controller calls -> CustomManagerService instantiates-> Form instantiates -> EventSubscriber needs-> EntityRepository
the maanager is already a service. It is a pain both to transmit a constructor repository argument from the manager through the form to the subscriber and it is a pain to set each form as a service.
Why can't I instantiate the repository in the subscriber directly ? I have read it is a bad practice.
EDIT : this is what I have so far :
in my controller :
$unitRepository = $this->getDoctrine()->getRepository('UnitRepository');
$myManager = $this->get('my_manager')
$form = $myManager->createForm($unitRepository);
in myManager:
public function createForm(UnitRepository $unitRepository){
return $this->formFactory->createForm(
new xxxType($unitRepository)
}
in my form:
use MyBundle/AddUnitFieldSubscriber;
protected $unitRepository;
public function __construct(UnitRepository $unitRepository)
{
$this->unitRepository = $unitRepository;
}
public function buildform()
{
$builder->addEventSubscriber(new AddUnitFieldSubscriber($this->unitRepository));
}
in my subscriber:
protected $unitRepository;
public function __construct(UnitRepository $unitRepository)
{
$this->unitRepository = $unitRepository;
}
public function preSetData(FormEvent $event)
{
$unitRepository = $this->unitRepository;
$unitRepository->doStuff()
}
I found this extremely lenghty, and sometimes I have a form calling a subform which is the one using the eventSubscriber. if I set the forms as services, I also sometimes get errors cause I am instantiating them without the required first constructur parameter.
What would be the shortest path to do it right and to not repeat all this knowing only the subscriber need access to the repository ?
Thanks a lot !
I'm really not sure I understood everything or even anything, but I'm going to try a response.
I would suggest you to define a service SubscriberProvider which will be responsible of the instantiation of the subscriber and the injection of the repository in the subscriber (via a setter of the subscriber). You could retrieve an instance of subscriber using a method get, retrieve, create, provide or whatever you prefer of the service SubscriberProvider. You could then inject this provider in another service.
EDIT
Here is the definition of the service related to your form type:
services:
your_own_bundle.form.type.unit:
class: Your\OwnBundle\Form\Type\UnitType
arguments:
- "#doctrine"
tags:
- { name: form.type, alias: unit }
And its class:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Doctrine\Common\Persistence\ManagerRegistry;
use Your\OwnBundle\Event\AddUnitFieldSubscriber;
class UnitType extends AbstractType
{
protected $unitRepository;
public function __construct(ManagerRegistry $doctrine)
{
$this->unitRepository = $doctrine->getRepository('UnitRepository');
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventSubscriber(
new AddUnitFieldSubscriber($this->unitRepository)
);
}
Then, you can use this type like that:
$builder->add('available_unit', 'unit', array());
This way, you don't have to pass the repository to your manager.
I would like to use a ResultFactory class as a service in my Symfony 2 application:
My Result factory class will be responsible to create a BaseResult instance.
Depending on the type passed to the get factory method, the ResultFactory will create the right ResultObject.
Here's what could be the code:
class ResultFactory
{
protected $translator;
public function __construct(Translator $translator)
{
$this->translator = $translator;
}
public function get($type, $param)
{
$instance = null;
switch ($type) {
case 'Type1':
$instance = new Type1Result($param);
break;
case 'Type2':
$instance = new Type2Result($param);
break;
}
return $instance;
}
}
My question is:
I would like to use a service in my ResultObject. How do i inject this service to my ResultObject?
Thanks!
You are not using your service inside a result object. your factory is generating the result object.
You can define your factory service in services.yml of your bundle as:
result.factory:
class: ResultFactory
arguments: ["#translator"]
And in your controller you can call the service:
$resultObject = $this->get('result_factory')->get($type, $param);
Also you have core example how to create factory service using symfony2 in [the docs].(http://symfony.com/doc/current/components/dependency_injection/factories.html)