Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

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
    }
}