Reputation: 3390
Doctrine2 has a built-in type 'array' that I find useful for my project. It perfectly works with arrays of scalar types. But now I want to use an array of objects. Something like this:
/**
* @ORM\Entity
*/
class MyEntity {
/**
* @var MyEntityParameter[] array of MyEntityParameter instances
*
* @ORM\Column(name="parameters", type="array", nullable=true)
*
*/
private $parameters;
}
Where MyEntityParameter is a class that can be serialized. I also use it in the Symfony's Form Builder.
My plan works perfectly, except that when the field in the MyEntityParameter instance gets changed, Doctrine doesn't detect it and thus doesn't update the record. If I delete or add array elements, Doctrine detects that. I realize that this happens because class instance object id doesn't change when I change its field, but then how can I make it so that Doctrine detects this change?
Upvotes: 1
Views: 1227
Reputation: 3390
I found a working solution for me. I don't think it's that elegant, but in case if there are no good ways to solve this problem it can work for me and for others.
First of all, I decided not to keep objects in the array, but instead keep arrays. I, however, still want to use MyEntityParameter
class in the Symfony's form builder. In this case, the idea is to disable mapping for our field:
In the form builder we do the following:
// Acme/Bundle/DemoBundle/Form/Type/MyEntityType.php
// ...
class MyEntityType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('parameters', 'collection', array(
'mapped' => false, // do not map this field
'type' => new MyEntityParameterType(),
// ... other options ...
));
}
}
In the child type (MyEntityParameterType
) we set 'data_class' to MyEntityParameter
(I don't show the code, as it's not related to the problem).
Now all we need is to manually fill and process the data for this not-mapped field.
In the controller:
public function editAction($id, Request $request)
{
// ...
// $object is an instance of MyEntity
$form = $this->createForm(new MyEntityType(), $object);
$parameters = $object->getParameters();
if ($parameters) {
foreach ($parameters as $key => $parameter)
{
$form->get('parameters')->add($key, new MyEntityParameterType(),
array(
// here I assume that the constructor of MyEntityParameter
// accepts the field data in an array format
'data' => new MyEntityParameter($parameter),
)
);
}
}
if ($request->isMethod('POST')) {
$form->submit($request);
$parameters = array();
foreach ($form->get('parameters')->all() as $parameter) {
// here first getData() gives MyEntityParameter instance
// and the second getData() is just a method of MyEntityParameter
// that returns all the fields in an array format
$parameters[] = $parameter->getData()->getData();
}
$object->setParameters($parameters);
// if the parameters were changed in the form,
// this change will be detected by UnitOfWork
}
// ...
}
Upvotes: 1
Reputation: 7818
I remember coming across this issue before, and a workaround that worked for me was to set a new object so Doctrine would recognise the entity property has been modified, then set the object that has the changes you want persisted.
$parameters = $entity->getParameters();
$parameters->foo = "bar";
$entity->setParameters(new MyEntityParameter());
$entity->setParameters($parameters);
$em->persist($entity);
If it's an array of MyEntityParameter
instances then the following code might work.
$parameters = $entity->getParameters();
$parameters[3]->foo = "bar"; // Just an example
$entity->setParameters(array(new MyEntityParameter()));
$entity->setParameters($parameters);
$em->persist($entity);
Upvotes: 0