I want to use two entities in one form and despite using tips from previous questions which were similar, it doesn't work. I have following controller:
<?php
namespace AppBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use AppBundle\Entity\User;
use AppBundle\Form\UserType;
use AppBundle\Entity\School;
use AppBundle\Form\SchoolType;
class RegistrationController extends Controller
{
/**
* #Route("/register", name="user_register")
*/
public function registerAction(Request $request)
{
$user = new User();
$form = $this->createForm(new UserType(), $user);
$form->add('status','hidden', array('attr' => array('value' => '0') ))
->add('school', new SchoolType());
return $this->render('AppBundle:Main:register.html.twig', array('form' => $form->createView()));
}
}
UserType.php
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList;
class UserType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('login', 'text', array('attr' => array(
'id' => 'login', 'name' => 'login', 'label' => 'Login:',
'class' => 'form-control', 'required' => true
)))
->add('password', 'password', array('attr' => array(
'id' => 'password', 'name' => 'password', 'label' => 'Password:',
'class' => 'form-control', 'required' => true
)))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\User'
));
}
/**
* #return string
*/
public function getName()
{
return 'appbundle_user';
}
}
SchoolType.php
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class SchoolType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'text', array('attr' => array(
'class' => 'form-control', 'id' => 'schoolName', 'name' => 'schoolName',
'placeholder' => 'np. Szkoła podstawowa nr. 5 w Warszawie', 'label' => 'Nazwa Szkoły',
'required' => true
)))
->add('shortcut', 'text', array('attr' => array(
'class' => 'form-control', 'id' => 'shortcut', 'name' => 'shortcut',
'placeholder' => 'np. SP5Warszawa', 'label' => 'Skrót', 'required' => true
)))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\School'
));
}
/**
* #return string
*/
public function getName()
{
return 'school';
}
}
and finally twig template:
{{ form_start(form) }}
<div class="form-group">
{{ form_row('form.school.name') }}
</div>
<div class="form-group">
{{ form_row('form.school.shortcut') }}
</div>
<div class="form-group">
{{ form_row('form.login') }}
</div>
<div class="form-group">
{{ form_row('form.login') }}
</div>
{{ form_row('form.status') }}
<span style="display:inline-block;">
<button type="submit" class="btn btn-default">Create</button>
{{ form_end(form) }}
Why it doesn't work? I receive following error:
Neither the property "school" nor one of the methods "getSchool()", "school()", "isSchool()", "hasSchool()", "__get()" exist and have public access in class "AppBundle\Entity\User".
Your User class must have a propriety called school.
/**
* #ORM\Entity()
* #ORM\Table(name="users")
*/
class User
{
/**
* #ORM\OneToMany(targetEntity="Bundle\Entity\Schools",mappedBy="user")
*/
protected $school;
Then, generate entities for it,
php app/console doctrine:generate:entities Bundle:User
or write the functions manually:
public function setSchool($school)
{
$this->school= $school;
return $this;
}
public function getSchool()
{
return $this->school;
}
It's telling you exactly what you need to know. In your User entity, you either don't have a relation to your School entity or you are missing a getter & setter for School. Assuming it is a one to one relationship you should have something like:
/**
* #OneToOne(targetEntity="School", inversedBy="user")
* #JoinColumn(name="school_id", referencedColumnName="id")
**/
private $school;
With getter & setter:
public function setSchool(School $school = null)
{
$this->school = $school;
return $this;
}
public function getSchool()
{
return $this->school;
}
Note these should be automatically generated by the doctrine generate entities command. You may simply not have run this if you have the User->School relationship set up correctly and the setter/getter missing.
Please also read the documentation on association mapping
Related
i have a entity called DynamicForm, which looks like this:
<?php
namespace App\Entity\Product\DynamicForm;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\Entity;
/**
* Class DynamicForm
* #package App\Entity\Product
*
* #Entity
* #ORM\Table(name="product_dynamic_form")
*/
class DynamicForm
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(name="id", type="integer", unique=true, nullable=false)
*/
private ?int $id = null;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Product\DynamicForm\Component\Text", mappedBy="dynamicForm")
* #ORM\JoinColumn(name="component_text_id", referencedColumnName="id")
*/
private ?Collection $textComponents;
/**
* #return Collection|null
*/
public function getTextComponents(): ?Collection
{
return $this->textComponents;
}
/**
* #param Collection|null $textComponents
*
* #return DynamicForm
*/
public function setTextComponents(?Collection $textComponents): DynamicForm
{
$this->textComponents = $textComponents;
return $this;
}
}
Also i created a related type for it - DynamicFormType:
<?php
namespace App\Type\Product\DynamicForm;
use App\Entity\Product\DynamicForm\DynamicForm;
use App\Type\Product\DynamicForm\Component\TextType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class DynamicFormType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('textComponents', CollectionType::class, [
'entry_type' => TextType::class,
'entry_options' => ['label' => false],
'allow_add' => true,
'allow_delete' => true,
'label' => ' '
])
->add('submit', SubmitType::class, [
'label' => 'form.basic.save'
]);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('data_class', DynamicForm::class);
}
}
The TextType entry type class from the namespace App\Type\Product\DynamicForm\Component\TextType looks like this:
<?php
namespace App\Type\Product\DynamicForm\Component;
use App\Entity\Product\DynamicForm\Component\Text;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type as FormType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Contracts\Translation\TranslatorInterface;
class TextType extends AbstractType
{
private TranslatorInterface $translator;
/**
* TextType constructor.
*
* #param TranslatorInterface $translator
*/
public function __construct(TranslatorInterface $translator)
{
$this->translator = $translator;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('label', FormType\TextType::class, [
'required' => true,
'translation_domain' => 'wedding',
'label' => 'setting.form.dynamic_by_user.query_to_guest'
])
->add('type', FormType\ChoiceType::class, [
'required' => true,
'translation_domain' => 'wedding',
'label' => 'setting.form.dynamic_by_user.select_type',
'attr' => [
'class' => 'enriched',
'data-search-placeholder' => $this->translator->trans('select.search'),
'data-search-no-results-text' => $this->translator->trans('select.search_no_results_found')
],
'choice_translation_domain' => 'wedding',
'choices' => [
'setting.form.dynamic_by_user.type_text' => Text::TYPE_TEXT_FIELD,
'setting.form.dynamic_by_user.type_textarea' => Text::TYPE_TEXT_AREA,
'setting.form.dynamic_by_user.type_email' => Text::TYPE_EMAIL_FIELD,
'setting.form.dynamic_by_user.type_number' => Text::TYPE_NUMBER_FIELD,
]
]);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('data_class', Text::class);
}
}
While i try to transmit the form, the request in the controller contains the form data as you can see it, in the following screenshot:
My problem is now that i always get the following error (while handling the request for the form via ->handleRequest($request) on created form in controller):
Expected argument of type "?Doctrine\Common\Collections\Collection", "array" given at property path "textComponents".
I have such collection settings also in other classes, but without problems - I don't know any further, can anyone please assist me or see the error?
(I am using Symfony version 5.2.9, if u need any further info just ask for it - I will give it to you as soon as possible)
Trying to add a constructor in your entity
public function __construct()
{
$this->textComponents = new ArrayCollection();
}
Add addTextComponent and removeTextComponent methods intead of setTextComponents
public function addTextComponent(Text $textComponent): self
{
$textComponent->setDynamicForm($this);
$this->textComponents->add($textComponent);
return $this;
}
public function removeTextComponent(Text $textComponent): self
{
$this->textComponents->removeElement($textComponent);
return $this;
}
Add 'by_reference' => false in the textComponents form params
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('textComponents', CollectionType::class, [
'entry_type' => TextType::class,
'entry_options' => ['label' => false],
'allow_add' => true,
'allow_delete' => true,
'label' => ' ',
'by_reference' => false,
])
->add('submit', SubmitType::class, [
'label' => 'form.basic.save'
]);
}
I was able to fix this by doing this, that allows us to pass a Collection or an empty array or an array of your entity which I think is Text:
/**
* #param Collection|Text[] $textComponents
*
* #return DynamicForm
*/
public function setTextComponents($textComponents): DynamicForm
{
$this->textComponents = $textComponents;
return $this;
}
I am creating a select that takes the data of an entity, called category.
The select that I want to develop, would basically be the same that I have developed and working, but with the values I take from the category entity.
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use BackendBundle\Entity\Categoria;
use BackendBundle\Entity\CategoriaRepository;
class ProductoType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('nombre', TextType::class, array(
'label' => 'Nombre',
'required' => 'required',
'attr' => array(
'class' => 'form-name form-control'
)
))
->add('categoria', ChoiceType::class, array(
'choices' => array(
'General' => '1',
'Coffe' => '2'
),
'required' => false,
'empty_data' => null
))
->add('Save', SubmitType::class, array(
"attr" =>array(
"class" => "form-submit btn btn-success"
)
))
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'BackendBundle\Entity\Producto'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'backendbundle_producto';
}
}
I would add a section like the following, but I get the error Could not load type "entity"
->add('categoria', 'entity', array(
'class' => 'BackendBundle:Categoria'
)
)
The original BBDD is documented in Object of class \BackendBundle\Entity\Categoria could not be converted to string
Firstly if your are using symfony 3 you must use Symfony\Bridge\Doctrine\Form\Type\EntityType and the class should be the class name not the entity name
->add('categoria', 'Symfony\Bridge\Doctrine\Form\Type\EntityType', array(
'class' => 'BackendBundle\Entity\Categoria'
)
)
and categoria should look like:
namespace BackendBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table()
* #ORM\Entity()
*/
class Categoria
{
/**
* #var int
*
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string")
*/
protected $name;
public function __toString()
{
return $this->name;
}
}
'entity' should be EntityType::class,
you should use the classname of the EntityType instead of just the 'entity' string
See:
https://github.com/symfony/symfony/blob/master/UPGRADE-3.0.md#form
I need to get preselected some values of an entity attribute that I get in the PRE_SET_DATA event, not from data base.
I have a Form working, all datas from my Entity AccessGroup is loaded but my problem is to get selected the ArrayCollection attribute named accessGroups from entity User which is not stored in database.
To make it clear, attribute accessGroups is loaded by User's roles.
Here is the FormType Class
namespace Pkg\ExtranetBundle\Form;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Doctrine\ORM\EntityManager;
class RoleType extends AbstractType
{
/**
* #var EntityManager
*/
protected $em;
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->em = $options['em'];
$builder->addEventListener(FormEvents::PRE_SUBMIT, array($this, 'onPreSubmit'));
}
/**
* Listener before normalizing data form
*
* #param FormEvent $event
*/
public function onPreSetData(FormEvent $event)
{
$user = $event->getData();
$accessGroups = $this->em->getRepository('PkgExtranetBundle:AccessGroup')->getSelected($user->getRoles());
$user->setAccessGroups(new ArrayCollection($accessGroups));
$event->setData($user);
$form = $event->getForm();
$form->add('accessGroups', EntityType::class, array(
'class' => 'PkgExtranetBundle:AccessGroup',
'choice_label' => 'name',
'choice_value' => 'role',
'multiple' => true,
'expanded' => false
))
->add('save', SubmitType::class, array('label' => 'registration.submit', 'translation_domain' => 'FOSUserBundle'));
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Pkg\ExtranetBundle\Entity\User',
'em' => null
));
}
}
Okey, the class I made has its own problem with the choice_value parameter :
$form->add('accessGroups', EntityType::class, array(
'class' => 'PkgExtranetBundle:AccessGroup',
'choice_label' => 'name',
'choice_value' => 'role', // If choice_value is not the entity index, then preselection will not be applied as the index could not be retrieved.
'multiple' => true,
'expanded' => false
))
I am trying to save entities of 2 classes in 1 form I have read this article about it. My code is :
class MeetingType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('meetingDay', 'text', array('label' => 'Dzień zbiórki'))
->add('meetingTime', 'text', array('label' => 'Godzina zbiórki'))
->add('congregation', 'entity', array(
'class' => 'ViszmanCongregationBundle:Congregation',
'property' => 'name', 'label' => 'Zbór'
));
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Viszman\CongregationBundle\Entity\Meeting'
));
}
/**
* #return string
*/
public function getName()
{
return 'viszman_congregationbundle_meeting';
}
}
And another TYPE:
class CongregationType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$plDays = array('day1', 'day1', 'day1', 'day1', 'day1', 'day1', 'day1');
$builder
->add('name', 'text', array('label' => 'Name'))
->add('meetingDay', 'choice', array('label' => 'meeting day', 'choices' => $plDays))
->add('meetings','collection', array('type' => new MeetingType(), 'allow_add' => true))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Viszman\CongregationBundle\Entity\Congregation'
));
}
/**
* #return string
*/
public function getName()
{
return 'viszman_congregationbundle_congregation';
}
}
And when I try do render this form I only get CongregationType and no MeetingType form. part of code responsible for rendering form is:
<h1>Congregation creation</h1>
{{ form_start(form) }}
{{ form_widget(form) }}
<h3>Meetings</h3>
<ul class="tags" data-prototype="{{ form_widget(form.meetings.vars.prototype)|e }}">
{{ form_widget(form.meetings) }}
</ul>
{{ form_end(form) }}
If I remember correctly the entity form field is used to reference an existing entity, not to create a new one.
This is not what you need, what you need is embedded forms, take a look at the symfony book.
I asked a similar question to this but I think it caused confusion so I decided to ask in this post with brushed up version.
What I want is to print all fields from two different entities in a single web form, BOTH TYPE. That's it.
Note: I tried using entity and collection keywords in the form type (BOTH TYPE) but twig doesn't echo anything. Keep getting; Method "brand" OR "car" for object does not exist in twig line whatever....
Relationship: 1 Brand has N Cars. one-to-many
I read the 'How to Embed a Collection of Forms', 'entity Field Type' and 'collection Field Type' but whatever I did, didn't work.
BRAND ENTITY
namespace Car\BrandBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
class BrandEntity
{
protected $id;
protected $name;
protected $origin;
/**
* #ORM\OneToMany(targetEntity = "CarEntity", mappedBy = "brand")
* #var object $car
*/
protected $car;
/**
* Constructor.
*/
public function __construct()
{
$this->car = new ArrayCollection();
}
}
CAR ENTITY
namespace Car\BrandBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
class CarEntity
{
protected $id;
protected $model;
protected $price;
/**
* #ORM\ManyToOne(targetEntity="BrandEntity", inversedBy="car")
* #ORM\JoinColumn(name="brand_id", referencedColumnName="id")
* #var object $brand
*/
protected $brand;
}
BRAND TYPE
namespace Car\BrandBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class BrandType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->setAction($options['action'])
->setMethod('POST')
->add('name', 'text', array('label' => 'Name'))
->add('origin', 'text', array('label' => 'Origin'))
->add('button', 'submit', array('label' => 'Add'))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Car\BrandBundle\Entity\BrandEntity')
);
}
public function getName()
{
return 'brand';
}
}
CAR TYPE
namespace Car\BrandBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class CarType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->setAction($options['action'])
->setMethod('POST')
->add('model', 'text', array('label' => 'Model'))
->add('price', 'text', array('label' => 'Price'))
->add('button', 'submit', array('label' => 'Add'))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Car\BrandBundle\Entity\CarEntity')
);
}
public function getName()
{
return 'car';
}
}
---------------------------------------------------------------------
-------- This section is the one I'm trying to get it working ------
---------------------------------------------------------------------
BOTH TYPE
namespace Car\BrandBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Test\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class BothType extends AbstractType
{
public function builder(FormBuilderInterface $builder, array $options)
{
$builder
->setAction($options['action'])
->setMethod('POST')
->add('brand', 'collection', array('type' => new BrandType()))
->add('car', 'collection', array('type' => new CarType()))
->add('button', 'submit', array('label' => 'Add'))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Car\BrandBundle\Entity\BrandEntity',
'cascade_validation' => true
));
}
public function getName()
{
return 'both';
}
}
CONTROLLER
namespace Car\BrandBundle\Controller;
use Car\BrandBundle\Form\Type\BothType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class BothController extends Controller
{
public function indexAction()
{
$form = $this->createForm(new BothType(), null,
array('action' => $this->generateUrl('bothCreate')));;
return $this->render('CarBrandBundle:Default:both.html.twig',
array('page' => 'Both', 'form' => $form->createView()));
}
}
TWIG
{% block body %}
{{ form_label(form.brand.name) }}
{{ form_widget(form.brand.name) }}
{{ form_label(form.brand.origin) }}
{{ form_widget(form.brand.origin) }}
{{ form_label(form.car.model) }}
{{ form_widget(form.car.model) }}
{{ form_label(form.car.price) }}
{{ form_widget(form.car.price) }}
{% endblock %}
Use an array to composite the two objects in your controller.
$formData = array(
'brand' = new Brand(),
'car' => new Car(),
);
$builder = $this->createFormBuilder($formData);
$builder->add('brand',new BrandFormType());
$builder->add('car', new CarFormType());
$form = $builder->getForm();
==============================================================
If you really want to make a BothType then just get rid of that collection type.
class BothType extends AbstractType
{
public function builder(FormBuilderInterface $builder, array $options)
{
$builder
->setAction($options['action'])
->setMethod('POST')
->add('brand', new BrandType())
->add('car', new CarType())
->add('button', 'submit', array('label' => 'Add'))
;
}
// Controller
$form = $this->createForm(new BothType(), $formData
collection is used when you have multiple instances of the same entity type.
By the way, creating classes for each composite form can quickly cause an explosion of form types. So unless you plan on reusing your BothFormType among multiple controllers then I'd suggest just building it right inside of the controller.