Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Updating an Entity with New Fields

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

It's time to get back to work on the article page... because... some of this stuff is still hardcoded! Lame! Like, the author, number of hearts, and this image. There are a few possible images in our project that our dummy articles can point to.

Our mission is clear: create three new fields in Article and use those to make all of this finally dynamic! Let's go!

Open your Article entity. The simplest way to add new fields is just to... add them by hand! It's easy enough to copy an existing property, paste, rename, and configure it. Of course, if you want a getter and setter method, you'll also need to create those.

Generating New Fields into the Entity

Because of that, my favorite way to add fields is to, once again, be lazy, and generate them! Find your terminal and run the same command as before:

php bin/console make:entity

If you pass this the name of an existing entity, it can actually update that class and add new fields. Magic! First, add author, use string as the type. And yea, in the future when we have a "user" system, this field might be a database relation to that table. But for now, use a string. Say no to nullable. Reminder: when you say no to nullable, it means that this field must be set in the database. If you try to save an entity without any data on it, you'll get a huge database exception.

Next, add heartCount, as an integer, and say not null: this should always have a value, even if it's zero. Then, finally, the image. In the database, we'll store only the image filename. And, full disclosure, uploading files is a whole different topic that we'll cover in a different tutorial. In this example, we're going to use a few existing images in the public/ directory. But, both in this situation and in a real-file upload situation, the field on your entity looks the same: imageFilename as a string and nullable yes, because maybe the image is optional when you first start writing an article.

Ok, hit enter and, done! Let's go check out the entity! Great: three new properties on top:

... lines 1 - 9
class Article
{
... lines 12 - 38
/**
* @ORM\Column(type="string", length=255)
*/
private $author;
/**
* @ORM\Column(type="integer")
*/
private $heartCount;
/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
private $imageFilename;
... lines 53 - 142
}

And of course, at the bottom, here are their getter and setter methods:

... lines 1 - 9
class Article
{
... lines 12 - 107
public function getAuthor(): ?string
{
return $this->author;
}
public function setAuthor(string $author): self
{
$this->author = $author;
return $this;
}
public function getHeartCount(): ?int
{
return $this->heartCount;
}
public function setHeartCount(int $heartCount): self
{
$this->heartCount = $heartCount;
return $this;
}
public function getImageFilename(): ?string
{
return $this->imageFilename;
}
public function setImageFilename(?string $imageFilename): self
{
$this->imageFilename = $imageFilename;
return $this;
}
}

Now that we have the new fields, don't forget! We need a migration:

php bin/console make:migration

When that finishes, go look at the new file to make sure it doesn't have any surprises: ALTER TABLE article, and then it adds author, heart_count and image_filename:

... lines 1 - 10
class Version20180414171443 extends AbstractMigration
{
public function up(Schema $schema)
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE article ADD author VARCHAR(255) NOT NULL, ADD heart_count INT NOT NULL, ADD image_filename VARCHAR(255) DEFAULT NULL');
}
public function down(Schema $schema)
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE article DROP author, DROP heart_count');
}
}

I love it!

Close that, run back to your terminal, and migrate!

php bin/console doctrine:migrations:migrate

Field Default Value

Next, we need to make sure these new fields are populated on our dummy articles. Open ArticleAdminController.

Oh, but first, remember that, in the Article entity, heartCount is required in the database:

... lines 1 - 9
class Article
{
... lines 12 - 43
/**
* @ORM\Column(type="integer")
*/
private $heartCount;
... lines 48 - 142
}

Actually, to be more clear: nullable=true means that it is allowed to be null in the database. If you don't see nullable, it uses the default value, which is false.

Anyways, this means that heartCount needs a value! But here's a cool idea: once our admin area is fully finished, when an author creates a new article, they shouldn't need to set the heartCount manually. I mean, it's not like we expect the form to have a "heart count" input field on it. Nope, we expect it to automatically default to zero for new articles.

So... how can we give a property a default value in the database? By giving it a default value in PHP: $heartCount = 0:

... lines 1 - 9
class Article
{
... lines 12 - 43
/**
* @ORM\Column(type="integer")
*/
private $heartCount = 0;
... lines 48 - 142
}

Using the new Fields

Ok, back to ArticleAdminController! Add $article->setAuthor() and use the same data we had on the original, hardcoded articles:

... lines 1 - 10
class ArticleAdminController extends AbstractController
{
... lines 13 - 15
public function new(EntityManagerInterface $em)
{
... lines 18 - 40
// publish most articles
if (rand(1, 10) > 2) {
$article->setPublishedAt(new \DateTime(sprintf('-%d days', rand(1, 100))));
}
$article->setAuthor('Mike Ferengi')
... lines 47 - 58
}
}

Then, ->setHeartCount() and give this a random number between, how about, 5 and 100:

... lines 1 - 10
class ArticleAdminController extends AbstractController
{
... lines 13 - 15
public function new(EntityManagerInterface $em)
{
... lines 18 - 45
$article->setAuthor('Mike Ferengi')
->setHeartCount(rand(5, 100))
... lines 48 - 58
}
}

And finally, ->setImageFilename(). The file we've been using is called asteroid.jpeg. Keep using that:

... lines 1 - 10
class ArticleAdminController extends AbstractController
{
... lines 13 - 15
public function new(EntityManagerInterface $em)
{
... lines 18 - 45
$article->setAuthor('Mike Ferengi')
->setHeartCount(rand(5, 100))
->setImageFilename('asteroid.jpeg')
;
... lines 50 - 58
}
}

Excelente! Because we already have a bunch of records in the database where these fields are blank, just to keep things simple, let's delete the table entirely and start fresh. Do that with:

php bin/console doctrine:query:sql "TRUNCATE TABLE article"

If you check out the page now and refresh... cool, it's empty. Now, go to /admin/article/new and... refresh a few times. Awesome! Check out the homepage!

We have articles... but actually... this author is still hardcoded in the template. Easy fix!

Updating the Templates

Open up homepage.html.twig. Let's first change the... where is it... ah, yes! The author's name: use {{ article.author }}:

... lines 1 - 2
{% block body %}
<div class="container">
<div class="row">
<!-- Article List -->
<div class="col-sm-12 col-md-8">
... lines 10 - 18
<!-- Supporting Articles -->
{% for article in articles %}
<div class="article-container my-1">
<a href="{{ path('article_show', {slug: article.slug}) }}">
... line 24
<div class="article-title d-inline-block pl-3 align-middle">
... lines 26 - 27
<span class="align-left article-details"><img class="article-author-img rounded-circle" src="{{ asset('images/alien-profile.png') }}"> {{ article.author }} </span>
... line 29
</div>
</a>
</div>
{% endfor %}
</div>
... lines 35 - 54
</div>
</div>
{% endblock %}

Then, in show.html.twig, change the article's heart count - here it is - to {{ article.heartCount }}. And also update the author, just like before:

... lines 1 - 4
{% block body %}
<div class="container">
<div class="row">
<div class="col-sm-12">
<div class="show-article-container p-3 mt-4">
<div class="row">
<div class="col-sm-12">
... line 13
<div class="show-article-title-container d-inline-block pl-3 align-middle">
... lines 15 - 16
<span class="align-left article-details"><img class="article-author-img rounded-circle" src="{{ asset('images/alien-profile.png') }}"> {{ article.author }} </span>
... lines 18 - 20
<span class="pl-2 article-details">
<span class="js-like-article-count">{{ article.heartCount }}</span>
... line 23
</span>
</div>
</div>
</div>
... lines 28 - 73
</div>
</div>
</div>
</div>
{% endblock %}
... lines 80 - 86

If you try the homepage now, ok, it looks exactly the same, but we know that these author names are now dynamic. If you click into an article.. yea! We have 88 hearts - that's definitely coming from the database.

Updating the Image Path

The last piece that's still hardcoded is this image. Go back to homepage.html.twig. The image path uses asset('images/asteroid.jpeg'):

... lines 1 - 2
{% block body %}
<div class="container">
<div class="row">
<!-- Article List -->
<div class="col-sm-12 col-md-8">
... lines 10 - 18
<!-- Supporting Articles -->
{% for article in articles %}
<div class="article-container my-1">
<a href="{{ path('article_show', {slug: article.slug}) }}">
<img class="article-img" src="{{ asset('images/asteroid.jpeg') }}">
... lines 25 - 30
</a>
</div>
{% endfor %}
</div>
... lines 35 - 54
</div>
</div>
{% endblock %}

So... this is a little bit tricky, because only part of this - the asteroid.jpeg part - is stored in the database. One solution would be to use Twig's concatenation operator, which is ~, then article.imageFilename:

{# templates/article/homepage.html.twig #}

{# ... #}
    <img class="article-img" src="{{ asset('images/'~article.imageFilename) }}">
{# ... #}

You don't see the ~ much in Twig, but it works like a . in PHP.

That's fine, but a nicer way would be to create a new method that does this for us. Open Article and, at the bottom, create a new public function getImagePath():

... lines 1 - 9
class Article
{
... lines 12 - 143
public function getImagePath()
{
... line 146
}
}

Inside, return images/ and then $this->getImageFilename():

... lines 1 - 9
class Article
{
... lines 12 - 143
public function getImagePath()
{
return 'images/'.$this->getImageFilename();
}
}

Thanks to this, in the template, we only need to say article.imagePath:

... lines 1 - 2
{% block body %}
<div class="container">
<div class="row">
<!-- Article List -->
<div class="col-sm-12 col-md-8">
... lines 10 - 18
<!-- Supporting Articles -->
{% for article in articles %}
<div class="article-container my-1">
<a href="{{ path('article_show', {slug: article.slug}) }}">
<img class="article-img" src="{{ asset(article.imagePath) }}">
... lines 25 - 30
</a>
</div>
{% endfor %}
</div>
... lines 35 - 54
</div>
</div>
{% endblock %}

And yea, imagePath is totally not a real property on Article! But thanks to the kung fu powers of Twig, this works fine.

Oh, and side note: notice that there is not an opening slash on these paths:

... lines 1 - 9
class Article
{
... lines 12 - 143
public function getImagePath()
{
return 'images/'.$this->getImageFilename();
}
}

As a reminder, you do not need to include the opening / when using the asset() function: Symfony will add it there automatically.

Ok, try it out - refresh! It still works! And now that we've centralized that method, in show.html.twig, it's super easy to make the same change: article.imagePath:

... lines 1 - 4
{% block body %}
<div class="container">
<div class="row">
<div class="col-sm-12">
<div class="show-article-container p-3 mt-4">
<div class="row">
<div class="col-sm-12">
<img class="show-article-img" src="{{ asset(article.imagePath) }}">
... lines 14 - 25
</div>
</div>
... lines 28 - 73
</div>
</div>
</div>
</div>
{% endblock %}
... lines 80 - 86

Awesome. And when you click on the show page, it works too.

Next! Now that the heart count is stored in the database, let's make our AJAX endpoint that "likes" an article actually work correctly. Right now, it does nothing, and returns a random number. We can do better.

Leave a comment!

42
Login or Register to join the conversation

Hi,
In the Article Entity I see a Column named "publishedAt" but in SQL DB is renamed to "published_at"
Is this because of datetime type?

24 Reply
MolloKhan Avatar MolloKhan | SFCASTS | Teo | posted 4 years ago | edited

Hey Teo

Nope, that's because of the convention being followed by Doctrine, you can choose the "underscore" or "camelcase" strategy, or create your own strategy. You can check the full config list here: https://symfony.com/doc/cur...

Cheers!

Reply
Dung L. Avatar
Dung L. Avatar Dung L. | posted 3 years ago

Hello,

We know with "php bin/console make:entity" we can add a new property to an existing Entity, so my question is how can we remove a property, change/update/add a relation of an property using "php bin/console make:entity".

I know we can edit directly and manually from the Entity.php file but I prefer using the make command. Please give example if it is possible to do so with make command.

Thanks very much!

Dung.

Reply

Hey Dung L. !

> so my question is how can we remove a property, change/update/add a relation of an property using "php bin/console make:entity".
> I know we can edit directly and manually from the Entity.php file but I prefer using the make command.

As you guessed, it's not possible with the make:entity command. I created this command... and it was a TON of work. What you're asking about... *might* be possible... but it also might be unreliable - e.g. what if you tell the command to remove a field called "name" but you've also added a custom method beyond just getName() and setName() like getFullName() which uses this property. Should the command remove that custom method? It's tricky... which is a big reason why it's not implemented.

So, sorry I can't give you the answer you want - but I hope this is a small detail ultimately :).

Cheers!

Reply
Dung L. Avatar

Thanks for sharing your insight, I do not want to miss the goodness offered by Symfony, at the very least I feel good that something is not only tricky for me but for you too :) - JK!

1 Reply
Dung L. Avatar
Dung L. Avatar Dung L. | posted 3 years ago

Good Morning,

When I update an entity with a new field this is what I get


/**
* @ORM\Column(type="integer", nullable=true)
*/
private $totalPoint;

Then I must manually typed and changed to the below to point this entity property to the corresponding database field. Should this syntax be auto generated? if so how do I make sure it happens every time I add new field?


/**
* @ORM\Column(name="total_point", type="integer", nullable=true)
*/
private $totalPoint;

Thank you!

Reply

Hey Dung L.

You only have to tweak your ORM config. It should use the "underscore" naming strategy instead of camel case.


// config/packages/doctrine.yaml

doctrine:
    orm:
        naming_strategy: doctrine.orm.naming_strategy.underscore

Cheers!

Reply
Dung L. Avatar
Dung L. Avatar Dung L. | MolloKhan | posted 3 years ago | edited

Hi MolloKhan ,
what is the annotation

(name="total_point",

can I remove it completely or it needs to be there to reference field "total_point" in the database?
Best regards,

Reply

I believe you don't need to specify the table name anymore but you can leave it as a future reference

Reply
Dung L. Avatar
Dung L. Avatar Dung L. | MolloKhan | posted 3 years ago | edited

Got that, Merry Christmas MolloKhan !

Reply

Merry Christmas to you too! (A bit late :p)

Reply
Dung L. Avatar
Dung L. Avatar Dung L. | posted 3 years ago

Hello there,

How do I use bin/console command line related doctrine & make bundle to set primary key property on a an existing db field/entity property in symfony. Or this has to be done using annotation?

Can you please instruct or point me to some sort of documents to learn from? I have tried googling but found no resources for this question.

Thank you very much for any help!

Reply
Dung L. Avatar

Thank you anyways, I found the answer to my question here https://www.doctrine-projec... so it is within doctrine document.

Reply

Hey Dung,

Glad you figured it out yourself! And thanks for sharing the link with others, might be useful

Cheers!

Reply

Hi, i don't have autocomplete for variables in twig template... for example i write article and nothing... Last phpstorm and other things are up-to-date...

Reply

Hey Stranger

Do you have Symfony plugin installed and configured?

Cheers!

Reply

Hmm created new project from that files... and now it suggests.... strange bug... thanks anyway!

Reply

Stranger

Great that it's solved now, There are sometimes issues with suggestions, most of time it's because of phpstorm caches, if you face with this problem again, try to invalidate caches and restart PHPStorm

Cheers!

Reply

Yes of cause... everywhere it works... for example it suggests |ago , but it doesn't suggest article object :(

Reply

Hey Stranger

I'm so sorry for so late answer, but honestly I can't say exactly why it's so. Probably it's related to PHPStorm configuration, the best I can advice for you is to re-check every option from PHPStorm preferences > Languages & Frameworks > PHP > Symfony and after it Invalidate caches and restart

Cheers!

Reply
Dung L. Avatar
Dung L. Avatar Dung L. | Stranger | posted 3 years ago | edited

Stranger
-try to debug it here is the code taken from https://symfony.com/doc/4.1...

{# templates/article/recent_list.html.twig #}
{# the contents of this variable are sent to the Web Debug Toolbar #}
{% dump articles %}

{% for article in articles %}
{# the contents of this variable are displayed on the web page #}
{{ dump(article) }}


{{ article.title }}

{% endfor %}

-make sure you see what/data you expect to be suggested!
-if you do not see what/data you expected check your Entity/Article.php and make sure they are properly generated!

Reply
Dung L. Avatar
Dung L. Avatar Dung L. | posted 3 years ago | edited

Hello there,
I added a property to existing entity the lazy way by ... make:entity, then make:migration, then doctrine:migrations;migrate. Everything went ok/green. But I have a question to ask: Why in my Migrations version I have so many queries when I only added 1 property namely "image_1o". I pasted the file in here please help me understand.
Thanks so much as usual!

`
/**

  • Auto-generated Migration: Please modify to your needs!
    */
    final class Version20191017191622 extends AbstractMigration
    {
    public function getDescription() : string
    {

     return '';
    

    }

    public function up(Schema $schema) : void
    {

     // this up() migration is auto-generated, please modify it to your needs
     $this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
    
     $this->addSql('ALTER TABLE address CHANGE city_id city_id INT DEFAULT NULL, CHANGE province_id province_id INT DEFAULT NULL, CHANGE postal_id postal_id INT DEFAULT NULL, CHANGE country_id country_id INT DEFAULT NULL');
     $this->addSql('ALTER TABLE article CHANGE published_at published_at DATETIME DEFAULT NULL, CHANGE image_filename image_filename VARCHAR(255) DEFAULT NULL, CHANGE location location VARCHAR(255) DEFAULT NULL, CHANGE specific_location_name specific_location_name VARCHAR(255) DEFAULT NULL');
     $this->addSql('ALTER TABLE estate DROP FOREIGN KEY estate_address_id_fk');
     $this->addSql('ALTER TABLE estate DROP FOREIGN KEY estate_agent_id_fk');
    

    ...
    ...
    ...
    d_fk FOREIGN KEY (remark_id) REFERENCES remark (id)');

     $this->addSql('ALTER TABLE exterior DROP exterior');
     $this->addSql('ALTER TABLE user CHANGE first_name first_name VARCHAR(255) DEFAULT NULL, CHANGE twitter_username twitter_username VARCHAR(255) DEFAULT NULL');
    

    }

}

`

Reply

Hey Dung L.

Whenever you create a migration via doctrine or a maker command it will synchronize your current database schema to your application (entities metadata). I believe your database schema was just not in sync. There is a command for checking it php bin/console doctrine:schema:validate

Cheers!

Reply
Dung L. Avatar
Dung L. Avatar Dung L. | MolloKhan | posted 3 years ago | edited

Hi MolloKhan , sorry I just figured out how to find this conversation using Disqus, never knew that before. After I ran the command:


php bin/console doctrine:schema:validate
Mapping
-------
[OK] The mapping files are correct.
Database
--------
[ERROR] The database schema is not in sync with the current mapping file.

Can you please suggest me what I need to do properly, I would like to correct this error before my project goes too far.

Thanks Diego!

Reply

Hey Dung! If you feel lost among all the changes that you have made to your schema. What I usually do is to commit everything, go to the master branch, re-create the database (drop, create, create schema), then go back to my deving-branch and just run bin/console doctrine:migration:diff. By doing so, you end up with one migration file containing all the difference between master and your branch

I hope this helps. Cheers!

Reply
Dung L. Avatar
Dung L. Avatar Dung L. | MolloKhan | posted 3 years ago | edited

Hi MolloKhan , I understand your suggestion, its clean and straight forward. Unfortunately, my story is a bit different. I started out the project with database creation first! I built the database populate with data then create and connect an application to it. Now, I know I should have populated the db tables in schema using Doctrine:schema:update within Symfony.

So now I am well into the project, and trying to make:entity, then make:migration, then doctrine:migrations;migrate - this is where I am standing, i got me to this trouble.

I do not know if you can still give me advice to get me back on track so that I can migrate from Symfony's doctrine. But I will try to back up my database then drop/create and re-generate the database with my current Entities and doctrine annotation to see if that will reset me to a clean working and proper doctrine/entity migration capability.

I will update this thread after I try. Thanks Diego!

Reply
Dung L. Avatar
Dung L. Avatar Dung L. | Dung L. | posted 3 years ago | edited

Hello Dung L. , Good news! Thinking of your suggestion, I thought I could apply it in my circumstance, so i backed up code as well database just in case things go wrong. Then I create a new empty schema (no tables yet) pointed my application to the new schema, removed all Versions.php under Migrations directory, cleaned out cache, then php bin/console doctrine:schema:validate, sure it is not in sync ([ERROR] The database schema is not in sync with the current mapping file.) Now I ran "php bin/console make:entity --regenerate" then " php bin/console doctrine:schema:update --dump-sql" to review only, then "php bin/console doctrine:schema:update --force" then "php bin/console make:migration" then "php bin/console doctrine:migrations:migrate" --- Surprisingly things went with out error :). And now I have a clean and tight app entities and dbase. Gotta love Symfony and Doctrine. Thank you as usual!

The database tables were created, in mysql the keys, indices, and foreign keys were not named the same as I had before because they are auto-generated by doctrine in Symfony, but that s not issue rather nicety.

Reply

That's great! If you have differences in your table and column names, you can always specify their names manually. e.g.


/**
 * @ORM\Table(name="orders")
 */
class Order 
{
    /**
     * @ORM\Column(name="order_id", type="string")
     */
    private $orderId;
}
Reply
Dung L. Avatar
Dung L. Avatar Dung L. | MolloKhan | posted 3 years ago | edited

Thank you MolloKhan , got that!

1 Reply
Yaroslav Y. Avatar
Yaroslav Y. Avatar Yaroslav Y. | posted 4 years ago | edited

it very wrong to get image path straight from the Article entity (getImagePath()), because the entity is only about storing its DB-related data, and must <b>never</b> be aware in what folder the images are actually stored;

instead, you should've added a Twig filter, e.g.

new TwigFilter('image_path', [$this, 'getImagePath']),

with actual implementation like:

`public function getImagePath($value)
{

return 'images/' . $value;

}`

Reply

Hey Yaroslav Y.

You're right but it was done in this way for simplicity/teaching reasons. The bundle VichUploaderBundle is very good at it, if you ever have to upload files, you may want to give it a try

Cheers!

1 Reply
Steve-D Avatar
Steve-D Avatar Steve-D | posted 4 years ago

Hi Ryan

I've just come back to this video from the next in the series as I have an issue with the make:entity command. When I now use make:entity I am prompted for the name but after typing the name "Comment" it doesn't give me the option to add any fields, it just creates the class with the id field. I can't use the make:entity command again to add fields (as it describes here although it did work when doing this tutorial, I just get an error, [ERROR] The file "src/Entity/Comment.php" can't be generated because it already exists.

Am I missing something?

Cheers

Steve

Reply
Steve-D Avatar

I've just run composer:update and all is now good.

Reply

Hey Steve,

Glad you got it working yourself! It was an easy fix ;)

Cheers!

Reply

When I use src="{{ asset(article.imagePath) }}" it renders a beginning slash <img class="article-img" src="/images/asteroid.jpg"> that goes nowhere. I figured out that I mispelled jpeg and put jpg instead. Why did it put a slash when it could not find the file?

Reply

Hey Skylar!

Ah, great question :). So, it is less magic than you might be thinking. First, here is some FULL information about what the asset function does in Symfony: https://knpuniversity.com/s...

Now, here is the shorter explanation: the asset() function ALWAYS adds a slash at the beginning. It doesn't know/care if the file exists - it doesn't do any checks for that. It just adds the slash in all cases. Actually, one of the reasons it exists is that, if you deploy your application under a subdirectory - e.g. example.com/my-app, then it will prefix the path with /my-app/. So, it *always* adds either an opening slash or the app's subdirectory, automatically. If you check out the HTML of the working version, you should see a slash there too :).

I hope that helps! Cheers!

Reply
Petru L. Avatar
Petru L. Avatar Petru L. | posted 5 years ago

Hello,

Just a minor confusion... you said here, that we should give the property a value so we can have a default value for the column, but i found a topic on SO saying that @ORM\Column(type="bigint", options={"default": 1}) is a better practice . Do you have any thoughts on which one is better and why? Should i do both?

Reply

Hey Petru L.!

Ah, very good catch :). So, it depends. Personally, setting it in PHP is much more important to me: I want my PHP object to have the value that will eventually be stored in the database. To use your example, if you ONLY have the "default": 1, then your object might have a null value for this field in PHP, but suddenly it changes to 1 in the database. That's strange to me.

However, I think the options={"default": 1} can be a useful "extra" thing you can add... if you want to (I don't use this, but I can see why some people do). With this config (in addition to defaulting the value in PHP), you make your database a bit more flexible. For example, if you ever need to manually hack a new row manually via an INSERT query, this default value will be reflected. So, it's up to you - but I don't think options= is a better practice, it's just an extra practice, which can be good, depending on how much you care about the underlying database :).

Cheers!

1 Reply
Default user avatar
Default user avatar boedah | Petru L. | posted 5 years ago | edited

Hi Petru L.,

just wanted to add some thoughts:

* Doctrine FAQs recommend setting default values using properties: https://www.doctrine-projec...
* Doctrine will not go back to the database after persisting a new object to get the default values
* if you rely on your DB to set default values you cannot create easier, faster tests for your entities (without using the DB)
* there could be problems when default values for e.g. date types are different per DBMS (when your live DBMS is MySQL but your tests run on SQLite for example)
* double check on Doctrine Forms overwriting your default values

Because of these reasons we almost always stick to solely setting defaults in the entities themselves.

EDIT: as this is about adding fields to entities, you might have problems with existing rows for a new column which should be not null and but has no default value...

1 Reply
Default user avatar

"as this is about adding fields to entities, you might have problems with existing rows for a new column which should be not null and but has no default value"

Well, for me that's a HUGE problem.

Imagine when you have a project in production and need to add NOT NULL (and no default) field. You cannot just truncate database table. Doctrine migrations become somewhat unusable in this case.

And it's not something special or rare: these kind of fields are actually the most prevalent ones.

Unfortunately I cannot see any reasonable solution rather than manually edit migration files to enter proper default values.

16 Reply

Hey Ivan!

This is precisely what I do in these situations: if your database column is NOT NULL and has no default, then you absolutely need to add some custom code in your migration to handle it - I totally agree this is the correct approach. I usually: create the column but allow NULL, set value on that column for all rows with the next query, and finally use one more ALTER TABLE to make it NOT NULL. Doctrine migrations are great for structure changes - but it can't correctly determine how to handle data in these situations.

Cheers!

Reply

Great answer! :)

Reply
Cat in space

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

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.1.3",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.7", // 1.7.0
        "knplabs/knp-time-bundle": "^1.8", // 1.8.0
        "nexylan/slack-bundle": "^2.0,<2.2.0", // v2.0.0
        "php-http/guzzle6-adapter": "^1.1", // v1.1.1
        "sensio/framework-extra-bundle": "^5.1", // v5.1.4
        "stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
        "symfony/asset": "^4.0", // v4.0.4
        "symfony/console": "^4.0", // v4.0.14
        "symfony/flex": "^1.0", // v1.17.6
        "symfony/framework-bundle": "^4.0", // v4.0.14
        "symfony/lts": "^4@dev", // dev-master
        "symfony/orm-pack": "^1.0", // v1.0.6
        "symfony/twig-bundle": "^4.0", // v4.0.4
        "symfony/web-server-bundle": "^4.0", // v4.0.4
        "symfony/yaml": "^4.0" // v4.0.14
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.0", // 3.0.2
        "easycorp/easy-log-handler": "^1.0.2", // v1.0.4
        "fzaninotto/faker": "^1.7", // v1.7.1
        "symfony/debug-bundle": "^3.3|^4.0", // v4.0.4
        "symfony/dotenv": "^4.0", // v4.0.14
        "symfony/maker-bundle": "^1.0", // v1.4.0
        "symfony/monolog-bundle": "^3.0", // v3.1.2
        "symfony/phpunit-bridge": "^3.3|^4.0", // v4.0.4
        "symfony/profiler-pack": "^1.0", // v1.0.3
        "symfony/var-dumper": "^3.3|^4.0" // v4.0.4
    }
}
userVoice