Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

To save entity with doctrine I should do like this

$em = $this->getDoctrine()->getEntityManager();
$em->persist($product);
$em->flush();

But maybe I can somehow do that in one line like

$product->save();

or

$this->saveEntity($product);

or

$this->getDoctrineEntityManager()->persistAndFlush($product);

If I need to create this methods by my self then how to do that in symfony way?

like image 992
redexp Avatar asked Aug 18 '12 15:08

redexp


4 Answers

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.

like image 136
Mun Mun Das Avatar answered Oct 31 '22 16:10

Mun Mun Das


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();
    }
}
like image 35
Илья Зеленько Avatar answered Oct 18 '22 20:10

Илья Зеленько


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);
    }
}
like image 11
Pete Mitchell Avatar answered Oct 31 '22 16:10

Pete Mitchell


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.

like image 1
T-Rex Avatar answered Oct 31 '22 16:10

T-Rex