75
Doctrine ORM & model @ProchazkaFilip

Doctrine ORM & model

Embed Size (px)

Citation preview

Page 1: Doctrine ORM & model

Doctrine ORM& model

@ProchazkaFilip

Page 2: Doctrine ORM & model

kdyby/doctrine v3.0

Page 3: Doctrine ORM & model

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

Page 4: Doctrine ORM & model

Jak psát entity

Page 5: Doctrine ORM & model

Databáze neexistuje

$articles = [

new Article('abc'),

new Article('def'),

new Article('ghi')

];

Page 6: Doctrine ORM & model

Asociace mezi entitami

class Comment {

private $article;

public function __construct(Article $article) {

$this->article = $article;

}

Page 7: Doctrine ORM & model

Asociace mezi entitamiclass Article {

private $comments;

public function __construct() {

$this->comments = new ArrayCollection();

}

function addComment(Comment $comment) {

$this->comments[] = $comment;

}

Page 8: Doctrine ORM & model

Nahaté kolekce? Never!

class Article {

// ...

function getComments() {

return $this->comments;

}

Page 9: Doctrine ORM & model

Magic to the rescue

use Kdyby\Doctrine\Entities\MagicAccessors;

class Article {

use MagicAccessors;

Page 10: Doctrine ORM & model

Magic to the rescue

- pouze na protected properties- magické (get|set)tery- magické collection accessory

Page 11: Doctrine ORM & model

Magic to the rescue

class Article {

private $comments;

function getComments() {

return $this->comments;

}

Page 12: Doctrine ORM & model

Magic to the rescue

class Article {

protected $comments;

Page 13: Doctrine ORM & model

Magic to the rescue

$article->comments

->add(new Comment($article)); // vyhodi vyjimku

$article->comments

->filter(function () { .. }); // ok

Page 14: Doctrine ORM & model

Magic to the rescue

class Article {

// ...

function getComments();

function addComment(Comment $comment);

function hasComment(Comment $comment);

function removeComment(Comment $comment);

Page 15: Doctrine ORM & model

Less is more

class Comment {

private $article;

// ...

function getArticle() {

return $this->article;

}

Page 16: Doctrine ORM & model

Less is moreprivate $published;

function isPublished() {

return $this->published;

}

function publish() {

$this->published = TRUE;

}

Page 17: Doctrine ORM & model

Tak od teď,databáze už zase existuje

Page 18: Doctrine ORM & model

Ž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

Page 19: Doctrine ORM & model

Aby entita byla entita

- musí mít metadata- její NS s driverem musí být registrovaný

Page 20: Doctrine ORM & model

Mapping/** @ORM\Entity() */

class Article {

use MagicAccessors;

use Identifier;

/** @ORM\Column(type="string") */

protected $title;

Page 21: Doctrine ORM & model

Mapping

/**

* @ORM\OneToMany(targetEntity="Comment", cascade={"persist"})

* @var Comment[]|ArrayCollection

*/

protected $comments;

Page 22: Doctrine ORM & model

Zaregistrujeme

extensions:

console: Kdyby\Console\DI\ConsoleExtension

events: Kdyby\Events\DI\EventsExtension

annotations: Kdyby\Annotations\DI\AnnotationsExtension

doctrine: Kdyby\Doctrine\DI\OrmExtension

Page 23: Doctrine ORM & model

Nakonfigurujeme

doctrine:

metadata:

App: %appDir%

Page 24: Doctrine ORM & model

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

Page 25: Doctrine ORM & model

Migrace

- doctrine/migrations:@dev

- zenify/doctrine-migrations

extensions:

migrations: Zenify\DoctrineMigrations\DI\MigrationsExtension

Page 26: Doctrine ORM & model

Používáme

$article = new Article('Abc');

$article->addComment(new Comment($article));

$em->persist($article);

$em->flush();

Page 27: Doctrine ORM & model

Používáme

$article = $em->find(Article::class, 1);

$article->title = "lol";

$em->flush();

Page 28: Doctrine ORM & model

Praktické použití entit

Page 29: Doctrine ORM & model

V presenterechprivate $article;

public function actionDefault() {

$this->article = $this->em->find(Article::class, 42);

}

public function renderDefault() {

$this->template->article = $this->article

}

Page 30: Doctrine ORM & model

Ve formulářích: vytvořeníprotected function createComponentForm() {

$form = new UI\Form;

$form->onSuccess[] = function ($form, $values) {

$article = new Article($values->title);

$this->em->persist($article);

$this->em->flush();

}

return $form;

}

Page 31: Doctrine ORM & model

Ve formulářích: editaceprotected function createComponentForm() {

$form = new UI\Form;

$form->onSuccess[] = function ($form, $values) {

$this->article->setTitle($values->title);

$this->em->flush();

}

return $form;

}

Page 32: Doctrine ORM & model

Ve formulářích: alternativně

- univerzální formuláře pro create/edit- form mappery (Kdyby/DoctrineForms ?)

Page 33: Doctrine ORM & model

V komponentáchclass ArticleControl extends UI\Control {

private $article;

function __construct(Article $article) {

$this->article = $article;

}

public function render() {

$this->template->article = $this->article;

}

Page 34: Doctrine ORM & model

Netriviální dotazování

Page 35: Doctrine ORM & model

EntityRepositoryfunction 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")

Page 36: Doctrine ORM & model

DQL & Query builder

$qb = $em->createQueryBuilder();

$qb->select('u')

->from(User::class, 'u')

->where('u.id = :id')->setParameter('id', 123)

->orderBy('u.name', 'ASC');

Page 37: Doctrine ORM & model

DQL & Query builder

$query = $qb->getQuery();

/** @var User[] $result */

$result = $query->getResult();

Page 38: Doctrine ORM & model

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();

Page 39: Doctrine ORM & model

ResultSet: why$paginator = $this['vp']->getPaginator();

$paginator->setItemsCount($articles->countAll());

$this->template->articles = $articles->findAll(

$paginator->getOffset(),

$paginator->getLength()

);

Page 40: Doctrine ORM & model

ResultSet: whyfunction findAll($limit, $offset) {

return $this->repository

->createQuery("SELECT a FROM App\Article a")

->setMaxResults($limit)

->setFirstResult($offset);

function countAll() {

return $this->repository

->createQuery("SELECT COUNT(a.id) FROM App\Article a")

->getSingleScalarResult();

Page 41: Doctrine ORM & model

ResultSet: howpublic function findAll() {

$query = $this->repository

->createQuery("SELECT a FROM App\Article a")

return new ResultSet($query);

}

// usage

$this->template->articles = $articles->findAll()

->applyPaginator($this['vp']->getPaginator());

Page 42: Doctrine ORM & model

Ještě složitější(ale mocnější) dotazování

Page 43: Doctrine ORM & model

Query Object

class QuestionsQuery extends Kdyby\Doctrine\QueryObject

{

/**

* @param \Kdyby\Persistence\Queryable $repository

* @return \Doctrine\ORM\QueryBuilder

*/

protected function doCreateQuery(Queryable $repository);

Page 44: Doctrine ORM & model

Query Object

function inCategory(Category $cat) {

$this->filter[] = function (QueryBuilder $qb) use ($cat) {

$qb->andWhere('q.category = :categoryId')

->setParameter('categoryId', $cat->getId());

};

return $this;

}

Page 45: Doctrine ORM & model

Query Object

public function withLastPost() {

$this->select[] = function (QueryBuilder $qb) {

$qb->addSelect(lp')

->leftJoin('q.lastPost', 'lp');

};

return $this;

}

Page 46: Doctrine ORM & model

Query Object

$query = (new QuestionsQuery())

->withLastPost()

->inCategory($category);

$result = $repository->fetch($query);

Page 47: Doctrine ORM & model

1+N problem

- optimální množství dat- vysoká komplexita- špatná odezva na síti- performance killer

Page 48: Doctrine ORM & model

1+N problem

$qb->select('article, comment')

->from(Article::class, 'article')

->leftJoin('article.comments', 'comment')

Page 49: Doctrine ORM & model

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

Page 50: Doctrine ORM & model
Page 51: Doctrine ORM & model

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

Page 52: Doctrine ORM & model

Query Object: postFetchpublic 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;

}

Page 53: Doctrine ORM & model

Query Object: postFetch

$query = (new ArticlesQuery())

->withComments();

$result = $repository->fetch($query);

Page 54: Doctrine ORM & model

Repozitáře, fasády, služby

aneb pětivrstvý model

Page 55: Doctrine ORM & model

Repozitáře

- vhodné použití ve facade- vhodné použití v services- ale klidně i v presenteru/komponentě- pouze čtení!

Page 56: Doctrine ORM & model

Repozitáře

/**

* @ORM\Entity(repositoryClass="ArticleRepository")

*/

class Article { }

class ArticleRepository

extends Kdyby\Doctrine\EntityRepository { }

Page 57: Doctrine ORM & model

services:

- App\Blog\ArticleRepository()

-

class: App\Blog\ArticleRepository()

tags: [doctrine.repositoryEntity: App\Blog\Article]

Repozitáře jako služby

Page 58: Doctrine ORM & model

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

Page 59: Doctrine ORM & model

Facade

class BlogFacade {

function findPublished();

function fetch(ArticlesQuery $query);

function saveComment(Comment $comment);

class RedactorFacade {

function createDraft();

function save(Article $article);

Page 60: Doctrine ORM & model

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

Page 61: Doctrine ORM & model

Eventy

Page 62: Doctrine ORM & model

Lifecycle eventy

- na entitě- “full blown” listenery- listenery pro typ

Page 63: Doctrine ORM & model

Lifecycle eventy na entitě

/** @ORM\Entity @ORM\HasLifecycleCallbacks */

class User {

/** @ORM\PrePersist */

public function doStuffOnPrePersist() {

$this->createdAt = date('Y-m-d H:i:s');

}

Page 64: Doctrine ORM & model

Listenery

class MyEventListener implements Kdyby\Events\Subscriber {

function preUpdate(LifecycleEventArgs $args) {

$entityManager = $args->getObjectManager();

$entity = $args->getObject();

if ($entity instanceof User) {

// do something with the User

}

}

Page 65: Doctrine ORM & model

Listenery pro typ

class MyEventListener {

function preUpdate(User $user, LifecycleEventArgs $args) {

$entityManager = $args->getObjectManager();

// do something with the User

}

Page 66: Doctrine ORM & model

Cache

Page 67: Doctrine ORM & model

Cachovací strategie

- result caching- 2nd level caching

Page 68: Doctrine ORM & model

Result caching

$query = $em->createQuery('SELECT u FROM App\User u');

$query->useResultCache(

true,

$lifetime,

$resultCacheId

);

Page 69: Doctrine ORM & model

2nd level cache (since 2.5)- READ_ONLY (výchozí)- NOSTRICT_READ_WRITE- READ_WRITE (zámky)

Page 70: Doctrine ORM & model

2nd level cache

doctrine:

secondLevelCache:

enabled: true

driver: redis(@redis.default_client::getDriver())

Page 71: Doctrine ORM & model

2nd level cache/**

* @ORM\Cache(region="product")

*/

class Product {

/**

* @ORM\ManyToMany(...)

* @ORM\Cache(usage="NONSTRICT_READ_WRITE", region="product")

*/

private $tags;

Page 72: Doctrine ORM & model

Shrnutí

- jak psát entity- práce s db schématem- použití entit- pokládání dotazů- modelové třídy- cache

Page 73: Doctrine ORM & model

Možná někdy příště

- Zápis do db, čtení z Elasticu (to dělá rohlík)- Command Query Responsibility Segregation

Page 74: Doctrine ORM & model

Dotazy?

Page 75: Doctrine ORM & model

Díky za pozornost!@ProchazkaFilip