Welcome to our Symfony course. In this lesson, we will be adding an important feature to your Symfony application: database integration using Doctrine ORM.
Persistent data storage is crucial for modern web applications. Integrating a database into your Symfony project will allow you to store, retrieve, and manage data, making your app more dynamic.
By the end of this lesson, you will have set up Doctrine and connected it to a SQLite database in your Symfony project. Let’s get started!
Before diving into the implementation, let's understand Doctrine and ORM (Object-Relational Mapping).
Doctrine is a powerful ORM library that maps PHP classes to database tables. It simplifies database interactions, allowing you to work with PHP objects instead of writing SQL queries. This keeps your code cleaner and easier to maintain.
ORM is a technique that helps manage database operations like inserting, updating, deleting, and querying data using the object-oriented structure of PHP. By using ORM, you avoid raw SQL and focus on handling data as PHP objects.
Before setting up a database in Symfony, ensure the following requirements are met:
- PHP and SQLite Extension: Make sure your PHP version supports SQLite. For this lesson, we're using PHP 8.1. You can install the
php8.1-sqlite3
package to add SQLite support:
Bash1sudo apt-get install php8.1-sqlite3
- Symfony ORM Pack: You need to install the
symfony/orm-pack
package, which includes all the necessary dependencies for Doctrine ORM:
Bash1composer require symfony/orm-pack
Once these are installed, you’re ready to proceed.
Now that the required packages are installed, we need to configure Doctrine to connect to an SQLite database. The configuration file is located at config/packages/doctrine.yaml
.
Here’s a basic configuration to connect Symfony to an SQLite database:
YAML1doctrine: 2 dbal: 3 url: '%env(resolve:DATABASE_URL)%' 4 # IMPORTANT: You MUST configure your server version, 5 # either here or in the DATABASE_URL env var (see .env file) 6 #server_version: '13' 7 orm: 8 auto_generate_proxy_classes: true 9 naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware 10 auto_mapping: true 11 mappings: 12 App: 13 is_bundle: false 14 type: annotation 15 dir: '%kernel.project_dir%/src/Entity' 16 prefix: 'App\Entity' 17 alias: App
This file configures Doctrine, allowing it to interact with an SQLite database. Here’s what it does:
- DBAL: The Database Abstraction Layer (DBAL) tells Symfony which database driver to use. Here, it's SQLite, and the URL is resolved from an environment variable.
- ORM: Object-Relational Mapping configuration. It tells Doctrine to automatically map PHP classes (entities) to database tables and where to find these entity classes.
- auto_generate_proxy_classes: Enables automatic generation of proxy classes, useful for development environments where you frequently change entity classes.
- naming_strategy: Defines the strategy Doctrine uses to create database table and column names from entity and field names, keeping them consistent and understandable.
- auto_mapping: Automatically maps all entity directories found within your app, simplifying setup in projects using default conventions.
- mappings: Specifies where Doctrine should look for entity classes, defining the connection between your PHP code and the database schema.
With this, Doctrine can now interact with your SQLite database.
Next, we will create an entity representing the Todo
task we want to store in the database.
php1<?php 2 3namespace App\Entity; 4 5use Doctrine\ORM\Mapping as ORM; 6 7/** 8 * @ORM\Entity() 9 */ 10class ToDo 11{ 12 /** 13 * @ORM\Id 14 * @ORM\GeneratedValue 15 * @ORM\Column(type="integer") 16 */ 17 private $id; 18 19 /** 20 * @ORM\Column(type="string", length=100) 21 */ 22 private $title; 23 24 /** 25 * @ORM\Column(type="text", nullable=true) 26 */ 27 private $description; 28 29 /** 30 * @ORM\Column(type="string", nullable=true) 31 */ 32 private $file; 33 34 // Getters and setters... 35}
The above code demonstrates a simple entity class named Todo.
@ORM\Entity
: Marks this class as an entity, linked to a database table.- Fields:
id
: Primary key with auto-increment.title
: A string column with a maximum length of 100 characters.description
: A text column that can be left empty.file
: An optional string column.
This structure allows Doctrine to map this PHP object to a corresponding database table automatically.
The repository class allows us to manage database operations for the Todo
entity. Here’s how we can create it:
php1<?php 2 3namespace App\Repository; 4 5use App\Entity\ToDo; 6use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; 7use Doctrine\Persistence\ManagerRegistry; 8 9class ToDoRepository extends ServiceEntityRepository 10{ 11 public function __construct(ManagerRegistry $registry) 12 { 13 parent::__construct($registry, ToDo::class); 14 } 15}
By creating a ToDoRepository
, we encapsulate all the database operations related to the Todo
entity in a single, reusable location. This repository will handle interactions with the todos
table, such as fetching, saving, and deleting Todo
entities. It extends ServiceEntityRepository
, which provides convenient methods for these common database operations. The ManagerRegistry
interface ensures our repository is correctly linked to the ToDo
entity and its associated database table. This approach keeps our code organized and makes it easier to manage Todo
data efficiently.
With our Todo
entity and TodoRepository
effectively set up, we now need to ensure our Symfony application's entry point is configured to utilize Doctrine and manage our database schema. The index.php
file in the public
directory is the entry point of your application. It is responsible for starting your application, by setting up the Kernel and handling HTTP requests and responses.
Here's how to set it up:
php1<?php 2 3use App\Kernel; 4use Doctrine\ORM\Tools\SchemaTool; 5 6require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; 7 8return function (array $context) { 9 $kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); 10 $kernel->boot(); 11 12 // Get the Entity Manager from the container 13 $entityManager = $kernel->getContainer()->get('doctrine.orm.entity_manager'); 14 15 // Initialize Doctrine's SchemaTool to manage the database schema 16 $schemaTool = new SchemaTool($entityManager); 17 18 // Retrieve metadata for all entities 19 $metadata = $entityManager->getMetadataFactory()->getAllMetadata(); 20 21 if (!empty($metadata)) { 22 // Update the schema to reflect the current entity metadata 23 $schemaTool->updateSchema($metadata, true); 24 } 25 26 return $kernel; 27};
In this merged setup, we initialize the Symfony Kernel, retrieve the Doctrine Entity Manager, and use SchemaTool to manage the database schema. This ensures that the necessary tables are created or updated according to the entity metadata.
Once you set up your Todo
entity and configure Doctrine, it will automatically create the necessary tables in your SQLite database. Here is an example of what you might see when you inspect the database:
Plain text1Tables found in the database: 2- todos 3- sqlite_sequence 4 5Schema for table 'todos': 6 - id (INTEGER) 7 - title (VARCHAR(255)) 8 - description (CLOB) 9 10Schema for table 'sqlite_sequence': 11 - name () 12 - seq ()
The todos table corresponds to our Todo
entity. It contains the following columns:
- id: An integer that automatically increments for each new
Todo
. - title: A string with a maximum length of 100 characters.
- description: A text field that can hold longer entries or be left blank.
- file: An optional string field.
The sqlite_sequence table is automatically created by SQLite. It keeps track of the auto-incremented values for tables with such columns.
With this setup, both the todos
table and the internal sqlite_sequence
table are created and managed efficiently, enabling smooth database operations for your Todo
entity.
Now it's your turn! Head over to the practice exercises and configure the Doctrine settings in your Symfony project. This hands-on practice will reinforce your understanding and give you the confidence to set up database connectivity in any Symfony application.