I have the collection model:
class EntityList {
/**
* #var Entity[]
* #Assert\Count(min=1)
* #Assert\Valid()
*/
private $entityList;
// ...
}
The single model:
class Entity {
/**
* #var int
* #Assert\NotBlank()
*/
private $id;
/**
* #var string
* #Assert\NotBlank()
*/
private $name;
// ...
}
The collection's form:
class EntityListType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('entity', CollectionType::class, [
'entry_type' => EntityType::class,
'allow_add' => true,
'property_path' => 'entityList',
]);
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefault('data_class', EntityList::class);
}
public function getBlockPrefix() {
return null;
}
}
And the entity's form:
class EntityType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('id', NumberType::class)
->add('name', TextType::class)
;
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefault('data_class', Entity::class);
}
}
The collection's form using in a controller:
$form = $this->createForm(EntityListType::class);
$form->handleRequest($request);
I send the package with no required title (it's an API):
curl -X POST ...
-d entity[0][id]=10
And that accept it! The method isValid returns true. But if form's data check manually with ValidationComponent as:
if ($form->isValid()) { // it's valid, okay
echo $this->get('validator')->validate($form->getData()); // it's still not valid!
}
I see correct errors ('title is required')! Why?
P.S. Symfony 3.2, stable.
P.P.S. I was found temporarily solution now. It's use $form->submit($request->request->all()); instead of $form->handleRequest($request);. But I don't understand this feature.
Related
I have a problem with a nested form. I can't get the values I want to pass.
Here is a simple example to reproduce my problem, I would like to pre-fill a form about a user according to the selected house in my form.
Here are the files, if you want to test. I would like to inject the values of roger and billy the good way but my user fields are always empty
The models
class Test
{
/**
* #var string|null
*/
private $house;
/**
* #var TestUser|null
*/
private $user;
// Getters & Setters of course...
}
class TestUser
{
/**
* #var string|null
*/
private $name;
/**
* #var int|null
*/
private $age;
// Getters & Setters again...
}
The main form
class TestType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('house', ChoiceType::class, [
'choices' => [
'first' => 1,
'second' => 2,
],
]
);
$builder->get('house')->addEventListener(FormEvents::POST_SUBMIT, [$this, 'addUser']);
}
function addUser(FormEvent $event)
{
$form = $event->getForm()->getParent();
$house = $event->getForm()->getData();
if (!$house) {
return;
}
// here is the part where I choose the user I want to use
// for the rest of the example (which does not work)
$testUser = $house === 1
? (new TestUser())->setName('roger')->setAge(65)
: (new TestUser())->setName('billy')->setAge(22);
$builder = $form->getConfig()->getFormFactory()->createNamedBuilder('user', TestUserType::class, $testUser, [
'auto_initialize' => false,
]);
$form->add($builder->getForm());
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Test::class,
]);
}
}
The user form type
class TestUserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class, ['label' => 'username'])
->add('age', IntegerType::class, ['label' => 'age']);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => TestUser::class,
]);
}
}
The controller
public function testForm(Request $request): RedirectResponse|Response
{
// ...
$test = new Test();
$form = $this->createForm(TestType::class, $test);
$form->handleRequest($request);
// ...
}
The view
{{ form_start(formtest) }}
<p>
<button class="btn btn-primary" type="submit">test go</button>
</p>
{{ form_end(formtest) }}
all help is welcome
Is setter actually returning $this?
When ->setAge(65) is executed.
Because it's not clear, it's not in your code you provided here.
you need to link the $user $age $house and $name to the input fields you have.
The reason why you always get an empty output is do to the face that non of the variables refer to any data source.
I would like to create a google categories matching(first field categorie from database and second field a user autocomplete field from google categories) form where i have an entity CategoriesConfig :
private $id;
/**
* #var string
*
* #ORM\Column(name="category_site", type="string", length=100)
*/
private $categorySite;
/**
* #var string
*
* #ORM\Column(name="category_google", type="string", length=100)
*/
private $categoryGoogle;
In my Controller i tried this
/**
* #Route("/adminDashboard/categoriesMatching", name="googleShopping_categories")
* #Security("has_role('ROLE_SUPER_ADMIN')")
*/
public function categoriesMatchingAction(Request $request)
{
// create a task and give it some dummy data for this example
$idSite = $this->get('session')->get('_defaultWebSite')->getId();
$categories = $this->getDoctrine()->getRepository('DataSiteBundle:SiteCategory')->findBy(array('IdSite' => $idSite));;
$categories_config = new CategoriesConfig();
//var_dump($categories);exit;
$form = $this->createForm(new CategoriesConfigType($categories), $categories_config);
return $this->render('GoogleShoppingBundle:Default:categoriesMatching.html.twig', array(
'form' => $form->createView()
));
}
And my form type : CategoriesConfigType:
class CategoriesConfigType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
private $site_categories;
public function __construct ($site_categories) {
$this->site_categories = $site_categories;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
foreach($this->site_categories as $k => $categorie){
$builder
->add('categorySite')
->add('categoryGoogle');
}
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Sp\GoogleShoppingBundle\Entity\CategoriesConfig'
));
}
}
I would like to have as many categories rows as row fields(website itecategorie and google categorie)
The result is like that:
Thank you in advance!
Your loop on $this->categories is uneffective, because the elements you add have the same name each time (categorySite and categoryGoogle), so the FormBuilder replaces the form field each time, instead of adding another one.
However, if you want your form to handle a Collection of CategoryConfigs, you need to take a different approach.
1) Create a CategoriesConfigType (as you did), but who is responsible of only a single CategoriesConfig entity
class CategoriesConfigType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('categorySite')
->add('categoryGoogle');
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Sp\GoogleShoppingBundle\Entity\CategoriesConfig'
));
}
}
2) Then use CollectionType field to manipulate your form as a whole collection of CategoryConfigTypes:
class YourCollectionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('categoriesConfigs', CollectionType::class, array(
'entry_type' => CategoriesConfigType::class,
'entry_options' => array('required' => false)
);
}
}
I have Symfony form type class and I want use them standalone and as a part of other form. My class looks this:
class PracownikType extends AbstractType
{
const DATA_CLASS = 'AppBundle\Entity\Pracownik';
/** #var IPracownikRepository */
private $pracownikRepository;
/** #var User */
private $user;
/**
* PracownikType constructor.
* #param IPracownikRepository $pracownikRepository
* #param TokenStorage $tokenStorage
*/
public function __construct(IPracownikRepository $pracownikRepository, TokenStorage $tokenStorage)
{
$this->pracownikRepository = $pracownikRepository;
$this->user = $tokenStorage->getToken()->getUser();
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('imie', 'text')
->add('nazw', 'text')
->add('email', 'text')
->add('iloscDniWolnych', 'integer')
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'class' => self::DATA_CLASS,
'choices' => $this->pracownikRepository->findAllByUser($this->user),
'choice_label' => 'getNazw'
]);
}
public function getParent()
{
return 'entity';
}
public function getName()
{
return 'pracownik';
}
}
I wand use this class to create standalone form:
$form = $this->createForm('pracownik', $this->dodajPracownikaCommand);
And part of other form:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('pracownik', 'pracownik');
}
How can I do this? Please help me. :)
instead of just using the string 'pracownik', create an instance like new PracownikType().
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('pracownik', new PracownikType());
}
if you must use the string you have to declare your form type as a service. See this: http://symfony.com/doc/current/book/forms.html#defining-your-forms-as-services
I don't recommend that at this time however as Symfony 2.8 is deprecating that type of behavior I think.
A continuation of this question:
I'm trying to embed a collection of forms as described in the official docs here. Unfortunately, I'm getting the following exception:
The form's view data is expected to be an instance of class Acme\SiteBundle\Entity\BlogPost, but is a(n) array. You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms a(n) array to an instance of Acme\SiteBundle\Entity\BlogPost.
Which is odd, as I believe I followed the official docs to a 't':
BlogPostType:
class BlogPostType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// static text fields/attrs (title, body, etc)
$builder->add('comments', 'collection', array('type' => new CommentType()));
}
public function getName()
{
return 'blogpost';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array('data_class' => 'Acme\SiteBundle\Entity\BlogPost'));
}
}
CommentType:
class CommentType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('commentId', 'hidden');
$builder->add('commentBody','text',array('label' => 'Comment:','attr'=>array('size'=>80,'class'=>'form-item-input form-type-texfield')));
}
public function getName()
{
return 'comment';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array('data_class' => 'Acme\SiteBundle\Entity\Comment'));
}
}
Relationship as defined in BlogPost:
/**
* #var \Acme\SiteBundle\Entity\Comment.php
*
* #ORM\OneToMany(targetEntity="Comment", mappedBy="blogpost",
cascade={"all"},orphanRemoval=true)
* #ORM\OrderBy({"commentId" = "ASC"})
*/
private $comments;
And in Comment:
/**
* #var BlogPost
*
* #ORM\ManyToOne(targetEntity="BlogPost", inversedBy="comments")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="blog_id", referencedColumnName="id")
* })
*/
private $blogPost;
So, I'm not sure where I've gone wrong. Any ideas?
Turns out I was an idiot (big surprise there) and was passing the form builder an array. Old code that used to work was breaking it now. Whee!
I have to create a multilingual table so i choose this schema:
Article ( id, name_translation_fk)
Translation ( id )
Translation_Text (id, language, translation_fk, text)
Now i need to add names in different languages for an article that allready exists.
Doctrine gives me this error:
Catchable Fatal Error: Argument 1 passed to Doctrine\Common\Collections\ArrayCollection::__construct() must be an array, object given, called in */vendor/doctrine/lib/Doctrine/ORM/UnitOfWork.php on line 416 and defined in */vendor/doctrine-common/lib/Doctrine/Common/Collections/ArrayCollection.php line 46
I have no clue what the problem could be since all the entities are well declared.I think the issue is somewhere in the Form Class.
I have posted below my entities, forms and views implicated.
Article
class Article
{
/**
* #ORM\ManyToOne(targetEntity="Translation", inversedBy="article_name", cascade= {"persist"})
* #ORM\JoinColumn(name ="name", referencedColumnName="id")
*/
protected $name;
}
Translation
class Translation
{
/**
* #ORM\OneToMany(targetEntity="Translation_Text", mappedBy="translation", cascade={"persist"})
*/
public $translation_text;
/**
* #ORM\OneToMany(targetEntity="Article", mappedBy="name", cascade={"persist"})
*/
protected $article_name;
public function __construct()
{
$this->translation_text = new ArrayCollection();
$this->article_name = new ArrayCollection();
}
}
Translation_Text
class Translation_Text
{
/**
* #ORM\ManyToOne(targetEntity="Language", inversedBy="translation_text")
* #ORM\JoinColumn(name ="language_id", referencedColumnName="id")
*/
protected $language;
/**
* #ORM\ManyToOne(targetEntity="Translation", inversedBy="translation_text")
* #ORM\JoinColumn(name ="translation_id", referencedColumnName="id")
*/
protected $translation;
}
The form
class TranslationTextType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('text','text');
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Udo\WebserviceBundle\Entity\Translation_Text',
);
}
public function getName()
{
return 'translation_text';
}
}
class TranslationType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('translation_text',new TranslationTextType());
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Udo\WebserviceBundle\Entity\Translation',
);
}
public function getName()
{
return 'translation';
}
}
class ArticleTranslationForm extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('name',new TranslationType());
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Udo\WebserviceBundle\Entity\Article',
);
}
public function getName()
{
return 'article';
}
}
The controller
$article = $em->getRepository('Udo\WebserviceBundle\Entity\Article')->find($id);
$form = $this->createForm(new ArticleTranslationForm(),$article);
The form view
<form action="{{path('article_translate', { 'id': entity.id }) }}" method="post" {{ form_enctype(form) }}>
{{form_row(form.name.translation_text.text)}}
{{form_rest(form)}}
<input type="submit" />
</form>
Since it is a ont-to-many relationship, you should use collection:
$builder->add('translation_text', 'collection', array('type' => new TranslationTextType()));
instead of:
$builder->add('translation_text',new TranslationTextType());