From no framework to Symfony, Doctrine and dealing with migrations

 When I first started using Symfony, I had already developed a social media app (github repository) through a Udemy Course and while it made me get into the practice of PHP writing, I wondered quite a few things:

1. How am I supposed to remember this file structure in ten months time? Is there any kind of universal criteria that we are not following?

2. How the hell do developers find a bug in a file with 400 lines of PHP + HTML 

3. How am I supposed to see the flow of the code?

Obviously, as this is a guided programming course, questions were solved through subsequent videos or, through the course forum, but I still could not picture myself solving those bugs on my own. Moreover, as you will be able to see if you clicked on the github link (Developement branch) above mentioned, the mixture of languages along the files made me think that in order to be a developer you need to get a scanning app installed in your brain which would allow you to boost your reading in between the lines skills. 

Summarizing, I finished my first app more discouraged than happy wondering whether I would be able to develop an app all by myself.

The first lesson I learned through developing my following project was that tools do make a difference. I'm not saying that any of the tools you are using are good or bad in essence. I do not recommend PHP Storm as your first IDE. It has plenty of possibilities, it will help you developing code through intuitive code suggestions and shortcuts, but if you are just starting, Sublime Text is a more suitable option probably, as it has way less features and it will allow you focusing on what is important in the first stages: writing code which works. 

My second lesson was a little bit more annoying and boring: you need to learn how to use GIT. And if you are not to make the same mistakes I made, don't just learn the basics. Try to understand the consecuences of Commiting, Pushing and Pulling. Of course, everything can be undone, but sometimes it can make you lose a whole day trying to fix your mistakes. 

As I entered the wonderful world of frameworks, I was amazed. Symfony was the most wonderful thing ever invented by the human being. Just one command and my register form was ready and working. And the same goes for login feature or the User entity. I felt like a mechanic using a screwdriver for the first time after having used his own hands for months. Doctrine was also a big advance:

php bin/console doctrine:migrations:generate

After going to migrations folder in your app you will find something simmilar to:


VoilĂ ! You have your first entity ready with all of its getters and setters. 


php bin/console doctrine:migrations:diff

And if you have set up your Database correctly, you have a beautiful table representing your entity in your Database. Not only that! A migration file is created, which contains the necessary information to recreate all structural changes in your databasea, so if you execute this migrations on a new Database, you will get all the tables from scratch. It's a snap! right? 
My advice is, again, don't get to excited as any new tool, feature and app that you use will in the long run have a downside. 
It took me a while to understand how migrations worked. As I have said in previous posts, I cannot emphasize enough the need to write a glossary before naming any entity or variable. That doesn't mean you should be "planning ahead" all of your vocabulary, but you need to name all of the realities (modules) that you want to abstract in your code. Otherwise your might find yourself going to PhpMyAdmin and changing the name of a table, or an attribute, and, believe me, you will wish you could just turn back in time to revert that action. Again, everything is fixable, but most of the time painful, so let me explain you a little bit how migrations work so that you won't have to cry later on.

I like to think about migrations as photos of all of the stages in a picture. Imagine you are painting a face:  
  • First you draw the outline, the ears and the neck and take a picture of that day progress (that is, you migrate your entity to your database schema)
  • Second day you add the "attributes" of eyes, nose and lips and take a beautiful photo of your progress
  • On the third day you realize you didn't add the brows and the lashes, so you add them without taking a picture of that step (so that change is not migrated to your database schema)
  • On the fourth day you decide to add a curlier shape to those lashes and make your brows bolder, and at the end of the day, you take the corresponding picture (that is, you migrate those changes to the database schema) 
Can you guess why the migration is going to fail? Exactly! You're a genius, you are telling your database schema to modify an attribute which doesn't exist. So, always bear in mind that order does matter when making migrations. In general terms, you will avoid this mistake if you just use the following command anytime there's changes to your mapping information (changes in your entities):


php bin/console doctrine:migrations:diff

This will create a migration contaning all of the necessary changes to your database schema, that is comparing your current database to your mapping information, and after that, you will only need to execute:

php bin/console doctrine:migrations:migrate

And you will have your database updated.

Another powerful command you can use is:


php bin/console doctrine:schema:update --force

Which will compare how your database should look like based on the information on the mapping information of your entities. Nevertheless this feature shall not be used in a production environment as it does not create a migration but is a mere update. 

Two important things you need to bear in mind when making migrations:
  1. Data are not migrated with all of the commands we have been mentioning. So how do you populate your database? First we need to distinguish between two cases:
  • Cases in which interaction of the user will cause data to be persisted to the database, for example a registration feature. In that case the registration web service will take care of that everytime a user registers, so your app can work with an empty users database.
  • Cases in which the app should start with a certain amount of data already persisted into the database in order to work. Think of a e-learning app where there's two kinds of users: administrators with access to creating lessons and exercises and students with access only to take the course. You will need the course already in the database if you are going to ask others to test your e-learning app, right? In that case, you will need to migrate your data by generating an empty migration first:


php bin/console doctrine:migrations:generate

Then, if you go to migrations folder you will find something very simmilar to this:

<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
use Egulias\EmailValidator\Warning\ObsoleteDTEXT;

/**
 * Auto-generated Migration: Please modify to your needs!
 */
final class Version20210623192926 extends AbstractMigration
{
    public function getDescription() : string
    {
        return 'Here you can add a title to your migration';
    }

    public function up(Schema $schema) : void
    {
        $sqlKatasUpdated1 = <<<'EOD'
        UPDATE `user` SET `name` = 'Laura' WHERE (`id` = '1');")
EOD;

        $this->addSql($sqlKatasUpdated1);


    }

    public function down(Schema $schema) : void
    {
        // this down() migration is auto-generated, please modify it to your needs

    }
}

As you can see, I have already populated this migration for you to see. The SQL quote is stored in a varaible using the heredoc syntax, and then we use the addSql() method to persist that to the database. getDescription() method is useful in order to always remember what that migration does. 

A good way of getting all of those SQL quotes is exporting your database and copying the quotes from the exported file after you have populated your database with data. This can be done either through the command line, which is painfully boring, through an app like PhpMyAdmin, which I don't really recommend, or creating a saving data feature in your app which allows you to create your course as an admin, which is what I would do. 

So, summarizing the most important thing you need to remember from all of this post is. When do we manually modify our database schema? Never, under no circumstances, not even if your life depends on that. God kills a kitten everytime you do so. Instead, you change the mapping information in your entity and update your database from the command line.

I know... today's post was a little bit long, but I hope it has cleared a few concepts for some of you. 

Also, I learn everyday and every sinlge day I discover new ways to code and develop apps, so if you find a mistake in all of this, so long as you approach it in a friendly way, I will always appreciate advices and suggestions as well as corrections. This is intended to be a collection of things I have learned in the process of self-learning how to code, in case it is useful for someone and so that I don't forget all of the things learned. 

I will be talking more in depth about pros and cons of frameworks and how I changed my mind as I continued learning and programming.

Comments

Popular posts from this blog

SOLID: The Single-Responsibility Principle (SRP)

Symfony HTTP client

Inheritance: Abstract classes and interfaces