Symfony2 exception when trying to embed a collection of forms - php

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!

Related

Why did Symfony forms does not using validator normally?

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.

save form with vich_image onetone

/**
* #Gedmo\Tree(type="nested")
* #ORM\Table(name="mKeyword")
* #ORM\Entity(repositoryClass="KeywordRepository")
*/
class Keyword {
/**
* #ORM\OneToOne(targetEntity="Image",mappedBy="keyword" ,cascade={"all"})
* #var Image
*/
private $logo;
}
/**
* #Vich\Uploadable
* #ORM\Entity
* #ORM\Table(name="mKeywordLogo")
*/
class Image {
}
form
class KeywordType extends AbstractType{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title','text')
->add('logo',new ImageType())
image form
class ImageType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('file','vich_image',array(
'label'=>'logo'
));
}
when save form
get
Expected value of type "KeywordsBundle\Entity\Image" for association field "Mea\KeywordsBundle\Entity\Keyword#$logo", got "array" instead.
i add array parser in Keyword
public function setLogo($logo)
{
if(is_array($logo))
$logo = reset($logo);
$this->logo = $logo;
}
so get error
Expected value of type "KeywordsBundle\Entity\Image" for association field "KeywordsBundle\Entity\Keyword#$logo", got "Symfony\Component\HttpFoundation\File\UploadedFile" instead.
I Fond error, Form ImageType don't havee defaults, when i add this. save work fine
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Mea\KeywordsBundle\Entity\Image'
));
}

Symfony 2 - show only one entity from OneToMany relationship

I need to display the min date from a group of entities in a relationship. Eg. I have User and Contracts -> the User has many Contracts, but I need to display the minimum date from contracts.
Here is the code:
class User implements UserInterface
{
/**
* #ORM\OneToMany(targetEntity="Comp\ContractBundle\Entity\Contract", mappedBy="user_id")
* #ORM\OrderBy({"id" = "DESC"})
*
*/
private $contracts;
}
class Contract
{
/**
* #var string $datastart
*
* #ORM\Column(name="datastart", type="datetime")
*/
private $datastart;
/**
* #var string $dataend
*
* #ORM\Column(name="dataend", type="datetime")
*/
private $dataend;
/**
* #var integer $user_id
*
* #ORM\ManyToOne(targetEntity="Comp\AuthBundle\Entity\User")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user_id;
}
class UserType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options){
#... other data
$builder->add('contracts','collection', array(
'type' => new ContractType()
) );
}
public function getName()
{
return 'User';
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Comp\AuthBundle\Entity\User',
);
}
}
And the ContractType:
class ContractType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options){
$builder->add('name','text');
$builder->add('datastart','datetime');
}
public function getName()
{
return 'Contract';
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Comp\ContractBundle\Entity\Contract',
);
}
}
The problem is, I get each Entity related to the User Entity - I just need to fetch one. If you know any good example - it would be great.
Change contracts field type from collection to entity, and google for query_builder attribute of entity field type.
In that attribute you can pass a closure (returning QueryBuilder) or QueryBuilder itself, in which you can retrieve specific records from given relationship.

Nested Symfony2 Forms: $options['data'] = null in nested form?

As the documentation around this topic is somewhat thin, I got to a dead end.
I have two models: Job and JobAttribute.
A Job has many JobAttributes and a JobAttribute has one Job:
class Job {
/**
* #ORM\OneToMany(targetEntity="JobAttribute", mappedBy="job_attributes")
*
* #var ArrayCollection
*/
private $attributes;
}
class JobAttribute {
/**
* #ORM\Column(name="type", type="string", length=50)
*
* #var string
*/
private $type;
/**
* #ORM\ManyToOne(targetEntity="Job", inversedBy="jobs")
*/
private $job;
Now,I have the following FormClass:
class JobType extends AbstractType {
public function buildForm(FormBuilder $f, array $options) {
$f->add('name', 'text');
$f->add('attributes', 'collection', array('type' => new JobAttributeType()));
}
public function getName() {
return 'job';
}
}
class JobAttributeType extends AbstractType {
public function buildForm(FormBuilder $f, array $options) {
$attribute = $options['data'];
$f->add('value', $attribute->getType());
}
public function getDefaultOptions(array $options) {
return array('data_class' => 'JWF\WorkflowBundle\Entity\JobAttribute');
}
public function getName() {
return 'job_attribute';
}
}
Yes, indeed, the type property of JobAttribute contains a Form field type, eg. text.
So, as I call a FormBuilder on JobType in my Controller, $options['data'] is correctly populated with a Job-Object within JobType.
But the nested JobAttributeType's $options['data'] doesn't point to an JobAttribute object. It's NULL.
What's the problem? Where is the association lost? Why is $options['data'] = NULL in nested forms?
Is there a workaround in order to get dynamic field types (out of Doctrine) in a nested form?
Thanks in advance!
You cannot rely on $options['data'] when you build the form, as the data can (and will) be changed anytime after building. You should use event listeners instead.
$formFactory = $builder->getFormFactory();
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($formFactory) {
$form = $event->getForm();
$data = $event->getData();
if ($data instanceof JobAttribute) {
$form->add($formFactory->createNamed('value', $data->getType());
}
});
The documentation for this can be found in the cookbook.

Symfony 2 embedded forms ArrayCollection error

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());

Categories