Airam
Airam

Reputation: 2058

How update an entity field after this is persisted (e.g. in postPersist method)?

I have an entity Task with a code field that needs to be populated with the first letter of its parent's name (entity Project) plus its own id field, but I have no id until the entity is persisted into the database, and in the postPersist method I can´t change the entity Task because these changes wouldn´t be persisted.

Here is my code:

class Project
{

/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(name="name", type="string", length=255)
 */
private $name;
}



class Task
{

/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(name="code", type="string", length=255)
 */
private $code;

/**
 * @ORM\ManyToOne(targetEntity="Project", inversedBy="tasks")
 * @ORM\JoinColumn(name="project_id", referencedColumnName="id")
 */
private $project;
}

Anyone has any idea?

Thanks in advance.

Upvotes: 1

Views: 2540

Answers (2)

Airam
Airam

Reputation: 2058

I resolved the problem with a listener.

I wrote in MyBundle->Resources->config->services.yml file:

parameters:
    task.listener.class: MyBundle\EventListener\TaskListener

services:
    task.listener:
        class: %task.listener.class%
        tags:
            - { name: doctrine.event_listener, event: postPersist }

And in the MyBundle->EventListener->TaskListener.php:

<?php
namespace MyBundle\EventListener;

use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\OnFlushEventArgs;

use MyBundle\Entity\Task;

class TaskListener
{
    public function postPersist(LifecycleEventArgs $args)
    {
        $entity     = $args->getEntity();
        $em         = $args->getEntityManager();

        if ($entity instanceof Task) {
            $entity->setCode(substr($entity->getProject()->getName(), 0, 1).$entity->getId());

            $em->persist($entity);
            $em->flush();
        }
    }
}

In this way, I use the postPersist event to set the code when the entity instance has an id in the database.

I hope that this information becomes useful for somebody.

Thanks.

Upvotes: 4

Mick
Mick

Reputation: 31919

Your design does not respect Database Normalization

In my opinion, what you're trying to achieve doesn't respect Database Normalization. You wouldn't respect the rules of the 1NF with this design.

Here are the 1NF rules:

- The table must contain only atomic fields
- Eliminate redundant information.

This great article should help you a lot to understand what is database normalization, what is 1NF, etc.. and why it's important to respect such rules: Demystified: Database Normalization.

You want:

An entity Task with a code field that needs to be populated with the first letter of its parent's name (entity Project) plus its own id field

The information will be redundant in a code field. What if the name of the product changes? Would you have to update the task code in such circumstances? Does this code have to be unique? Why store twice what's already available?

What you could do:

Solution 1 In your case, why not simply use the id field?

Solution 2 Why not simply use a getter without storing the code in the database?

Solution 3 Create a code field for the product instead (you mentioned one letter, so maybe this wouldn't be unique for all your products). I would opt out for 3 letters. For example:

iPad : ipa
iPhone : iph
iMac: ima

It's your call to decide whether you want this code to be unique or not. So validate uniqueness with the UniqueEntity field type if needed. You could then have a code made of the "product code" + "id". As mentioned above, I wouldn't create a "task code" field in the database to avoid breaking 1NF rules. You would just have a getter that gets the simple combination "product code" + "id".

Upvotes: 0

Related Questions