+ All Categories
Home > Software > Doctrine - Co dělat když entity nestačí [Filip Procházka] (7. sraz, Praha)

Doctrine - Co dělat když entity nestačí [Filip Procházka] (7. sraz, Praha)

Date post: 09-Jan-2017
Category:
Upload: pehapkari
View: 225 times
Download: 7 times
Share this document with a friend
35
Doctrine: Co dělat, když entity nestačí? @ProchazkaFilip #makeCodeNotWar
Transcript

Doctrine: Co dělat,když entity nestačí?

@ProchazkaFilip

#makeCodeNotWar

Co si povíme?● minimum využití databáze každého Doctrinisty● batch operace● rozšiřování DQL● metody hydratace● native queries

#makeCodeNotWar

Minimum využití databázekaždého Doctrinisty

Minimum využití databáze každého Doctrinisty● Co za vás dělá Doctrine

○ indexy pro vazby a primární klíče○ vzdálené klíče

● Co si musíte pohlídat sami○ unique indexy○ vlastní typy

Indexy a vzdálené klíče

class User {

/**

* @ORM\Id()

* @ORM\Column(type="uuid")

*/

private $id;

class Order {

/**

* @ORM\ManyToOne(targetEntity=User::class)

* @ORM\JoinColumn(nullable=false)

*/

private $user;

Unique indexy

class User {

/**

* @ORM\Column(type="string", unique=true)

*/

private $email;

Unique indexy/**

* @ORM\Entity()

* @ORM\Table(name="order_item_rating",

* uniqueConstraints={

* @ORM\UniqueConstraint(name="order_item_rating_x_unique", columns={

* "order_item_id", "user_id"

* })

* }

* )

*/

class OrderItemRating

Unique indexy: vkládání a race conditions● Opravdu to potřebujete řešit?

○ >90% aplikacím stačí check přes repository před flushem○ >90% aplikací nikdy nenaroste natolik, aby to byl skutečný problém

● Pokud to opravdu opravdu potřebujete řešit○ kdyby/doctrine ... NonLockingUniqueInserter ○ ^ brzy i samostatně, jako kdyby/doctrine-nonlocking-unique-inserter○ ^ Symfony friendly ❤○ ^ sledujte issue kdyby/doctrine#238

Vlastní datový typclass UuidType extends Type {

const NAME = 'uuid';

function getName() { return self::NAME; }

function getSQLDeclaration(

array $fieldDeclaration, AbstractPlatform $platform);

function convertToPHPValue(

$value, AbstractPlatform $platform);

function convertToDatabaseValue(

$value, AbstractPlatform $platform);

Zdroj: ramsey/uuid-doctrine

Vlastní datový typ: komentáře ve schématu

class SomethingBasedOnStringType extends StringType {

function requiresSQLCommentHint(

AbstractPlatform $platform) : bool

// …

$platform->markDoctrineTypeCommented(Type::getType($type));

Vlastní datový typ: konverze na úrovni DB

class GeometryType extends Type {

function canRequireSQLConversion() : bool;

function convertToDatabaseValueSQL(

$sqlExpr, AbstractPlatform $platform);

function convertToPHPValueSQL(

$sqlExpr, $platform);

Vlastní datový typ: registrace

doctrine:

dbal:

types:

uuid:

class: Ramsey\Uuid\Doctrine\UuidType

Zdroj: ramsey/uuid-doctrine

Vlastní datový typ: použití

/**

* @ORM\Id()

* @ORM\Column(type="uuid")

*/

private $id;

CREATE TABLE "user" (

id UUID NOT NULL,

email VARCHAR(255) DEFAULT NULL

-- atd

);

COMMENT ON COLUMN "user".id

IS '(DC2Type:uuid)';

Logika v databázivs

Logika v modelu

Premature optimization is the root of all evil ~ Donald Knuth

Batch operace

Batch operace s DQL

“UPDATE/DELETE statements are ported directly into a Database statement and therefore bypass any locking scheme, events and do not increment the version column. Entities that are already loaded into the persistence context will NOT be synced with the updated database state. It is recommended to call EntityManager#clear() and retrieve new instances of any affected entity.”

~ Dokumentace

Batch operace s DQL● Zkuste nejprve chytřejší způsoby iterace nad výsledkem

○ ORM\Query::iterate();

○ Stránkování

● Raději DQL update, než SQL update

● Neumí JOINy :(

Batch operace s DQL

$select = $em->createQueryBuilder()

->addSelect('orderItem.price')

->from(OrderItem::class, 'orderItem')

->andWhere('orderItem.order = theOrder.id');

$update = $em->createQueryBuilder()

->update(Order::class, 'theOrder')

->set('theOrder.totalPrice', sprintf('(%s)', $select))

->getQuery();

Batch operace s DQL

UPDATE "order"

SET total_price = (

SELECT SUM(o0_.price) AS dctrn__1

FROM order_item o0_

WHERE o0_.order_id = order.id

)

Batch operace s DBAL● $connection->prepare('UPDATE ...');

● $connection->createQueryBuilder();

Rozšiřování DQL

Rozšiřování DQL● TreeWalker - může modifikovat AST● output SqlWalker - generuje samotný SQL dotaz● vlastní funkce

Rozšiřování DQL: limitace● Gramatika je jasně definovaná, není možné to nijak ohackovat● Tree Walker může modifikovat výsledné AST, ale opět nezmění gramatiku● SqlWalker to nemá jak zachránit

Rozšiřování DQL: co jdeKam nemůže DQL

SELECT order.finishedTime IS NULL AS something

FROM ...;

Tam musí funkce

SELECT IS_NULL(order.finishedTime) AS something

FROM ...;

Rozšiřování DQL: vlastní funkceclass IsNull extends FunctionNode {

private $expression;

public function getSql(SqlWalker $sqlWalker) {

return $sqlWalker->walkArithmeticPrimary($this->expression) . ' IS NULL';

}

public function parse(Parser $parser) {

$parser->match(Lexer::T_IDENTIFIER);

$parser->match(Lexer::T_OPEN_PARENTHESIS);

$this->expression = $parser->ArithmeticPrimary();

$parser->match(Lexer::T_CLOSE_PARENTHESIS);

}

SELECT employee,

AVG(salary) OVER (PARTITION BY employee.department) AS avgSalary

FROM My\Employee employee;

SELECT employee,

WINDOW_OVER(AVG(salary), employee.department) AS avgSalary

FROM My\Employee employee;

SELECT employee,

WINDOW(AVG(salary) OVER (PARTITION BY employee.department)) AS avgSalary

FROM My\Employee employee;

Rozšiřování DQL: komplexnější syntaxe

Rozšiřování DQL: existující knihovny● oro/doctrine-extensions (MySQL, PostgreSQL)● opsway/doctrine-dbal-postgresql (PostgreSQL)● beberlei/DoctrineExtensions (MySQL, Oracle)● syslogic/doctrine-json-functions (MySQL)● další na: https://packagist.org/search/?q=dql

Hydratace

Druhy hydratace● entity

○ ORM\Query::getResult();

● scalar + entity (mixed results)○ ORM\Query::getResult();

● array - vytvoří strukturu jako pro entity a tu vrátí○ ORM\Query::getArrayResult();

● scalar - přejmenuje sloupce na fieldy a přetypuje○ ORM\Query::getScalarResult();

Native queries

$rsm = new ResultSetMappingBuilder($em);

$rsm->addRootEntityFromClassMetadata(OrderItem::class, 'order_item')

$connection->createQueryBuilder()

->addSelect($rsm->generateSelectClause())

->from(...)

$em->createNativeQuery($sql, $rsm)

Shrnutí: co si z toho odnést?● Logika v modelu dokud to jenom trochu jde● Nebát se využívat hojně vlastní typy● Rozšiřování DQL je snadné● Doctrine není určená na batch operace

Kam dál?● Youtube kanál “Nette Framework” > search “Doctrine”

Dotazy?

Díky za pozornost!@ProchazkaFilip

#makeCodeNotWar

Díky za pozornost!@ProchazkaFilip

#makeCodeNotWar


Recommended