Has anyone encountered this problem when using doctrine embeddables and symfony forms?
if you don't know Doctrine Embeddables are, you can read about it here http://doctrine-orm.readthedocs.org/en/latest/tutorials/embeddables.html
When using value object (CategoryType in my case) with symfony form on form submission (during persisting to DB) I get the following warning
Warning: ReflectionProperty::getValue() expects parameter 1 to be object, string given
This happens because symfony form returns string instead of embeddable object.
The only workaround I have right now is to use mapped => false
on type
field and create valid embeddable object inside controller action just before persist. But that's far from "nice" solution and I want to avoid that.
My Entity, Value Object and form (simplified for the sake of question)
Foo\BarBundle\Entity\CategoryType
<?php
namespace Foo\BarBundle\Entity;
class CategoryType
{
/**
* @var string
*/
protected $value;
public function __toString()
{
return (string) $this->getValue();
}
/**
* @return string
*/
public function getValue()
{
return $this->value;
}
/**
* @param string $value
*
* @return $this
*/
public function setValue($value)
{
$this->value = $value;
return $this;
}
}
Foo\BarBundle\Resources\config\doctrine\CategoryType.orm.yml
CategoryType.orm.yml
Foo\BarBundle\Entity\CategoryType:
type: embeddable
fields:
value:
type: string
Foo\BarBundle\Entity\Category
<?php
namespace Foo\BarBundle\Entity;
class Category
{
/**
* @var integer
*/
protected $id;
/**
* @var string
*/
protected $name;
/**
* @var CategoryType
*/
protected $type;
/**
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* @return \Foo\BarBundle\Entity\CategoryType
*/
public function getType()
{
return $this->type;
}
/**
* @param \Foo\BarBundle\Entity\CategoryType $type
*
* @return $this
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* @return string
*/
public function __toString()
{
return (string) $this->getName();
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
*
* @return $this
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
}
Foo\BarBundle\Resources\config\doctrine\Category.orm.yml
Foo\BarBundle\Entity\Category:
type: entity
fields:
id:
type: integer
id: true
generator:
strategy: AUTO
name:
type: string
embedded:
type:
class: CategoryType
Foo\BarBundle\Form\CategoryType
<?php
namespace Foo\BarBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class CategoryType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('type')
;
}
/**
* @param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(
array(
'data_class' => 'Foo\BarBundle\Entity\Category'
)
);
}
/**
* @return string
*/
public function getName()
{
return 'category_form';
}
}
The doctrine embedabble behavior is just a persistence mechanism, so, it can not break the form component which is independant from it (and works with all objects graph).
The issue is your form is not well designed (it does not follow your object graph). Here, you have a category which wraps a category type. So, your form must follow the same structure at the definition level.
You must create a
CategoryTypeType
form (mapped to yourCategoryType
class) where your add avalue
field. Then, in yourCategoryType
(the form one), you must embed theCategoryTypeType
for thetype
field.Then, the form component will automatically creates a
Category
which wraps aCategoryType
. Then, Doctrine will simply persists your object as embeddable and everything will work :)