Praktický návod

Jak správně používat Doctrine ORM

Entity drž doménové, repository zaměř na dotazy a změny ulož jedním flushem na hranici use-case.

30 minut · Doctrine

Nejdřív stručně

ORM mapuje objekty, ne odpovědnost

Doctrine ORM mapuje entity na tabulky a přes unit of work sleduje jejich změny. Entita ale stále nese doménový stav a pravidla; nemá si sama hledat služby, posílat e-maily ani volat databázi.

Repository soustředí dotazy na entity a pojmenovává je podle potřeb aplikace. Aplikační use-case načte potřebné objekty, provede změnu a na jasné hranici nechá unit of work změny uložit.

Připrav si

Co budeš potřebovat

Začni jedním skutečným use-case a jeho datovým modelem, ne univerzální abstrakcí nad Doctrine.

  • Symfony projekt s Doctrine ORM a nakonfigurovaným databázovým připojením.
  • Jednu doménovou operaci, například potvrzení objednávky, a pravidla, která musí zachovat.
  • Testovací databázi a možnost spouštět integrační testy repository.
  • Databázové migrace ve verzovacím systému. Schéma neměň ručně podle entity v produkci.

Kroky 1 až 3

Rozděl odpovědnost mezi entity, repository a use-case

Každá vrstva má jednoduchou úlohu. Entity chrání stav, repository hledá a aplikační služba řídí operaci.

1. Navrhni entity kolem doménových pravidel

  1. Mapuj stabilní identitu, stav a vztahy, které use-case skutečně potřebuje. Kolekce inicializuj v konstruktoru a u obou stran asociace udržuj konzistentní stav.
  2. Místo veřejných setterů použij metody popisující záměr, například confirm() nebo changeDeliveryAddress(). Entita tak může odmítnout neplatný přechod.
  3. Do entity neinjektuj service locator, EntityManager, repository ani Symfony container. Pokud pravidlo potřebuje externí data, získej je v aplikační službě a předej výsledek explicitně.
  4. Neprocházej bez rozmyslu lazy kolekce při serializaci. Pro čtecí obrazovku často lépe poslouží cílený dotaz a výstupní DTO.
composer require symfony/orm-pack
Oficiální Doctrine dokumentace k asociacím

2. Piš repository pro konkrétní dotazy

  1. Jednoduché načtení entity podle identity nech repository. Složitější dotaz pojmenuj podle potřeby aplikace, například findPayableOrders(), ne podle použitého SQL.
  2. Výběr sloupců, JOIN, řazení a stránkování řeš uvnitř repository pomocí QueryBuilderu nebo DQL. Volající nemusí znát mapování tabulek.
  3. Repository nemá řídit celý workflow, posílat notifikace ani volat flush. Jeho úkolem je načíst nebo přidat entity a provést specializované datové dotazy.
  4. U čtecích dotazů zvaž DTO nebo skalární výsledek. Nenačítej velký graf entit jen proto, abys z něj přečetl tři hodnoty.
php bin/console debug:container --tag=doctrine.repository_service
Oficiální Symfony dokumentace k repository

3. Uzavři unit of work na hranici use-case

  1. EntityManager sleduje spravované entity. U nové entity zavolej persist(); změnu už načtené spravované entity obvykle stačí provést její doménovou metodou.
  2. Flush volej jednou po dokončení aplikační operace, ne uvnitř entity ani každého repository. Hranice use-case pak určuje, které změny patří k sobě.
  3. Jeden flush používá databázovou transakci. Explicitní transakci přidej, když use-case obsahuje více flushů, přímé DBAL operace nebo zámky, které musí být atomické.
  4. Uvnitř transakce nečekej na vzdálené API. Externí události řeš po commitu nebo pomocí outboxu; při výjimce počítej s rollbackem a uzavřeným EntityManagerem.
Oficiální Doctrine dokumentace k transakcím

Krok 4

Ověř mapování i chování

Správné atributy nestačí. Testuj výsledné dotazy, změny stavu a hranici transakce.

  1. Zkontroluj mapování proti schématu

    Příkaz odhalí neplatné asociace i rozdíl mezi mapováním a testovací databází.

    php bin/console doctrine:schema:validate
  2. Spusť integrační testy repository

    Ověř filtr, řazení, prázdný výsledek i hranice stránkování nad skutečnou databází.

    php bin/phpunit tests/Integration/Repository
  3. Zkontroluj stav migrací

    Mapování a databázové schéma musí být nasaditelné pomocí verzovaných migrací.

    php bin/console doctrine:migrations:status

Když to zlobí

Nejčastější chyby

Změna entity se neuložila

Ověř, že entita pochází ze stejného otevřeného EntityManageru a use-case došel až k flush. U odpojené entity ji znovu načti; nepřenášej spravované entity přes frontu nebo dlouhý proces.

Jeden požadavek provádí příliš mnoho dotazů

Zapni profiler a hledej lazy loading ve smyčce. Potřebné vztahy načti cíleným JOINem nebo použij DTO dotaz, ale nenačítej automaticky celý objektový graf.

php bin/console debug:config doctrine
Lazy kolekce selže po skončení aplikační operace

Kód čte entitu mimo životní cyklus EntityManageru. Potřebná data načti a převeď na výstupní DTO uvnitř use-case místo serializace odpojené entity.

Repository volá flush po každé změně

Přesuň flush na hranici aplikačního use-case. Jedna operace pak může změnit více entit atomicky a test přesně ví, kdy má být práce potvrzena.

Hotovo

Doctrine má v aplikaci jasné hranice.

Doctrine ORM teď mapuje doménové entity, repository soustředí dotazy a aplikační use-case rozhoduje o flush a transakci. Při dalším rozšiřování sleduj také počet dotazů a velikost načítaného grafu.

Zavolejte mi

Zavolám vám následující pracovní den mezi 9:00 a 17:00.

Můžete mi také zavolat rovnou.

+420 605 181 728

Nechte mi telefonní číslo a pošlete žádost o zpětné zavolání.

Odesláním souhlasíte se zpracováním údajů pro vyřízení žádosti.