This is the first time I am working with EventListener of a form so I am struggling on how to inject EntityManager in it.
I have this formType called UserType and in this class I have an EventSubscriber AddDepartmentDegreeCourseFieldSubscriber which needs access to EntityManager
class UserType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventSubscriber(new AddProfileFieldSubscriber());
$builder->addEventSubscriber(new AddDepartmentDegreeCourseFieldSubscriber());
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\User'
));
}
}
This is my services.yml
app.department_course_degree_subscriber:
class: AppBundle\Form\EventListener\AddDepartmentDegreeCourseFieldSubscriber
arguments: ["#doctrine.orm.entity_manager"]
tags:
- { name: kernel.event_subscriber }
The error I get is as following
Catchable Fatal Error: Argument 1 passed to
AppBundle\Form\EventListener\AddDepartmentDegreeCourseFieldSubscriber::__construct()
must be an instance of Doctrine\ORM\EntityManager, none given, called
in /Users/shairyar/Sites/oxford-portal/src/AppBundle/Form/UserType.php
on line 21 and defined
I know what the error means but I thought the service i registered in services.yml should inject the EntityManager so why I am getting this error? What am i missing here? Any help will be really appreciated.
It's because, you pass new instance of AddDepartmentDegreeCourseFieldSubscriber when building form. You need to pass instance from service container.
use AppBundle\Form\EventListener\AddDepartmentDegreeCourseFieldSubscriber;
class UserType extends AbstractType
{
private $addDepartmentDegreeCourseFieldSubscriber;
public function __construct(AddDepartmentDegreeCourseFieldSubscriber $subscriber)
{
$this->addDepartmentDegreeCourseFieldSubscriber = $subscriber;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventSubscriber($this->addDepartmentDegreeCourseFieldSubscriber);
}
}
# app/config/services.yml
services:
app.department_course_degree_subscriber:
class: AppBundle\Form\EventListener\AddDepartmentDegreeCourseFieldSubscriber
arguments: ["#doctrine.orm.entity_manager"]
tags:
- { name: kernel.event_subscriber }
app.form.type.my_user_form:
class: AppBundle\Form\UserType
arguments: [ "#app.department_course_degree_subscriber" ]
tags:
- { name: form.type }
Related
with this article I'm trying to override RegisterFormType.php, and do not see the result, but Symfony Profiler shows in Forms fos_user_registration is overwritten by app_user_registration.
Firstly, I have in AppKernel.php:
new FOS\UserBundle\FOSUserBundle(),
new Sonata\UserBundle\SonataUserBundle('FOSUserBundle'),
new Application\Sonata\UserBundle\ApplicationSonataUserBundle(),
services.yml
app.form.registration:
class: Application\Sonata\UserBundle\Form\RegistrationFormType
tags:
- { name: form.type, alias: app_user_registration }
config.yml:
fos_user:
db_driver: orm # can be orm or odm
firewall_name: main
user_class: Application\Sonata\UserBundle\Entity\User
use_listener: true
group:
group_class: Application\Sonata\UserBundle\Entity\Group
group_manager: sonata.user.orm.group_manager # If you're using doctrine orm (use sonata.user.mongodb.group_manager for mongodb)
service:
user_manager: sonata.user.orm.user_manager # If you're using doctrine orm (use sonata.user.mongodb.user_manager for mongodb)
registration:
form:
type: app_user_registration
#validation_groups: [AppRegistration]
profile:
form:
name: app_user_profile
src/Application/Sonata/UserBundle/Form/RegistrationForm.php:
namespace Application\Sonata\UserBundle\Form;
use ...
class RegistrationFormType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('fullName')
;
}
public function getParent() {
return "fos_user_registration";
}
public function getName() {
return 'app_user_registration';
}
}
src/Application/Sonata/UserBundle/Entity/User.php:
namespace Application\Sonata\UserBundle\Entity;
use Sonata\UserBundle\Entity\BaseUser as BaseUser;
class User extends BaseUser
{
/**
* #var int $id
*/
protected $id;
/**
* #var string
*/
protected $fullName;
/**
* #return string
*/
public function getFullName() {
return $this->fullName;
}
/**
* #param string $fullName
*/
public function setFullName($fullName) {
$this->fullName = $fullName;
}
/**
* Get id
*
* #return int $id
*/
public function getId() { return $this->id;}
}
As other way (I think not correct) tried to override RegistrationController.php in my custom directory, changed there $form = $this->container->get('app.form.registration') (alias for my form), but I receive error Attempted to call an undefined method named "createView" of class "Application\Sonata\UserBundle\Form\RegistrationFormType. After this I changed this class' parent extended class from ContentAware to Controller, and can see form and new field, but after submitting receive unknown error, even in Profiler not written about it name.
Symfony 2.8.18, FOSUserBundle 1.3.7. Thank you for your time.
Did you update the configuration of the FOSUserBundle in config.yml ?
fos_user:
# ...
registration:
form:
name: app_user_registration
I tried inject memcached service into entity repository, but my variant not work.
services:
work.repository.company:
class: WorkBundle\Repository\CompanyRepository
factory: ['#doctrine.orm.entity_manager', getRepository]
arguments:
- 'WorkBundle:Company'
calls:
- [setCacheService, ['#memcache.default']]
CompanyRepository have setter setCacheService, but it's not called.
class CompanyExtension extends \Twig_Extension
{
/**
* #var EntityManager
*/
private $em;
public function setEntityManager(EntityManager $entityManager)
{
$this->em = $entityManager;
}
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('getCompaniesCount', array($this, 'getCompaniesCount'))
);
}
/**
* #return integer
*/
public function getCompaniesCount()
{
return $this->em->getRepository('WorkBundle:Company')->getActiveCompaniesCount();
}
public function getName()
{
return 'work_company_extension';
}
}
Why this code not works?
Have you registered repositoryClass in your WorkBundle:Company entity? Your entity should contain something like: #ORM\Entity(repositoryClass="Work\Company") or yaml equivalent.
You should let Symfony create the repository by injecting work.repository.company into your Twig extension.
I'm creating new invoice form, which includes select input with some constant values.
I've made it through services:
services.yml
parameters:
stawki_vat:
0: 0%
5: 5%
8: 8%
23: 23%
zw: zw.
services:
acme.form.type.stawki_vat:
class: Acme\FakturyBundle\Form\Type\StawkiVatType
arguments:
- "%stawki_vat%"
tags:
- { name: form.type, alias: stawki_vat }
StawkiVatType.php
class StawkiVatType extends AbstractType
{
private $stawkiVatChoices;
public function __construct(array $stawkiVatChoices) {
$this->stawkiVatChoices = $stawkiVatChoices;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'choices' => $this->stawkiVatChoices,
));
}
public function getParent()
{
return 'choice';
}
public function getName()
{
return 'stawki_vat';
}
}
TowarType.php
class TowarType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('nazwa_towar', null, array('label' => 'Nazwa towaru/usługi'))
->add('cena_netto', null, array(
'label' => 'Cena netto'
))
->add('vat', 'stawki_vat', array(
'attr' => array('class' => 'styled'),
'label' => 'Stawka VAT',
))
;
}
In form, everything works perfect.
But now, I want to get a value stored in database (key of stawki_vat) and show a value of stawki_vat array.
How to achieve this in simple way?
You need to pass your entity manager to your custom form type service. Presuming you are using Doctrine:
services.yml
services:
acme.form.type.stawki_vat:
class: Acme\FakturyBundle\Form\Type\StawkiVatType
arguments: ["#doctrine.orm.entity_manager","%stawki_vat%"]
StawkiVatType.php
class StawkiVatType extends AbstractType
{
private $stawkiVatChoices;
private $em;
public function __construct(EntityManager $em, array $stawkiVatChoices) {
$this->em = $em;
$this->stawkiVatChoices = $stawkiVatChoices;
}
// ...
I'm trying to implement events on FOSUserBundle
<?php
namespace EasyApp\UserBundle\Service;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Doctrine\Bundle\DoctrineBundle\Registry as Doctrine;
use EasyApp\UserBundle\Entity\UserLogin;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use FOS\UserBundle\FOSUserBundle;
class LoginManager implements EventSubscriberInterface
{
/** #var \Symfony\Component\Security\Core\SecurityContext */
private $securityContext;
/** #var \Doctrine\ORM\EntityManager */
private $em;
/**
* Constructor
*
* #param SecurityContext $securityContext
* #param Doctrine $doctrine
*/
public function __construct(SecurityContext $securityContext, Doctrine $doctrine)
{
$this->securityContext = $securityContext;
$this->em = $doctrine->getEntityManager();
}
/**
* {#inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::SECURITY_IMPLICIT_LOGIN => 'onSecurityImplicitLogin',
FOSUserEvents::REGISTRATION_COMPLETED=> 'onRegistrationCompleted'
);
}
public function onSecurityImplicitLogin(UserEvent $event)
{
die;
$user = $event->getAuthenticationToken()->getUser();
$request = $event->getRequest();
if ($this->securityContext->isGranted('IS_AUTHENTICATED_FULLY')) {
// user has just logged in
$this->saveLogin($request, $user);
}
}
public function onRegistrationCompleted(FilterUserResponseEvent $event){
$user = $event->getAuthenticationToken()->getUser();
$request = $event->getRequest();
saveLogin($request, $user);
}
public function saveLogin($request, $user){
$login = new UserLogin();
$login->setIp($request->getClientIp());
$login->setUser($user);
$this->em->persist($login);
$this->em->flush();
}
}
And my service
services:
user_login_manager:
class: 'EasyApp\UserBundle\Service\LoginManager'
arguments: ['#security.context', '#doctrine']
tags:
- { name: 'kernel.event_subscriber', event: 'fos_user.security.interactive_login'}
- { name: 'kernel.event_subscriber', event: 'fos_user.registration.completed'}
But I have problems. When I login nothing happen, the getSubscribedEvents() is called but not onSecurityImplicitLogin(UserEvent $event)
And the other problem is on register. Following error occurs on onSecurityImplicitLogin(UserEvent $event)
Catchable Fatal Error: Argument 1 passed to EasyApp\UserBundle\Service\LoginManager::onSecurityImplicitLogin() must be an instance of EasyApp\UserBundle\Service\UserEvent, instance of FOS\UserBundle\Event\UserEvent given in /Users/antoine/Documents/projects/easyApp/application/src/EasyApp/UserBundle/Service/LoginManager.php line 46
and if I comment this line I got the same error on onRegistrationCompleted(FilterUserResponseEvent $event)
Edit
services:
user_login_manager:
class: 'EasyApp\UserBundle\Service\LoginManager'
arguments: ['#security.context', '#doctrine']
tags:
- { name: 'kernel.event_subscriber'}
- { name: 'kernel.event_listener', event: 'security.interactive_login' }
Two points:
In the service definition - { name: 'kernel.event_subscriber'} is enough, no event entry. This the difference between subscriber and listener. A listener can only listen to one event, defined trough event and method params in the service definition. A subscriber can listen to multiple events, defined trough the method getSubscribedEvents(), so no further params in the service definition needed.
Second point, you are type hinting against the event classes (UserEvent andFilterUserResponseEvent), but you never imported them, so PHP assumes them in the same namespace (as said in the error message).
use FOS\UserBundle\Event\UserEvent;
use FOS\UserBundle\Event\FilterUserResponseEvent;
I am trying to build a form for multiple entities. Let me first introduce some sample classes:
For clarity I do not show all annotations or abbreviate them, and I do not show the use or namespace commands.
/**
* The Entity class
* #ORM ... mapping to ORM
*/
class EntityA {
/**
* #var ModelArray
* #ORM\Column(name="...", type="object")
*/
private $modelArray;
// Getters and Setters, default constructor
}
/**
* An array Wrapper, keeping the array always unique, sorting it by criteria etc.
* #ORM(...)
*/
class ModelArray {
/**
* #var array<A>
*/
private $array;
// Getter, Adder, Remover, Constructor, other private logic
}
Notice especially that the class ModelArray stores only objects of a given Type A:
/**
* One more Model
*/
class A {
/**
* #var boolean
*/
private $bool;
/**
* #var string
*/
private $string;
// Getters, Setters
}
I chose this data structure, because I never need objects of A to exist outside the EntityA class, and to keep the logic out of my Entity, I chose to implement the ModelArray class in between. The EntityA class is persisted to a database, and with it the child objects.
Now I want to create a form, where I can edit the $->bool attributes of all A instances of an Array of EntityA at once.
So what I provide is array<EntityA>.
I would then proceed as follows:
//In a controller action
$data = array(
'as' => $arrayOfEntityA,
);
$form = $this->createForm(new GridType(), $data);
// Create view, render
My form type would look like this:
class GridType extends AbstractType {
public function getName() {
return 'a_grid';
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('as', 'collection', array(
'type' => new GridAType(),
));
}
}
And use this form type
class GridAType extends AbstractType {
public function getName() {
return 'a_grid_a';
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('modelArray', new ModelArrayType());
}
public function getDefaultOptions(array $options) {
return array(
'data_class' => 'EntityA',
);
}
}
With the model array type
class ModelArrayType extends AbstractType {
public function getName() {
return 'model_array';
}
public function buildForm(FormBuilderInterface $builder, array $options) {
// ???
}
public function getDefaultOptions(array $options) {
return array(
'data_class' => 'ModelArray',
);
}
}
Notice the // ???: I would like to only edit the boolean attribute of the As strored in the ModelArray, and I think the appropriate form type to continue would be a 'collection'. But I can't really find out how to use the array ($modelArray->array)here as a collection.
How should I do this?
An then, I'm not entirely sure if it is good practice to implement such a lot of form types just to achieve one usable form. Is there a different and better way?