ItraoryGoogleMapBundle的DataTransformer自动完成(数组)到地址实体

时间:2014-06-01 22:26:22

标签: symfony autocomplete fosuserbundle

我使用IvoryGoogleMapBundle为RegistrationFormType(FOSUserBundle)添加了一个自动填充字段。

用户可以拥有一个地址=> OneToOne关系

我通过遵循Symfony文档创建了一个Datatransformer来将自动完成字段(数组)转换为Address实体,但似乎我错过了一些东西:(

我遇到以下错误:缺少必需的选项“em”。

SW / UserBundle /实体/ 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 / BlogBu​​ndle /实体/ 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 / BlogBu​​ndle /窗体/ 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 /窗体/类型/ 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 / BlogBu​​ndle /窗体/ 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 /控制器/ 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(),
            ));
        }
    }

我不知道将自动填充字段保存为地址实体是否是正确且最简单的方法......

感谢您的帮助

2 个答案:

答案 0 :(得分:0)

首先,我必须回答一个与您的问题相关的前置问题:考虑在Symfony中使用ObjectManager和EntityManager。

ObjectManager也支持ORM和ODM,所以我建议你在FormTypes和Controllers中使用它。详细答案如下:Difference between ObjectManager and EntityManager in Symfony2?

您的RegistrationFormType在构造函数中使用EntityManager,在DefaultOptions中使用ObjectManager。我建议您在构造函数中仅使用ObjectManager并从DefaultOptions中删除不必要的ObjectManager,并仅使用与实体相关的data_class作为Option。您错过了在代码中添加Constructor参数。

控制器代码:

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

FormType代码:

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.
    }

我已经改进了部分代码,它可以帮助您使其工作。

答案 1 :(得分:0)

我在您的代码中看到了2个问题。第一个(不是很重要),您应该依赖DI组件(容器)在需要依赖时创建表单类型。有关详细信息,请参阅http://symfony.com/doc/current/cookbook/form/create_custom_field_type.html#creating-your-field-type-as-a-service

通过这种方式,您可以使您的依赖项显式化(您的对象管理器可以直接传递给表单类型的构造函数,而不是将其作为选项传递给表单)。

第二个问题是地理编码器创建效果不佳。在这里,您创建了一个地理编码器,但没有任何提供者。有关详细信息https://github.com/egeloen/ivory-google-map/issues/32#issuecomment-20461264

,请参阅此评论

然后,如果您将表单类型重构为服务,那么捆绑包提供的地理编码器已经配置了提供程序。注入ivory_google_map.geocoder服务就足够了。