PHP and Composer are ready to use on your local environment. For the purposes of this exercise PHP is installed locally and Composer is installed globally. Visit PHP installation and Composer installation for more.
For the initial commit we only run the commands to have a Symfony skeleton application and declare our dependency of Doctrine. For this introduction we are using the Doctrine ORM Pack.
# create project
composer create-project symfony/skeleton doctrine-intro
# add doctrine dependency
composer require symfony/orm-pack
composer require --dev symfony/maker-bundle
Additionally we are using the maker-bundle
from Symfony in order to use Doctrine's helper commands to generate
files/code. More on Symfony packs.
In this commit we will first set up our Doctrine settings. For the purposes of this introduction we will use SQLite. All
we have to do is change the DATABASE_URL
parameter in our .env
to sqlite:///%kernel.project_dir%/var/data.db
.
In order to test that you have a successful database connection run the following command:
bin/console doctrine:database:create
If everything is successful you should read Created database .../doctrine-intro/var/data.db for connection named default
.
Where ...
is your local directory location.
Now we can continue to create our entities. We will work with Publisher, Book, Author and Address. All of them will first have primitive property values. To create them we run the following command for each entity.
bin/console make:entity
Last we run the following command to synchronise our physical database schema with our application's schema.
bin/console doctrine:schema:create
You should see a warning telling you that this operation should not be executed in a production environment and a success
message stating [OK] Database schema created successfully!
Another way to keep your physical database schema with the application's schema is with another component from Doctrine included in the ORM pack named Doctrine Migration(s).
At this point, we have created our entities and their ORM mappings. Instead of using doctrine:schema:create
we run:
bin/console doctrine:migrations:diff
This command will add a MigrationVERSION class in the configured dir_name
of our doctrine_migrations.yaml
file. Since
this is the first time we are doing a diff against an empty schema, you will notice that Doctrine has added all table
creation queries. Go on and execute it:
bin/console doctrine:migrations:migrate
The end result is the same as the first command shown above. This way offers more control and lets you have surgical precision on your native SQL queries.
In this commit we are creating simple commands to showcase the main usage of persisting (INSERT
), finding (SELECT
)
and removing (DELETE
) entities from our storage/database.
- For Publisher, we have respectively: create, find and remove.
- For Book, we have respectively: create, find and remove.
- For Author, we have respectively: create, find and remove.
- For Address, we have respectively: create, find and remove.
In this commit we make use again of the make:entity
command multiple times to define relationships between our
Publisher, Book, Author and
Address entities. The relationship structure is shown in the table below:
Which? | Target | Relationship |
---|---|---|
Publisher | Address | OneToOne unidirectional |
Book | Publisher | ManyToOne unidirectional |
Book | Author | ManyToMany bidirectional |
Author | Address | OneToMany bidirectional |
At this point the physical schema is out of sync with our application. Let's use again the migration tool from Doctrine to fix that.
bin/console doctrine:migrations:diff
A new MigrationVERSION class was added automatically in our Migrations
directory. We run once more:
bin/console doctrine:migrations:migrate
The end result will not be as the last migration. Our entity relationships stop us from moving forward. This will be addressed at a latter commit, for now you can drop your database, create it, and run all the migrations. You can always check your migrations status with the following command:
bin/console doctrine:migrations:status
This is a useful command to use in case you are lost in your migration history. It is not mandatory to run it every time you execute a migration.
With new relationship definitions the old commands are outdated. In this commit, we update the following:
We also change slightly doctrine's default generated code.
- We make Publisher - Address a read only interaction.
- We change Book relationship fetching mechanism to better suit our needs.
In this commit we add an update command for the Publisher entity. The command offers us the possibility to immediately pick a specific publisher and update their data, or look up all publishers and then pick one.
To keep things simple we will not add update commands for the remaining entities. With this command we can dissect all SQL instructions that Doctrine is executing in the background for us. It is good exercise to add the remaining commands yourself if you would like to test yourself.
In this commit we introduce different types of Doctrine events. Lifecycle callbacks directly on the Publisher entity. We set up an Entity Listener for the Book entity so that we can "mail" changes as they happen. Lastly we also add a Doctrine event listener to persist history entries for our Book price changes.
We make use once more of the make:entity
command to create our PriceHistory entity.