So I start off with my database layout, I have a User table which has an accounts_id link to another table Accounts.
Inside Accounts I have companyname. Now I have built a AbstractType for my user sign up. I want this form to include a field for companyname. But what I have tried is not working, so where is what I have done so far,
use XBundle\Form\AccountType;
class UserType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('firstname', 'text',['label'=>'Firstname'])
->add('surname', 'text',['label'=>'Surname'])
//->add('companyname','text', ['data_class' => 'XBundle\Entity\Accounts'])
->add('companyname', new AccountType()) <- current attempt
->add('email', 'email',['label'=>'Email'])
->add('password', 'password',['label'=>'Password']);
//->add('confirm', 'password', ['mapped' => false,'label'=>'Re-type password'])
//->add('homepage', 'text',['label'=>'Homepage'])
//->add('save', 'submit', ['label'=>'Register']);
}
public function getName() {
return 'registration';
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults([
'data_class' => 'XBundle\Entity\Users',
'cascade_validation' => true,
]);
}
}
And for my AccountType I have the following,
class AccountType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('companyname','text', ['label'=>'Company']);
}
public function getName() {
return 'account';
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults([
'data_class' => 'XBundle\Entity\Accounts',
]);
}
}
When I build a createForm & createView for my twig (which renders without any problems without the companyname / AccountType). I get the following error,
Neither the property "companyname"
nor one of the methods "getCompanyname()",
"companyname()", "isCompanyname()", "hasCompanyname()",
"__get()" exist and have public access in class "XBundle\Entity\Users"
Now I know that companyname is in my Accounts Entity. I have also done a test to make sure my AccountType works, by building a new form with that type, which renders the Company Name field without any problems.
But I am not sure what I am doing wrong, or even if this is the right way about going about adding another entities field on my user sign up form, please help :)
The error you get is expected.
AccountType is a form to edit Account entity details. By adding companyname to the builder you explicily say that setters and getters exists for that property. An this part is correct.
UserType is a form to edit User entity details, NOT Account details. If you add an AccountType property named companyname,the form component expect you to provide these methods:
public function getCompanyname() {/*...*/}
public function setCompanyname(Account $account) {/*...*/}
Which is not what you are expecting, and exactly what the error is saying.
You need to provide accessor for the Account to your user entity, and then define how the form should handle it.
Option 1: you want to select an existing account
Add this to the UserType builder:
public function buildForm(FormBuilderInterface $builder, array $options) {
// options are guessed anyway, you choose if set it explicitly or not
$builder->add('account'/*, 'entity', array(
'class' => 'XBundle:Account'
)*/);
}
And the following to your User entity:
class User {
private $account;
public function getAccount() {
return $this->account;
}
public function setAccount(Account $account /* = null, if optional */) {
$this->account = $account;
}
}
Option 2: you want one-to-one association and an editable account
Add this to the UserType builder:
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('account', new AccountType());
}
And the following to your User entity:
class User {
private $account;
public function __construct() {
$this->account = new Account();
}
public function getAccount() {
return $this->account;
}
}
Related
I am working with Symfony 2.7 and have created a custom FormType based on Symfonys EntityType. Within the FormEvents::POST_SUBMIT of this type I update the received data by adding a special entity. However this chance is not send back to the controller:
Action within controller:
public function customEditAction(Request $request) {
$formData = $this->getFormData();
// formData has a property myEntities which is array of MyEntity
$form = $this->createForm('my_form_type', $formData);
$form->handleRequest($request);
if ($form->isValid()) {
foreach ($formData->getMyEntities() as $myEntity)
dump($myEntity);
}
...
}
my_form_type
class MyFormType extends AbstractType {
...
public function getName() {
return 'my_form_type';
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('myEntities', 'my_entities_type', array(
...
),
));
...
}
}
my_entities_type
class MyEntitiesType extends AbstractType {
...
public function getParent() {
return 'entity';
}
public function getName() {
return 'my_entities_type';
}
public function buildView(FormView $view, FormInterface $form, array $options) {
...
}
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) use ($options) {
// Update data with a special entity.
$data = $event->getData();
$specialEntity = $this->getSomeSpecialEntity();
$data[] = $specialEntity;
// Dump shows, that the entiy was correctly added to $data
foreach ($data as $myEntity)
dump($myEntity);
$event->setData($data);
}
...
}
}
Expected Result:
Form is created with some set of entities within $formData->getMyEntities()
Form is presented to the user. EntityType (parent of MyEntitiesType) fetched all available Entities to build a selection. Entities within $formData->getMyEntities() are pre-selected
User makes his choice and selects a new set of Entities, e.g. Entity1 and Entity2
Form is submitted
In FormEvents::POST_SUBMIT $specialEntity is added to the data/selection
Both dumps (within POST_SUBMIT and within the controller) should show the selected entities including $specialEntity
Everything works fine beside step 6: While the dump within POST_SUBMIT shows that $specialEntity is in $data, the dump within the controller only shows the user selected entities...
Why isn't the changed data submitted back to the controller? What do I have to do, to get the updated data to the controller?
EDIT:
Adding the special entity in FormEvents::SUBMIT does not work, since this entity is not managed and will thus result in an exception within the EntityType implementation.
I get the following error when trying to create a form from another entity to pass through to my view.
I have two entities in this context CourseGuide and CourseGuideRow and I would like to pass through a form view of CourseGuideRowType to my view - how can I do this?
The form's view data is expected to be an instance of class
CRMPicco\CourseBundle\Entity\CourseGuide, but is an instance of class
CRMPicco\CourseBundle\Entity\CourseGuideRow. You can avoid this error
by setting the "data_class" option to null or by adding a view
transformer that transforms an instance of class
CRMPicco\CourseBundle\Entity\CourseGuideRow to an instance of
CRMPicco\CourseBundle\Entity\CourseGuide.
This is my controller:
// CourseGuideController.php
public function viewAction(Request $request)
{
if (!$courseId = $request->get('id')) {
throw new NotFoundHttpException('No Course ID provided in ' . __METHOD__);
}
$resource = $this->get('crmpicco.repository.course_guide_row')->createNew();
$form = $this->getForm($resource);
// ...
}
My Symfony FormBuilder class:
// CourseGuideRowType.php
use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType;
use Symfony\Component\Form\FormBuilderInterface;
class CourseGuideRowType extends AbstractResourceType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('channel', 'crmpicco_channel_choice', array('data_class' => null))
->add('name', 'text')
->add('courses', 'text')
;
}
/**
* #return string name
*/
public function getName()
{
return 'crmpicco_course_guide_row';
}
}
I have tried the data_class => null suggestion mentioned elsewhere, but this has no effect.
If I pass through the data_class like this:
$form = $this->getForm($resource, array('data_class' => 'CRMPicco\CourseBundle\Entity\CourseGuideRow'));
I then get this:
Neither the property "translations" nor one of the methods
"getTranslations()", "translations()", "isTranslations()",
"hasTranslations()", "__get()" exist and have public access in class
"CRMPicco\CourseBundle\Entity\CourseGuideRow".
Why is this? There are translations attached to the CourseGuide entity but not the CourseGuideRow.
try to add this function in your FormType:
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'YourBundle\Entity\YourEntity',
));
}
And don't forget the specific use:
use Symfony\Component\OptionsResolver\OptionsResolver;
EDIT
In native Symfony (with the Form component):
public function showAction()
{
/.../
$entity = new YourEntity();
$form = $this->createForm('name_of_your_form_type', $entity);
# And the response:
return $this->render('your_template.html.twig', ['form' => $form->createView()]);
}
Hi stackexchange users,
I have a data (model) class which has two methods which look like this:
class ContactDetails {
public function setWebsite($address, $type) {
//do something...
}
public function getWebsite($type) {
//do something...
}
}
Now I want to create a form where the user can input a website address and choose a type (e.g. "private" or "business") for the address.
To make this possible I have created a custom form type like this
class ContactDetailsType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('type', 'text') //better: choice, but for the sake of demo...
->add('website', 'text')
;
}
public function getName() {
return 'ContactDetailsType';
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver
->setDefaults(array(
'data_class' => 'ContactDetails',
));
}
}
The controller then looks like this:
public function indexAction(Request $request) {
//generate completely new cost unit
$costunit = new ContactDetails();
//generate form
$form = $this->createForm(new ContactDetailsType(), $costunit);
$form->add('save', 'submit');
$form->handleRequest($request);
if ($form->isValid()) {
//yay!
}
}
This obviously doesn't work, as the form component doesn't know how to map these two fields from the type to the data model class.
Question: What is the best practise to map the data of two fields of a form to one method call in a data model class and vice-versa?
On your place a i would make both fields virtual in form and then use event listener to set data in entity.
Info about form events
I have a form with a choice type element. I need to populate it with data. As I know there are 3 methods.
1. Controller:
// Controller
public function myAction()
{
$choices = ...; // create choices array
$form = $this->createForm(new MyFormType($dm), null, array(
'choices' => $choices,
));
}
// Form
class MyFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('cars', 'choice', array(
'choices' => $options['choices']
));
}
}
2. Form class + repository
// Controller
public function myAction()
{
$dm = $this->get('doctrine')->getManager();
$form = $this->createForm(new MyFormType($dm));
}
// Form
class MyFormType extends AbstractType
{
private $dm;
public function __construct($dm)
{
$this->dm = $dm;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('cars', 'choice', array(
'choices' => $options['choices']
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$list = array();
foreach($this->dm->getRepository('MyBundle:Cars')->findAll() as $car) {
$list[$car->getName()] = $car->getName();
}
$resolver->setDefaults(array(
'choices' => $list,
));
}
}
3. Form class + custom service
// Controller
public function myAction()
{
$dm = $this->get('doctrine')->getManager();
$form = $this->createForm(new MyFormType(), null, array(
'myservice' => $this->get('myservice'),
));
}
// Form
class MyFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('cars', 'choice', array(
'choices' => $options['myservice']->getCars()
));
}
}
// Service
class MyService
{
const ENTITY_CAR = 'MyBundle:Cars';
/** #var DocumentManager */
private $dm;
public function __construct(DocumentManager $dm)
{
$this->dm = $dm;
}
public function getCars()
{
return $this->dm->getRepository("MyBundle:Cars")->findAll();
}
}
I'll express my thoughts.
The 1st option is not the best practice. Especially when complicated logic is involved. Controllers should be as tiny as possible.
The 2nd is much better. But it exposes entity name and problems may occur if I decide to rename it.
The 3rd is the best option, imho. Entity names are concentrated in one place, better IDE type hinting, centralized entity management (search, save, remove...). The main disadvantage is a possible over-engineered class as it's becoming responsible for many read/write operations. On the other hand it can be divided into pieces.
What do you think about it?
The third option is good if you have to reuse that service elsewhere in your code (and if that service will grown in comparison of that you've wrote, we'll see it later). In that way, as you said, "manager" of that entity is one and contains itself the name of repo,a const, and so on.
BUT
If this service is use only as a "pusher" for reach your repository by hiding its name, I don't think that this solution is still much good as it seems.
Obviously if that service is thought for have multiple persistance options and multiple retrieve option (base on what ORM you've selected), in that case this could be the best practice.
In other cases, I suppose that the second one is always the better.
The first isn't practicable unless you want to ignore all good practices
I suggest a fourth solution : use an entity field as it is designed to be a choice field with options loaded from DB !
Here is the official doc http://symfony.com/doc/master/reference/forms/types/entity.html
And how you may use it :
// Form
class MyFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('cars', 'entity', array(
'class' => 'MyBundle:Cars',
'property' => 'name',
//Optionnal if you need to condition the selection
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('u')->orderBy('u.username', 'ASC');
},
));
}
}
I have an entity with a OneToOne association to another entity. For these purposes I'll call the initial entity "Parent" and the associated Entity "Child".
I have a Parent form working fine that embeds the child form and all the form elements for both entities appear, and I can save the data fine in the controller.
Now I want to set defaults for a number of attributes in the embedded doctrine entity. I could set values for the new entity in the controller, but the child entity is created in the embedded form class:
// Parent form
class Parent extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
//parent->add(...)
$builder->add('child', new Child(), array());
}
// In Child Form
class Child extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('visibilitycode', 'entity', array('label' => 'Visibility', 'class'=>'Acme\MyBundle\Entity\Visibility', 'property'=>'name'));
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Acme\MyBundle\Entity\Child',
);
}
Many of these defaults are for associated foreign keys, so if I was setting them in the controller I might use something like this:
$child->setVisibilityCode($em->getReference('AcmeMybundle:Visibility', 'P'));
Two solutions are offered to you (or maybe more :-) ):
initialize default values in Child entity itself ( in constructor for example)
use the empty_data option of the Form component:
class Child extends AbstractType {
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('visibilitycode', 'entity', array(
'label' => 'Visibility',
'class'=>'Acme\MyBundle\Entity\Visibility',
'property'=>'name'
));
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Acme\MyBundle\Entity\Child',
'empty_data' => function() use($visibility) {
$child = new Child();
$child->setVisibility($visibility);
return $child;
}
);
}
}