This tutorial has a new version, check it out!

Inserting new Objects

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Fearless aquanauts are constantly discovering and re-classifying deep-sea animals. If a new genus needed to be added to the system, what would that look like? Well, we would probably have a URL like /genus/new. The user would fill out a form, hit submit, and the database fairies would insert a new record into the genus table.

Sounds good to me! In GenusController, create a newAction() with the URL /genus/new. I won't give the route a name yet - that's not needed until we link to it:

... lines 1 - 11
class GenusController extends Controller
{
/**
* @Route("/genus/new")
*/
public function newAction()
{
... line 19
}
/**
* @Route("/genus/{genusName}")
*/
public function showAction($genusName)
{
... lines 27 - 46
}
... lines 48 - 65
}

Be Careful with Route Ordering!

Oh, and side-note: I put newAction() above showAction(). Does that matter? In this case, absolutely. Remember, routes match from top to bottom. If I had put newAction() below showAction(), going to /genus/new would have matched showAction() - passing the word "new" as the genusName(). To avoid this, put your most generic-matching routes near the bottom.

Go Deeper!

You can often also use route requirements to make a wildcard only match certain patterns (instead of matching everything).

Inserting a Genus

Ok, back to the database-world. Let's be really lazy and skip creating a form - there's a whole series on forms later, and, I'd just hate to spoil the fun.

Instead, insert some hardcoded data. How? Simple: start with $genus = new Genus(), put some data on that object, and tell Doctrine to save it:

... lines 1 - 4
use AppBundle\Entity\Genus;
... lines 6 - 11
class GenusController extends Controller
{
/**
* @Route("/genus/new")
*/
public function newAction()
{
$genus = new Genus();
}
... lines 21 - 65
}

Doctrine wants you to stop thinking about queries, and instead think about objects.

Right now... name is the only real field we have. And it's a private property, so we can't actually put data on it. To make this mutable - to use a really fancy term that means "changeable" - go to the bottom of the class and open the "Code"->"Generate" menu. Select "Getters and Setters" - we'll need the getter function later:

... lines 1 - 10
class Genus
{
... lines 13 - 24
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
}

Great! Now use $genus->setName() and call it Octopus with a random ending to make things more fun!

... lines 1 - 11
class GenusController extends Controller
{
... lines 14 - 16
public function newAction()
{
$genus = new Genus();
$genus->setName('Octopus'.rand(1, 100));
... lines 21 - 26
}
... lines 28 - 72
}

Object, check! Populated with data, check! The last step is to say:

Hey Doctrine. I want you to save this to our genus table.

Remember how everything in Symfony is done with a service? Doctrine is no exception: it has one magical service that saves and queries. It's called, the entity manager. In fact, it's so hip that it has its own controller shortcut to get it: $em = $this->getDoctrine()->getManager(). What a celebrity.

To save data, use two methods $em->persist($genus) and $em->flush():

... lines 1 - 18
$genus = new Genus();
$genus->setName('Octopus'.rand(1, 100));
$em = $this->getDoctrine()->getManager();
$em->persist($genus);
$em->flush();
... lines 25 - 74

And yes, you need both lines. The first just tells Doctrine that you want to save this. But the query isn't made until you call flush(). What's really cool is that you'll use these exact two lines whether you're inserting a new Genus or updating an existing one. Doctrine figures out the right query to use.

Finishing the New Page

Ok, let's finish up! Do you remember what a controller must always return? Yep! A Response object. Skip a template and just return new Response() - the one from the HttpFoundation component - with Genus Created:

... lines 1 - 11
class GenusController extends Controller
{
... lines 14 - 16
public function newAction()
{
... lines 19 - 25
return new Response('Genus created!');
}
... lines 28 - 72
}

Deep breath. Head to /genus/new. Okay, okay - no errors. I think we're winning?

Debugging with the Web Debug Toolbar

The web debug toolbar has a way to see the queries that were made... but huh, it's missing! Why? Because we don't have a full, valid HTML page. That's a bummer - so go back to the controller and hack in some HTML markup into the response:

... lines 1 - 25
return new Response('<html><body>Genus created!</body></html>');
... lines 27 - 74

Try it again! Ah, there you are fancy web debug toolbar. There are actually three database queries. Interesting. Click the icon to enter the profiler. Ah, there's the insert query, hiding inside a transaction.

And by the way, how sweet is this for debugging? You can see a formatted query, a runnable version, or run EXPLAIN on a slow query.

Running SQL Queries in the Terminal

I still can't believe it's working - things never work on the first try! To triple-check it, head to the terminal. To run a raw SQL query, use:

./bin/console doctrine:query:sql 'SELECT * FROM genus'

There they are. So inserting objects with Doctrine... pretty darn easy.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.1.*", // v3.1.4
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.4
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
        "doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0", // v3.1.3
        "nelmio/alice": "^2.1", // 2.1.4
        "doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
    }
}