I have the following structure:
Category property that contains link to property and its value:
<?php class CategoryProperty { // ... /** * @var Property * * @ORMManyToOne(targetEntity="AppEntityProperty") * @ORMJoinColumn(onDelete="cascade", nullable=false) */ private $property; /** * Набор значений свойства доступных в product builder, null если любое значение. * * @var PropertyValueEntry * @AssertValid * * @ORMOneToOne(targetEntity="AppEntityPropertiesPropertyValuesPropertyValueEntry", * cascade={"persist", "remove"}) */ private $propertyValue; // ... }
Abstract property value type with a discriminator map:
<?php /** * @ORMEntity * @ORMInheritanceType("JOINED") * @ORMDiscriminatorColumn(name="type", type="integer") * @ORMDiscriminatorMap({ * "1": "StringValue", * "2": "IntegerValue", * "3": "BooleanValue", * "4": "TextValue", * "6": "EnumValue", * "7": "SetValue", * "9": "LengthValue", * "10": "AreaValue", * "11": "VolumeValue", * "12": "MassValue", * }) * @ORMTable(name="properties_values__value_entry") */ abstract class PropertyValueEntry { /** * @var Property * * @ORMManyToOne(targetEntity="AppEntityProperty") */ private $property; public function __construct(Property $property) { $this->property = $property; } public function getProperty(): Property { return $this->property; } /** * @return mixed */ abstract public function getValue(); /** * @param mixed $value */ abstract public function setValue($value): void; }
And a sample concrete value type:
<?php /** * @ORMEntity * @ORMTable(name="properties_values__integer_value") */ class IntegerValue extends PropertyValueEntry { /** * @var int * @AssertNotNull * * @ORMColumn(type="integer") */ private $value = 0; public function getValue(): int { return $this->value; } /** * @param int|null $value */ public function setValue($value): void { if (!is_int($value)) { throw new InvalidArgumentException('BooleanValue accepts integer values only'); } $this->value = $value; } }
For some reason, when form is submitted, instead of updating a value for IntegerValue, a new entity gets created, and new row in properties_values__value_entry / properties_values__integer_value. I tried tracking through the $this->em->persist($entity), where $entity is CategoryProperty, and it seems that IntegerValue gets marked as dirty and created anew. How can I track the cause of this happening? My form processing is pretty standard:
<?php public function editAction(): Response { $id = $this->request->query->get('id'); $easyadmin = $this->request->attributes->get('easyadmin'); $entity = $easyadmin['item']; $isReload = 'reload' === $this->request->request->get('action'); $editForm = $this->createForm(CategoryPropertyType::class, $entity, [ 'category' => $this->getCatalog(), 'is_reload' => $isReload, ]); $deleteForm = $this->createDeleteForm($this->entity['name'], $id); $editForm->handleRequest($this->request); if ($editForm->isSubmitted() && $editForm->isValid()) { if (!$isReload) { $this->em->persist($entity); $this->em->flush(); return $this->redirectToReferrer(); } } return $this->render($this->entity['templates']['edit'], [ 'form' => $editForm->createView(), 'entity' => $entity, 'delete_form' => $deleteForm->createView(), ]); }
UPDATE #1 What I already tried: Retrieve category property by ID from entity manager through
$entity = $this->em->find(CategoryProperty::class, $id);
Altogether it seems this may be related to the fact that I have a dynamic form being created based on the selection. When I add a category property, I display a dropdown with a list of property types (integer, string, area, volume etc), and after selection a new form for that property is displayed. Though this works fine and adds new property without a problem, it seems that the code for EDITING same property is missing something, and instead of update it creates it anew.
Advertisement
Answer
Thanks to everyone participating, I have been reading through Symfony documentation and came across the ‘by_reference’ form attribute. Having considered that my form structure overall looks like this:
Category => CategoryPropertyType => PropertyValueType => [Set, Enum, Integer, Boolean, String, Volume]
for the form, I decided to set it to true in PropertyValueType configureOptions method. As it is explained in the documentation, with it being set to false, the entity is getting cloned (which in my case is true), thus creating a new object at the end.
Note that I’m still learning Symfony and will be refining the answer when I get a better understanding of what’s going on behind the scenes.