Doctrine (PHP)
Doctrine je ORM framework pro jazyk PHP. Nabízí vysokou míru abstrakce databázové vrstvy použitím objektového přístupu. Umožňuje tak pracovat s daty jako s objekty. Jedna z klíčových vlastností Doctrine je psaní databázových dotazů použitím Doctrine Query Language (DQL), který vychází z ORM frameworku Hibernate určeným pro Javu. Doctrine je šířen pod licencí LGPL.
Aktuální verze | 2.5.12 (23. října 2017) |
---|---|
Operační systém | multiplatformní |
Vyvíjeno v | PHP |
Typ softwaru | Objektově relační mapování framework |
Licence | GNU Lesser General Public License |
Web | www.doctrine-project.org |
Některá data mohou pocházet z datové položky. |
Struktura frameworku
Framework Doctrine je rozdělen do tří vrstev.
- Common
Obsahuje obecná rozhraní, třídy a knihovny (práce s kolekcemi, anotacemi, cachování). Vrstva je na ostatních dvou vrstvách nezávislá, ale Common vrstvu ostatní vrstvy používají.
- DBAL (DataBase Abstraction Layer)
Zajišťuje databázovou nezávislost. Definuje DQL (Doctrine Query Language). Je závislá na vrstvě Common.
- ORM (Object-Relational Mapping)
Jedná se o nejvyšší vrstvu a zajišťuje mapování objektů do relační databáze, jejich ukládání a načítání.
Požadavky
Doctrine pro svůj běh vyžaduje minimálně PHP 5.3 (pro Doctrine 2). Podporuje tyto databáze[1]:
- MySQL a MariaDB
- PostgreSQL
- Oracle
- SQLite
- Drizzle
- SAP Sybase SQL Anywhere
- Microsoft SQL Server
Výhody
Výhodou Doctrine je nízká náročnost na konfiguraci. Doctrine umí generovat object-class z existující databáze, programátor pak již pouze specifikuje vazby mezi entitami a další funkce. Na rozdíl od jiných ORM frameworků Doctrine nepracuje se složitým popisem databázového schématu pomocí XML, ale v případě potřeby je možno použít i XML popis.
Doctrine je postavena na návrhovém vzoru Data Mapper (na rozdíl od jiných ORM frameworků, které vycházejí z návrhového vzoru Active Record). K definici databázových entit se využívá komentářové anotace. Podle návrhového vzoru Data Mapper objekt neobsahuje žádné operace. O tyto operace se stará oddělený objekt (v případě Doctrine je to třída EntityManager). Tímto přístupem je zajištěna nezávislost na databázi.
Další výhody:
- Podpora stromových struktur dat
- Výchozí použití transakcí u databází, které je podporují
- Databázová nezávislost
- Komentářová anotace
- Optimalizace výkonu (cachování na několika úrovních)
Definice a práce entitami
Entita je základní kámen Doctrine a vůbec objektového mapování. Reprezentuje objekt reálného světa. Zjednodušeně si lze entitu představit jako jeden řádek v databázové tabulce. Entitou může být např. student, učitel, předmět.
Příklad definice entity:
<?php
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity()
* @ORM\Table(name="student")
*/
class Student
{
/**
* @var int
* @ORM\Id()
* @ORM\Column(type="integer")
* @ORM\GeneratedValue()
*/
private $id;
/**
* @var string
* @ORM\Column(length=100)
*/
private $jmeno;
/**
* @var string
* @ORM\Column(length=100)
*/
private $prijmeni;
public function getId(): int
{
return $this->id;
}
public function getJmeno(): string
{
return $this->jmeno;
}
public function setJmeno(string $jmeno): void
{
$this->jmeno = $jmeno;
}
public function getPrijmeni(): string
{
return $this->prijmeni;
}
public function setPrijmeni(string $prijmeni): void
{
$this->prijmeni = $prijmeni;
}
}
Takto jsme nadefinovali entitu student se třemi atributy: id, jméno a příjmení. Při této definici jsme použili tzv. komentářovou anotaci. Druhý způsob definice entity vede přes XML nebo YAML soubory. Atribut id je primární klíč a jeho hodnota se automaticky generuje. Jako název tabulky a jejich sloupců se defaultně použijí názvy entit a jejich členských proměnných. Toto chování lze změnit uvedením atributu anotace name. S entitou se pak pracuje stejně jako s jakýmkoli jiným objektem. K ukládání nových entit a vyhledávání slouží třída EntityManager, jejíž instance je zavedena při startu aplikace. Příklad vytvoření nové entity a hledání:
<?php
$student = new Student;
$student->setJmeno('Franta');
$student->setPrijmeni('Vomáčka');
echo $student->getJmeno();
echo $student->getPrijmeni();
$entityManager->persist($student); // persistuje entitu a vytvoří ID
$entityManager->flush($student); // uloží fyzicky do DB
// vyhledání studenta s id=5
$student2 = $entityManager->find('Student', 5);
echo $student2->getJmeno();
echo $student2->getPrijmeni();
// změna a uložení
$student2->setJmeno('Jaromir');
$entityManager->flush(); // Uloží všechny změny
Příklad ukazuje základní vlastnosti objektového mapování, kdy s daty pracujeme jako s objektem a veškeré změny jsou přenášeny do databáze. Místo ukládání entit se v Doctrine používá výraz persistence. Důvod je spíš filozofický a vychází z významu slov, persistovat entitu můžeme jenom jednou a to po vytvoření nové instance.
Příklad smazání entity:
$entityManager->remove($student); // Odebere entitu v UnitOfWork
$entityManager->flush(); // Provede fyzické odebrání v databázi
Metodě remove je předána instance entity, kterou chceme smazat.
DQL
DQL (Doctrine Query Language) je dotazovací jazyk pro použití s frameworkem Doctrine. Je podobný jako SQL, ale vychází z jiných principů. Koncepčně vychází z Hibernate Query Language (HQL) a z Java Persistence Query Language (JPQL). Nejsilnější stránkou DQL je kombinace jednoduchosti SQL a nezávislosti objektové vrstvy.
Výsledkem DQL dotazu není jako v případě SQL tabulka s řádky, ale načtené instance entit. Při vytváření DQL dotazu vycházíme z definovaných názvů entit a jejich členských proměnných. Takto jsme zcela oproštěni od samotné existence databázových tabulek. Tím, že při definici entit jsme popsali vazby mezi nimi, výsledkem dotazu bude odkaz na asociaci, kterou jsou entity v dotazu propojeny.
S DQL je možno provádět operace SELECT, UPDATE, DELETE. Příkazy GRANT, ALTER, CREATE, DROP nelze provést, protože je to v rozporu se základní myšlenkou Doctrine (tyto příkazy se týkají přímo fyzické vrstvy).
Základní dotaz:
$query=$entityManager->createQuery("SELECT s FROM Project\Model\Student s WHERE s.jmeno = 'Franta'");
//pole objektů Student
$results=$query->getResult();
V tomto dotazu jsme použili alias pro entitu Student. Cestu k entitě definuje namespace entity.
Dotaz s využitím spojení tabulek:
$query = $entityManager->createQuery('SELECT a FROM Article a JOIN a.user u ORDER BY u.name ASC');
// pole objektů Article
$articles = $query->getResult();
Při spojování tabulek nemusíme uvádět, kterých sloupců se to týká, protože to framework rozpozná sám díky již nadefinovaným asociacím.
Dotaz využívající pojmenované parametry:
$query = $entityManager->createQuery('SELECT u FROM User u WHERE u.username = :name');
$query->setParameter('name', 'Franta');
$users = $query->getResult();
Kromě tohoto běžného použití DQL má programátor možnost vytvořit dotaz postupným voláním jeho metod a vytvořit tak specifický databázový dotaz. K tomu slouží třída Doctrine\ORM\QueryBuilder. V konečné fázi je možno použít čisté SQL, ve všech třech případech dotazů (DQL, QueryBuilder, SQL) dojde k předání výsledné instance na entitu.
Omezení
Omezení vycházejí z podstaty jazyka PHP. Jedním z nich je, že PHP je typově slabý jazyk, takže nekontroluje datový typ proměnných. Programátor tak musí tuto skutečnost brát v úvahu a provádět validaci dat.
Dalším omezením, které ale spíše patří do kategorie programátorských zlozvyků, je definici členských proměnných jako public. V tomto případě může dojít k neočekávanému chování aplikace.
Programátor dokonce ani nemusí definovat gettery a settery, ty slouží pouze pro vnější komunikaci s entitou. Doctrine ale dokáže s entitou komunikovat i bez nich, opět to souvisí s omezeními jazyka PHP.
Rovněž se nedoporučuje klonování a serializace entit. Může dojít k neočekávanému chování.