Doctrine Tutorial - CRUD


This video is a continuation from the previous video in this sub-section on integrating Doctrine with Symfony 2.7.

Whilst in the previous video we looked at configuring our database interactivity, in this video and the next we are going to see how we can actually use Doctrine in a Symfony project.

By the end of this Doctrine tutorial video you will have seen how we can Create, Read, Updated, and Delete (commonly known as CRUD) a record, or multiple records from our database.

Remember, thanks to the Doctrine DBAL it doesn't matter too much which database we are using. This is one of the nice parts of Doctrine - it hides the complexity of the underlying database connectivity from us.

Creating

Depending on personal preference, some people like to start with creating a new record and then saving (or persisting in Doctrine terminology) off to our database.

Others prefer to start with a pre-populated database (usually using a third party database management tool or database fixtures to add in some data) and instead choose to do a find or SELECT in SQL terms.

In this video we are going to start by creating a new record and then persisting (saving) this record off to our database.

Entity Manager

Whenever we do anything with Doctrine we don't talk about handling Objects, instead we talk about an Entity or a collection of Entities.

An Entity is a fancy word for an object we have pre-defined (think User, or Product, or BlogPost, or PurpleWidget) inside our src/OurBundle/Entity directory that contains all our Doctrine annotations and some form of identification - most commonly our id field.

An example might be:

// src/AppBundle/Entity/Employee.php
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="employee")
 */
class Employee
{
    /**
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length=100)
     */
    protected $firstName;

    // etc
}

To save, find, remove, or update any of these entities we need a way of accessing them.

Whilst we didn't have to set it up directly, Symfony comes pre-wired to work with Doctrine and as long as our database credentials (remember, we added our username and password etc to app/config/parameters.yml in the previous video) are all fine and dandy, we can begin managing our entities without much further effort.

First of all we must get a hold of the Entity Manager.

This is a very common operation, and you will find yourself doing this frequently.

From inside any Controller action (that bit is important, as I will explain in a minute) you can do the following to get the Entity Manager:

public function someControllerAction()
{
    $em = $this->getDoctrine()->getManager();
}

It's common practice to call this the $em - shorthand for Entity Manager.

Potential Pitfall

One mistake I see newcomers to the Symfony framework make is to think that you can get the Entity Manager using the $em = $this->getDoctrine()->getManager(); command from anywhere. This isn't the case.

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class DefaultController extends Controller
{
}

The reason this works inside a Controller is that our Controller files extend the Controller or Symfony\Bundle\FrameworkBundle\Controller\Controller. This gives us access to all these extra convenience methods that aren't just magically available everywhere because we are using Symfony.

This is a more advanced topic, but I see it so frequently that I thought it best to point it out.

Back To Business

With our $em setup and available, creating a new record inside our database is just a case of creating the object and then telling Doctrine to persist it to the database.

You can only create Entities that you have previously defined, so taking our Employee class we might do something like:

use AppBundle\Entity\Employee;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class DefaultController extends Controller
{
    public function createAnEmployeeAction()
    {
        $employee = new Employee();
        $employee->setName('Bob');

        $em = $this->getDoctrine()->getManager();
        $em->persist($employee);
        $em->flush();
    }
}   

Note the use statement for Employee. Without that you will get various errors.

We have created a new instance of an Employee, and then set his name to Bob. We don't need to set the ID for this record - Doctrine will take care of that for us. Remember, we used the special annotations?

// src/AppBundle/Entity/Employee.php
/**
 * @ORM\Column(type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id;

As you can see, we have set the $id to automatically get a new ID for us through Doctrine. Most of the time you would never have a setId method on a Doctrine Entity, as changing the ID is not something you want to handle by yourself.

We then call $em->persist($employee);.

This tells Doctrine to start managing this Employee data but it will only save this data to the database whenever the next flush() happens.

In our case we immediately invoke the flush() method on the next line, but that may not be the case. Beware that between persisting and flushing, the data has not yet been written off to the database yet. Again, another advanced topic but it can cause some real headaches / head scratchers trying to figure out why data isn't quite as expected.

Flushing data to the database can be quite an expensive (read: slow, in computer terms at least) operation, so shouldn't be called unless necessary - that's how you can get into the situation I just described above.

But for now, don't worry about that. Just persist and flush and all will be grand.

Want to read a little more? Try this section of the official Symfony docs.

Doctrine Update

Now that we have created some data, let's go ahead and edit it.

First, we must retrieve the data from the database.

Doctrine Read

Code is worth a thousand words, so let's just see some code:

public function updateAnEmployeeAction()
{
    $em = $this->getDoctrine()->getManager();

    // let's pretend that employee Tim has an ID of 6 
    $employee = $em->getRepository('AppBundle:Employee')->find(6); 

    // Tim has complained that he prefers his Sunday name on official documents 
    // such as payslips and what not, so let's add an extra 'y' to his name to 
    // annoy him further
    $employee->setName('Timothyy');

    // Calling persist is not actually necessary here, as the Entity Employee with id 6
    // is already managed by Doctrine, but I like to call it anyway  
    $em->persist($employee);
    $em->flush();
}

Hopefully the comments explain what's going on.

First we pull out a record by its internal ID. Of course, you will need a way to get the ID's ahead of time - we aren't worried about that for now.

As we are in development we have full access to our database and have manually added Tim (ahem, Timothy) and so we know already that he has id 6.

To get to the Employee data we go by the Employee repository.

A Doctrine repository is a store for all our Doctrine query logic. Even though we haven't specifically defined a Doctrine Repository for our Employee entity class, we get one for free with some basic methods on it (find, findAll, findBy, etc).

Though somehwat confusing at first, all that $em->getRepository('AppBundle:Employee')->find(6); is saying is find the entity store containing our Employee objects, that lives in our AppBundle (AppBundle:Employee) and then find the record inside that store (read: database) that has an id of 6.

There's more info to read on this on the official Symfony docs if you're finding this confusing.

After you have done this a few times it starts to feel normal.

As we use our repository, the returned result will be objects of the type found in that repository. In this case objects of type Employee. As such, they have all the methods / functionality of an Employee - that's why we can then call setName().

Doctrine Delete

We have created, read data from our database, and updated it then saved it back.

Now, let's delete a record.

Deleting is very easy. But of course, it contains a new method for us to learn.

We can't just delete nothing, so we must first grab something from the database as we did in the previous example.

Let's say Timothy got all shirty with our hilarious mispelling of his name. Whilst only intended as a joke, Tim took it way too seriously and put his fist through his brand new 27" iMac and has now been fired.

HR have been on and told us to remove Tim double quick sharpish. So let's get to work:

public function deleteAnEmployeeAction()
{
    $em = $this->getDoctrine()->getManager();

    // We know by now that Tim has id 6 
    $tim = $em->getRepository('AppBundle:Employee')->find(6); 

    // instead of persist we call remove - easy enough, this removes the 
    // entity from being managed by Doctrine
    $em->remove($tim);

    // we still need to flush this change to the database
    $em->flush();
}

Much like the persist operation from earlier, remove won't actually delete our entity until we call flush. This is because of how Doctrine works behind the scenes. Don't worry too much about this right now, just remember to remove and flush.

With that done, Tim will no longer exist as a row inside our database.

Hurrah!

Episodes