The standard edition of Symfony PHP framework comes integrated with Doctrine, an object-relational mapper (ORM) and a database abstraction layer (DBAL) which makes working with databases very easy. This tutorial will focus on using Doctrine in Symfony — how to create models of database tables and its relations, how to query the data, and more.
Creating and Configuring the Database
The Doctrine configuration can be found as a part of the larger Symfony configuration file in app/config/config.yml and would look like this:
# Doctrine Configuration
doctrine:
dbal:
driver: pdo_mysql
host: "%database_host%"
port: "%database_port%"
dbname: "%database_name%"
user: "%database_user%"
password: "%database_password%"
charset: utf8mb4
collate: utf8mb4_unicode_ci
As Doctrine is a layer on top of PDO, it is possible to use any database management system that is supported by PDO (such as MySQL, PostgreSQL, Microsoft SQL, MongoDB and MariaDB). It is also recommended to set the default database character set and collation to newer utf8mb4. Unlike older utf8 charset, it supports 4-byte Unicode characters. The database host, port, name, username and password are usually set during the composer install, but you can also edit them in app/config/parameters.yml.
Creating Entity Classes
Each entity (database table) should be represented as a class and located in the AppBundle/Entity directory:
// src/AppBundle/Entity/User.php
namespace AppBundleEntity;
use DoctrineORMMapping as ORM;
class User
{
private $id;
private $name;
private $email;
private $password;
private $createdAt;
}
This class would represent the database table user with columns id, name, email, password and created_at. To provide Doctrine with information about the column types and relations, we will use annotations:
/**
* @ORMEntity
* @ORMTable(name="user")
*/
class User
{
/**
* @ORMId
* @ORMColumn(type="integer")
* @ORMGeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $email;
/**
* @var string
*/
private $password;
/**
* @ORMColumn(type="datetime")
*/
private $createdAt;
/**
* @ORMManyToOne(targetEntity="Group", inversedBy="users")
* @ORMJoinColumn(name="group_id", referencedColumnName="id")
*/
private $group;
}
The table name annotation is optional and, if omitted, the Doctrine will automatically generate it based on the class name. In the code above, only some field types are shown. To read more about them, check the Doctrine mapping reference.
Now, let’s create a table that will describe user groups, where each user could be in only one group:
// src/AppBundle/Entity/Group.php
/**
* @ORMEntity
* @ORMTable(name="group")
*/
class Group
{
/**
* @ORMId
* @ORMColumn(type="integer")
* @ORMGeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*/
private $name;
/**
* @ORMColumn(nullable=true)
* @ORMColumn(type="text")
*/
private $description;
/**
* @ORMOneToMany(targetEntity="User", mappedBy="group")
*/
private $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
}
Note that relations between tables are also described in annotations. OneToMany is the name of the relation, tagetEntity is the entity class, and JoinColumn defines the foreign key. Also, in OneToMany relations, we need to declare the variable as ArrayCollection in the constructor.
As class variables are set to private, we will need to create getters and setters in order to access their values. You can do that manually, but it is easier to run the following command from your project directory:
php app/console doctrine:generate:entities AppBundle/Entity/User
Upon executing the command, the class User would look like this:
/**
* @ORMEntity
* @ORMTable(name="user")
*/
class User
{
/**
* @ORMId
* @ORMColumn(type="integer")
* @ORMGeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $email;
/**
* @var string
*/
private $password;
/**
* @ORMColumn(type="datetime")
*/
private $createdAt;
/**
* @ORMManyToOne(targetEntity="Group", inversedBy="users")
* @ORMJoinColumn(name="group_id", referencedColumnName="id")
*/
private $group;
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @param mixed $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* @return mixed
*/
public function getName()
{
return $this->name;
}
/**
* @param mixed $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* @return mixed
*/
public function getEmail()
{
return $this->email;
}
/**
* @param mixed $email
*/
public function setEmail($email)
{
$this->email = $email;
}
/**
* @return mixed
*/
public function getPassword()
{
return $this->password;
}
/**
* @param mixed $password
*/
public function setPassword($password)
{
$this->password = $password;
}
/**
* @return mixed
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* @param mixed $createdAt
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
}
}
Creating and Updating the Database Schema
In Doctrine, the database schema can be easily created based on entity classes by executing the following command:
php app/console doctrine:schema:update --force
Not only can this command create the schema, but it can also update it at any point during development. As it executes SQL queries on the database, it is recommended not to use schema:update in production environment.
Querying the Database
At this point, we have created the entity classes, defined its relations and generated the database schema. Now we will learn how to create, fetch, update and delete objects from the database.
$group = new Group();
$group->setName('Administrator');
$group->setDescription('Users with highest privileges in the system');
$em = $this->getDoctrine()->getManager();
// Tells Doctrine you want to save the group (no queries yet)
$em->persist($group);
// This line actually executes the SQL query
$em->flush();
To fetch the id of the group that has just been created:
$group->getId();
To fetch the object from the database:
$group = $this->getDoctrine()
->getRepository('AppBundle:Group)
->find($groupId);
To update an object, do the following:
// Fetch the group
$em = $this->getDoctrine()->getManager();
$group = $em->getRepository('AppBundle:Group)->find($groupId);
// If the group does not exist, throw an exception
if (!$group) {
throw $this->createNotFoundException(
'Group not found'
);
}
// Update data
$group>setDescription('New group description.');
$em->flush();
Finally, deleting an object from the database is done like this:
// Fetch group
$em = $this->getDoctrine()->getManager();
$group = $em->getRepository('AppBundle:Group)->find($groupId);
// Remove group
$em->remove($group);
$em->flush();
Next Steps
In the second part of this tutorial, I will explain how to query objects with relations, use custom repositories to work with data, as well as some useful commands that will automate your development.