Symfony2 - Doctrine - Is there any way to save entity in one line?

I know your desire. At the first time a single save method looks nice.

But if you have 2 methods you are able to collect the statements before sending them to the database. Thats not the actual work of doctrine i guess, but maybe with an update you can use flush("together"). On this way you can save a lot of overhead.


If you are using the controller in the framework bundle, and writing your persistence logic in your controllers, you could extend Symfony\Bundle\FrameworkBundle\Controller\Controller with the following

namespace ExampleNameSpace\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class BaseController extends Controller
{
    public function persistAndSave($entity)
    {
        $em = $this->getDoctrine()->getEntityManager();
        $em->persist($entity);
        $em->flush();
    }
}

In all of your other controllers, you would then extend your ExampleNameSpace\Controller\BaseController instead of the one from the Symfony Frameworkbundle.

Alternatively, and the approach I take, is to write a manager class for each entity that has the class name and the doctrine entity manager injected. Each manager class extends an abstract manager class with the following methods:

<?php

abstract class AbstractManager
{
    /**
     * The Entity Manager
     *
     * @var \Doctrine\ORM\EntityManager  The Doctrine Entity Manager
     */
    protected $em;

    /**
     * The full class path associated with the repository
     *
     * @var string
     */
    protected $class;

    /**
     * The repository for the manager
     *
     * @var \Doctrine\ORM\EntityRepository
     */
    protected $repository;

    /**
     * Creates a new instance of the primary class managed by a given
     * manager
     *
     * @return object       A new instance of the entity being managed
     */
    public function create()
    {
        return new $this->class();
    }

    /**
     * {@inheritDoc}
     */
    public function save($object, $flush = false)
    {
        if( ! $this->supportsClass($object))
        {
            throw new \InvalidArgumentException(sprintf('Invalid entity passed to this manager, expected instance of %s', $this->class));
        }

        $this->em->persist($object);

        if($flush === true)
        {
            $this->flush();
        }

        return $object;
    }

    /**
     * {@inheritDoc}
     */
    public function delete($object, $flush = false)
    {
        if( ! $this->supportsClass($object))
        {
            throw new \InvalidArgumentException(sprintf('Invalid entity passed to this manager, expected instance of %s', $this->class));
        }

        $this->em->remove($object);

        if($flush === true)
        {
            $this->flush();
        }

        return true;
    }

    /**
     * Convenience method providing access to the entity manager flush method
     */
    public function flush()
    {
        $this->em->flush();
    }

    /**
     * {@inheritDoc}
     */
    public function supportsClass($object)
    {
        return $object instanceof $this->class || is_subclass_of($object, $this->class);
    }

    /**
     * Set class. Setter for dependency injection
     *
     * @param object $class  A class related to this manager
     */
    public function setClass($class)
    {
        $this->class = $class;
    }

    /**
     * Set entity manager. Setter for dependency injection
     *
     * @param \Doctrine\ORM\EntityManager $entity_manager
     */
    public function setEntityManager(\Doctrine\ORM\EntityManager $entity_manager)
    {
        $this->em = $entity_manager;
    }

    /**
     * Returns the repository
     *
     * @return \Doctrine\ORM\EntityRepository    A Doctrine Repository for the
     *                                          class related to this Manager
     */
    protected function getRepository()
    {
        if( ! $this->repository)
        {
            $this->repository = $this->em->getRepository($this->class);
        }

        return $this->repository;
    }
}

The managers are configured in the dependency injection container, with the appropriate class for the entity, and provide access to creating, saving and deleting the entity they are responsible for, as well as accessing the repository.

One would create an entity in a controller with the manager as following:

public function createAction(Request $request)
{
    $entityManager = $this->get('some.entity.manager');

    $entity = $entityManager->create();

    $form = $this->createForm(new EntityForm(), $entity);

    $form->bindRequest($request);

    if($form->isValid())
    {
        $entityManager->save($entity, true);
    }
}

Well persist() and flush() are totally different and independent operation. When you persist an entity object you are telling the entity manager to track changes of the object. When you call flush() method the entity manager will push the changes of the entity objects the entity manager tracks to the database in single transaction. Most of the time entity manager have to manage multiple object. For example besides your product entity you may also have to track tag or cart entity. Calling persistAndFlush() every time when you save those entity object will cause multiple IO connection to DB. Which is not efficient. So I think it is better to treat them as a separate operation.


Based on this post (it’s written about it at the very end) you can write the save code to the repository:

class DoctrineORMCustomerRepository extends EntityRepository implements CustomerRepository
{
    public function save(Customer $customer)
    {
        $this->_em->persist($customer);
        $this->_em->flush();
    }
}