Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Doctrine Entity increase value (Download counter)

I want to increase a value in my doctrine entity.
Currently I'm doing it this way.

$file->setDownloadCounter($file->getDownloadCounter() + 1);
$em = $this->getDoctrine()->getManager();
$em->persist($fileVersion);
$em->flush();

Is there way to execute something like this in doctrine:

UPDATE file SET downloadCounter = downloadCounter + 1 WHERE id = 1

EDIT:

The problem in the doctrine example above is that between loading and flush is time where others could download the file and so the counter is not correct.

like image 904
Alexander Schranz Avatar asked Jul 10 '14 16:07

Alexander Schranz


2 Answers

Safest way to do this is using Doctrine DBAL and call raw SQL that way you remove the chance of race condition and make the change atomic. Other option is to make the field versioned and use optimistic locking or pessimistic DB-level locking.

like image 86
Tomáš Fejfar Avatar answered Nov 02 '22 20:11

Tomáš Fejfar


You can also do the following in an entity repository:

return $this
    ->createQueryBuilder('f')
    ->update($this->getEntityName(), 'f')
    ->set('f.downloadCounter', $file->getDownloadCounter() + 1)
    ->where('f.id = :id')->setParameter('id', $file->getId())
    ->getQuery()
    ->execute();

Or using DQL:

 $em = $this->getDoctrine()->getManager();
 $query = $em->createQuery(
     'UPDATE YourBundle:File f
      SET f.downloadCounter = :downloadCounter'
 )->setParameter('downloadCounter', $file->getDownloadCounter() + 1);

Or through a simplified DQL:

 $em = $this->getDoctrine()->getManager();
 $query = $em->createQuery(
     'UPDATE YourBundle:File f
      SET f.downloadCounter = f.downloadCounter + 1'
 );

The drawback with these solutions: if your entity was already loaded it will have the previous count and not the incremented count.

The way you did is perfectly fine but a better way is to add an increment method to your entity.

Follow-up from Radu C comment below: the simplified DQL is the only solution that guarantees proper count.

The query increments based on the value in the database and locks the table guaranteeing queries to be executed in a sequence.

Whereas the other queries use the value in PHP runtime which may be an outdated value: some other request may have already incremented the value in the database therefore incrementing based on value in PHP memory will override increment made by other requests.

like image 6
Thomas Potaire Avatar answered Nov 02 '22 19:11

Thomas Potaire