Symfony2只有一个收集字段记录的注册表单


Symfony2 Registration form with only single record of collection field

所以我试图在Symfony 2中创建一个包含我的"Person"实体的注册表单。person实体有一个一对多连接,我希望注册表单允许用户选择连接的这个"多"侧的单个实例。

结构为UsersInstitutions。一个用户可以拥有多个机构。我希望用户在注册时选择一个单一的机构(但模型允许以后更多)。

基本结构为:

RegistrationType -> PersonType -> PersonInstitutionType

…与对应的模型:

Registration (simple model) -> Person (doctrine entity) -> PersonInstitution (doctrine entity, oneToMany relation from Person)

我试图预填充一个空的Person &PersonInstitution记录在RegistrationController中,但它给了我错误:

给定类型为"string or Symfony'Component'Form'FormTypeInterface", "TB' ctobbundle 'Entity'PersonInstitution"的期望参数

(上面的ok已被修复)。

我已经把代码从我的网站移到了下面,试图删除所有不相关的位。

src/结核/CtoBundle/形式/模型/Registration.php

namespace TB'CtoBundle'Form'Model;
use TB'CtoBundle'Entity'Person;
class Registration 
{
  /**
   * @var Person
   */
  private $person
  private $termsAccepted;
}

src/结核/CtoBundle/形式/RegistrationType.php

namespace TB'CtoBundle'Form;
use TB'CtoBundle'Form'PersonType;
class RegistrationType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('person',  new PersonType());
        $builder->add('termsAccepted','checkbox');
    }
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'TB'CtoBundle'Form'Model'Registration',
            'cascade_validation' => true,
        ));
    }
    public function getName()
    {
        return 'registration';
    }
}

src/结核/CtoBundle/实体/Person.php

namespace TB'CtoBundle'Entity;
use TB'CtoBundle'Entity'PersonInstitution
/**
 * @ORM'Entity()
 */
class Person
{
    /**
     * @var ArrayCollection
     * @ORM'OneToMany(targetEntity="PersonInstitution", mappedBy="person", cascade={"persist"})
     */
    private $institutions;
}

src/结核/CtoBundle/形式/PersonType.php

namespace TB'CtoBundle'Form;
class PersonType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('institutions', 'collection', array('type' => new PersonInstitutionType()))
        ;
    }
   /**
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'TB'CtoBundle'Entity'Person',
        ));
    }
    /**
     * @return string
     */
    public function getName()
    {
        return 'tb_ctobundle_person';
    }
}

src/结核/CtoBundle/实体/PersonInstitution.php

namespace TB'CtoBundle'Entity
/**
 * PersonInstitution
 *
 * @ORM'Table()
 * @ORM'Entity
 */
class PersonInstitution
{
    /**
     * @ORM'ManyToOne(targetEntity="Person", inversedBy="institutions", cascade={"persist"})
     */
    private $person;
    /**
     * @ORM'ManyToOne(targetEntity="Institution", inversedBy="members")
     */
    private $institution;
    /**
     * @ORM'Column(type="boolean")
     */
    private $approved;
}

src/结核/CtoBundle/形式/PersonInstititionType.php

namespace TB'CtoBundle'Form;
use Symfony'Component'Form'AbstractType;
use Symfony'Component'Form'FormBuilderInterface;
use Symfony'Component'OptionsResolver'OptionsResolverInterface;
class PersonInstitutionType extends AbstractType
{
        /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('approved')
            ->add('person')
            ->add('institution')
        ;
    }
    /**
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'TB'CtoBundle'Entity'PersonInstitution'
        ));
    }
    /**
     * @return string
     */
    public function getName()
    {
        return 'tb_ctobundle_personinstitution';
    }
}

src/结核/CtoBundle/控制器/Registration.php

namespace TB'CtoBundle'Controller;
class RegisterController extends Controller
{
    /**
     * 
     * @param Request $request
     * @return 'Symfony'Component'HttpFoundation'RedirectResponse|'Symfony'Component'HttpFoundation'Response
     */
    public function registerAction(Request $request)
    {
        $registration = new Registration;
        $person = new Person();
        $institution = new PersonInstitution();
        $person->addInstitution($institution);
        $registration->setPerson($person);
// this causes error:
// Entities passed to the choice field must be managed. Maybe persist them in the entity manager? 
//        $institution->setPerson($person);
        $form = $this->createForm(new RegistrationType(), $registration);
        $form->handleRequest($request);
        if($form->isValid()) {
            $registration = $form->getData();
            $person = $registration->getPerson();
            // new registration - account status is "pending" 
            $person->setAccountStatus("P");
            // I'd like to get rid of this if possible
            // for each "PersonInstitution" record, set the 'person' value
            foreach($person->getInstitutions() as $rec) {
                $rec->setPerson($person);
            }
            $em = $this->getDoctrine()->getManager();
            $em->persist($person);
            $em->flush();
        }
        return $this->render('TBCtoBundle:Register:register.html.twig', array('form' => $form->createView()));
    }
}

下面是向Person实体和formType添加Collection字段的详细解决方案。您与注册实体的复杂问题可以解决与此。如果确实需要,我建议您使用这3个实体相关的连接。(只因为术语接受数据!?)

如果你不想改变你的观点,那么使用这个注释: 注册码:

use TB'CtoBundle'Entity'Person;
/**
  * @ORM'OneToOne(targetEntity="Person")
  * @var Person
  */
protected $person;
<<p> 人代码/strong>:
use TB'CtoBundle'Entity'PersonInstitution;
/**
  * @ORM'OneToMany(targetEntity="PersonInstitution", mappedBy = "person")
  * @var ArrayCollection
  */
private $institutions;
/* I suggest you to define these functions:
setInstitutions(ArrayCollection $institutions),
getInstitutions()
addInstitution(PersonInstitution $institution)
removeInstitution(PersonInstitution $institution)
*/
<<p> PersonInstitution代码/strong>:
use TB'CtoBundle'Entity'Person;
/**
  * @ORM'ManyToOne(targetEntity="Person", inversedBy="institutions", cascade={"persist"}))
  * @var Person
  */
private $person;
<<p> PersonType代码/strong>:
use TB'CtoBundle'Form'PersonInstitutionType;
->add('institutions', 'collection', array(
                'type' => new PersonInstitutionType(), // here is your mistake!
                // Other options can be selected here.
                //'allow_add' => TRUE,
                //'allow_delete' => TRUE,
                //'prototype' => TRUE,
                //'by_reference' => FALSE,
));
<<p> PersonController代码/strong>:
use TB'CtoBundle'Entity'Person;
use TB'CtoBundle'Entity'PersonInstitution;
/**
  * ...
  */
public funtcion newAction()
{
  $person = new Person;
  $institution = new PersonInstitution;
  $institution->setPerson($person);
  $person->addInstitution($institution);
  $form = $this->createForm(new PersonType($), $person); // you can use formFactory too.
  // If institution field is required, then you have to check,
  // that is there any institution able to chose in the form by the user.
  // Might you can redirect to institution newAction in that case.
  return array( '...' => $others, 'form' => $form);
}

如果你需要更多的帮助在小枝代码,然后问它