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

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.

Related

Doctrine Array type field not being updated

My question is more or less the same as this one:
How to force Doctrine to update array type fields?
but for some reason the solution didn't work for me, so there must be a detail I am missing and I would be happy if someone could point it out to me.
The context is a Symfony 5.2 app with Doctrine ^2.7 being used.
Entity-Class-Excerpt:
class MyEntity {
// some properties
/**
* #var string[]
* #Groups("read")
* #ORM\Column(type="array")
* #Assert\Valid
*/
protected array $abbreviations = [];
public function getAbbreviations(): ?array
{
return $this->abbreviations;
}
//this is pretty much the same set-function as in the question I referenced
public function setAbbreviations(array $abbreviations)
{
if (!empty($abbreviations) && $abbreviations === $this->abbreviations) {
reset($abbreviations);
$abbreviations[key($abbreviations)] = clone current($abbreviations);
}
$this->abbreviations = $abbreviations;
}
public function addAbbreviation(LocalizableStringEmbeddable $abbreviation): self
{
foreach ($this->abbreviations as $existingAbbreviation) {
if ($abbreviation->equals($abbreviation)) {
return $this;
}
}
$this->abbreviations[] = $abbreviation;
return $this;
}
public function removeAbbreviation(LocalizableStringEmbeddable $abbreviation): self
{
foreach ($this->abbreviations as $i => $existingAbbreviation) {
if ($abbreviation->equals($existingAbbreviation)) {
array_splice($this->abbreviations, $i, 1);
return $this;
}
}
return $this;
}
}
But none of these methods are ever being called (I also tried removing add-/removeAbbreviation only leaving get/set in place).
LocalizableStringEmbeddable being an Embeddable like this:
* #ORM\Embeddable
*/
class LocalizableStringEmbeddable
{
/**
* #var string|null
* #Groups("read")
* #ORM\Column(type="string", nullable=false)
*/
private ?string $text;
/**
* #var string|null
* #Groups("read")
* #ORM\Column(type="string", nullable=true)
* #Assert\Language
*/
private ?string $language;
//getters/setters/equals/#Assert\Callback
}
By using dd(...) I can furthermore say that in my controller on submit
$myEntity = $form->getData();
yields a correctly filled MyEntity with an updated array but my subsequent call to
$entityManager->persist($myEntity);
$entityManager->flush();
doesn't change the database.
What am I missing?
EDIT: I was asked to give information about the Type I use. It is a custom one that is based on this class. So technically at the base of things I am using a collection type.
abstract class AbstractLocalizableUnicodeStringArrayType extends AbstractType implements DataMapperInterface
{
abstract public function getDataClassName(): string;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('items', CollectionType::class, [
'entry_type' => LocalizedStringEmbeddableType::class,
'entry_options' => [
'data_class' => $this->getDataClassName(),
'attr' => ['class' => 'usa-item-list--item-box'],
],
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
])
;
$builder->setDataMapper($this);
}
public function mapDataToForms($viewData, iterable $forms): void
{
$forms = iterator_to_array($forms);
if (null === $viewData) {
return;
}
if (!is_array($viewData)) {
throw new UnexpectedTypeException($viewData, "array");
}
$forms['items']->setData($viewData);
}
public function mapFormsToData(iterable $forms, &$viewData): void
{
$forms = iterator_to_array($forms);
$viewData = $forms['items']->getData();
}
}
As I found out this can be solved (in an unelegant way in my opinion but until something better comes around I want to state this) by combining two answers from another post on SO:
https://stackoverflow.com/a/13231876/6294605
https://stackoverflow.com/a/59898632/6294605
This means combining a preflush-hook in your entity class with a "fake-change" of the array in question.
Remark that doing the fake-change in a setter/adder/remover didn't work for me as those are not being called when editing an existing entity. In this case only setters of the changed objects inside the array will be called thus making Doctrine not recognize there was a change to the array itself as no deep-check seems to be made.
Another thing that was not stated in the other thread I wanna point out:
don't forget to annotate your entity class with
#ORM\HasLifecycleCallbacks
or else your preflush-hook will not be executed.

How create a symfony form type for tags with select2 v4

Im trying to update an already-created form type (TagsType) from select2 v3 to v4. But I found the problem that the new select2 use selects instead of text field. The following code works fine with v3.
class TagsType extends AbstractType
{
/**
* #inheritdoc
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addViewTransformer(new StringToArrayTransformer());
}
/**
* {#inheritdoc}
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
$attr = isset($view->vars['attr']) ? $view->vars['attr'] : [];
$view->vars['attr'] = array_merge($attr, ['data-toggle' => 'tags']);
}
/**
* #inheritdoc
*/
public function getParent()
{
return 'text';
}
/**
* #inheritdoc
*/
public function getName()
{
return 'tags';
}
}
StringToArrayTransformer:
class StringToArrayTransformer implements DataTransformerInterface
{
/**
* #inheritdoc
*/
public function transform($value)
{
if (!$value ) {
return [];
}
if (!is_array($value)) {
throw new TransformationFailedException('Expected a Array.');
}
return implode(',', $value);
}
/**
* #inheritdoc
*/
public function reverseTransform($value)
{
if (null === $value) {
return [];
}
if (!is_string($value)) {
throw new TransformationFailedException('Expected a String.');
}
return explode(',', $value);
}
}
The above code works fine with a Entity with field of type simple_array (comma separated). I need do the same but using the new vesion of select2.
Refactoring:
class TagsType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildView(FormView $view, FormInterface $form, array $options)
{
$attr = isset($view->vars['attr']) ? $view->vars['attr'] : [];
$view->vars['attr'] = array_merge($attr, ['data-toggle' => 'tags']);
}
/**
* #inheritdoc
*/
public function getParent()
{
return 'choice';
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(['multiple' => true]);
}
/**
* #inheritdoc
*/
public function getName()
{
return 'tags';
}
}
The refactor generate a select tag with multiple but empty. And when try to add some option (Tag) the validation fails because dynamic creation of new options in the select.
My questions:
How to implement a choice with dynamic options in symfony?
How to set default choices from field data (array)
Forget all about ',' and convert string to array or vice versa and save each Tag entity individually.
So, to make your form compatible with select2-v4.x you need one EntityType field with 'multiple' attribute set to TRUE.
Every thing is handled by symfony, each option(s) in select tag of your form is connected to its entity.
I just found this solution and it works for me, but I don't know maybe some things are missing...
Very old question, but I found it from Search engine. I tried to make it with simple_array + data transformer — it's wrong way (as #YaserKH said). Better to make tags as entity Tag etc. (you can find in the official docs). But if you need simple solution for some reasons (as me):
Create Text type column in database (not simple_array)
Define it as HiddenType in form builder:
$builder->add('tagsForSearch', HiddenType::class, [ 'required' => false ])
Make twig like:
{{ form_row(form.tagsForSearch) }}
<select id="tags" multiple="multiple">
{% for tag in form.tagsForSearch.vars.value|split(',')|default %}
<option value="{{ tag|raw }}" selected="selected">{{ tag|raw }}</option>
{% endfor %}
</select>
And script:
$('#tags').select2({
tags: true
});
$('#form').submit(function () {
var tags = $('#tags').select2().val().join(',');
$('#{{ form.tags.vars.id }}').val(tags);
});

how to hide an array type of entity in a form in symfony2

I have an array type in an entity I want to add it to form type as a hidden field. I tried the following but it doesnt work. It just kills the browser.
Any help will be appreciated.
//Entity
class Test{
/**
* #ORM\Column(name="test_image_files", type="array",nullable=true)
*/
private $testImages;
/**
* #return mixed
*/
public function getTestImages()
{
return $this->testImages;
}
/**
* #param mixed $testImages
*/
public function setTestImages($testImages)
{
$this->testImages = $testImages;
}
}
// FormType
class TestType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('testImages','hidden');
}
}
//twig
{{ form_widget(form.testImages) }}
This will help you to handle current situation,
Echo form_widget(form.testImages) inside <div style="display:none"></div>.
I hope this helps.

symfony : can't we have a hidden entity field?

I am rendering a form with an entity field in symfony.
It works well when i choose a regular entity field.
$builder
->add('parent','entity',array(
'class' => 'AppBundle:FoodAnalytics\Recipe',
'attr' => array(
'class' => 'hidden'
)
))
It throws the following error when I choose ->add('parent','hidden') :
The form's view data is expected to be of type scalar, array or an
instance of \ArrayAccess, but is an instance of class
AppBundle\Entity\FoodAnalytics\Recipe. You can avoid this error by
setting the "data_class" option to
"AppBundle\Entity\FoodAnalytics\Recipe" or by adding a view
transformer that transforms an instance of class
AppBundle\Entity\FoodAnalytics\Recipe to scalar, array or an instance
of \ArrayAccess. 500 Internal Server Error - LogicException
Can't we have hidden entity fields ?? Why not? Am I obliged to put another hidden field to retrieve the entity id?
EDIT :
Basically, what I'm trying to do is to hydrate the form before displaying it but prevent the user to change one of its fields (the parent here).
This is because I need to pass the Id as a parameter and I can't do it in the form action url.
I think you are simply confused about the field types and what they each represent.
An entity field is a type of choice field. Choice fields are meant to contain values selectable by a user in a form. When this form is rendered, Symfony will generate a list of possible choices based on the underlying class of the entity field, and the value of each choice in the list is the id of the respective entity. Once the form is submitted, Symfony will hydrate an object for you representing the selected entity. The entity field is typically used for rendering entity associations (like for example a list of roles you can select to assign to a user).
If you are simply trying to create a placeholder for an ID field of an entity, then you would use the hidden input. But this only works if the form class you are creating represents an entity (ie the form's data_class refers to an entity you have defined). The ID field will then properly map to the ID of an entity of the type defined by the form's data_class.
EDIT: One solution to your particular situation described below would be to create a new field type (let's call it EntityHidden) that extends the hidden field type but handles data transformation for converting to/from an entity/id. In this way, your form will contain the entity ID as a hidden field, but the application will have access to the entity itself once the form is submitted. The conversion, of course, is performed by the data transformer.
Here is an example of such an implementation, for posterity:
namespace My\Bundle\Form\Extension\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\DataTransformerInterface;
/**
* Entity hidden custom type class definition
*/
class EntityHiddenType extends AbstractType
{
/**
* #var DataTransformerInterface $transformer
*/
private $transformer;
/**
* Constructor
*
* #param DataTransformerInterface $transformer
*/
public function __construct(DataTransformerInterface $transformer)
{
$this->transformer = $transformer;
}
/**
* #inheritDoc
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
// attach the specified model transformer for this entity list field
// this will convert data between object and string formats
$builder->addModelTransformer($this->transformer);
}
/**
* #inheritDoc
*/
public function getParent()
{
return 'hidden';
}
/**
* #inheritDoc
*/
public function getName()
{
return 'entityhidden';
}
}
Just note that in the form type class, all you have to do is assign your hidden entity to its corresponding form field property (within the form model/data class) and Symfony will generate the hidden input HTML properly with the ID of the entity as its value. Hope that helps.
Just made this on Symfony 3 and realised it's a bit different from what has already been posted here so I figured it was worth sharing.
I just made a generic data transformer that could be easily reusable across all your form types. You just have to pass in your form type and that's it. No need to create a custom form type.
First of all let's take a look at the data transformer:
<?php
namespace AppBundle\Form;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
/**
* Class EntityHiddenTransformer
*
* #package AppBundle\Form
* #author Francesco Casula <fra.casula#gmail.com>
*/
class EntityHiddenTransformer implements DataTransformerInterface
{
/**
* #var ObjectManager
*/
private $objectManager;
/**
* #var string
*/
private $className;
/**
* #var string
*/
private $primaryKey;
/**
* EntityHiddenType constructor.
*
* #param ObjectManager $objectManager
* #param string $className
* #param string $primaryKey
*/
public function __construct(ObjectManager $objectManager, $className, $primaryKey)
{
$this->objectManager = $objectManager;
$this->className = $className;
$this->primaryKey = $primaryKey;
}
/**
* #return ObjectManager
*/
public function getObjectManager()
{
return $this->objectManager;
}
/**
* #return string
*/
public function getClassName()
{
return $this->className;
}
/**
* #return string
*/
public function getPrimaryKey()
{
return $this->primaryKey;
}
/**
* Transforms an object (entity) to a string (number).
*
* #param object|null $entity
*
* #return string
*/
public function transform($entity)
{
if (null === $entity) {
return '';
}
$method = 'get' . ucfirst($this->getPrimaryKey());
// Probably worth throwing an exception if the method doesn't exist
// Note: you can always use reflection to get the PK even though there's no public getter for it
return $entity->$method();
}
/**
* Transforms a string (number) to an object (entity).
*
* #param string $identifier
*
* #return object|null
* #throws TransformationFailedException if object (entity) is not found.
*/
public function reverseTransform($identifier)
{
if (!$identifier) {
return null;
}
$entity = $this->getObjectManager()
->getRepository($this->getClassName())
->find($identifier);
if (null === $entity) {
// causes a validation error
// this message is not shown to the user
// see the invalid_message option
throw new TransformationFailedException(sprintf(
'An entity with ID "%s" does not exist!',
$identifier
));
}
return $entity;
}
}
So the idea is that you call it by passing the object manager there, the entity that you want to use and then the field name to get the entity ID.
Basically like this:
new EntityHiddenTransformer(
$this->getObjectManager(),
Article::class, // in your case this would be FoodAnalytics\Recipe::class
'articleId' // I guess this for you would be recipeId?
)
Let's put it all together now. We just need the form type and a bit of YAML configuration and then we're good to go.
<?php
namespace AppBundle\Form;
use AppBundle\Entity\Article;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Class JustAFormType
*
* #package AppBundle\CmsBundle\Form
* #author Francesco Casula <fra.casula#gmail.com>
*/
class JustAFormType extends AbstractType
{
/**
* #var ObjectManager
*/
private $objectManager;
/**
* JustAFormType constructor.
*
* #param ObjectManager $objectManager
*/
public function __construct(ObjectManager $objectManager)
{
$this->objectManager = $objectManager;
}
/**
* #return ObjectManager
*/
public function getObjectManager()
{
return $this->objectManager;
}
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('article', HiddenType::class)
->add('save', SubmitType::class);
$builder
->get('article')
->addModelTransformer(new EntityHiddenTransformer(
$this->getObjectManager(),
Article::class,
'articleId'
));
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\MyEntity',
]);
}
}
And then in your services.yml file:
app.form.type.article:
class: AppBundle\Form\JustAFormType
arguments: ["#doctrine.orm.entity_manager"]
tags:
- { name: form.type }
And in your controller:
$form = $this->createForm(JustAFormType::class, new MyEntity());
$form->handleRequest($request);
That's it :-)
With Symfony 5, I use the solution of a Hidden type that implements DataTransformerInterface interface.
<?php
namespace App\Form\Type;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
/**
* Defines the custom form field type used to add a hidden entity
*
* See https://symfony.com/doc/current/form/create_custom_field_type.html
*/
class EntityHiddenType extends HiddenType implements DataTransformerInterface
{
/** #var ManagerRegistry $dm */
private $dm;
/** #var string $entityClass */
private $entityClass;
/**
*
* #param ManagerRegistry $doctrine
*/
public function __construct(ManagerRegistry $doctrine)
{
$this->dm = $doctrine;
}
/**
*
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options): void
{
// Set class, eg: App\Entity\RuleSet
$this->entityClass = sprintf('App\Entity\%s', ucfirst($builder->getName()));
$builder->addModelTransformer($this);
}
public function transform($data): string
{
// Modified from comments to use instanceof so that base classes or interfaces can be specified
if (null === $data || !$data instanceof $this->entityClass) {
return '';
}
$res = $data->getId();
return $res;
}
public function reverseTransform($data)
{
if (!$data) {
return null;
}
$res = null;
try {
$rep = $this->dm->getRepository($this->entityClass);
$res = $rep->findOneBy(array(
"id" => $data
));
}
catch (\Exception $e) {
throw new TransformationFailedException($e->getMessage());
}
if ($res === null) {
throw new TransformationFailedException(sprintf('A %s with id "%s" does not exist!', $this->entityClass, $data));
}
return $res;
}
}
And to use the field in the form:
use App\Form\Type\EntityHiddenType;
public function buildForm(FormBuilderInterface $builder, array $options): void
{
// Field name must match entity class, eg 'ruleSet' for App\Entity\RuleSet
$builder->add('ruleSet', EntityHiddenType::class);
}
This can be achieved fairly cleanly with form theming, using the standard hidden field theme in place of that for the entity. I think using transformers is probably overkill, given that hidden and select fields will give the same format.
{% block _recipe_parent_widget %}
{%- set type = 'hidden' -%}
{{ block('form_widget_simple') }}
{% endblock %}
A quick solution whitout creating new transformer and type classes. When you want to prepopulate an related entity from the db.
// Hidden selected single group
$builder->add('idGroup', 'entity', array(
'label' => false,
'class' => 'MyVendorCoreBundle:Group',
'query_builder' => function (EntityRepository $er) {
$qb = $er->createQueryBuilder('c');
return $qb->where($qb->expr()->eq('c.groupid', $this->groupId()));
},
'attr' => array(
'class' => 'hidden'
)
));
This results a single hidden selection like:
<select id="mytool_idGroup" name="mytool[idGroup]" class="hidden">
<option value="1">MyGroup</option>
</select>
But yes, i agree that with a little more effort by using a DataTransformer you can achieve something like:
<input type="hidden" value="1" id="mytool_idGroup" name="mytool[idGroup]"/>
That will do what you need:
$builder->add('parent', 'hidden', array('property_path' => 'parent.id'));
one advantage of using transformer with text/hidden field over entity type field is that the entity field preloads choices.
<?php declare(strict_types=1);
namespace App\Form\DataTransformer;
use Doctrine\Persistence\ObjectRepository;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
class EntityIdTransformer implements DataTransformerInterface
{
private \Closure $getter;
private \Closure $loader;
public function __construct(
private readonly ObjectRepository $repository,
\Closure|string $getter,
\Closure|string $loader)
{
$this->getter = is_string($getter) ? fn($e) => $e->{'get'.ucfirst($getter)}() : $getter;
$this->loader = is_string($loader) ? fn($id) => $this->repository->findOneBy([$loader => $id]) : $loader;
}
/**
* Transforms an object (entity) to a string|number.
*
* #param object|null $entity
* #return string|int|null
*/
public function transform(mixed $entity): string|int|null
{
if (empty($entity)) {
return null;
}
return $this->getIdentifier($entity);
}
/**
* Transforms a string|number to an object (entity).
*
* #throws TransformationFailedException if entity is not found
* #param string|int $identifier
* #return object|null
*/
public function reverseTransform(mixed $identifier): object|null
{
if (empty($identifier)) {
return null;
}
//TODO: is this needed?
if (is_object($identifier)) {
$identifier = $this->transform($identifier);
}
$entity = $this->fetchEntity($identifier);
if (null === $entity) {
throw new TransformationFailedException(sprintf(
'An entity with ID "%s" does not exist!',
$identifier
));
}
return $entity;
}
protected function getIdentifier(object $entity): int|string
{
$getter = $this->getter;
return $getter($entity);
}
protected function fetchEntity(int|string $identifier): object|null
{
$loader = $this->loader;
return $loader($identifier, $this->repository);
}
}
can be used like
$builder
->add('parent', FormType\TextType::class, [
'label' => 'Parent',
])->get('parent')->addModelTransformer(new EntityIdTransformer(
repository: $this->em->getRepository($options['data_class']),
getter: 'id',
loader: 'id',
));
or
$builder
->add('parent', FormType\TextType::class, [
'label' => 'Parent',
])->get('parent')->addModelTransformer(new EntityIdTransformer(
repository: $this->em->getRepository($options['data_class']),
getter: fn($e) => $e->getHash(),
loader: fn($id, $repo) => $repo->findOneBy(['hash' => $id])
));
or even
$builder
->add('parent', FormType\TextType::class, [
'label' => 'Parent',
])->get('parent')->addModelTransformer(new EntityIdTransformer(
repository: $this->em->getRepository($options['data_class']),
getter: 'hash',
loader: fn($id, $repo) => $repo->createQueryBuilder('e')->andWhere('e.hash = :hash')->andWhere('e.disabled = 0')->setParameter('hash', $id)->getQuery()->getOneOrNullResult()
));

Symfony2 exception when trying to embed a collection of forms

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!

Categories