We have a Movie class, but its missing all of its annotations! Yikes! I really don't feel like typing all of that.

Let me introduce you to another really important shortcut: generate. You can get to it with command+n. Or just click the code menu at the top, and select generate. Either way in this class, go down to "Generate ORM Class": and just like all menus in PhpStorm, you can just start typing to search for it.

Sweet - this automatically added all the Entity annotation stuff on top for us.

13 lines src/AppBundle/Entity/Movie.php
... lines 1 - 4
/**
* @Doctrine\ORM\Mapping\Entity
* @Doctrine\ORM\Mapping\Table(name="movie")
*/
class Movie
{
}

But it looks different: usually we'd just have the @ORM\Entity stuff on top, and it added it this way because we don't have the use statement yet. There's a simple solution to that. Just copy the full Entity annotation, say use, paste that and then delete the last part and instead stay as ORM;:

15 lines src/AppBundle/Entity/Movie.php
... lines 1 - 4
use Doctrine\ORM\Mapping as ORM;
... lines 6 - 15

That's the standard use statement that you see at the top of all Doctrine entities.

Let's get rid of these lines here, then get back inside of the class because context] is important with command+n. Hit command n and search for 'ORM' and select "Generate ORM Class":

15 lines src/AppBundle/Entity/Movie.php
... lines 1 - 4
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="movie")
*/
class Movie
{
}

Perfect!

Let's add our properties, private $id;, private $title; for the movie title, private $samsCharacterName for Sam's character, private $rating to give the movie appearance a rating, private $isMainCharacter so we can see if Sam was the lead in that movie, and lastly private $releasedAt which will tell us exactly when the movie came out.

27 lines src/AppBundle/Entity/Movie.php
... lines 1 - 10
class Movie
{
private $id;
private $title;
private $samsCharacterName;
private $rating;
private $isMainCharacter;
private $releasedAt;
}

Easy!

The warning here is that each of these is an unused private field, which is true so far. But good to know, in the future that could just be extra code.

Generate ORM Annotations

Now that we have these we can go back to command n, search ORM and select "Generate ORM annotations". Select all of the fields that are in the menu and hit ok:

45 lines src/AppBundle/Entity/Movie.php
... lines 1 - 10
class Movie
{
/**
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string")
*/
private $title;
/**
* @ORM\Column(type="string")
*/
private $samsCharacterName;
/**
* @ORM\Column(type="string")
*/
private $rating;
/**
* @ORM\Column(type="boolean")
*/
private $isMainCharacter;
/**
* @ORM\Column(type="datetime")
*/
private $releasedAt;
}

Awesome! Each field has all its annotations! Even better than that, it recognizes that id is our primary key so it set that up and it noticed that $isMainCharacter is probably a boolean, because of the 'is' in the beginning. And it also saw that $releasedAt is a datetime. This isn't perfect, I would prefer $releasedAt to just be a date, and rating up here is not a string, but an integer:

45 lines src/AppBundle/Entity/Movie.php
... lines 1 - 10
class Movie
{
... lines 13 - 29
/**
* @ORM\Column(type="integer")
*/
private $rating;
... lines 34 - 39
/**
* @ORM\Column(type="date", nullable=true)
*/
private $releasedAt;
}

But I do love getting autocomplete on all of those different types. Pressing control+space gives you a list of all the different types you have access to.

Ok, let's give $isMainCharacter a default value just in case it's ever not set. We'll make $releasedAt optional since a movie might not be released yet: set nullable=true: more autocomplete:

45 lines src/AppBundle/Entity/Movie.php
... lines 1 - 10
class Movie
{
... lines 13 - 34
/**
* @ORM\Column(type="boolean")
*/
private $isMainCharacter = false;
/**
* @ORM\Column(type="date", nullable=true)
*/
private $releasedAt;
}

Up here for $samsCharacterName, well this probably won't be too long so we can give it a length of 100 instead of the default 255:

45 lines src/AppBundle/Entity/Movie.php
... lines 1 - 10
class Movie
{
... lines 13 - 24
/**
* @ORM\Column(type="string", length=100)
*/
private $samsCharacterName;
... lines 29 - 43
}

Alright, this is all looking really nice.

Generating Getters and Setters

At this point we just have private properties so we need our getters and setters. Back to generate! Use our favorite shortcut, command+n, select getters and then $id:

53 lines src/AppBundle/Entity/Movie.php
... lines 1 - 10
class Movie
{
... lines 13 - 47
public function getId()
{
return $this->id;
}
}

Then back to generate and select getters and setters and select everything else. Before I finish this I want to pause and say that you don't necessarily need a getter and setter for every field in Doctrine. Sometimes you might want to wait to add the getters and setters until you actually need them. Then when the need arises you have these awesome shortcuts available.

100 lines src/AppBundle/Entity/Movie.php
... lines 1 - 10
class Movie
{
... lines 13 - 49
public function getTitle()
{
return $this->title;
}
public function setTitle($title)
{
$this->title = $title;
}
public function getSamsCharacterName()
{
return $this->samsCharacterName;
}
public function setSamsCharacterName($samsCharacterName)
{
$this->samsCharacterName = $samsCharacterName;
}
... lines 69 - 98
}

If I press command+, to get into preferences, you'll find that the templates that generate these methods are editable. Search for "templates", and you'll see the area where you can modify the getter and setter templates. I've already done this: usually they generate with some PHPDoc, which to me is kind of meaningless, so I've already removed it for nice clean rendering.

Generating the Repository

One last thing here! This entity needs a repository. Back to the action shortcut, which is... alt+enter! This opens up a menu to add the doctrine repository, which as you may have guessed, adds a repository class here:

16 lines src/AppBundle/Entity/MovieRepository.php
... lines 1 - 2
namespace AppBundle\Entity;
use Doctrine\ORM\EntityRepository;
... lines 6 - 12
class MovieRepository extends EntityRepository
{
}

100 lines src/AppBundle/Entity/Movie.php
... lines 1 - 6
/**
* @ORM\Entity(repositoryClass="MovieRepository")
... line 9
*/
class Movie
{
... lines 13 - 98
}

In my case I prefer to have these in a Repository directory. So that's nice that it helped me create that, but I'll move it manually.

A quick copy and paste will do that, then update the namespace to end with Repository:

16 lines src/AppBundle/Repository/MovieRepository.php
... lines 1 - 2
namespace AppBundle\Repository;
use Doctrine\ORM\EntityRepository;
... lines 6 - 12
class MovieRepository extends EntityRepository
{
}

Even though I had to move that manually, what's really cool is that it's highlighting and saying "Yo! Your repository Class is messed up! You can't use the short class name because now it's in a different namespace." I can delete what's there and type MovieRepository and I get autocomplete on the entire name:

100 lines src/AppBundle/Entity/Movie.php
... lines 1 - 6
/**
* @ORM\Entity(repositoryClass="AppBundle\Repository\MovieRepository")
... line 9
*/
class Movie
{
... lines 13 - 98
}

And there's more autocompleting goodness I won't show here for when you're building relationships and using the Query Builder.

Leave a comment!

  • 2016-09-19 weaverryan

    That's what we do on the newer tutorials, except for the beginner PHP or OO tutorials. But, I'm happy that this was a surprise - that's the point!

    Cheers!

  • 2016-09-19 Maksym Minenko

    Wow, what a surprise... And I'm definitely not sure it's the right way. I believe the best tutorials are recorded by the authors themselves.

  • 2016-09-19 weaverryan

    Ah yes, voiced by Leanna, written by me :)

  • 2016-09-19 Maksym Minenko

    Well, ok, but the chapters I commented on were by Leanna...

  • 2016-09-19 weaverryan

    Hey Maksym!

    Hmm, sorry you're disappointed! If I do something different than the "norm", it's usually to show something in the easier, more "pure" way first, and then later we build on the complexity (instead if building the "normal", but more complex thing first). It's definitely not my intention to be extra fancy - quite the opposite!

    And there's a bit of history to this: I work on the Symfony core, and during much of Symfony2, sometimes we (the community) made things too hard. At times, I'll make things simpler in the tutorials because of this. Fortunately, over time, that's changed - a lot of easy-win simplifications have been adopted in Symfony 3 (really, many were adopted towards the end of Symfony2), and now I go "off-script" quite a bit less.

    Anyways, I hope that gives you a bit of background! And if you have any questions about why I did something, or if you find anything specifically confusing, just ask and we'll be happy to have a conversation about that!

    Cheers!

  • 2016-09-19 Maksym Minenko

    Quite disappointing actually, frankly speaking... :( "Let's change that, we don't need this, let's create play.php"...
    Come on! The framework is quite overwhelming as it is, so go with the default flow first, explain everything and only then (possibly) try to introduce some "tricks".
    I hope your Symfony 3 tutorials are better.

  • 2016-09-05 weaverryan

    Woohoooo! Now keep rocking!

  • 2016-09-03 Max

    Hey Ryan!

    It worked using the /app/autoload.php require statement! Awesome! :D Thank you so much for your quick and detailed reply!

    Cheers!
    Max

  • 2016-09-02 weaverryan

    Hi Max!

    Awesome - so glad you're finding the tutorials useful! :D

    Now, about your error. It's interesting... the class you mentioned DOES exist in Symfony 2.8 (https://github.com/symfony/sym.... So, the problem is related to autoloading somehow. Which file are you requiring on top - app/autoload.php or app/bootstrap.php.cache? There's a subtle difference between the two starting in Symfony 2.8 (we tweaked some directory structure things in Symfony). If in doubt, copy the web/app_dev.php file from *your* project as your starting point for the play.php file (instead of making your copy look exactly like mine).

    If you're still having trouble, just post your full play.php file - and also, post the "autoload" section of your composer.json - and we'll work it out :).

    Cheers!

  • 2016-09-02 Max

    Hey there!
    First of all thank you so much for these amazing tutorials! :) I really fell in love with them!

    running the play.php with symfony 2.8 I get the following:

    PHP Fatal error: Class 'Symfony\Component\Debug\Debug' not found in /Users/maxschons/Sites/knpuniversity2/play.php on line 9
    PHP Stack trace:
    PHP 1. {main}() /Users/maxschons/Sites/knpuniversity2/play.php:0

    Fatal error: Class 'Symfony\Component\Debug\Debug' not found in /Users/maxschons/Sites/knpuniversity2/play.php on line 9

    Call Stack:
    0.0016 229856 1. {main}() /Users/maxschons/Sites/knpuniversity2/play.php:0

    Any idea? Thx!

  • 2016-06-14 JLChafardet

    Yeah, i guessed as much, it makes sense, keeping things organized allow for better understanding and easier scalability.
    cheers mate! thanks for taking the time to answer :D

  • 2016-06-14 weaverryan

    Hey JLChafardet! Welcome - late, but still here ;).

    About the "Repository" directory itself, I like this because the only other alternative that people use is to put their *Repository classes into the Entity directory (and then it's a mixture of entities and repositories). But, no deeper meaning :). If you're asking why I like organizing things into repository classes in general, it's because I *love* having 100% of my database queries in 1 spot - it makes future database changes and re-using query logic really easy.

    Anyways - I hope I hit on your question. Cheers!

  • 2016-06-13 JLChafardet

    yo weaverryan thanks for the effort of the screencast, im a tad late but hey! im here! lol.

    I have a question for you, regarding the Repository directory, in regards to structure, etc, why do you like it so? any specific reasons other than just "organization" of your code? or its there a deeper meaning to it?

  • 2015-09-08 Justice Sommer

    I was running MySQL via XAMPP. I manually added a DB named "symfony". then ran the command via the command line again. That seems to have fixed it.

  • 2015-09-08 weaverryan

    Hey Justice!

    Your database doesn't exist yet - so don't forget to have Symfony create it for you :). This is explained in the next chapter: https://knpuniversity.com/scre...

    Cheers!

  • 2015-09-08 Justice Sommer

    after entering the Entity shortcut name I get...

    [Doctrine\DBAL\Exception\ConnectionException]
    An exception occured in driver: SQLSTATE[HY000] [1049] Unknown database 'symfony'

  • 2015-08-04 Shairyar Baig

    Very helpful tutorial

  • 2015-01-29 Diego Aguiar

    I did what you said and it worked!

    I just added a "generateId" method to my repository and made my listener to call it on PrePersist event

    Thanks for your time :D

  • 2015-01-29 weaverryan

    Hmm, you know, I'm not sure :). But I would try it! Remove the `$id` (the normal one that Doctrine adds), then add a property for your varchar one that exists, and give it @ORM\Column and @ORM\Id, but don't give it @ORM\GeneratedValue. I think in theory that should work. Doctrine needs (I believe) someething to be the @ORM\Id of the entity, but really, it doesn't care if it's an integer or auto-increment. Not having auto-increment causes other problems of course: specifically you need to manually set this before saving.

    Let me know how it goes!

  • 2015-01-28 Diego Aguiar

    Hey there!

    I have a question about entities id's column

    I already have an users table in my project and it's ID is not an "AUTO_INCREMENT" id, it is a custom varchar ID

    Is it possible to change the auto generated ID column of Doctrine to this one ?

    Thanks for your time! :]