Opis struktury danych
Do komunikacji PHP z baz膮 danych u偶yjemy Doctrine. Jest to zestaw bibliotek, kt贸re pomagaj膮 zarz膮dza膰 bazami danych: Doctrine DBAL (warstwa abstrakcji bazy danych), Doctrine ORM (biblioteka do zarz膮dzania danymi w bazie danych, za pomoc膮 obiekt贸w PHP), oraz Doctrine Migrations.
Konfigurowanie Doctrine ORM
Sk膮d Doctrine wie, jak po艂膮czy膰 si臋 z baz膮 danych? Przepis (ang. recipe) instaluj膮cy Doctrine doda艂 odpowiedni plik konfiguracyjny (config/packages/doctrine.yaml
), kt贸ry kontroluje jego zachowanie. G艂贸wnym ustawieniem jest database DSN, napis zawieraj膮cy wszystkie informacje o po艂膮czeniu: dane uwierzytelniaj膮ce, host, port itp. Domy艣lnie Doctrine szuka zmiennej 艣rodowiskowej DATABASE_URL
.
Prawie wszystkie zainstalowane pakiety posiadaj膮 swoj膮 konfiguracj臋 w katalogu config/packages/
. W wi臋kszo艣ci przypadk贸w, warto艣ci domy艣lne by艂y wybierane ostro偶nie, aby dzia艂a艂y w wi臋kszo艣ci aplikacji.
Zrozumienie konwencji zmiennych 艣rodowiskowych w Symfony
Mo偶esz zdefiniowa膰 zmienn膮 艣rodowiskow膮 DATABASE_URL
r臋cznie w pliku .env
lub .env.local
. Dzi臋ki przepisowi (ang. recipe) pakietu, mo偶esz bazowa膰 na przyk艂adowej warto艣ci zmiennej 艣rodowiskowej DATABASE_URL
ju偶 wpisanej w pliku .env
. Pojawia si臋 jednak problem uci膮偶liwej r臋cznej aktualizacji wpisu po ka偶dej zmianie portu bazy danych PostgreSQL udost臋pnionego przez Dockera. Lepiej wi臋c podej艣膰 do sprawy w inny spos贸b.
Zamiast dokonywa膰 sztywnego ustawienia zmiennej 艣rodowiskowej DATABASE_URL
w pliku, mo偶esz poprzedza膰 wszystkie polecenia s艂owem symfony
. Dzi臋ki temu wszystkie us艂ugi dzia艂aj膮ce w kontenerze Docker i/lub Platform.sh (wy艂膮cznie je艣li mamy otwarty tunel z Platform.sh) b臋d膮 automatycznie ustawione jako zmienne 艣rodowiskowe.
Dzi臋ki zmiennym 艣rodowiskowym integracja Symfony z Docker Compose i Platform.sh jest bezproblemowa.
Mo偶esz sprawdzi膰 aktualne zmienne 艣rodowiskowe w konsoli poprzez u偶ycie polecenia symfony var:export
:
1
$ symfony var:export
1 2
DATABASE_URL=postgres://main:main@127.0.0.1:32781/main?sslmode=disable&charset=utf8
# ...
Pami臋tasz nazw臋 us艂ugi database
, kt贸rej u偶yli艣my w konfiguracji Docker i Platform.sh? Nazwy us艂ug s膮 u偶ywane jako prefiksy do definiowania zmiennych 艣rodowiskowych, takich jak DATABASE_URL
. Je艣li twoje us艂ugi s膮 nazwane zgodnie z konwencjami Symfony, 偶adna dodatkowa konfiguracja nie jest potrzebna.
Note
Bazy danych nie s膮 jedyn膮 us艂ug膮, kt贸ra korzysta tej z konwencji. To samo dotyczy na przyk艂ad Mailera (zmienna 艣rodowiskowa MAILER_DSN
).
Zmiana domy艣lnej warto艣ci DATABASE_URL w pliku .env
Zmienimy plik .env
tak, aby ustawi膰 domy艣ln膮 warto艣膰 zmiennej 艣rodowiskowej DATABASE_URL
dla PostgreSQL:
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 ###
Dlaczego musimy powiela膰 t臋 sam膮 informacj臋 w dw贸ch r贸偶nych miejscach? Poniewa偶 na niekt贸rych platformach chmurowych, w czasie budowania aplikacji, adres URL bazy danych mo偶e nie by膰 jeszcze znany, a Doctrine potrzebuje informacji o silniku bazy danych, aby zbudowa膰 odpowiedni膮 dla niego konfiguracj臋. Tak wi臋c host, nazwa u偶ytkownika i has艂o nie maj膮 wi臋kszego znaczenia.
Tworzenie klas encji
Konferencj臋 mo偶na opisa膰 kilkoma atrybutami:
- city - miasto, w kt贸rym organizowana jest konferencja;
- year - rok, w kt贸rym odbywa si臋 konferencja;
- isInternational - flaga wskazuj膮ca, czy konferencja jest krajowa, czy mi臋dzynarodowa (SymfonyLive vs SymfonyCon).
Maker Bundle pomo偶e nam wygenerowa膰 klas臋 encji (ang. entity), kt贸ra b臋dzie reprezentowa艂a konferencj臋.
Pora wygenerowa膰 encj臋 Conference
:
1
$ symfony console make:entity Conference
To polecenie jest uruchamiane w trybie interaktywnym - poprowadzi Ci臋 przez proces dodawania wszystkich potrzebnych p贸l. U偶yj nast臋puj膮cych odpowiedzi (wi臋kszo艣膰 z nich to odpowiedzi domy艣lne, wi臋c mo偶esz nacisn膮膰 klawisz "Enter", aby ich u偶y膰):
city
,string
,255
,no
;year
,string
,4
,no
;isInternational
,boolean
,no
.
Oto pe艂ne wyj艣cie (ang. output) po uruchomieniu polecenia:
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
Klasa Conference
zosta艂a zapisana w przestrzeni nazw App\Entity\
.
Polecenie wygenerowa艂o r贸wnie偶 klas臋 repozytorium (ang. repository) Doctrine: App\Repository\ConferenceRepository
.
Wygenerowany kod wygl膮da nast臋puj膮co (tylko niewielka cz臋艣膰 pliku jest tu pokazana):
Zauwa偶, 偶e w艂a艣nie utworzona klasa jest zwyk艂膮 klas膮 PHP - nie ma w niej element贸w Doctrine. Metadane wykorzystywane przez Doctrine do powi膮zania klasy z tabel膮 w bazie danych dodajemy, u偶ywaj膮c atrybut贸w.
Doctrine doda艂 atrybut id
, aby zachowa膰 klucz g艂贸wny w tabeli bazy danych. Ten klucz (ORM\Id()
) jest automatycznie generowany (ORM\GeneratedValue()
) w spos贸b zale偶ny od silnika bazy danych (oparty o wzorzec strategii).
Teraz wygeneruj klas臋 encji dla komentarzy:
1
$ symfony console make:entity Comment
Wprowad藕 nast臋puj膮ce odpowiedzi:
author
,string
,255
,no
;text
,text
,no
;email
,string
,255
,no
;createdAt
,datetime_immutable
,no
.
艁膮czenie encji
Obie encje, Conference
i Comment
, powinny by膰 ze sob膮 powi膮zane. Konferencja mo偶e mie膰 zero lub wi臋cej komentarzy, co nazywamy relacj膮 jeden do wielu (ang. one to many).
U偶yj ponownie polecenia make:entity
, aby zdefiniowa膰 t臋 relacj臋 w klasie Conference
:
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
Je艣li wpiszesz ?
jako odpowied藕 w pytaniu o typ pola, otrzymasz list臋 wszystkich obs艂ugiwanych typ贸w:
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
Przyjrzyj si臋 li艣cie r贸偶nic dla klas encji po dodaniu relacji:
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;
+ }
}
Wszystko, czego potrzebujesz do zarz膮dzania t膮 relacj膮, zosta艂o wygenerowane automatycznie. P贸藕niej kod mo偶esz zmienia膰 jak chcesz.
Dodawanie kolejnych atrybut贸w
W艂a艣nie zda艂em sobie spraw臋, 偶e zapomnieli艣my doda膰 pewien atrybut w encji Comment
: uczestnicy mog膮 chcie膰 do艂膮czy膰 zdj臋cie z konferencji, aby zilustrowa膰 swoje opinie.
Uruchom make:entity
jeszcze raz i dodaj atrybut photoFilename
jako kolumn臋 typu string
. Pozw贸l jej przyjmowa膰 warto艣膰 null
, poniewa偶 dodanie zdj臋cia jest opcjonalne:
1
$ symfony console make:entity Comment
Migracja bazy danych
Model projektu sk艂ada si臋 teraz z dw贸ch w艂a艣nie wygenerowanych klas.
W kolejnym kroku musimy utworzy膰 tabele w bazie danych zwi膮zane z naszymi encjami w PHP.
Biblioteka Doctrine Migrations to narz臋dzie idealnie dopasowane do tego zadania. Zosta艂a ona ju偶 zainstalowana jako cz臋艣膰 zale偶no艣ci orm
.
Migracja (ang. migration) jest klas膮, kt贸ra opisuje zmiany wykonywane w bazie danych, aby z obecnego schematu przej艣膰 na nowy, zdefiniowany w atrybutach encji. Poniewa偶 baza danych jest na razie pusta, migracja powinna sk艂ada膰 si臋 z operacji tworz膮cych dwie tabele.
Zobaczmy, co wygeneruje Doctrine:
1
$ symfony console make:migration
Zwr贸膰 uwag臋 na wygenerowan膮 nazw臋 pliku, kt贸ra powinna przypomina膰 migrations/Version20191019083640.php
:
Aktualizacja lokalnej bazy danych
Mo偶esz teraz uruchomi膰 migracj臋, aby zaktualizowa膰 schemat lokalnej bazy danych:
1
$ symfony console doctrine:migrations:migrate
Schemat lokalnej bazy danych jest teraz aktualny i przygotowany do przechowywania niekt贸rych danych.
Aktualizacja produkcyjnej bazy danych
Kroki potrzebne do wykonania migracji na produkcyjnej bazie danych s膮 takie same jak te, kt贸re ju偶 znasz: zatwierd藕 zmiany (ang. commit) i wdra偶aj.
Podczas wdra偶ania projektu, Platform.sh opr贸cz aktualizacji kodu uruchamia tak偶e migracj臋 bazy danych, je艣li taka istnieje (wykrywa, czy istnieje polecenie doctrine:migrations:migrate
).
Id膮c dalej
$ symfony console make:entity Conference