Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Entity Class

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.

Doctrine is an ORM: an object relational mapper. That's a fancy way of saying that, for each table in the database, we will have a corresponding class in PHP. And for each column on that table, there will be a property in that class. When you query for a row in a table, Doctrine will give you an object with that row's data set on the properties.

So if you want to create a database table in Doctrine, the way you do that is actually by creating the class it will map to. These "classes" that map to tables have a special name: entity classes.

make:entity

You can create an entity class by hand - there's nothing very special about them. But... come on! There's a much easier way. Find your terminal and run one of my favorite bin/console commands:

php bin/console make:entity

You can also run:

symfony console make:entity

It doesn't matter in this case, because this command won't talk directly to the database, and so, it doesn't need the environment variables. This command just generates code.

Now remember: the whole point of our site is for witches and wizards to ask questions and then post answers. So the very first thing we need is a Question entity. So, a question table in the database.

Enter Question. The command immediately starts asking what properties we want, which really, also means what columns we want in the table. Let's add a few. Call the first one name - that will be the short name or "title" of the question like "Reversing a Spell". The command then asks what "type" we want. Doctrine has its own type system: enter "?" to see the full list.

These aren't MySQL column types, but each maps to one. For example, a string maps to a VARCHAR in MySQL. And there are a bunch more.

In our case, choose string: that's good for any text 255 characters or less. For the column length, I'll use the default 255. The next question says:

Can this field be nullable in the database?

Say "No". This means that the column will be required in the database.

And... congrats! We just added our first field! Let's add a few more. Call the next slug: this will be the URL-safe version of the name that shows up in the URL. It will also be a string, let's set its length to 100 and, once again, I'll say "no" to "nullable".

For the actual body of the question, let's call it question. This will be long, so we can't use the string type. Instead, use text... and make this also required in the database.

Add one more field: askedAt. This will be a date field - kind of like a "published at" field. For the type, ooh! It recommends datetime - that is exactly what we want! Hit enter and this time say "yes" to nullable. The idea is that users will be able to start writing a question and save it to the database with a null askedAt because it's not finished. When the user is finished and ready to post it, then we would set that value.

And... we're done! Hit enter one more time to exit make:entity.

Hello Entity Class

So... what did this do? Well, first, I'll tell you that this made absolutely no changes to our database: we do not have a question database table yet.

If you scroll back up to the top of the command, you can see that it created 2 files: src/Entity/Question.php and src/Repository/QuestionRepository.php.

For now, completely ignore the repository class: it's not important yet and we'll talk about it later.

This entity class, however, is super important. Go open it up: src/Entity/Question.php.

... lines 1 - 2
namespace App\Entity;
use App\Repository\QuestionRepository;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass=QuestionRepository::class)
*/
class Question
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $name;
/**
* @ORM\Column(type="string", length=100)
*/
private $slug;
/**
* @ORM\Column(type="text")
*/
private $question;
/**
* @ORM\Column(type="datetime", nullable=true)
*/
private $askedAt;
... lines 39 - 91
}

As we talked about, we're going to have one class per table.

The first thing I want you to notice is that... there's nothing special about this class. It's a boring, normal class... it doesn't even extend a base class! It has several private properties and, to access those, the command generated getter and setter methods, like getName(), getSlug() and setSlug().

... lines 1 - 10
class Question
{
... lines 13 - 39
public function getId(): ?int
{
return $this->id;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getSlug(): ?string
{
return $this->slug;
}
public function setSlug(string $slug): self
{
$this->slug = $slug;
return $this;
}
public function getQuestion(): ?string
{
return $this->question;
}
public function setQuestion(string $question): self
{
$this->question = $question;
return $this;
}
public function getAskedAt(): ?\DateTimeInterface
{
return $this->askedAt;
}
public function setAskedAt(?\DateTimeInterface $askedAt): self
{
$this->askedAt = $askedAt;
return $this;
}
}

It's just about the most boring class you'll ever see.

But of course, if Doctrine is going to map this class and its properties to a database table, it needs to know a few things. For example, it needs to know that the name property should map to a name column and that its type is a string.

... lines 1 - 10
class Question
{
... lines 13 - 19
/**
* @ORM\Column(type="string", length=255)
*/
private $name;
... lines 24 - 91
}

The way that Doctrine does this is by reading annotations. Well, you can also use XML, but I love annotations.

For example, the @ORM\Entity() above the class is what actually tells Doctrine:

... lines 1 - 7
/**
* @ORM\Entity(repositoryClass=QuestionRepository::class)
*/
class Question
{
... lines 13 - 91
}

Hey! This is not just a normal, boring PHP class. This is an "entity": a class that I want to be able to store in the database.

Then, the @ORM\Column() above the properties is how Doctrine knows which properties should be stored in the table and their types.

Annotations Reference

Now, there are a bunch of different annotations and options you can use to configure Doctrine. Most are pretty simple - but let me show you an awesome reference. Search for doctrine annotations reference to find a really nice spot on their site where you can see a list of all the different possible annotations and their options.

For example, @Column() tells you all the different options that you can pass to it, like a name option. So if you wanted to control the name of the slug column - instead of letting Doctrine determine it automatically - you would do that by adding a name option.

One of the other annotations that you'll see here is @Table. Oh, and notice, in the docs, the annotation will be called @Table. But inside Symfony, we always use @ORM\Table. Those are both referring to the same thing.

Anyways, if you wanted to control the name of the table, we could say @ORM\Table() and pass it name="" with some cool table name.

But I won't bother doing that because Doctrine will guess a good table name from the class. Oh, and by the way, the auto-completion that you're seeing on the annotations comes from the "PHP Annotations" plugin in PhpStorm.

Ok: status check! The make:entity command helped us create this new entity class, but it did not talk to the database. There is still no question table.

How do we create the table? By generating a migration. Doctrine's migrations system is amazing. It will even allow us to perform table changes with basically zero work. Let's find out how next.

Leave a comment!

29
Login or Register to join the conversation
Anton S. Avatar
Anton S. Avatar Anton S. | posted 1 year ago

is there a detailed idea of ​​how to publish a ready-made symfony-5 application on a shared hosting and so that it works exactly the same as on a local one ???

Reply

Hey there!

Could you describe your idea a bit more? :) What do you mean about "it works exactly the same as on a local one"? The Symfony application should work exactly as on your local machine, but it depends on the server/php configuration. What differences do you have?

Cheers!

Reply
Robin R. Avatar
Robin R. Avatar Robin R. | posted 1 year ago

I made a mistake during make:entity > Question I named the column asked_at instead of askedAt.
It generates me some errors and diffculties for rest of the tutos (chapter 21 particulary).
I tried to rename it in the Question entity and then launch migration and then migrate after, but the field name doesn't update in the database.

How can I roll back and "re-make" the question Entity class or just rename the askedAt column ?

Reply

Hey Robin R.!

Ah, excellent question! So, a few things:

> I tried to rename it in the Question entity and then launch migration and then migrate after, but the field name doesn't update in the database.

You probably updated the Question entity correctly :). By default (well, more accurately, thanks to this line of config that lives on your config/packages/doctrine.yaml file - https://github.com/symfony/... ) when Doctrine creates a table from a class or a column from a property name, it "underscores" it. So if you have an askedAt property, it creates an asked_at column in the database. So, it's very likely that you updated the Question entity *just* fine... but that the column in the database doesn't need to change. No migration needed!

> How can I roll back and "re-make" the question Entity class or just rename the askedAt column ?

This, as you correctly guessed, needs to be done by hand :). If you had noticed the mistake immediately, you could have always hit Ctrl+C to exit the make:entity process and then rolled back via git (git checkout src/Entity/Question.php) the changes to your entity... then re-ran make:entity.

I hope this helps!

Cheers!

Reply
desc Avatar

Ryan - I hope you can point me at some help. As I was using the https://symfony.com/doc/cur... - this did not generate the repositories in this video - is there a way to generate those form the Entity Classes I now have?

found an answer
php bin/console make:entity --regenerate App

Reply

Hey @desc

Good catch! Glad you were able to find the solution yourself :) Another alternative solution would be to copy/paste the code from another repository class and change class names.

Cheers!

Reply
desc Avatar

Thank you - can you point me to a symfony queries place to ask general questions that may not be specific to a video? Much appreciated. Enjoying the learning and progress on my project.

Reply

Hey @desc,

Because of our limited bandwidth we do not answer questions related to user personal projects, but if it's related to our videos - the best place to ask questions is below the video in "Conversation" section. If you have other Symfony-related questions regarding your private projects and do not related to our screencasts - please, ask them in Symfony Slack channel or in StackOverflow, you can find them here: https://symfony.com/support

Thanks for understanding!

Cheers!

Reply
desc Avatar

thank you

Reply
Jayant B. Avatar
Jayant B. Avatar Jayant B. | posted 1 year ago

Hello There

With mysql Server_Version 5.7 or 8.0 on doctrine.yaml file when i am trying to make:entity, I am getting following error -

In FileLoader.php line 173:

The file "C:\wamp64\www\Symfony001\Symfony-Doctrine\config/packages/doctrine.yaml" does not contain valid YAML: A Y
AML file cannot contain tabs as indentation in "C:\\wamp64\\www\\Symfony001\\Symfony-Doctrine\\config/packages/doct
rine.yaml" at line 8 (near " server_version: '5.7'") in C:\wamp64\www\Symfony001\Symfony-Doctrine\config/packages/
doctrine.yaml (which is being imported from "C:\wamp64\www\Symfony001\Symfony-Doctrine\src\Kernel.php").

In YamlFileLoader.php line 745:

The file "C:\wamp64\www\Symfony001\Symfony-Doctrine\config/packages/doctrine.yaml" does not contain valid YAML: A Y
AML file cannot contain tabs as indentation in "C:\\wamp64\\www\\Symfony001\\Symfony-Doctrine\\config/packages/doct
rine.yaml" at line 8 (near " server_version: '5.7'").

In Parser.php line 152:

A YAML file cannot contain tabs as indentation in "C:\\wamp64\\www\\Symfony001\\Symfony-Doctrine\\config/packages/d
octrine.yaml" at line 8 (near " server_version: '5.7'").

Please let me know how to resolve this issue.

Thanks

Reply

Hey Jayant B.

You only need to update your `config/packages/doctrine.yaml` so it doesn't contain tabs, and use "spaces" instead.

Cheers!

Reply
Jayant B. Avatar

Thank you! That worked!

Cheers!

1 Reply
Kleo Avatar

Hi!
After I create the entity with php bin/console make:entity , I get this error which will not allow me to create the fields!
http://prntscr.com/10xmj37

Reply

Hey @Kleo

Which php version do you use?

Cheers!

Reply
Jean charles Avatar
Jean charles Avatar Jean charles | sadikoff | posted 1 year ago

Hi Vladimir,
I have this same error and i run php 8.0.3.

Cheers!

Reply

Hey @Jean Charles,

Thanks for the tip. For PHP 8, I'd recommend you to upgrade php parser library with following command

composer upgrade nikic/php-parser

This should fix this type of issues =)

Cheers!

Reply

QUESTION! Is it required to have the getters and setters in the class? I prefer using traits for better organization and reuse of my code!

Reply

Hey mikesmit1992!

Traits work perfectly fine with Doctrine :). So you could move the properties+getters & setters into a trait and then use that trait from your entity (or from multiple entities). You are also free to make your properties public and not have any getter and setter methods at all. So basically... you can do whatever you want ;).

Cheers!

Reply
Johannes Avatar
Johannes Avatar Johannes | posted 1 year ago

Heyho!
I get the following error every time I use the "./bin/console make:entity" command: https://prnt.sc/w67v2e

Do you know why and how I can fix it?

Reply

Hey Johannes!

That is SUPER weird... like, it's one of those errors that should *never* happen: a file that should 100% be there is not (and so Composer is exploding). Honestly, I would try removing your vendor/ directory entirely and then re-running "composer install". I bet something weird just happened with Composer.

Let me know!

Cheers!

Reply
Emus Avatar

Hi!

New property name (press <return> to stop adding fields):
> name

In Validator.php line 158:

Argument 2 passed to Symfony\Bundle\MakerBundle\Validator::validateDoctrineFieldName() must be an instance of Doctrine\Common\Persistence\ManagerRegistry, instance
of Doctrine\Bundle\DoctrineBundle\Registry given, called in /home/emilio/Sites/symfony_doctrine/vendor/symfony/maker-bundle/src/Maker/MakeEntity.php on line 303

Reply

Hi @Emilio!

Are you running the "code download" from this tutorial? Or are you working on your own project? If you are getting this from the code download, please let me know! That shouldn't happen! If you are getting this in your own project, try upgrading to at least MakerBundle 1.21.0, which fixed this bug https://github.com/symfony/...

Cheers!

Reply
Антон С. Avatar
Антон С. Avatar Антон С. | weaverryan | posted 2 years ago

i use code from this lesson and have the same issue.

i updated version of symfony/maker-bundle to ^1.21.1 and it works greate.

1 Reply
Kevin G. Avatar

Using course zip file for project.

Argument 2 passed to Symfony\Bundle\MakerBundle\Validator::validateDoctrineFieldName() must be an instance of Doctrine\Common\Persistence\ManagerRegistry, instance of Doctrine\Bundle\Doctrine
Bundle\Registry given, called in /Users/kevingehrke/Projects/SymfonyCasts/code-symfony-doctrine/start/vendor/symfony/maker-bundle/src/Maker/MakeEntity.php on line 303

Update to symfony/maker-bundle to ^1.21.1 resolved issue.

Reply

Hey KGehrke,

We're sorry to hear you had an issue, but I'm happy that you were able to fix it with the solution from Smidl.

Could you provide a bit more info about the problem? What exactly did you do when see this error? Running "composer install" command? Are you in start/ directory of the downloaded course code?

Cheers!

Reply

Hey victor!

Just following up - we were a bit slower because of the holidays :). Victor checked into this further and found the problem - as you suspected a bug in the MakerBundle version we were using (if you're using a new doctrine/persistence package).

I've just updated the code download so that it should work out of the box. Thanks for the report!

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial also works great for Symfony 6!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.4.1",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "doctrine/doctrine-bundle": "^2.1", // 2.1.1
        "doctrine/doctrine-migrations-bundle": "^3.0", // 3.0.2
        "doctrine/orm": "^2.7", // 2.8.2
        "knplabs/knp-markdown-bundle": "^1.8", // 1.9.0
        "knplabs/knp-time-bundle": "^1.11", // v1.16.0
        "sensio/framework-extra-bundle": "^6.0", // v6.2.1
        "sentry/sentry-symfony": "^4.0", // 4.0.3
        "stof/doctrine-extensions-bundle": "^1.4", // v1.5.0
        "symfony/asset": "5.1.*", // v5.1.2
        "symfony/console": "5.1.*", // v5.1.2
        "symfony/dotenv": "5.1.*", // v5.1.2
        "symfony/flex": "^1.3.1", // v1.17.5
        "symfony/framework-bundle": "5.1.*", // v5.1.2
        "symfony/monolog-bundle": "^3.0", // v3.5.0
        "symfony/stopwatch": "5.1.*", // v5.1.2
        "symfony/twig-bundle": "5.1.*", // v5.1.2
        "symfony/webpack-encore-bundle": "^1.7", // v1.8.0
        "symfony/yaml": "5.1.*", // v5.1.2
        "twig/extra-bundle": "^2.12|^3.0", // v3.0.4
        "twig/twig": "^2.12|^3.0" // v3.0.4
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.3", // 3.4.0
        "symfony/debug-bundle": "5.1.*", // v5.1.2
        "symfony/maker-bundle": "^1.15", // v1.23.0
        "symfony/var-dumper": "5.1.*", // v5.1.2
        "symfony/web-profiler-bundle": "5.1.*", // v5.1.2
        "zenstruck/foundry": "^1.1" // v1.5.0
    }
}