Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Timestampable & Failed Migrations

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

Ok team: I've got one more mission for us: to add createdAt and updatedAt fields to our Question entity and make sure that these are automatically set whenever we create or update that entity. This functionality is called timestampable, and Doctrine Extensions totally has a feature for it.

Activating Timestampable

Start by activating it in the config file: stof_doctrine_extensions.yaml. Add timestampable: true.

... lines 1 - 2
stof_doctrine_extensions:
default_locale: en_US
orm:
default:
sluggable: true
timestampable: true

Back at the browser, click into the Doctrine Extensions docs and find the Timestampable page. Scroll down to the example... Ah, this works a lot like Sluggable: add createdAt and updatedAt fields, then put an annotation above each to tell the library to set them automatically.

The TimestampableEntity Trait

Easy! But oh, this library makes it even easier! It has a trait that holds the fields and annotations! Check it out: at the top of Question, add use TimestampableEntity.

... lines 1 - 7
use Gedmo\Timestampable\Traits\TimestampableEntity;
... lines 9 - 12
class Question
{
use TimestampableEntity;
... lines 16 - 134
}

That's it. Hold command or control and click to open that trait. How beautiful is this? It holds the two properties with the ORM annotations and the Timestampable annotations. It even has getter and setter methods. It's everything we need.

But since this does mean that we just added two new fields to our entity, we need a migration! At your terminal run:

symfony console make:migration

Then go check it out to make sure it doesn't contain any surprises. Yup! It looks good: it adds the two columns.

... lines 1 - 2
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
... lines 9 - 12
final class Version20200709153558 extends AbstractMigration
{
... lines 15 - 19
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE question ADD created_at DATETIME NOT NULL, ADD updated_at DATETIME NOT NULL');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE question DROP created_at, DROP updated_at');
}
}

Back at the terminal, run it with:

symfony console doctrine:migrations:migrate

And... yikes!

Invalid datetime format 0000 for column created_at at row one.

When Migrations Fail

The problem is that our database already has rows in the question table! And so, when we add a new datetime field that does not allow null, MySQL... kinda freaks out! How can we fix this?

There are two options depending on your situation. First, if you have not deployed your app to production yet, then you can reset your local database and start over. Why? Because when you eventually deploy, you will not have any questions in the database yet and so you will not have this error when the migration runs. I'll show you the commands to drop a database in a minute.

But if you have already deployed to production and your production database does have questions in it, then when you deploy, this will be a problem. To fix it, we need to be smart.

Let's see... what we need to do is first create the columns but make them optional in the database. Then, with a second query, we can set the created_at and updated_at of all the existing records to right now. And finally, once that's done, we can execute another alter table query to make the two columns required. That will make this migration safe.

Modifying a Migration

Ok! Let's get to work. Usually we don't need to modify a migration by hand, but this is one rare case when we do. Start by changing both columns to DEFAULT NULL.

... lines 1 - 12
final class Version20200709153558 extends AbstractMigration
{
... lines 15 - 19
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE question ADD created_at DATETIME DEFAULT NULL, ADD updated_at DATETIME DEFAULT NULL');
... line 24
}
... lines 26 - 31
}

Next call $this->addSql() with:

UPDATE question SET created_at = NOW(), updated_at = NOW()

... lines 1 - 12
final class Version20200709153558 extends AbstractMigration
{
... lines 15 - 19
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE question ADD created_at DATETIME DEFAULT NULL, ADD updated_at DATETIME DEFAULT NULL');
$this->addSql('UPDATE question SET created_at = NOW(), updated_at = NOW()');
}
... lines 26 - 31
}

Let's start here: we'll worry about making the columns required in another migration.

The big question now is... should we just run our migrations again? Not so fast. That might be safe - and would in this case - but you need to be careful. If a migration has multiple SQL statements and it fails, it's possible that part of the migration was executed successfully and part was not. This can leave us in a, sort of, invalid migration state.

symfony console doctrine:migrations:list

It would look like a migration was not executed, when in fact, maybe half of it actually was! Oh, and by the way, if you use something like PostgreSQL, which supports transactional DDL statements, then this is not a problem. In that case, if any part of the migration fails, all the changes are rolled back.

Safely Re-Testing the Migration

Anyways, let's play it extra safe by resetting our database back to its original state and then testing the new migration. Start by dropping the database completely by running:

symfony console doctrine:database:drop --force

Then doctrine:database:create to re-create it:

symfony console doctrine:database:create

Next, I'll temporarily comment out the new trait in Question. That will allow us to reload the fixtures using the old database structure - the one before the migration. I also need to do a little hack and take the .php off of the new migration file so that Doctrine won't see it. I'm doing this so that I can easily run all the migrations except for this one.

Let's do it:

symfony console doctrine:migrations:migrate

Excellent: we're back to the database structure before the new columns. Now load some data:

symfony console doctrine:fixtures:load

Beautiful. Back in our editor, undo those changes: put the .php back on the end of the migration filename. And, in Question, re-add the TimestampableEntity trait.

Now we can properly test the new version of the migration. Do it with:

symfony console doctrine:migrations:migrate

And this time... yes! It works perfectly. We can even run:

symfony console doctrine:query:sql 'SELECT * FROM question'

to see those beautiful new created_at and updated_at columns.

Making the Columns Required

The final thing we need to do is create another migration to make the two columns required in the database. And... we can just make Doctrine do this for us:

symfony console make:migration

Go check out the new file. Doctrine: you smartie! Doctrine noticed that the columns were not required in the database and generated the ALTER TABLE statement needed to fix that.

... lines 1 - 2
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
... lines 9 - 12
final class Version20200709155920 extends AbstractMigration
{
... lines 15 - 19
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE question CHANGE created_at created_at DATETIME NOT NULL, CHANGE updated_at updated_at DATETIME NOT NULL');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE question CHANGE created_at created_at DATETIME DEFAULT NULL, CHANGE updated_at updated_at DATETIME DEFAULT NULL');
}
}

Run the migrations one last time:

symfony console doctrine:migrations:migrate

And... got it! These are two perfectly safe migrations.

Okay, friends, we did it! We just unlocked huge potential in our app thanks to Doctrine. We know how to create entities, update entities, generate migrations, persist data, create dummy fixtures and more! The only big thing that we have not talked about yet is doctrine relations. That's an important enough topic that we'll save it for the next tutorial.

Until then start building and, if you have questions, thoughts, or want to show us what you're building - whether that's a Symfony app or an extravagant Lego creation, we're here for you down in the comments.

Alright friends, seeya next time.

Leave a comment!

43
Login or Register to join the conversation
Steve-D Avatar

Hi

How do we deal with daylight savings time? All my "created at" and "updated at" entries are using UTC.

Thank you

Steve

Reply
Steve-D Avatar

Smack me with a wet fish... php.ini timezone setting.

Reply

Hey Steve, yep, PHP gets the date from the server, and you can configure the timezone as a PHP setting or at the moment of creating your date objects.

Cheers!

Reply

could not add created at and updated at due to i didn't install stoff composer, tryed all the solutions in comments but with no luck,
if i didn't add ctreatedat and updated fields to the table will that be a problem in the next videos ?

Reply

Hey SouFiane,

What exact error do you see? We could try to help you install that bundle. It should be possible in case you download the course code and started from the start/ directory. But in general, you can miss those fields and it would be be OK... but you would need to keep it in mind and do not use those fields in migrations, DB queries, etc. You can use different fields you have on that entity instead, it's fine for learning purposes ;)

Let us know the exact error you see in case you want to make it installed into your project.

Cheers!

Reply
Kevin Avatar

The big question now is... should we just run our migrations again? Not so fast. That might be safe - and would in this case - but you need to be careful. If a migration has multiple SQL statements and it fails, it's possible that part of the migration was executed successfully and part was not. This can leave us in a, sort of, invalid migration state.

A bit confused about this particular part and the steps afterwards. What would you have to do if it was already on production with many rows of data? The next step says to be safe and reset the db with `doctrine:database:drop --force` but what if this was production?

Reply

Hey Kevin!

Yea, this is tricky. What I do locally to test this migration is not what we would on production. Let me elaborate:

LOCALLY:

We are in a weird state because we have a half-executed migration. We fix it... but then we're not totally sure that it's working until we properly test it. So... we do all the "dropping the database" and re-adding the fixtures in order to get our database back into the "state" that it was BEFORE we ran that migration: a state that has the old database structure AND rows of data in it. Then we can re-try the migration.

PRODUCTION

But on production, we don't have this problem. Imagine this workflow:

A) We write some code, we deploy, we run some migrations. Everything is happy, and our "question" table DOES have real data on it.
B) Locally, we add the new created_at column to Question. We generate a migration, run it and it explodes! We are now in the situation described in this chapter. But, we go through the process, fix it, and test it.
C) THEN, we finally deploy our "already fixed" migration. This migration now runs just fine on production because the broken migration was never executed there.

So, tl;dr is that we "jump through hoops" locally to ensure that our migration IS fixed. But ultimately, when we deploy, we do nothing different than normal: we just deploy. When we do, this migration (which is now fixed) will execute just one time, successfully.

Let me know if that clarifies :).

Cheers!

Reply
davidmintz Avatar

If this were a production scenario, and you decided to start keeping this metadata in your questions table -- created_at, updated_at -- after some rows had already been created without it, you couldn't just set those fields to NOW(). Well, you could, but it wouldn't be accurate. Am I right? Theoretically, if you had complete logs somewhere -- web server logs, perhaps -- you could undertake the chore of parsing those and trying to reconstruct... it sounds painful.

Maybe I am overthinking this. It's just an example for the sake of the tutorial, right?

Reply

Hey davidmintz!

> you couldn't just set those fields to NOW(). Well, you could, but it wouldn't be accurate. Am I right? Theoretically, if you had complete logs somewhere -- web server logs, perhaps -- you could undertake the chore of parsing those and trying to reconstruct... it sounds painful.

You're absolutely right :). In my experience, this has been so painful (and not SO important) that we've never bothered :). But of course, there could definitely be mission-critical situations where this WOULD be important, and then you'd have some work to do! However, my instinct is that if you have some records where the created_at and updated_at *must* be 100% accurate, then you probably (not 100%, but probably) would have remembered to include them when you first created your entity.

> Maybe I am overthinking this. It's just an example for the sake of the tutorial, right?

So overall, this isn't just an example for the tutorial: this is how I would do things in most cases. If I DID need to dig through some logs to set the real date, i'd probably allow the field to be nullable and deploy. Then I would build a custom console command I could run that would look for null dates, dig for their real date (however you're doing that), then populate those fields. Once that's done, i'd deploy the "not null" change.

Cheers!

Reply
davidmintz Avatar

yeah that makes sense.

Reply
Kevin Avatar

Oh I see!! I think I get it now!

So because we created extra migrations on local to fix the db issues we would only have to deploy/run it on production and the columns will be added without any problems and will also have the default values that were set in the second query.

Reply
ssi-anik Avatar
ssi-anik Avatar ssi-anik | posted 2 years ago

Unlike Laravel migrations, symfony migrations are generated for specific database like pgsql or mysql. isn't it? So, once you generate the migration for postgres and changing the database later at some point, the migrations won't work. Am I wrong?

Reply

Hey Syed,

I'm not sure how it works in Laravel, but yes, you're right, Doctrine generates database-specific migrations. And yes, if you generate it for PgSQL, you won't be able to run it for MySQL... and the vice versa. Why? Well, first of all, because different DB should have different queries, i.e. MySQL query won't work 100% on PgSQL and the reverse, right? So, it's made this way for you, to avoid unexpected problems and prevent losing data.

In case you want to migrate to a different DB at some point, you should do it manually, and then you don't need all those old migrations actually, you can just drop them and start creating new migrations if needed, but already for the new DB. In other words, having some migrations until today that will apply to MySQL and some of them since today that will apply to PgSQL sounds very weird :)

I hope this helps!

Cheers!

1 Reply
ssi-anik Avatar

Your words are crystal clear to me.
And regarding how Laravel does the migration. It uses the environment variable to pick the default database/connection you want to run the migrations. Builds queries on the fly. Runs them against the default db connection. And also, you can specify the db connection in the migration files to use any specific db if you don't want to apply the migrations to the default database.

Reply

Hey Syed,

Yeah, then the Laravel way sounds similar to what Doctrine migrations bundle does :)

Cheers!

Reply
ssi-anik Avatar
ssi-anik Avatar ssi-anik | Victor | posted 1 year ago | edited

Hi victor
I guess the doctrine migrations are different.

In Laravel, they write classes for migrations, and when you run migrations, then those classes are compiled to SQL Queries, and then runs against the currently defined database connection. It can be pgsql or mysql or anything when they are running.

An example: https://github.com/ssi-anik...

On the other hand, the symfony migrations are SQL statements already prepared for specific database when you're generating. So, if you change the connection later from MySQL to PostgreSQL, will they run correctly for that database?
Here is an example in my case, I might be wrong.
`$this->addSql('CREATE UNIQUE INDEX UNIQ_B6F7494E989D9B62 ON question (slug)');`

Thanks.

Reply

Hey Syed,

Fairly speaking, I'm not aware how Laravel migrations work, but if they work like you described - you're right, they work different from Doctrine migrations. And yes, you're right, Doctrine migrations create raw SQL queries at the moment you generate a migration, and then the system will execute those queries when you run the migrate command. From this, yes, if you generate the migration for MySQL - you won't be able to run this migration on other SQL platforms, i.e. Doctrine migrations are platform-specific. And it makes sense, because migrations are supposed to help with changing production DB without loosing data, not changing the DB provider, etc.

I hope this helps!

Cheers!

1 Reply
ssi-anik Avatar

Thanks man. <3

Reply
Sherri Avatar

I'm thinking that we can now use the 'createdAt' field in place of the askedAt field.

Reply

Hey Sherri

You're right, in this case, it would render basically the same date.

Cheers!

Reply
Sherri Avatar
Sherri Avatar Sherri | posted 2 years ago | edited

Alternate approach, you can set a default value in the migration for MySQL version 5.6.5+:

$this->addSql('ALTER TABLE question ADD created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, ADD updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP');

Reply

Hey Sherri,

Thanks for sharing this tip with others. Hm, fairly speaking I'm not sure you will have the valid mapping after this migration, you can check if Doctrine is happy with this running:
$ bin/console doctrine:schema:validate

The problem is that in your example you change the schema, i.e. you will have a slightly different schema than we have in the video, and Doctrine may not like this. In our example we use the schema that is generated by Doctrine, i.e. Doctrine is happy about that schema, and then we just modify the data in that table, no schema is affected.

Cheers!

Reply
Sherri Avatar

So if I wanted the default value set in the table schema (for example if there are other applications accessing the table and I want to force the default value)... would I have to copy the trait into my entity and set the default values on the entity properties too?

Reply
Sherri Avatar
Sherri Avatar Sherri | Sherri | posted 2 years ago | edited

Answered my own Question. To keep the entity and migration in sync.... just override the properties of the trait in your entity and add the default value to the annotations:


...
    use TimestampableEntity;

    /**
     * @var \DateTime
     * @Gedmo\Timestampable(on="create")
     * @ORM\Column(type="datetime", options={"default": "CURRENT_TIMESTAMP"})
     */
    protected $createdAt;

    /**
     * @var \DateTime
     * @Gedmo\Timestampable(on="update")
     * @ORM\Column(type="datetime", options={"default": "CURRENT_TIMESTAMP"})
     */
    protected $updatedAt;
...

This gives you a valid mapping and schema when running php bin/console doctrine:schema:validate.

This is useful <b>if</b> you want SQL to also enforce the default values.

Reply

Hey Sherri,

Thanks for sharing your solution with others! Yes, you have to reflect any changes to the schema in the entity's mapping, i.e. in PHP annotations in your case, so you did it correct :)

Cheers!

Reply
Antoine R. Avatar
Antoine R. Avatar Antoine R. | posted 2 years ago

Thanks for the three tutorials about fundamentals ! It was very nice to build the code with you from scratch. From now, what topic could I follow that starts with more or less the same code than this one ?

Reply

Hey Antoine R.!

Excellent! I'm so glad you found them useful! From here, unfortunately, we don't (yet) continue *this* exact code in any tutorial. I've gotten a bit delayed (my fault... and the fault of other tutorials) in releasing other Symfony 5 tutorials. However, though they use a different project, the Symfony 4 versions of the next few tutorials - are still quite good and relevant. They are:

* Doctrine relations: https://symfonycasts.com/sc...
* Security: https://symfonycasts.com/sc...
* Forms https://symfonycasts.com/sc...

And from there, the topics get a bit more specific depending on what you're looking for. Sorry I can't get you a better answer about tutorials with THIS exact code, but I hope these will serve you well :).

Cheers!

Reply
Balázs S. Avatar
Balázs S. Avatar Balázs S. | posted 2 years ago

Quick question: after adding the TimestampableEntity trait, I checked the generated migration file, and noticed, it actually didn't add the default current_timestamp and on update current_timestamp definitions that are set in the annotations of its two properties. I checked Ryan's video again, they're not there either. So how is it going to work? Is there some mechanism behind Symfony's curtains that will take care of this? Why is it better than defining the columns in MySQL and let it handle these things? I have my doubts. :-P

Reply

Hey Balázs S.

IIRC this timestampable works via listeners defined in bundle, that's why it's not necessary to configure MySQL structure for it. Honestly I can't say why it's done so, my guess is that you have more control on DateTime objects and timezones.

Cheers!

1 Reply
Balázs S. Avatar

Thanks, Vladimir. Well, I don't like it too much because it assumes that only our beloved Symfony app will access and write into the database. If data could come from different sources, and those other sources are not prepared to handle timestamps themselves, it will lead to inconsistencies in the database. I'm not saying that it could not happen if you leave this to MySQL to handle, it certainly could, but you have to be really determined to screw this up. So went back to manually edit migrations and addig ON UPDATE CURRENT_TIMESTAMP() into certain field declarations.

Love you guys. Cheers.

Reply

I guess your schema is not synced with this manual changes, and make:migration will always generate query to remove this configuration. Also you should configure correct timezones for mysql and php to be sure that you will always have correct dates =)

Cheers!

Reply

Ho Rayan! Thank you for this wonderful tutorial! I have question why you use always sprintf rather than the old echo?
Thank you!

Reply

Hey @Houssem Zitoun!

> Thank you for this wonderful tutorial

Really happy you enjoyed it!

> have question why you use always sprintf rather than the old echo?

The true answer is that sprintf() is used a lot in Symfony's code base, and I learned to use it from there ;). But in general, it's definitely better than concatenating strings and variables. And, I'm not sure if there is a good reason why, but you don't see "Double quotes with $variables inside" very often in the Symfony world. But overall, it doesn't really matter - do what you like - no *real* advantage/disadvantage between these options :).

Cheers!

Reply
Mohammed Avatar
Mohammed Avatar Mohammed | posted 2 years ago | edited

Thank you so much for yet another fantastic tutorial! So glad I came across this website!

I just have a question. How can I save my database?
I'm so new to Docker, I tried to commit my container into a new image to run it whenever I need my data but it didn't work.
I googled for a solution and I did find this:
`
# Backup
docker exec CONTAINER /usr/bin/mysqldump -u root --password=root DATABASE > backup.sql

# Restore
cat backup.sql | docker exec -i CONTAINER /usr/bin/mysql -u root --password=root DATABASE
`

It works great, but I just want to know if there's a better solution.

Thank you again for your amazing work.

Reply

Hey Mohammed!

Yes, this is an excellent question - and when we designed make:docker:databse, we thought about "should we make the data persistence"? So the answer is "volumes" - it's basically a way where you can make a directory from inside the container (the directory that actually holds the MySQL data) be shared onto your local file system. The result is that, when you shutdown and even delete your container, that directory is still there in your project. Then when you start your container again, that directory will be inside the container.

I have an example here; https://github.com/weaverryan/js-workshop-qro/blob/3b0111abb6425df36ab17d4cf9753e706df6c36d/docker-compose.yaml#L12 - the docker/database/data directory is the local directory (in your project) where you will see the directory. The other path is the directory in the container.

Let me know if this helps! I'm also not a Docker expert - but I've really been enjoying this new way of using it :).

Cheers!

Reply
n0 Avatar

Please make the next courses about events and how to persist vars with sessions or pass them to different controllers.

Reply
MolloKhan Avatar MolloKhan | SFCASTS | n0 | posted 2 years ago | edited

Hey n0

Thanks for your feedback. Could you detail a bit more what you picture about working with events? Do you mean Symfony events, right?
Working with the session variables in Symfony is quite simple, there is a service that allows you to set/get variables from the session, I recommend you to read this docs to get a better idea https://symfony.com/doc/cur...
By the way, controllers should not communicate between each other. They should work independently

Cheers!

Reply

Just for reference, I talked to MolloKhan on a different thread and (actually) he solved his own issue. The answer was basically storing things in the session or using Doctrine cache (to avoid queries on each page) :)

Reply
Tomáš K. Avatar
Tomáš K. Avatar Tomáš K. | posted 2 years ago

Hi, thanks for a great course, now I am yet more thrilled about Symfony! Still, I have one question: should I wait for the Symfony 5 Doctrine Relations tutorial (if you are planning one) or start with the one for Symfony 4? Are there many differences regarding these two versions?

Reply

Hey Tomik,

Unfortunately, no any plans to release it soon, so probably if you watched the Doctrine tutorial for Symfony 5 course: https://symfonycasts.com/sc... - I'd recommend you to go with the old Doctrine relations tutorial. Nothing much is changed in it, and what exactly changed to Symfony 5 and Doctrine were covered in the https://symfonycasts.com/sc... so you should be aware of it I think. Just do things in Symfony 5 ways and it should be enough.

And as always, if you stuck on something following the tutorial on Symfony 5 - just let us know in comments and we will help you to figure out the problem. And if you discover a BC break - we will even add a note to the tutorial that would help other people to follow it on Symfony 5.

Cheers!

1 Reply
Diaconescu Avatar
Diaconescu Avatar Diaconescu | posted 2 years ago | edited

Please weaverryan, I need help.
I try to work with redux and react router dom. I have two projects api part at https://github.com/petre-symfony/api-part-biblioteca and redux part at https://github.com/petre-symfony/redux-part-biblioteca-. In api part you have a folder named Dump20200830. There are SQL dumps made with MysqlWorkbenck.
Run


./bin/console doctrine:database:create && ./bin/console doctrine:migrations:migrate && ./bin/console doctrine:database:import  Dump20200830/books_author.sql &&  ./bin/console doctrine:database:import  Dump20200830/books_publisher.sql && ./bin/console doctrine:database:import  Dump20200830/books_category.sql && ./bin/console doctrine:database:import  Dump20200830/books_sub_category.sql &&  ./bin/console doctrine:database:import  Dump20200830/books_book.sql && ./bin/console doctrine:database:import  books_book_author.sql && ./bin/console doctrine:database:import  Dump20200830/books_book_publisher.sql

and you should have around 2200 books there. They are my parent's books.
I created a interface in react and redux side to display these books and their details. In api side I created the posibility to look them by author.

If you write in adress bar localhost:3000 you'll see under authors paragraph some links. If you hover over them you'll see something like localhost:3000/1/author=whateverAuthor and click on them suppose to list only the books written by this respective author. When I write localhost:3000 all books are listed with pagination. The second task works. First task is built on the skeleton of the second and uses the same action creator. And doesn't work by intended design. Somehow I see the Ajax Request over the network that returns the correct response. So the books property should exist in redux store. But in this second case it gets null when render the BookListContainer component. It's peculiar because seems to follow the same flow as localhost:3000 follows. In localhost:3000 is no problem: books property,returned by bookListReducer state, contains the list of books that's displayed at the time of rendering the component. But when I put the author part I see the book in bookListReducer state , that contains the correct response (I checked that), but dissapears at the time of rendering BookListContainer. I don't know why and where to modify to make this work. I put some console.logs in the code to see this.
I tried to be as explicit as I can in my commits.

Thank you for your effort.

Reply

Hi Diaconescu!

Sorry for my slow reply! Ok, let's see what we can figure out! First, while I know React pretty well, I do *not* know Redux. So if this is more of a question about Redux... I don't think I'm going to be able to help. If this is a question more about Symfony or ApiPlatform, I might be able to. But right now, it looks like most of the problem is inside Redux somewhere? Of so, like I said, I don't think I'll be much help :/. Let me know.

Cheers!

Reply
Diaconescu Avatar

Ok, I really think that the problem is in Redux. Thank you anyway. I'll look somewhere else for help

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