gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
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.
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
.
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.
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.
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!
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 ?
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/recipes/blob/master/doctrine/doctrine-bundle/2.4/config/packages/doctrine.yaml#L10 ) 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!
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
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!
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.
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!
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
Hey Jayant B.
You only need to update your `config/packages/doctrine.yaml` so it doesn't contain tabs, and use "spaces" instead.
Cheers!
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
Hey @Jean Charles,
Thanks for the tip. For PHP 8, I'd recommend you to upgrade php parser library with following command<br />composer upgrade nikic/php-parser<br />
This should fix this type of issues =)
Cheers!
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!
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!
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?
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!
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
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!
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.
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.
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!
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!
// 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
}
}
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 ???