Create Genus Note

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.

It's you again! Welcome back friend! In this tutorial, we're diving back into Doctrine: this time to master database relations. And to have fun of course - databases are super fun.

Like usual, you should code along with me or risk 7 years of bad luck. Sorry. To do that, download the code from the course page and use the start directory. I already have the code - so I'll startup our fancy, built-in web server:

./bin/console server:run

You may also need to run composer install and a few other tasks. Check the README in the download for those details.

When you're ready, pull up the genus list page at http://localhost:8000/genus. Nice!

Create the GenusNote Entity

Click to view a specific genus. See these notes down here? These are loaded via a ReactJS app that talks to our app like an API. But, the notes themselves are still hardcoded right in a controller. Bummer! Time to make them dynamic - time to create a second database table to store genus notes.

To do that, create a new GenusNote class in the Entity directory. Copy the ORM use statement from Genus that all entities need and paste it here:

... lines 1 - 2
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
... lines 6 - 10
class GenusNote
{
... lines 13 - 78
}

With that, open the "Code"->"Generate" menu - or Cmd + N on a Mac - and select "ORM Class":

... lines 1 - 2
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="genus_note")
*/
class GenusNote
{
... lines 13 - 78
}

Bam! This is now an entity!

Next: add the properties we need. Let's see... we need a username, an userAvatarFilename, notes and a createdAt property:

... lines 1 - 10
class GenusNote
{
... lines 13 - 17
private $id;
... lines 19 - 22
private $username;
... lines 24 - 27
private $userAvatarFilename;
... lines 29 - 32
private $note;
... lines 34 - 37
private $createdAt;
... lines 39 - 78
}

When we add a user table later - we'll replace username with a relationship to that table. But for now, keep it simple.

Open the "Code"->"Generate" menu again and select "ORM Annotation". Make sure each field type looks right. Hmm, we probably want to change $note to be a text type - that type can hold a lot more than the normal 255 characters:

... lines 1 - 10
class GenusNote
{
/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string")
*/
private $username;
/**
* @ORM\Column(type="string")
*/
private $userAvatarFilename;
/**
* @ORM\Column(type="text")
*/
private $note;
/**
* @ORM\Column(type="datetime")
*/
private $createdAt;
... lines 39 - 78
}

Finally, go back to our best friend - the "Code"->"Generate" menu - and generate the getter and setters for every field - except for id. You don't usually want to set the id, but generate a getter for it:

... lines 1 - 10
class GenusNote
{
... lines 13 - 39
public function getId()
{
return $this->id;
}
public function getUsername()
{
return $this->username;
}
public function setUsername($username)
{
$this->username = $username;
}
public function getUserAvatarFilename()
{
return $this->userAvatarFilename;
}
public function setUserAvatarFilename($userAvatarFilename)
{
$this->userAvatarFilename = $userAvatarFilename;
}
public function getNote()
{
return $this->note;
}
public function setNote($note)
{
$this->note = $note;
}
public function getCreatedAt()
{
return $this->createdAt;
}
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
}
}

Generate Migrations

Entity done! Well, almost done - we still need to somehow relate each GenusNote to a Genus. We'll handle that in a second.

But first, don't forget to generate a migration for the new table:

./bin/console doctrine:migrations:diff

Open up that file to make sure it looks right - it lives in app/DoctrineMigrations. CREATE TABLE genus_note - it looks great! Head back to the console and run the migration:

./bin/console doctrine:migrations:migrate

Adding Fixtures

Man, that was easy. We'll want some good dummy notes too. Open up the fixtures.yml file and add a new section for AppBundle\Entity\GenusNote. Start just like before: genus.note_ and - let's create 100 notes - so use 1..100:

... lines 1 - 8
AppBundle\Entity\GenusNote:
genus.note_{1..100}:
... lines 11 - 15

Next, fill in each property using the Faker functions: username: <username()> and then userAvatarFilename: Ok, eventually users might upload their own avatars, but for now, we have two hardcoded options: leanna.jpeg and ryan.jpeg. Let's select one of these randomly with a sweet syntax: 50%? leanna.jpeg : ryan.jpeg. That's Alice awesomeness:

... lines 1 - 7
AppBundle\Entity\GenusNote:
genus.note_{1..100}:
username: <userName()>
userAvatarFilename: '50%? leanna.jpeg : ryan.jpeg'
... lines 13 - 15

The rest are easy: note: <paragraph()> and createdAt: <dateTimeBetween('-6 months', 'now')>:

... lines 1 - 8
AppBundle\Entity\GenusNote:
genus.note_{1..100}:
username: <userName()>
userAvatarFilename: '50%? leanna.jpeg : ryan.jpeg'
note: <paragraph()>
createdAt: <dateTimeBetween('-6 months', 'now')>

Ok, run the fixtures!

./bin/console doctrine:fixtures:load

Double-check them with a query:

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

So awesome! Ok team, we have two entities: let's add a relationship!

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
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "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
    }
}