SlideShare ist ein Scribd-Unternehmen logo
1 von 75
Downloaden Sie, um offline zu lesen
Doctrine ORM
& model
@ProchazkaFilip
kdyby/doctrine v3.0
Co si povíme
- jak psát entity
- práce s db schématem
- použití entit
- pokládání dotazů
- modelové třídy (a jak nepsat repozitáře)
- jak cachovat
Jak psát entity
Databáze neexistuje
$articles = [
new Article('abc'),
new Article('def'),
new Article('ghi')
];
Asociace mezi entitami
class Comment {
private $article;
public function __construct(Article $article) {
$this->article = $article;
}
Asociace mezi entitami
class Article {
private $comments;
public function __construct() {
$this->comments = new ArrayCollection();
}
function addComment(Comment $comment) {
$this->comments[] = $comment;
}
Nahaté kolekce? Never!
class Article {
// ...
function getComments() {
return $this->comments;
}
Magic to the rescue
use KdybyDoctrineEntitiesMagicAccessors;
class Article {
use MagicAccessors;
Magic to the rescue
- pouze na protected properties
- magické (get|set)tery
- magické collection accessory
Magic to the rescue
class Article {
private $comments;
function getComments() {
return $this->comments;
}
Magic to the rescue
class Article {
protected $comments;
Magic to the rescue
$article->comments
->add(new Comment($article)); // vyhodi vyjimku
$article->comments
->filter(function () { .. }); // ok
Magic to the rescue
class Article {
// ...
function getComments();
function addComment(Comment $comment);
function hasComment(Comment $comment);
function removeComment(Comment $comment);
Less is more
class Comment {
private $article;
// ...
function getArticle() {
return $this->article;
}
Less is more
private $published;
function isPublished() {
return $this->published;
}
function publish() {
$this->published = TRUE;
}
Tak od teď,
databáze už zase existuje
Životní cyklus entity
- create -> persist -> flush -> konec rq
- load -> update -> flush -> konec rq
- load -> delete -> flush -> konec rq
Existence entity
- začíná s create
- končí s delete+flush
Aby entita byla entita
- musí mít metadata
- její NS s driverem musí být registrovaný
Mapping
/** @ORMEntity() */
class Article {
use MagicAccessors;
use Identifier;
/** @ORMColumn(type="string") */
protected $title;
Mapping
/**
* @ORMOneToMany(targetEntity="Comment", cascade={"persist"})
* @var Comment[]|ArrayCollection
*/
protected $comments;
Zaregistrujeme
extensions:
console: KdybyConsoleDIConsoleExtension
events: KdybyEventsDIEventsExtension
annotations: KdybyAnnotationsDIAnnotationsExtension
doctrine: KdybyDoctrineDIOrmExtension
Nakonfigurujeme
doctrine:
metadata:
App: %appDir%
Schéma (pomocí sf console)
$ php www/index.php
orm:schema:create
orm:schema:update --dump-sql
$ php www/index.php o:s:u --dump-sql
Migrace
- doctrine/migrations:@dev
- zenify/doctrine-migrations
extensions:
migrations: ZenifyDoctrineMigrationsDIMigrationsExtension
Používáme
$article = new Article('Abc');
$article->addComment(new Comment($article));
$em->persist($article);
$em->flush();
Používáme
$article = $em->find(Article::class, 1);
$article->title = "lol";
$em->flush();
Praktické použití entit
V presenterech
private $article;
public function actionDefault() {
$this->article = $this->em->find(Article::class, 42);
}
public function renderDefault() {
$this->template->article = $this->article
}
Ve formulářích: vytvoření
protected function createComponentForm() {
$form = new UIForm;
$form->onSuccess[] = function ($form, $values) {
$article = new Article($values->title);
$this->em->persist($article);
$this->em->flush();
}
return $form;
}
Ve formulářích: editace
protected function createComponentForm() {
$form = new UIForm;
$form->onSuccess[] = function ($form, $values) {
$this->article->setTitle($values->title);
$this->em->flush();
}
return $form;
}
Ve formulářích: alternativně
- univerzální formuláře pro create/edit
- form mappery (Kdyby/DoctrineForms ?)
V komponentách
class ArticleControl extends UIControl {
private $article;
function __construct(Article $article) {
$this->article = $article;
}
public function render() {
$this->template->article = $this->article;
}
Netriviální dotazování
EntityRepository
function findBy(array $criteria, array $orderBy, $limit, $offset)
$repo->findBy(['article.title' => $title]);
$repo->findBy([], ['article.title' => 'DESC'])
function countBy(array $criteria)
$repo->countBy(['article.author' => $author]);
function findPairs($criteria, $value, $orderBy, $key)
$repo->findPairs(['currency' => 'USD'], "name")
DQL & Query builder
$qb = $em->createQueryBuilder();
$qb->select('u')
->from(User::class, 'u')
->where('u.id = :id')->setParameter('id', 123)
->orderBy('u.name', 'ASC');
DQL & Query builder
$query = $qb->getQuery();
/** @var User[] $result */
$result = $query->getResult();
Native query
$sql = 'SELECT * FROM users WHERE name = ?';
$rsm = new ResultSetMapping();
// build rsm here
$query = $entityManager->createNativeQuery($sql, $rsm);
$query->setParameter(1, 'romanb');
$users = $query->getResult();
ResultSet: why
$paginator = $this['vp']->getPaginator();
$paginator->setItemsCount($articles->countAll());
$this->template->articles = $articles->findAll(
$paginator->getOffset(),
$paginator->getLength()
);
ResultSet: why
function findAll($limit, $offset) {
return $this->repository
->createQuery("SELECT a FROM AppArticle a")
->setMaxResults($limit)
->setFirstResult($offset);
function countAll() {
return $this->repository
->createQuery("SELECT COUNT(a.id) FROM AppArticle a")
->getSingleScalarResult();
ResultSet: how
public function findAll() {
$query = $this->repository
->createQuery("SELECT a FROM AppArticle a")
return new ResultSet($query);
}
// usage
$this->template->articles = $articles->findAll()
->applyPaginator($this['vp']->getPaginator());
Ještě složitější
(ale mocnější) dotazování
Query Object
class QuestionsQuery extends KdybyDoctrineQueryObject
{
/**
* @param KdybyPersistenceQueryable $repository
* @return DoctrineORMQueryBuilder
*/
protected function doCreateQuery(Queryable $repository);
Query Object
function inCategory(Category $cat) {
$this->filter[] = function (QueryBuilder $qb) use ($cat) {
$qb->andWhere('q.category = :categoryId')
->setParameter('categoryId', $cat->getId());
};
return $this;
}
Query Object
public function withLastPost() {
$this->select[] = function (QueryBuilder $qb) {
$qb->addSelect(lp')
->leftJoin('q.lastPost', 'lp');
};
return $this;
}
Query Object
$query = (new QuestionsQuery())
->withLastPost()
->inCategory($category);
$result = $repository->fetch($query);
1+N problem
- optimální množství dat
- vysoká komplexita
- špatná odezva na síti
- performance killer
1+N problem
$qb->select('article, comment')
->from(Article::class, 'article')
->leftJoin('article.comments', 'comment')
M*N problem: násobení řádků
- moc dat na projití
- vysoká komplexita
- moc práce pro databázi
- moc práce pro doctrine
- performance killer
Query Object: efektivně
- každá query musí načítat pouze toOne
relace
- toMany relace načítat s WHERE IN dalšími
queries (postFetch)
- konstatní počet queries
Query Object: postFetch
public function withComments() {
$this->onPostFetch[] = function ($_, Queryable $repository, Iterator $iterator) {
$ids = array_keys(iterator_to_array($iterator, TRUE));
$repository->createQueryBuilder()
->select('partial article.{id}', 'comments')
->from(Article::class, 'article')
->leftJoin('article.comments', 'comments')
->andWhere('article.id IN (:ids)')->setParameter('ids', $ids)
->getQuery()->getResult();
}
return $this;
}
Query Object: postFetch
$query = (new ArticlesQuery())
->withComments();
$result = $repository->fetch($query);
Repozitáře, fasády,
služby
aneb pětivrstvý model
Repozitáře
- vhodné použití ve facade
- vhodné použití v services
- ale klidně i v presenteru/komponentě
- pouze čtení!
Repozitáře
/**
* @ORMEntity(repositoryClass="ArticleRepository")
*/
class Article { }
class ArticleRepository
extends KdybyDoctrineEntityRepository { }
services:
- AppBlogArticleRepository()
-
class: AppBlogArticleRepository()
tags: [doctrine.repositoryEntity: AppBlogArticle]
Repozitáře jako služby
Facade
- brána mezi presenterem/komponentou a
modelem
- snadnější sdílení logiky mezi api a frontem
- vhodné místo pro flush
- není nutné mít 1:1 k entitám
Facade
class BlogFacade {
function findPublished();
function fetch(ArticlesQuery $query);
function saveComment(Comment $comment);
class RedactorFacade {
function createDraft();
function save(Article $article);
Služby
- dělení aplikace na menší logické celky
- nějaké konkrétní operace nad db/entitami
- klidně i externí api
- používají se ve facades
Eventy
Lifecycle eventy
- na entitě
- “full blown” listenery
- listenery pro typ
Lifecycle eventy na entitě
/** @ORMEntity @ORMHasLifecycleCallbacks */
class User {
/** @ORMPrePersist */
public function doStuffOnPrePersist() {
$this->createdAt = date('Y-m-d H:i:s');
}
Listenery
class MyEventListener implements KdybyEventsSubscriber {
function preUpdate(LifecycleEventArgs $args) {
$entityManager = $args->getObjectManager();
$entity = $args->getObject();
if ($entity instanceof User) {
// do something with the User
}
}
Listenery pro typ
class MyEventListener {
function preUpdate(User $user, LifecycleEventArgs $args) {
$entityManager = $args->getObjectManager();
// do something with the User
}
Cache
Cachovací strategie
- result caching
- 2nd level caching
Result caching
$query = $em->createQuery('SELECT u FROM AppUser u');
$query->useResultCache(
true,
$lifetime,
$resultCacheId
);
2nd level cache (since 2.5)
- READ_ONLY (výchozí)
- NOSTRICT_READ_WRITE
- READ_WRITE (zámky)
2nd level cache
doctrine:
secondLevelCache:
enabled: true
driver: redis(@redis.default_client::getDriver())
2nd level cache
/**
* @ORMCache(region="product")
*/
class Product {
/**
* @ORMManyToMany(...)
* @ORMCache(usage="NONSTRICT_READ_WRITE", region="product")
*/
private $tags;
Shrnutí
- jak psát entity
- práce s db schématem
- použití entit
- pokládání dotazů
- modelové třídy
- cache
Možná někdy příště
- Zápis do db, čtení z Elasticu (to dělá rohlík)
- Command Query Responsibility Segregation
Dotazy?
Díky za pozornost!
@ProchazkaFilip

Weitere ähnliche Inhalte

Was ist angesagt?

CRCE - přehled datového modelu a vybraná API
CRCE - přehled datového modelu a vybraná APICRCE - přehled datového modelu a vybraná API
CRCE - přehled datového modelu a vybraná API
Premek Brada
 

Was ist angesagt? (13)

Clean code
Clean codeClean code
Clean code
 
Kdyby/Redis
Kdyby/RedisKdyby/Redis
Kdyby/Redis
 
CQRS v rohlik.cz
CQRS v rohlik.czCQRS v rohlik.cz
CQRS v rohlik.cz
 
MicroKernel - aneb špatný název pro Helper (5. sraz přátel Symfony v Praze)
MicroKernel - aneb špatný název pro Helper (5. sraz přátel Symfony v Praze)MicroKernel - aneb špatný název pro Helper (5. sraz přátel Symfony v Praze)
MicroKernel - aneb špatný název pro Helper (5. sraz přátel Symfony v Praze)
 
Drupal Front-end
Drupal Front-endDrupal Front-end
Drupal Front-end
 
201502.ReinIT.Dev
201502.ReinIT.Dev201502.ReinIT.Dev
201502.ReinIT.Dev
 
Nette Tester / Posobota
Nette Tester / PosobotaNette Tester / Posobota
Nette Tester / Posobota
 
CRCE - přehled datového modelu a vybraná API
CRCE - přehled datového modelu a vybraná APICRCE - přehled datového modelu a vybraná API
CRCE - přehled datového modelu a vybraná API
 
Jak přemigrovat Slevomat na Doctrine za jedno dopoledne
Jak přemigrovat Slevomat na Doctrine za jedno dopoledneJak přemigrovat Slevomat na Doctrine za jedno dopoledne
Jak přemigrovat Slevomat na Doctrine za jedno dopoledne
 
Na co si dát v Javascriptu pozor? - Barcamp Hradec Králové 2015
Na co si dát v Javascriptu pozor? - Barcamp Hradec Králové 2015Na co si dát v Javascriptu pozor? - Barcamp Hradec Králové 2015
Na co si dát v Javascriptu pozor? - Barcamp Hradec Králové 2015
 
Vývoj aplikací pro iOS
Vývoj aplikací pro iOSVývoj aplikací pro iOS
Vývoj aplikací pro iOS
 
Django
DjangoDjango
Django
 
Aplikační nastavení v .NET
Aplikační nastavení v .NETAplikační nastavení v .NET
Aplikační nastavení v .NET
 

Andere mochten auch

Symfony2 and Doctrine2 Integration
Symfony2 and Doctrine2 IntegrationSymfony2 and Doctrine2 Integration
Symfony2 and Doctrine2 Integration
Jonathan Wage
 
Triangulodesuelostextura
TriangulodesuelostexturaTriangulodesuelostextura
Triangulodesuelostextura
Paulina Azpiroz
 
Fairy tail 001_24
Fairy tail 001_24Fairy tail 001_24
Fairy tail 001_24
Acnologia
 
2015_CTI_BSc-IT_Module-Description_Final1
2015_CTI_BSc-IT_Module-Description_Final12015_CTI_BSc-IT_Module-Description_Final1
2015_CTI_BSc-IT_Module-Description_Final1
Moses75
 
Cover page (joriz)
Cover page (joriz)Cover page (joriz)
Cover page (joriz)
graessel
 
Doctrine In The Real World sflive2011 Paris
Doctrine In The Real World sflive2011 ParisDoctrine In The Real World sflive2011 Paris
Doctrine In The Real World sflive2011 Paris
Jonathan Wage
 

Andere mochten auch (19)

Understanding Doctrine at True North PHP 2013
Understanding Doctrine at True North PHP 2013Understanding Doctrine at True North PHP 2013
Understanding Doctrine at True North PHP 2013
 
Symfony2. Database and Doctrine
Symfony2. Database and DoctrineSymfony2. Database and Doctrine
Symfony2. Database and Doctrine
 
Symfony2 and Doctrine2 Integration
Symfony2 and Doctrine2 IntegrationSymfony2 and Doctrine2 Integration
Symfony2 and Doctrine2 Integration
 
ORO Meetups - Doctrine Events
ORO Meetups - Doctrine EventsORO Meetups - Doctrine Events
ORO Meetups - Doctrine Events
 
ORM dont kill your DB, developers do
ORM dont kill your DB, developers doORM dont kill your DB, developers do
ORM dont kill your DB, developers do
 
Triangulodesuelostextura
TriangulodesuelostexturaTriangulodesuelostextura
Triangulodesuelostextura
 
Fairy tail 001_24
Fairy tail 001_24Fairy tail 001_24
Fairy tail 001_24
 
2015_CTI_BSc-IT_Module-Description_Final1
2015_CTI_BSc-IT_Module-Description_Final12015_CTI_BSc-IT_Module-Description_Final1
2015_CTI_BSc-IT_Module-Description_Final1
 
Cover page (joriz)
Cover page (joriz)Cover page (joriz)
Cover page (joriz)
 
the language of literaure
the language of literaurethe language of literaure
the language of literaure
 
Poblacio d'Espanya (II)
Poblacio d'Espanya (II)Poblacio d'Espanya (II)
Poblacio d'Espanya (II)
 
Climate and weather
Climate and weatherClimate and weather
Climate and weather
 
εφηβεία
εφηβείαεφηβεία
εφηβεία
 
Testování prakticky
Testování praktickyTestování prakticky
Testování prakticky
 
Value Added Architecture2
Value Added Architecture2Value Added Architecture2
Value Added Architecture2
 
HEALTHCARE ARCHITECTURE
HEALTHCARE ARCHITECTUREHEALTHCARE ARCHITECTURE
HEALTHCARE ARCHITECTURE
 
Effective Doctrine2: Performance Tips for Symfony2 Developers
Effective Doctrine2: Performance Tips for Symfony2 DevelopersEffective Doctrine2: Performance Tips for Symfony2 Developers
Effective Doctrine2: Performance Tips for Symfony2 Developers
 
Doctrine In The Real World sflive2011 Paris
Doctrine In The Real World sflive2011 ParisDoctrine In The Real World sflive2011 Paris
Doctrine In The Real World sflive2011 Paris
 
Doctrine 2 - Not The Same Old Php Orm
Doctrine 2 - Not The Same Old Php OrmDoctrine 2 - Not The Same Old Php Orm
Doctrine 2 - Not The Same Old Php Orm
 

Ähnlich wie Doctrine ORM & model

Testování presenterů v Nette
Testování presenterů v NetteTestování presenterů v Nette
Testování presenterů v Nette
Taste Medio
 

Ähnlich wie Doctrine ORM & model (11)

Petr Heinz - Čisté testy, dobré testy
Petr Heinz - Čisté testy, dobré testyPetr Heinz - Čisté testy, dobré testy
Petr Heinz - Čisté testy, dobré testy
 
MicroKernel aneb spatny nazev pro Helper (5. sraz pratel Symfony)
MicroKernel aneb spatny nazev pro Helper (5. sraz pratel Symfony)MicroKernel aneb spatny nazev pro Helper (5. sraz pratel Symfony)
MicroKernel aneb spatny nazev pro Helper (5. sraz pratel Symfony)
 
Miroslav Bajtoš - Nativní async/await v Node.js - už tam jsme?
Miroslav Bajtoš - Nativní async/await v Node.js - už tam jsme?Miroslav Bajtoš - Nativní async/await v Node.js - už tam jsme?
Miroslav Bajtoš - Nativní async/await v Node.js - už tam jsme?
 
MoroSystems na ostravském CZJUGu o Apache Wicket
MoroSystems na ostravském CZJUGu o Apache WicketMoroSystems na ostravském CZJUGu o Apache Wicket
MoroSystems na ostravském CZJUGu o Apache Wicket
 
Novinky v PostgreSQL 9.4 a JSONB
Novinky v PostgreSQL 9.4 a JSONBNovinky v PostgreSQL 9.4 a JSONB
Novinky v PostgreSQL 9.4 a JSONB
 
Péhápkaři - Píšeme čitelný kód #3 by Driveto
Péhápkaři - Píšeme čitelný kód #3 by DrivetoPéhápkaři - Píšeme čitelný kód #3 by Driveto
Péhápkaři - Píšeme čitelný kód #3 by Driveto
 
Nette\Utils a příbuzní: skryté klenoty (Poslední sobota #68)
Nette\Utils a příbuzní: skryté klenoty (Poslední sobota #68)Nette\Utils a příbuzní: skryté klenoty (Poslední sobota #68)
Nette\Utils a příbuzní: skryté klenoty (Poslední sobota #68)
 
Testování presenterů v Nette
Testování presenterů v NetteTestování presenterů v Nette
Testování presenterů v Nette
 
Technologie užívané při vývoji velkých e-shopů
Technologie užívané při vývoji velkých e-shopůTechnologie užívané při vývoji velkých e-shopů
Technologie užívané při vývoji velkých e-shopů
 
Czechitas - školení PHP/Symfony MicroKernel
Czechitas - školení PHP/Symfony MicroKernelCzechitas - školení PHP/Symfony MicroKernel
Czechitas - školení PHP/Symfony MicroKernel
 
Scala - jazyk budoucnosti
Scala - jazyk budoucnostiScala - jazyk budoucnosti
Scala - jazyk budoucnosti
 

Doctrine ORM & model