Symfony embarque un outils qui va nous permettre de gérer intégralement une base de donnée. Cette outils est Doctrine. Il en existe d'autres et on les appelle des ORM pour "object-relational mapping".
Tous d'abord, il va falloir dire à Symfony (ou plutôt Doctrine) où se situe le moteur de base de donnée et comment s'y connecter.
Cela se passe dans le fichier .env
# In all environments, the following files are loaded if they exist,
# the latter taking precedence over the former:
#
# * .env contains default values for the environment variables needed by the app
# * .env.local uncommitted file with local overrides
# * .env.$APP_ENV committed environment-specific defaults
# * .env.$APP_ENV.local uncommitted environment-specific overrides
#
# Real environment variables win over .env files.
#
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
# https://symfony.com/doc/current/configuration/secrets.html
#
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=44db9ac120846fc296608bd6d947a875
###< symfony/framework-bundle ###
###> symfony/webapp-pack ###
MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0
###< symfony/webapp-pack ###
###> doctrine/doctrine-bundle ### <== ICI
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
#
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8&charset=utf8mb4"
DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=14&charset=utf8"
###< doctrine/doctrine-bundle ###
###> symfony/messenger ###
# Choose one of the transports below
# MESSENGER_TRANSPORT_DSN=doctrine://default
# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages
# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages
###< symfony/messenger ###
###> symfony/mailer ###
# MAILER_DSN=null://null
###< symfony/mailer ###
Dans la section ###> doctrine/doctrine-bundle ###
vous pouvez voir que des lignes d'exemples ont été écrite pour nous.
Dans notre cas, nous allons connecter une base de donnée "MariaDB". Cette base est hébergée en local, joignable sur le port 3306. L'utilisateur est root et son mot de passe est tiger. Ma base de donnée va s'appeler symf6
Je commente donc la ligne concernant "postgresql" et j'ajoute:
DATABASE_URL="mysql://root:tiger@127.0.0.1:3306/symf6?serverVersion=mariadb-10.3.32&charset=utf8mb4"
Il est possible également de créer un fichier .env.local et d'y mettre uniquement les lignes spécifiques à nos réglages locaux
Symfony nous permet de créer directement la base à partir de sa CLI avec la commande:
symfony console doctrine:database:create
Prenons un exemple simple de base de donnée. Une base où on peut sauver des notes et les ranger par thèmes.
Une entité est la correspondance de la table pour le logiciel. On pourra également y ajouter des comportements spécifiques par la suite.
Le danger avec Doctrine serait de copier coller le schéma précédent. En général, nous laissons l'ORM gérer pour nous et nous lui donnons les informations essentielles.
Ici, commençons par la table note. Cette table a d'abord 2 propriétés significatives: sujet et contenu.
Symfony a aussi une commande pour ça:
symfony console make:entity
Class name of the entity to create or update (e.g. OrangePizza):
> Note
created: src/Entity/Note.php
created: src/Repository/NoteRepository.php
Entity generated! Now let's add some fields!
You can always add more fields later manually or by re-running this command.
New property name (press <return> to stop adding fields):
> sujet
Field type (enter ? to see all types) [string]:
>
Field length [255]:
>
Can this field be null in the database (nullable) (yes/no) [no]:
>
updated: src/Entity/Note.php
Add another property? Enter the property name (or press <return> to stop adding fields):
> contenu
Field type (enter ? to see all types) [string]:
> text
Can this field be null in the database (nullable) (yes/no) [no]:
> yes
updated: src/Entity/Note.php
Add another property? Enter the property name (or press <return> to stop adding fields):
>
Success!
Next: When you're ready, create a migration with php bin/console make:migration
Symfony a généré pour nous l'entité Note dans le dossier Entity:
<?php
namespace App\Entity;
use App\Repository\NoteRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: NoteRepository::class)]
class Note
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $sujet = null;
#[ORM\Column(type: Types::TEXT, nullable: true)]
private ?string $contenu = null;
public function getId(): ?int
{
return $this->id;
}
public function getSujet(): ?string
{
return $this->sujet;
}
public function setSujet(string $sujet): self
{
$this->sujet = $sujet;
return $this;
}
public function getContenu(): ?string
{
return $this->contenu;
}
public function setContenu(?string $contenu): self
{
$this->contenu = $contenu;
return $this;
}
}
L'entité est une classe comme on peut en trouver d'habitude. Elle respecte le principe d'encapsulation. Vous y trouvez des attributs privés et des assesseurs.
On trouve aussi les indications pour la base dans les attributs. Par exemple, pour l'attribut contenu on a:
#[ORM\Column(type: Types::TEXT, nullable: true)]
private ?string $contenu = null;
Le type est TEXT, nullable. En résumé, tout ce que nous avons spécifié dans la console est répercuté dans les attributs.
On fait ensuite le même travail pour la table theme et on obtient:
<?php
namespace App\Entity;
use App\Repository\ThemeRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: ThemeRepository::class)]
class Theme
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $nom = null;
#[ORM\Column(type: Types::TEXT, nullable: true)]
private ?string $description = null;
public function getId(): ?int
{
return $this->id;
}
public function getNom(): ?string
{
return $this->nom;
}
public function setNom(string $nom): self
{
$this->nom = $nom;
return $this;
}
public function getDescription(): ?string
{
return $this->description;
}
public function setDescription(?string $description): self
{
$this->description = $description;
return $this;
}
}
Il nous reste la relation à mettre en place.
Nous avons plusieurs notes par thème et un thème pour plusieurs notes.
dans la console reprenons notre entité note:
symfony console make:entity
Class name of the entity to create or update (e.g. TinyKangaroo):
> Note
Your entity already exists! So let's add some new fields!
New property name (press <return> to stop adding fields):
> theme
Field type (enter ? to see all types) [string]:
> relation
What class should this entity be related to?:
> Theme
On répond sagement aux questions de Symfony et il nous propose plusieurs type de relations.
What type of relationship is this?
------------ -----------------------------------------------------------------
Type Description
------------ -----------------------------------------------------------------
ManyToOne Each Note relates to (has) one Theme.
Each Theme can relate to (can have) many Note objects
OneToMany Each Note can relate to (can have) many Theme objects.
Each Theme relates to (has) one Note
ManyToMany Each Note can relate to (can have) many Theme objects.
Each Theme can also relate to (can also have) many Note objects
OneToOne Each Note relates to (has) exactly one Theme.
Each Theme also relates to (has) exactly one Note.
------------ -----------------------------------------------------------------
Ici c'est le ManyOne qui convient à notre problème.
Relation type? [ManyToOne, OneToMany, ManyToMany, OneToOne]:
> ManyToOne
Is the Note.theme property allowed to be null (nullable)? (yes/no) [yes]:
> no
Do you want to add a new property to Theme so that you can access/update Note objects from it - e.g. $theme->getNotes()? (yes/no) [yes]:
>
A new property will also be added to the Theme class so that you can access the related Note objects from it.
New field name inside Theme [notes]:
>
Do you want to activate orphanRemoval on your relationship?
A Note is "orphaned" when it is removed from its related Theme.
e.g. $theme->removeNote($note)
NOTE: If a Note may *change* from one Theme to another, answer "no".
Do you want to automatically delete orphaned App\Entity\Note objects (orphanRemoval)? (yes/no) [no]:
>
updated: src/Entity/Note.php
updated: src/Entity/Theme.php
Add another property? Enter the property name (or press <return> to stop adding fields):
>
Success!
Next: When you're ready, create a migration with php bin/console make:migration
Enfin la CLI nous indique qu'il reste à persister tout ça ne base avec.
symfony console make:migration
puis
symfony console doctrine:migrations:migrate