Beschrijving van de gegevensstructuur
Om in PHP met een database te werken gaan we gebruik maken van Doctrine , een set libraries die ons, ontwikkelaars, helpt om met databases om te gaan: Doctrine DBAL (een database abstractie laag), Doctrine ORM (een library om database inhoud te manipuleren door gebruik te maken van PHP objecten), en Doctrine Migrations.
Doctrine ORM configureren
Hoe weet Doctrine met welke database te verbinden? Doctrine's recipe voegde een configuratiebestand toe config/packages/doctrine.yaml
, dat zijn gedrag regelt. De belangrijkste instelling is de database DSN, een string die alle informatie over de verbinding bevat: gebruikersnaam, wachtwoord, host, poort, enz. Standaard probeert Doctrine deze gegevens uit de DATABASE_URL
omgevingsvariabele te halen.
Bijna alle geïnstalleerde packages bevatten configuratie in de config/packages/
-map. Meestal zijn de standaardinstellingen zorgvuldig gekozen om voor de meeste toepassingen te werken.
Conventies van Symfony-omgevingsvariabelen begrijpen
Je kan de DATABASE_URL
handmatig in het .env
of .env.local
bestand definiëren. Dankzij het recipe van de package zie je bijvoorbeeld DATABASE_URL
in jouw .env
bestand. Maar omdat Docker de lokale poort voor PostgreSQL vrij kiest, is dat hinderlijk. Er is een betere manier.
In plaats van de DATABASE_URL
hard te coderen in een bestand, kunnen we alle commando's met symfony
prefixen. Dit zorgt ervoor dat de Docker en/of Platform.sh (wanneer de tunnel open is) services gedetecteerd worden en automatisch als omgevingsvariabele ingesteld worden.
Docker Compose en Platform.sh werken naadloos samen met Symfony dankzij deze omgevingsvariabelen.
Bekijk alle beschikbare omgevingsvariabelen door het uitvoeren van symfony var:export
:
1
$ symfony var:export
1 2
DATABASE_URL=postgres://main:main@127.0.0.1:32781/main?sslmode=disable&charset=utf8
# ...
Herinner je je de database
servicenaam die in de Docker en Platform.sh configuraties wordt gebruikt? De servicenamen worden gebruikt als prefix bij het definiëren van omgevingsvariabelen zoals DATABASE_URL
. Als services de Symfony naamgevingsconventies volgen, is er geen extra configuratie nodig.
Note
De database is niet de enige service die profiteert van de Symfony conventies. Hetzelfde geldt bijvoorbeeld voor Mailer (via de MAILER_DSN
omgevingsvariabele).
De standaard DATABASE_URL waarde in .env aanpassen
We zullen het .env
bestand nog steeds aanpassen om de standaard DATABASE_URL
voor het gebruik van PostgreSQL in te stellen:
1 2 3 4 5 6 7 8 9 10 11
--- a/.env
+++ b/.env
@@ -28,7 +28,7 @@ MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0
#
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
# DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7"
-DATABASE_URL="postgresql://symfony:ChangeMe@127.0.0.1:5432/app?serverVersion=13&charset=utf8"
+DATABASE_URL="postgresql://127.0.0.1:5432/db?serverVersion=13&charset=utf8"
###< doctrine/doctrine-bundle ###
###> symfony/messenger ###
Waarom moet de informatie op twee verschillende plaatsen worden herhaald? Omdat op sommige Cloud platformen tijdens de build, de URL van de database nog onbekend kan zijn, maar Doctrine wel het database systeem moet kennen om de configuratie te kunnen opbouwen. Dus, de host, gebruikersnaam en wachtwoord doen er niet toe.
Entity classes aanmaken
Een conferentie kunnen we beschrijven aan de hand van een aantal eigenschappen:
- De stad waar de conferentie wordt georganiseerd;
- Het jaar van de conferentie;
- Een internationale vlag om aan te geven of de conferentie lokaal of internationaal is (SymfonyLive vs SymfonyCon).
De Maker bundle kan ons helpen om een class (een Entity class) te genereren voor de conferentie.
Het is nu tijd om de Conference
entity te genereren:
1
$ symfony console make:entity Conference
Dit commando is interactief: het begeleidt je bij het toevoegen van de nodige velden. Gebruik de volgende antwoorden (de meeste zijn de standaard antwoorden, dus je kunt op de "Enter" toets drukken om ze te aanvaarden):
city
,string
,255
,no
;year
,string
,4
,no
;isInternational
,boolean
,no
.
Dit is de volledige uitvoer van het commando:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
created: src/Entity/Conference.php
created: src/Repository/ConferenceRepository.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):
> city
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/Conference.php
Add another property? Enter the property name (or press <return> to stop adding fields):
> year
Field type (enter ? to see all types) [string]:
>
Field length [255]:
> 4
Can this field be null in the database (nullable) (yes/no) [no]:
>
updated: src/Entity/Conference.php
Add another property? Enter the property name (or press <return> to stop adding fields):
> isInternational
Field type (enter ? to see all types) [boolean]:
>
Can this field be null in the database (nullable) (yes/no) [no]:
>
updated: src/Entity/Conference.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 make:migration
De Conference
class is opgeslagen onder de App\Entity\
namespace.
Het commando genereerde ook een Doctrine repository class: App\Repository\ConferenceRepository
.
De gegenereerde code ziet er als volgt uit (slechts een klein deel van het bestand wordt hier getoond):
Merk op dat de class een gewone PHP class is zonder invloeden van Doctrine. Attributen worden gebruikt om metadata toe te voegen die Doctrine gebruikt om de class te kunnen koppelen aan de bijhorende databasetabel.
Doctrine heeft een id
eigenschap toegevoegd om de primaire sleutel van de rij te bewaren in de tabel. Deze sleutel (ORM\Id()
) wordt automatisch gegenereerd (ORM\GeneratedValue()
) via een strategie die afhankelijk is van het gebruikte databasesysteem.
Genereer nu een Entity class voor reacties op de conferentie:
Geef de volgende antwoorden:
author
,string
,255
,no
;text
,text
,no
;email
,string
,255
,no
;createdAt
,datetime_immutable
,no
.
Entities aan elkaar koppelen
De twee entities, Conference en Comment, moeten aan elkaar worden gekoppeld. Een conferentie kan nul of meer reacties hebben, wat een one-to-many-relatie wordt genoemd.
Gebruik het make:entity
commando opnieuw om de relatie toe te voegen aan de Conference
class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
Your entity already exists! So let's add some new fields!
New property name (press <return> to stop adding fields):
> comments
Field type (enter ? to see all types) [string]:
> OneToMany
What class should this entity be related to?:
> Comment
A new property will also be added to the Comment class...
New field name inside Comment [conference]:
>
Is the Comment.conference property allowed to be null (nullable)? (yes/no) [yes]:
> no
Do you want to activate orphanRemoval on your relationship?
A Comment is "orphaned" when it is removed from its related Conference.
e.g. $conference->removeComment($comment)
NOTE: If a Comment may *change* from one Conference to another, answer "no".
Do you want to automatically delete orphaned App\Entity\Comment objects (orphanRemoval)? (yes/no) [no]:
> yes
updated: src/Entity/Conference.php
updated: src/Entity/Comment.php
Note
Als je ?
als antwoord voor het type intypt, krijg je een lijst met alle ondersteunde types:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
Main types
* string
* text
* boolean
* integer (or smallint, bigint)
* float
Relationships / Associations
* relation (a wizard will help you build the relation)
* ManyToOne
* OneToMany
* ManyToMany
* OneToOne
Array/Object Types
* array (or simple_array)
* json
* object
* binary
* blob
Date/Time Types
* datetime (or datetime_immutable)
* datetimetz (or datetimetz_immutable)
* date (or date_immutable)
* time (or time_immutable)
* dateinterval
Other Types
* decimal
* guid
* json_array
Bekijk de volledige diff van de entity class na het toevoegen van de relatie:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
--- a/src/Entity/Comment.php
+++ b/src/Entity/Comment.php
@@ -36,6 +36,12 @@ class Comment
*/
private $createdAt;
+ #[ORM\ManyToOne(inversedBy: 'comments')]
+ #[ORM\JoinColumn(nullable: false)]
+ private Conference $conference;
+
public function getId(): ?int
{
return $this->id;
@@ -88,4 +94,16 @@ class Comment
return $this;
}
+
+ public function getConference(): ?Conference
+ {
+ return $this->conference;
+ }
+
+ public function setConference(?Conference $conference): self
+ {
+ $this->conference = $conference;
+
+ return $this;
+ }
}
--- a/src/Entity/Conference.php
+++ b/src/Entity/Conference.php
@@ -2,6 +2,8 @@
namespace App\Entity;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
@@ -31,6 +33,16 @@ class Conference
*/
private $isInternational;
+ #[ORM\OneToMany(targetEntity: Comment::class, mappedBy: "conference", orphanRemoval: true)]
+ private $comments;
+
+ public function __construct()
+ {
+ $this->comments = new ArrayCollection();
+ }
+
public function getId(): ?int
{
return $this->id;
@@ -71,4 +83,35 @@ class Conference
return $this;
}
+
+ /**
+ * @return Collection|Comment[]
+ */
+ public function getComments(): Collection
+ {
+ return $this->comments;
+ }
+
+ public function addComment(Comment $comment): self
+ {
+ if (!$this->comments->contains($comment)) {
+ $this->comments[] = $comment;
+ $comment->setConference($this);
+ }
+
+ return $this;
+ }
+
+ public function removeComment(Comment $comment): self
+ {
+ if ($this->comments->contains($comment)) {
+ $this->comments->removeElement($comment);
+ // set the owning side to null (unless already changed)
+ if ($comment->getConference() === $this) {
+ $comment->setConference(null);
+ }
+ }
+
+ return $this;
+ }
}
Alles wat nodig is om de relatie te beheren is nu voor je gegenereerd. Eenmaal gegenereerd, wordt dit jouw code; je bent vrij om de code aan te passen als dat nodig is.
Extra eigenschappen toevoegen
Ik realiseerde me net dat we vergeten zijn een eigenschap toe te voegen aan de Comment entity: de deelnemers willen misschien een foto van de conferentie toevoegen om hun feedback kracht bij te zetten.
Voer make:entity
opnieuw uit en voeg een photoFilename
eigenschap/kolom van het type string
toe, maar laat null
toe omdat het uploaden van een foto optioneel is:
1
$ symfony console make:entity Comment
De database migreren
Het model van het project wordt nu volledig beschreven door de twee gegenereerde classes.
Vervolgens moeten we ook nog de databasetabellen aanmaken die bij deze PHP entities horen.
Doctrine Migrations is hiervoor het beste gereedschap. Het werd al geïnstalleerd als onderdeel van de orm
dependency.
Een migratie is een class die database schemawijzigingen beschrijft. Met die schemawijzigingen kan je de database van de huidige naar de nieuwe versie brengen. De schemawijzigingen worden gegenereerd op basis van de attributen die op de entity gedefinieerd zijn. De database is momenteel leeg, dus de migratie zou de creatie van twee tabellen moeten bevatten.
Laten we eens bekijken wat Doctrine genereert:
1
$ symfony console make:migration
Let op de gegenereerde bestandsnaam (ziet eruit als migrations/Version20191019083640.php
):
Bijwerken van de lokale database
Je kan nu de gegenereerde migratie uitvoeren om het lokale database schema bij te werken:
1
$ symfony console doctrine:migrations:migrate
Het lokale database-schema is nu up-to-date, klaar om gegevens te bewaren.
De productiedatabase bijwerken
De stappen die nodig zijn om de productiedatabase te migreren zijn dezelfde als die waarmee je al bekend bent: commit de wijzigingen en deploy deze.
Bij het deployen van het project brengt Platform.sh de code up-to-date en voert ook de databasemigraties uit (indien het doctrine:migrations:migrate
commando bestaat).
$ symfony console make:entity Conference