Yannick Pereira-Reis bio photo

Yannick Pereira-Reis

DevOps (docker swarm, haproxy, CI/CD, ELK, prometheus, grafana, ansible, automation, RabbitMQ, LVM, MySQL replication...) and fullstack web developer Symfony 2/3/4/5 + VueJs in Valence (France).

Twitter LinkedIn Github

When doing multiple database operations in a single http request, command line, method,… we often need to use a database transaction to keep data safe.

Symfony

Example in an Action of a Controller

<?php
public function testAction()
{
    $conn = $this->getDoctrine()->getConnection();
    $conn->setAutoCommit(false);
    $conn->beginTransaction();
    
    try {
        $everythingIsFine = $this->get('service')->do();
        if ($everythingIsFine) {
            $conn->commit();
            return new Response("OK");
        }
        
        $conn->rollback();
        return new Response("NOT OK");
    
    } catch (\Exception $ex) {
        $conn->rollback();
        return new Response("NOT OK");
    }
}

A better choice

  • Add a method that allows you to keep your code DRY.
  • This method gets a callable param (a function/method to execute) and deals with potential exceptions, and transaction commit() and rollback() operations.
  • It’s a really dead simple example and a reminder, feel free to improve it.
  • You could also move it into a service for instance.
<?php

protected function transactionalExec(callable $func)
{
    $conn = $this->getDoctrine()->getConnection();
    $conn->setAutoCommit(false);
    $conn->beginTransaction();

    try {
        $success = $func();
        
        if (null === $success) {
            throw new \Exception('Your transactional callable must return a boolean value');
        }
    
        if ($success) {
            $conn->commit();
        } else {
            $conn->rollback();
        }
        
    } catch (\Exception $ex) {
        $conn->rollback();
        $success = false;
    }
    

    return $success;
}

public function testAction()
{

    $transactionalSuccess = $this->transactionalExec(function()
        use (...)
    {
        $everythingIsFine = $this->get('service')->do();
        
        return $everythingIsFine;
    });


    if ($transactionalSuccess) {
        return new Response("OK");
    }

    return new Response("NOT OK");

}