0

I added an autocomplete field to the RegistrationFormType (FOSUserBundle) by using IvoryGoogleMapBundle.

An User can have one Address => OneToOne relation

I created a Datatransformer to Transform Autocomplete field (array) to an Address entity by following the Symfony documentation but seems that I missed something :(

I have the following error: The required option "em" is missing.

sw/UserBundle/Entity/User.php

    class user extends BaseUser
    {

    /**
      * @ORM\OneToOne(targetEntity="sw\BlogBundle\Entity\Address", cascade={"persist"})
      * @ORM\JoinColumn(name="address_id", referencedColumnName="id")
    */
    private $address;


    /**
     * Set address
     *
     * @param \sw\BlogtBundle\Entity\Address $address
     * @return User
     */
     public function setAddress(\sw\BlogBundle\Entity\Address $address = null)
     {
       $this->address = $address;

       return $this;
     }        


     /**
      * Get address
      *
      * @return \sw\BlogBundle\Entity\Address
      */
     public function getAddress()
     {
      return $this->address;
     }

sw/BlogBundle/Entity/Address.php

  class Address
  {
  /**
   * @ORM\Id
   * @ORM\Column(type="integer")
   * @ORM\GeneratedValue(strategy="AUTO")
   */
protected $id;

/**
 * @ORM\Column(type="string", length=10)
 */
private $zipcode;

/**
 * @ORM\Column(type="string", length=45)
 */
private $city;

/**
 * @ORM\Column(type="string", length=45)
 */
private $country;

sw/BlogBundle/Form/AutocompleteFromType.php

    use Ivory\GoogleMap\Places\AutocompleteType;
    use Ivory\GoogleMap\Places\AutocompleteComponentRestriction;

    class MapFormType extends AbstractType
    {
      public function buildForm(FormBuilderInterface $builder, array $options)
      {

        $builder->add('Ville','places_autocomplete', array(
         'prefix' => 'js_prefix_',
         'types'  => array(AutocompleteType::CITIES),
         'async' => false,
         'language' => 'fr',
         'component_restrictions' => array(
         AutocompleteComponentRestriction::COUNTRY => 'FR'),

         ));

     }

     public function getName()
     {
       return 'sw_blog_autocomplete';
     }
  }

sw/UserBundle/Form/Type/RegistrationFormType.php

    use sw\BlogBundle\Form\AutocompleteFormType;
    use sw\BlogBundle\Form\DataTransformer\AutocompleteToAddressTransformer;

    class RegistrationFormType extends AbstractType
    {
        protected $em;

        public function __construct(EntityManager $em)
        {
            $this->em = $em;
        }
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $entityManager = $options['em'];
            $transformer = new AutocompleteToAddressTransformer($entityManager);


            parent::buildForm($builder, $options);


            $builder->add('username', 'text', array('label' => 'Pseudo :', 'max_length' => 10));
            .........

             $builder->add($builder->create('address', new MapFormType(), array('label' => 'Adresse :','required' => true))->addModelTransformer($transformer));
        }

        public function setDefaultOptions(OptionsResolverInterface $resolver)
        {
            $resolver
                ->setDefaults(array(
                    'data_class' => 'sw\UserBundle\Entity\User',
                ))
                ->setRequired(array(
                    'em',
                ))
                ->setAllowedTypes(array(
                    'em' => 'Doctrine\Common\Persistence\ObjectManager',
                ));

            // ...
        }

        public function getName()
        {
            return 'sw_user_registration';
        }
    }

sw/BlogBundle/Form/DataTransformer/AutocompleteToAddressTransformer.php

    use sw\BlogBundle\Entity\Address;
    use Ivory\GoogleMap\Services\Geocoding\Geocoder as GeocoderService;

    class AutocompleteToAddressTransformer implements DataTransformerInterface
    {
    /**
    * @var ObjectManager
    */
    private $om;

    /**
    * @param ObjectManager $om
    */
    public function __construct(ObjectManager $om)
    {
    $this->om = $om;
    }

    /**
    * Transforms an array to a object (address).
    *
    * @param  Array|null $autocomplete
    * @return Address
    */
    public function transform($autocomplete)
        {
            if (null === $autocomplete) {
            return "";
            }

            $geocoder = new GeocoderService();

            try {
                $result = $geocoder->geocode($autocomplete);
                var_export($result);
            } catch (Exception $e) {
                echo $e->getMessage();
            }

            $address = new Address();

            $address->setCity($result->getCity());
            $address->setCoutry($result->getCoutry());
            $address->setZipcode($result->getZipcode());

            return $address;
        }

        /**
         * Transforms an object (Address) to an array.
         *
         * @param  Address $address
         *
         * @return autocomplete|null
         *
         * @throws TransformationFailedException if array is not found.
         */
        public function reverseTransform($address)
        {
            if (!$address) {
                return null;
            }

            $autocomplete = $address->getCity. ' , ' .$address->getCountry;

            return $autocomplete;
        }
    }

sw/UserBundle/Controller/RegistrationController.php

    <?php

    namespace sw\UserBundle\Controller;

    use Symfony\Component\HttpFoundation\RedirectResponse;
    use FOS\UserBundle\Controller\RegistrationController as BaseController;

    use Symfony\Component\HttpFoundation\Request;
    use FOS\UserBundle\FOSUserEvents;
    use FOS\UserBundle\Event\UserEvent;
    use FOS\UserBundle\Event\FormEvent;
    use FOS\UserBundle\Event\FilterUserResponseEvent;
    use sw\UserBundle\Form\Type\RegistrationFormType;


    class RegistrationController extends BaseController
    {

        public function registerAction(Request $request)
        {
            /** @var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
            $formFactory = $this->container->get('fos_user.registration.form.factory');
            /** @var $userManager \FOS\UserBundle\Model\UserManagerInterface */
            $userManager = $this->container->get('fos_user.user_manager');
            /** @var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
            $dispatcher = $this->container->get('event_dispatcher');

            $user = $userManager->createUser();
            $user->setEnabled(true);

            $dispatcher->dispatch(FOSUserEvents::REGISTRATION_INITIALIZE, new UserEvent($user, $request));

            $form = $formFactory->createForm(new RegistrationFormType(), $user, array(
                'em' => $this->container->get('doctrine')->getManager(),
                ));

            $form->setData($user);

            if ('POST' === $request->getMethod()) {

                $form->bind($request);

                if ($form->isValid()) {

                    $form->bind($request);
                    $event = new FormEvent($form, $request);
                    $dispatcher->dispatch(FOSUserEvents::REGISTRATION_SUCCESS, $event);

                    $userManager->updateUser($user);


                    if (null === $response = $event->getResponse()) {
                        $url = $this->container->get('router')->generate('fos_user_registration_confirmed');
                        $response = new RedirectResponse($url);
                    }

                    $dispatcher->dispatch(FOSUserEvents::REGISTRATION_COMPLETED, new FilterUserResponseEvent($user, $request, $response));

                    return $response;
                }
            }

            return $this->container->get('templating')->renderResponse('FOSUserBundle:Registration:register.html.'.$this->getEngine(), array(
                'form' => $form->createView(),
            ));
        }
    }

I don't know if it is the right and easiest way to save the Autocomplete field as Address entity...

Thank you for your help

Skwary
  • 1

2 Answers2

0

First of all I have to answer a pre-question, which is related to your problem: considering use of ObjectManager and EntityManager in Symfony.

ObjectManager supports ORM and ODM too, so I suggest You to use it in FormTypes and Controllers to. Detailed answer here: Difference between ObjectManager and EntityManager in Symfony2?

Your RegistrationFormType uses EntityManager in Constructor and ObjectManager in DefaultOptions. I suggest You to use only ObjectManager in Constructor and remove unnecessary ObjectManager from DefaultOptions and use only the Entity related data_class as Option. You missed to add the Constructor parameter in your code.

Controller code:

    $om = $this->getDoctrine()->getManager(); ...
    $form = $this->createForm(new RegistrationFormType($om), $user)

FormType code:

use Doctrine\Common\Persistence\ObjectManager; ...
    /**
     * @var \Doctrine\Common\Persistence\ObjectManager
     */
    protected $om;

    public function __construct(ObjectManager $om)
    {
        $this->om = $om;
    }
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
            $objectManager = $this->om;
            // And put this $objectManager variable as parameter to the Transformer file.
    }

I've improved some part of the code, it could help you to make it work.

Community
  • 1
  • 1
szecsikecso
  • 301
  • 1
  • 4
  • Hello @szecsikecso, I tried to align between what I give to the FromType and waht I give in the controller as you suggested to me but still not working :( **Error: Argument 1 passed to RegistrationFormType::__construct() must be an instance of sw\UserBundle\Form\Type\ObjectManager, instance of EntityManager538c978c59e2d_546a8d27f194334ee012bfe64f629947b07e4919\__CG__\Doctrine\ORM\EntityManager given, called in C:\wamp\www\Symfony\src\sw\UserBundle\Controller\RegistrationController.php on line 37 and defined in C:\wamp\www\Symfony\src\sw\UserBundle\Form\Type\RegistrationFormType.php line 26** – Skwary Jun 02 '14 at 15:47
  • I've improved some part of the code, it could help you to make it work. What is the version of your symfony? – szecsikecso Jun 03 '14 at 08:00
  • I'm using **Symfony 2.4.5** – Skwary Jun 03 '14 at 14:05
  • I tried what you sggested to me but I got the following error: **ContextErrorException: Catchable Fatal Error: Argument 1 passed to sw\UserBundle\Form\Type\RegistrationFormType::__construct() must implement interface Doctrine\Common\Persistence\ObjectManager, string given, called in C:\wamp\www\Symfony\app\cache\dev\appDevDebugProjectContainer.php on line 5114 and defined in C:\wamp\www\Symfony\src\sw\UserBundle\Form\Type\RegistrationFormType.php line 25** – Skwary Jun 04 '14 at 13:29
0

I see 2 issues in your code. The first one (not really important), is you should rely on the DI component (container) to create your form type when it needs a dependency. See http://symfony.com/doc/current/cookbook/form/create_custom_field_type.html#creating-your-field-type-as-a-service for more details.

This way, you will be able make your dependency explicit (your object manager can be directly pass to the constructor of your form type instead of passing it to your form as option).

The second issue is the geocoder is not well created. Here, you create a geocoder but without any provider. See this comment for more details https://github.com/egeloen/ivory-google-map/issues/32#issuecomment-20461264

Then, be awre if you refactor your form type as service, the geocoder provided by the bundle is already configured with a provider. SO injecting the ivory_google_map.geocoder service will be enough.

egeloen
  • 5,844
  • 1
  • 27
  • 38
  • Hello @egeloen, Thank you for your answer. I re created my geocoder by following your comment. currently, I don't know how can I solve the first issue. Is there any other way to save the autocomplete field provided by egeloen/ivory-google-map ? I'm blocked in this issue since some days :( – Skwary Jun 02 '14 at 16:05
  • What is the first issue? The one about the entity manager or about the geocoder? Additionally, please copy/paste the complete exception message. – egeloen Jun 02 '14 at 19:32
  • First issue regarding your comment whe you said that you saw 2 issues. I corrected the geocoder but my main issue is how to save the autocomplete value in my RegistrationForm – Skwary Jun 02 '14 at 19:43
  • the entire error regarding the provided code: **error: The required option "em" is missing** – Skwary Jun 02 '14 at 19:46
  • I tried to change my code as suggested by @szecsikecso but I got this error: **ContextErrorException: Catchable Fatal Error: Argument 1 passed to sw\UserBundle\Form\Type\RegistrationFormType::__construct() must be an instance of sw\UserBundle\Form\Type\ObjectManager, instance of EntityManager538c978c59e2d_546a8d27f194334ee012bfe64f629947b07e4919\__CG__\Doctrine\ORM\EntityManager given, called in C:\wamp\www\Symfony\src\sw\UserBundle\Controller\RegistrationController.php on line 37 and defined in C:\wamp\www\Symfony\src\sw\UserBundle\Form\Type\RegistrationFormType.php line 26** – Skwary Jun 02 '14 at 19:47
  • This is because you have forgotten to add a use statement for the EntityManager passed to the constructor. – egeloen Jun 03 '14 at 07:58
  • I added the use statement for the EntityManager\ObjectManger (I tried with both of them) but still have an error: **ContextErrorException: Catchable Fatal Error: Argument 1 passed to sw\UserBundle\Form\Type\RegistrationFormType::__construct() must implement interface Doctrine\Common\Persistence\ObjectManager, string given, called in C:\wamp\www\Symfony\app\cache\dev\appDevDebugProjectContainer.php on line 5114 and defined in C:\wamp\www\Symfony\src\sw\UserBundle\Form\Type\RegistrationFormType.php line** – Skwary Jun 04 '14 at 13:31
  • you have change something else in your code because before, it is passed a `EntityManager538c978c59e2d_546a8d27f194334ee012bfe64f629947b07e4919__CG__\Doctrin‌​e\ORM\EntityManager` (instance of EntityManager) whereas now, it is passed a string comming from the Symfony container. Have you convert your form as service? If yes, there is an issue in your form service definition. – egeloen Jun 04 '14 at 13:34
  • I don't convert my form as service, I'm using **$em = $this->container->get('doctrine')->getManager();** to pass the $em to the constructor, **$form = $formFactory->createForm(new RegistrationFormType($em), $user);**, I don't know from where come this error :( – Skwary Jun 04 '14 at 13:50
  • I'm sorry but I don't remember how does work the registration form of the FOSUserBundle. – egeloen Jun 04 '14 at 21:12