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
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.
/**
* #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'
));
}
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.
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.
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());