SlideShare une entreprise Scribd logo
1  sur  77
Doctrine | SymfonyCon2019 1
Doctrine
-
Doctrine ORM
Doctrine | SymfonyCon2019 2
Doctrine
●
Doctrine project architecture
●
Common
●
DBAL (Database Abstraction Layer)
●
ORM (Object Relationnal Mapping)
●
How to with ORM
Doctrine | SymfonyCon2019 3
The doctrine project
●
A bunch of projects
●
Like Symfony Components
●
Standalone
●
Some dependencies around them
●
https://www.doctrine-project.org/projects.html
Doctrine | SymfonyCon2019 4
●
An organisation designing libraries for modern PHP development
●
Database focused
●
Database abstraction
●
Universal component
●
Common
●
General pupose tools
●
DBAL
●
Database Abstract Layer
●
Based on, and using PDO
●
ORM
●
Object Relationnal Mapper
What is Doctrine ?
Doctrine | SymfonyCon2019 5
Doctrine project
Doctrine | SymfonyCon2019 6
What we'll study
Doctrine | SymfonyCon2019 7
Doctrine-Annotations
use DoctrineCommonAnnotationsAnnotation;
class MyAnnot extends Annotation
{
public $something;
public $hey = array();
}
/**
* @MyAnnot(something="foo", hey="hola")
*/
class Foo
{ }
$an = new DoctrineCommonAnnotationsAnnotationReader();
var_dump($an->getClassAnnotations(new ReflectionClass('Foo')));
Annotation ::= "@" AnnotationName ["(" [Values] ")"]
AnnotationName ::= QualifiedName | SimpleName
QualifiedName ::= NameSpacePart "" {NameSpacePart ""}* SimpleName
NameSpacePart ::= identifier | null | false | true
SimpleName ::= identifier | null | false | true
array
0 =>
object(MyAnnot)[32]
public 'something' => string 'foo'
public 'hey' => string 'hola'
public 'value' => null
Doctrine | SymfonyCon2019 8
Doctrine - DBAL
Doctrine | SymfonyCon2019 9
DBAL : Database Abstraction Layer
●
Uses PDO and copies its API
●
You must know PDO
●
DBAL :
●
Allows simple CRUD
●
Allows query building using OO
●
Abstracts SQL types and allow mapping them on PHP types
●
Allows to play with the DB Schema structure
●
Can log queries
Doctrine | SymfonyCon2019 10
DBAL : Connection
●
The main element
Doctrine | SymfonyCon2019 11
DBAL : connection
●
Main element
●
Allows to drive the whole DB
●
Like :
●
Connect() - close()
●
Insert()
●
Update() - executeUpdate()
●
Delete()
●
Query() - executeQuery()- exec() - prepare()
●
fetchAll() - fetchArray() - fetchColumn() - fetchRow()
●
BeginTransaction() - commit() - rollback()
Doctrine | SymfonyCon2019 12
DBAL : Easy
$stmt = $con->query("SELECT id, title, comment FROM Ratings");
while($result = $stmt->fetch()) {
vprintf("%d, %s, %s", $result);
}
$params = array('unix_socket'=>'/var/run/mysqld/mysqld.sock',
'user'=>'foobar',
'password'=>'secret',
'driver'=>'pdo_mysql',
'dbname'=>'foobar');
$con = DoctrineDBALDriverManager::getConnection($params);
Doctrine | SymfonyCon2019 13
DBAL looks like PDO
●
DBAL has a very similar interface that PDO
●
But it overpowers it significantly
$stmt = $con->executeQuery("SELECT id, text FROM Ratings WHERE InsertTimeStamp > ?
AND id IN(?)",
[new DateTime("now"), [2,3,4]],
['datetime', DoctrineDBALConnection::PARAM_INT_ARRAY];
while($result = $stmt->fetch()) {
vprintf("%d, %s", $result);
}
$con->delete('Ratings', ['id' => 3] ) ;
$con->update('Ratings', ['comment'=>'hey !!'] ,['id' => 3] ) ;
$con->insert('Ratings', [/* Columns detail */]) ;
Doctrine | SymfonyCon2019 14
DBAL QueryBuilder
●
Build and practice queries with OO API
●
An ExpressionBuilder also exists
$query = $con->createQueryBuilder() ;
$query->from('User', 'u')
->select('u.id')
->addSelect("DATE_FORMAT(User.regdate, '%Y%m%d') as DATESORT")
->add('join', [
'c' => [
'joinType' => 'straight',
'joinTable' => 'Comments',
'joinAlias' => 'comment',
'joinCondition' => 'u.comment=c.id'
]
], true)
/* … etc … */
echo $query; /* __toString() */
Doctrine | SymfonyCon2019 15
DBAL and schema introspection
●
API :
●
listDatabases()
●
listFunctions() - listSequences()
●
listTableColumns($tableName)
●
listTableConstraints($tableName)
●
listTableDetails($tableName)
●
listTableForeignKeys($tableName)
●
listTableIndexes($tableName)
●
listTables() platform = $con->getDatabasePlatform();
$schema = new DoctrineDBALSchemaSchema();
$myTable = $schema->createTable("Users");
$myTable->addColumn("id", "integer", ["unsigned" => true]);
$myTable->addColumn("username", "string", ["length" => 32]);
$myTable->setPrimaryKey(["id"]);
$queries = $schema->toSql($platform);
Doctrine | SymfonyCon2019 16
Doctrine - ORM
Doctrine | SymfonyCon2019 17
ORM ?
●
System design to adapt relationnal system to OO system
●
Persistence : mecanism to flush object data into database so that
data can survive across HTTP requests by being restored
●
ORM makes use of metadata to bind OO model to relationnal
model
●
Metadata need to be in sync with the model
O.R.M
Doctrine | SymfonyCon2019 18
Doctrine ORM
Entities
Repository
EntityManager
UnitOfWork
DBAL Connection
PDO
ClassMetadata
IdentityMap
DataPersisters
Doctrine | SymfonyCon2019 19
Entity
●
Business layer synchronized data object
●
Describes metadata
●
Annotations
●
Xml
●
Do not extend any class (DTO)
●
Can have getters and setters
●
Can have some business logic
●
Can be validated by Sf Validators (Sf Form usage)
●
Should not depend on any other service
Doctrine | SymfonyCon2019 20
Entities
namespace Entities;
/**
* @Entity
* @Table(name="my_users")
*/
class User
{
/**
* @Id
* @GeneratedValue
* @Column(type="integer", length="5")
*/
protected $id;
/**
* @Column(type="text", name="user_name")
*/
protected $name;
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
}
public function getId() {
return $this->id;
}
}
●
Can be generated from the DB schema
analysis
●
Mapping is usually described using
annotations for metadata
Doctrine | SymfonyCon2019 21
Types mapping
●
PHP types are mapped onto RDBM types depending on the RDBM brand
●
You can add your own types
●
DoctrineDBALTypesType
Doctrine | SymfonyCon2019 22
RDBM - SQL Doctrine
Metadata
●
Doctrine needs to read the metadata for each entity access or operation
●
Metadata explain Doctrine how to convert the data between formats
Entity
Metadata
Table 1
Entity
Entity
Table 2
Table 3
Doctrine | SymfonyCon2019 23
metadata
var_dump($em->getClassMetadata('FooBar::class'));
object(DoctrineORMMappingClassMetadata)#321 (36) {
["name"]=>
string(7) "FooBar"
["namespace"]=>
string(3) "Foo"
["rootEntityName"]=>
string(7) "FooBar"
["customGeneratorDefinition"]=>
NULL
["customRepositoryClassName"]=>
NULL
["isMappedSuperclass"]=>
bool(false)
["parentClasses"]=>
array(0) {
}
["subClasses"]=>
array(0) {
}
["namedQueries"]=>
array(0) {
}
["namedNativeQueries"]=>
array(0) {
}
...
...
["sqlResultSetMappings"]=>
array(0) {
}
["identifier"]=>
array(1) {
[0]=>
string(2) "id"
}
["inheritanceType"]=>
int(1)
["generatorType"]=>
int(4)
["fieldMappings"]=>
array(8) {
["id"]=>
Doctrine | SymfonyCon2019 24
Metadata
●
Metadata are taken by parsing mapping infos (annot or XML)
●
They are cached
●
Using Sf Cache in Sf apps
●
Must be refreshed, updated, for each mapping change
●
Add / remove / change type of field
●
Add / remove entity or table
●
Add / remove change type of association
Doctrine | SymfonyCon2019 25
EntityManager
●
find()
●
clear()
●
detach()
●
persist()
●
refresh()
●
remove()
●
flush()
$user = new EntitiesUser;
$user->setName("foo") ;
$user->setAddress("somewhere") ;
$em->persist($user) ;
$em->flush($user) ;
●
persist() attaches a new entity into the IdentityMap
●
May also be used with deferred_explicit change tracking policy
●
flush() persists the whole IdentityMap (sync it with the DB)
Doctrine | SymfonyCon2019 26
EntityManager
●
find()
●
clear()
●
detach()
●
persist()
●
refresh()
●
remove()
●
flush()
$user = $em->find('EntitiesUsers', 3);
$user->setName("foo") ;
$em->flush($user) ;
●
find() finds by PK and attaches the found entity to the IdentityMap
●
find() actually SELECT * all fields, take care of that.
●
flush() persists the IdentityMap (performs an "update" if some fields have
been modified on entities)
Doctrine | SymfonyCon2019 27
EntityManager
●
find()
●
clear()
●
detach()
●
persist()
●
refresh()
●
remove()
●
flush()
$user = $em->find('EntitiesUsers', 3);
$em->remove($user) ;
$em->flush($user) ;
●
remove() marks the entity as "to be deleted" into the IdentityMap
●
flush() persists the state (issues a "delete" on the DB).
●
Cascading is honnored
Doctrine | SymfonyCon2019 28
EntityManager
●
find()
●
clear()
●
detach()
●
persist()
●
refresh()
●
remove()
●
flush()
$user = $em->find('EntitiesUsers', 3);
$em->detach($user) ;
●
detach() deletes the entity from the IdentityMap
●
Opposite to persist()
●
Once detached, the entity is not tracked anymore for changes
●
Cascading is honnored
Doctrine | SymfonyCon2019 29
EntityManager
●
find()
●
clear()
●
detach()
●
persist()
●
refresh()
●
remove()
●
flush()
$user = $em->find('EntitiesUsers', 3);
echo $user->getName() // "bar"
$user->setName("FOO") ;
echo $user->getName() // "FOO"
$em->refresh($user) ;
echo $user->getName() // "bar"
●
refresh() Cancels any modification done to the entity so far from the
IdentityMap.
●
Entity is loaded, tracked for modifications and those modifications are
cancelled by refresh().
●
Usually , an SQL query is not issued for that. ORM knows about the original
data of any Entity
●
Cascading is honnored
Doctrine | SymfonyCon2019 30
EntityManager
●
find()
●
clear()
●
detach()
●
persist()
●
refresh()
●
remove()
●
flush()
$user = $em->find('EntitiesUsers', 3);
$user = $em->find('EntitiesUsers', 4);
$user = $em->find('EntitiesUsers', 5);
$em->clear() ;
●
clear() empties the IdentityMap in the UnitOfWork
Doctrine | SymfonyCon2019 31
EntityManager in theory
●
The IdentityMap into the UnitOfWork gets filled by entities which are
queried for, or persist()ed
●
UOW then tracks modifications of those entities (by default)
●
Calling flush(), UOW computes a change matrix of what have changed on
known tracked entities
●
Computes a diff
●
Orders it
●
Uses a DB transaction to play the modifications
●
Synchronizes with DB
●
If exception is thrown, transaction is rollbacked
●
Forgetting a call to flush() means forgetting a sync
●
Calling flush() on a big IdentityMap will impact performances
Doctrine | SymfonyCon2019 32
Doctrine ORM
Entities
Repository
EntityManager
UnitOfWork
DBAL Connection
PDO
ClassMetadata
IdentityMap
DataPersisters
Doctrine | SymfonyCon2019 33
EM & UOW
●
You usually don't access the UOW by yourself, but may :
●
UOW is the central object of the ORM .
●
EntityManager is just a thin layer on top of it.
●
See computeChangeSets()
$em->getUnitOfWork()
Doctrine | SymfonyCon2019 34
UOW Performances
●
The more entities into the IdentityMap, the slower the
computation for changes
var_dump($em->getUnitOfWork()->size());
$obj = $em->find('BazOffer', 1);
var_dump($em->getUnitOfWork()->size());
int(0)
int(3)
Doctrine | SymfonyCon2019 35
UOW Analysis
●
getIdentityMap() returns the IdentityMap and thus the entities actually
tracked by Doctrine ORM.
●
All dependencies are also in
$obj = $em->find('FooBar', 1);
Debug::dump($em->getUnitOfWork()->getIdentityMap());
array(3) {
["FooBar"]=>
array(1) {
[1]=>
string(8) "FooBar"
}
["FooWow"]=>
array(1) {
[1]=>
string(28) "DoctrineProxies__CG__FooWow"
}
["FooUser"]=>
array(1) {
[10]=>
string(14) "FooUser"
}
}
Doctrine | SymfonyCon2019 36
UOW changeset
●
getEntityChangeSet($entity) allows to see what operations are to
be sent to the DB for $entity, when flush() will come
var_dump($em->getUnitOfWork()->size());
$to = $em->find('FooBar', 1);
$to->setCurrency('BAR');
var_dump($em->getUnitOfWork()->size());
$em->getUnitOfWork()->computeChangeSets();
dump($em->getUnitOfWork()->getEntityChangeSet($to));
int(0)
int(3)
array(1) {
["currency"]=>
array(2) {
[0]=>
string(3) "foo"
[1]=>
string(3) "BAR"
}
}
Doctrine | SymfonyCon2019 37
Change Tracking Policy
●
Deferred implicit
●
Default mode, the more comfortable, but the less performant
●
Compare every attribute of every entities, and cascades
●
Will be very heavy on big payloads ! (foreach(){foreach(){}})
●
Deferred explicit
●
Only compare entities that are explicitely persisted back after
modification
●
The best mode, balance against performances and lines of code
●
Notify
●
User must notify the UOW about what changes it performed so that the
UOW doesn't have to compute those by itself
●
The most performant mode, but needs more code to be written
Doctrine | SymfonyCon2019 38
Example Deferred implicit
/**
* @ORMTable(name="User")
* @ORMEntity
*/
class User {
$user = $em->find('User', 1);
$user->setAge(30);
$em->flush();
Doctrine | SymfonyCon2019 39
Example Deferred explicit
●
You tell UOW what entities to track
●
Prevents the UOW from tracking a very big group of entities
/**
* @ORMTable(name="User")
* @ORMEntity
* @ORMChangeTrackingPolicy("DEFERRED_EXPLICIT")
*/
class User {
$user = $em->find('User', 1);
$user->setAge(30);
$em->persist($user);
$em->flush();
Doctrine | SymfonyCon2019 40
Identity map
●
Doctrine memorises entities into the IdentityMap and re-provides them when
re-queried later, not performing additionnal SQL query
●
Some cases bypass the identity map
●
DQL queries
●
Partial entities queries
●
Queries not selecting using pk
$u1 = $em->find('EntitiesUser', 1) ;
$u1->setName('foobarbaz') ;
/* ... */
$u2 = $em->find('EntitiesUser', 1) ; /* SQL is NOT re-run */
echo $u2->getName() ; /* foobarbaz */
assert($u1 === $u2) ; /* true */
Doctrine | SymfonyCon2019 41
Repository
●
Place where you write queries concerning an entity
Doctrine | SymfonyCon2019 42
Repository API
●
find(), findAll(), findBy(), findOneBy()
●
Only find() makes use of the IdentityMap
/* findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */
$results = $repo->findBy(array('name'=>'foo'), array('name'=>'asc'), 2, 3);
Doctrine | SymfonyCon2019 43
Association mapping
●
Relations ?
●
OneToOne
●
OneToMany
●
ManyToOne
●
ManyToMany
Doctrine | SymfonyCon2019 44
Associations
●
bi-directional :
●
One User can post several trips -> OneToMany
●
Several trips can reference a same User -> ManyToOne
namespace Entities;
/** @Entity */
class User
{
/** @OneToMany(targetEntity="TripOffer", mappedBy="user") */
protected $tripOffer;
/* ... */
}
namespace Entities;
/** @Entity */
class TripOffer
{
/**@ManyToOne(targetEntity="User", inversedBy="tripOffer")
* @JoinColumn(name="Users_Id", referencedColumnName="id")
*/
protected $user;
/* ... */
}
Doctrine | SymfonyCon2019 45
Associations
●
ArrayCollection is used to handle the "Many" part
●
Otherwise the Entity itself
namespace Entities;
use DoctrineCommonCollectionsArrayCollection;
/** @Entity */
class User
{
/** @OneToMany(targetEntity="TripOffer", mappedBy="user") */
protected $tripOffer;
public function __construct()
{ $this->tripOffer = new ArrayCollection; }
public function getTripOffer()
{ return $this->tripOffer; }
public function addTripOffer(TripOffer $t)
{ $this->tripOffer[] = $t; }
public function removeTripOffer(TripOffer $t)
{ $this->tripOffer->removeElement($t); }
}
Doctrine | SymfonyCon2019 46
Proxy objects
●
By default, the "LAZY" fetch mode: dependancies are not loaded but
replaced by Proxy classes or collections :
●
Informations are read from DB only when entities are accessed
●
You can ask for a proxy explicitely :
$r = $em->find('EntitiesRating', 1);
var_dump($r) ;
object(EntitiesRating)[54]
protected 'id' => int 1
protected 'creator' =>
object(DoctrineProxiesEntitiesUserProxy)[86]
private '_entityPersister' => ...
...
$realRating = $em->find('EntitiesRating', 1);
$proxyRating = $em->getReference('EntitiesRating', 1);
Doctrine | SymfonyCon2019 47
Hydration modes
●
LAZY
●
Default
●
Uses Proxies and only loads the data when those are accessed
●
EAGER
●
Always loads the data (even if it is not used)
●
EXTRA_LAZY
●
Do not load the data for accesses :
●
Collection#contains($entity)
●
Collection#containsKey($key)
●
Collection#count()
●
Collection#get($key)
●
Collection#slice($offset, $length = null)
Doctrine | SymfonyCon2019 48
Examples hydration modes
●
EAGER
●
Association is always loaded
●
Cascaded
●
Several requests are used
namespace Entities;
/** @Entity */
class User
{
/** @OneToMany(targetEntity="TripOffer", mappedBy="user", fetch="EAGER") */
protected $tripOffer;
/* ... */
}
SELECT t0.id AS id1, t0.name AS name2, t0.userName AS userName3, t0.isvip AS isvip4
FROM users t0 WHERE t0.id = ?
SELECT t0.id AS id1, t0.SeatsOffered AS SeatsOffered2, t0.TripOfferType AS TripOfferType3, t0.Users_Id AS Users_Id4
FROM TripOffer t0 WHERE t0.Users_Id = ?
$u = $em->find('EntitiesUser', 1) ;
Doctrine | SymfonyCon2019 49
Examples hydration modes
●
EXTRA_LAZY
●
Like LAZY, but does not load the data if some statistical questions
are asked for it
●
User, do you own some TripOffers ?
●
No need to load all the trips to know that
namespace Entities;
/** @Entity */
class User
{
/** @OneToMany(targetEntity="TripOffer", mappedBy="user", fetch="EXTRA_LAZY") */
protected $tripOffer;
/* ... */
}
$u = $em->find('EntitiesUser', 1) ;
echo $u->getTripOffer()->count() ; /* SELECT count(*) FROM TripOffer WHERE Users_Id = ? */
/* SELECT t0.id AS id1, t0.SeatsOffered AS SeatsOffered2, t0.TripOfferType AS TripOfferType3, t0.Users_Id AS Users_Id4
FROM TripOffer t0 WHERE t0.Users_Id = ? LIMIT 8 OFFSET 3 */
$someTripOffers = $u->getTripOffer()->slice(3, 8) ;
Doctrine | SymfonyCon2019 50
Collections and hydration
$u = $em->find('EntitiesUser', 1);
$tripStatus = $u->getTripOffer()->get(0)->getTrips()->get(0)->getStatus();
Doctrine | SymfonyCon2019 51
Cascades
●
What to do with dependencies when acting on a root entity :
●
persist
●
remove
●
merge
●
detach
●
refresh
●
all
●
By default, no cascades are used
Doctrine | SymfonyCon2019 52
Example cascade (persist)
namespace Entities;
/** @Entity */
class User
{
/** @OneToMany(targetEntity="TripOffer", mappedBy="user", cascade={"persist"}) */
protected $tripOffer;
/* ... */
}
Doctrine | SymfonyCon2019 53
DQL
●
Doctrine Query Language
●
Looks like SQL but
●
Queries Entities, not tables
●
Associations are used, not foreign keys
●
Mapping informations is used to convert to SQL
●
INSERT does not exist
●
DQL is fully extensible by the user
Doctrine | SymfonyCon2019 54
DQL example
●
You query entities, not tables
●
createQuery() for a DQL query
●
createNamedQuery() for a pre-recorded DQL query
●
createNativeQuery() for a SQL query
●
createNamedNativeQuery() for a pre-recorded SQL query
$q = $em->createQuery('SELECT u FROM EntitiesUser u WHERE u.name = ?1');
$q->setParameter(1, 'foo');
$r = $q->getResult();
$q = $em->createQuery('SELECT u, r FROM EntitiesUser u LEFT JOIN u.ratings r');
$r = $q->getResult();
Doctrine | SymfonyCon2019 55
DQL and joins
namespace Entities;
use DoctrineCommonCollectionsArrayCollection;
/**
* @Table(name="my_users")
*/
class User
{
/**
* @OneToMany(targetEntity="Rating", mappedBy="userCreator")
*/
protected $ratings;
}
$q = $em->createQuery("SELECT u, r FROM EntitiesUser u JOIN u.ratings r");
$r = $q->getArrayResult();
Doctrine | SymfonyCon2019 56
Named queries
●
Queries recorded to be recalled / re-run later
$dql = "SELECT ... ... ..." ;
$conf = new DoctrineORMConfiguration();
$conf->addNamedQuery('search', $dql);
$q = $em->createNamedQuery('search');
$r = $q->getResult();
Doctrine | SymfonyCon2019 57
DQL and return values
●
By default, the return type is an array of Entities
$q = $em->createQuery('SELECT u FROM EntitiesUser u WHERE u.name = ?1');
$q->setParameter(1, 'foo');
$r = $q->getResult();
array
0 =>
object(DoctrineProxiesEntitiesUserProxy)[81]
$q = $em->createQuery('SELECT u FROM EntitiesUser u WHERE u.name = ?1');
$q->setParameter(1, 'foo');
$r = $q->getSingleResult();
object(DoctrineProxiesEntitiesUserProxy)[81]
Doctrine | SymfonyCon2019 58
DQL and return values
$q->getArrayResult()
array
0 => &
array
'id' => int 2
'name' => string 'foo' (length=3)
'userName' => string 'fooname' (length=7)
'vip' => int 0
SELECT u FROM EntitiesUser u WHERE u.name = 'foo'
Doctrine | SymfonyCon2019 59
DQL and return values
●
Use the right result type you need
●
single**() must be used on single results, if not :
●
NoResultException
●
NonUniqueResultException
SELECT COUNT(u) FROM EntitiesUser u
$q->getScalarResult()
$q->getSingleResult()
$q->getSingleScalarResult()
array
1 => string '5008'
array
0 =>
array
1 => string '5008'
string '5008'
Doctrine | SymfonyCon2019 60
DQL and return values
●
Often used : getResult()
●
If you select not all fields of an entity :
●
An array will be returned
●
You can ask for a partial entity
●
select('PARTIAL alias.{col1, col2}')
$q = $em->createQuery("SELECT u.name, u.userName FROM EntitiesUser u");
$r = $q->getResult();
array
0 =>
array
'name' => string 'bar' (length=3)
'userName' => string 'barname' (length=7)
1 =>
array
'name' => string 'foo' (length=3)
'userName' => string 'fooname' (length=7)
Doctrine | SymfonyCon2019 61
DQL and return values
$q = $em->createQuery("SELECT u, UPPER(r.comment), r.id, to.type FROM EntitiesUser u
LEFT JOIN u.ratings r LEFT JOIN u.tripOffer to");
$results = $q->getResult();
array
0 =>
array
0 => object(EntitiesUser)[103]
1 => string 'THIS IS A COMMENT' (length=17)
'id' => string '1' (length=1)
'type' => null
1 => (...)
Doctrine | SymfonyCon2019 62
DQL and Identity Map
●
DQL queries store into the IdentityMap but don't read from it
●
Store selected entities
●
If they are full (all fields selected)
●
If the result is asked to be an entity, and not an array
$q = $em->createQuery('SELECT r FROM FooRating r WHERE r.id=?1');
$q->setParameter(1, 1);
$result = $q->getSingleResult();
$rating1 = $em->find('FooRating', 1); /* Query is not re-played */
assert($rating1 === $result); /* that's true */
$rating1 = $em->find('FooRating', 1);
$q = $em->createQuery('SELECT r FROM FooRating r WHERE r.id=?1');
$q->setParameter(1, 1);
$result = $q->getSingleResult(); /* Query is re-played */
assert($rating1 === $result); /* that's true */
Doctrine | SymfonyCon2019 63
DQL and Identity Map
●
Dependancies benefit from IdentityMap
$q = $em->createQuery('SELECT u, r FROM EntitiesUser u JOIN u.ratings r');
$results = $q->getResult()
$rating1 = $em->find('FooRating', 1); /* No query played */
foreach ($results as $user) {
$user->getRatings(); /* No query played */
}
$rating1 = $em->find('FooRating', 1);
$rating1->setComment('I have changed');
$q = $em->createQuery('SELECT r FROM FooRating r WHERE r.id=?1');
$q->setParameter(1, 1);
$q->setHint(DoctrineORMQuery::HINT_REFRESH, 1);
$result = $q->getSingleResult();
assert($rating1 === $result);
assert($rating1->getComment() != 'I have changed');
Doctrine | SymfonyCon2019 64
DQL functions
$q = $em->createQuery("SELECT u, CONCAT('foo','bar') as baz FROM EntitiesUser u");
$r = $q->getResult();
array
0 =>
array
0 =>
object(EntitiesUser)[30]
...
'baz' => string 'foobar' (length=6)
1 =>
array ...
$rating1 = $em->find('EntitiesRating', 1) ;
$q = $em->createQuery("SELECT u FROM EntitiesUser u WHERE ?1 MEMBER OF u.ratings");
$q->setParameter(1, $rating1);
/* A sub-select is used */
Doctrine | SymfonyCon2019 65
Writing using DQL
●
INSERT not possible
●
DELETE and UPDATE are OK
●
Warning, this could desync the UnitOfWork
$user = $em->find('EntitiesUser', 66);
$q = $em->createQuery('DELETE EntitiesUser u WHERE u.id=66');
$q->execute();
$user->setName('hello');
$em->flush(); /* UPDATE users SET name = ? WHERE id = ? */
Doctrine | SymfonyCon2019 66
SQL
●
SQL can still be used, through DBAL
●
But :
●
That bypasses all the Entities and the mapping done
●
That can desync the UnitOfWork
$results = $em->getConnection()->fetchAll("/* some query here*/") ;
Doctrine | SymfonyCon2019 67
DQL or SQL ?
●
DQL uses Entities and mapping informations, not the DB and its tables directly
●
DQL is parsed and turned to SQL
●
This transformation should get cached
●
$query->getSQL();
●
DQL can be deeply hooked
●
DQL can return Entities
●
SQL returns arrays
Doctrine | SymfonyCon2019 68
Cache
●
Several caches may (must) be used
●
"Query Cache" (DQL->SQL)
●
Result Cache : caches a result from a query
●
Metadata Cache
●
Using SF, Doctrine will be bound to SF caches
Doctrine | SymfonyCon2019 69
Query Cache
●
Activated per query
$conf = new DoctrineORMConfiguration();
$conf->setResultCacheImpl(new DoctrineCommonCacheApcCache());
$q = $em->createQuery('SELECT u, r, r2 FROM EntitiesUser u JOIN u.ratings r
JOIN u.ratingsConcerned r2');
$q->useResultCache(1, 3600, 'foo_result') ;
$r = $q->execute();
Doctrine | SymfonyCon2019 70
Q/A
●
?
●
?
●
?
●
?
●
?
●
?
●
?
●
?
●
?
Doctrine | SymfonyCon2019 71
Practice
●
Setup symfony-demo
●
Get familiar with the DB structure
●
Navigate into the BlogController and the PostRepository
> composer create-project symfony/symfony-demo some_project_dir
> bin/console s:r
Doctrine | SymfonyCon2019 72
Practice
●
Create a query using DQL to get 3 random posts
Doctrine | SymfonyCon2019 73
Practice
●
Create a query using DQL to get 3 random posts
●
See how the authors are replaced with proxies
●
Access one author field
●
See how N+1 query is issued by Doctrine
●
Give a hint to the QueryBuilder to load partial entities
●
See how dependencies are now NULLed
●
Change the fetchmode of the author dependency to EAGER
●
See how the authors are now gathered by Doctrine using N+1
●
In every case, dump the identity map and check the state of each
entity as being STATE_MANAGED
Doctrine | SymfonyCon2019 74
Practice
●
Create a query using DQL to get 3 random posts
●
Get the first post from the collection
●
Modify the post content
●
Compute the changeset from the UOW
●
Dump the changeset
●
Change the tracking policy of posts to DEFERRED_EXPLICIT
●
Modify the post content
●
Compute the changeset from the UOW
●
Dump the changeset
●
What happens ? How to do ?
Doctrine | SymfonyCon2019 75
Practice
●
Add a new "avatar" field to the User
●
Play the migration
●
Patch the User form to add a new type to upload an avatar
●
Create an entityListener to treat the avatar
●
The DB should save the avatar file path
Doctrine | SymfonyCon2019 76
Practice
●
Add a new RoleType to the Type mappings
●
This type maps sf ROLE_** to an integer
Doctrine | SymfonyCon2019 77
Practice
●
Create a query to get the comments written by ROLE_ADMIN
●
Delete those

Contenu connexe

Tendances

Scalable JavaScript Application Architecture
Scalable JavaScript Application ArchitectureScalable JavaScript Application Architecture
Scalable JavaScript Application ArchitectureNicholas Zakas
 
Spring I/O 2012: Natural Templating in Spring MVC with Thymeleaf
Spring I/O 2012: Natural Templating in Spring MVC with ThymeleafSpring I/O 2012: Natural Templating in Spring MVC with Thymeleaf
Spring I/O 2012: Natural Templating in Spring MVC with ThymeleafThymeleaf
 
Modern Java web applications with Spring Boot and Thymeleaf
Modern Java web applications with Spring Boot and ThymeleafModern Java web applications with Spring Boot and Thymeleaf
Modern Java web applications with Spring Boot and ThymeleafLAY Leangsros
 
An introduction to SQLAlchemy
An introduction to SQLAlchemyAn introduction to SQLAlchemy
An introduction to SQLAlchemymengukagan
 
JavaScript - Chapter 5 - Operators
 JavaScript - Chapter 5 - Operators JavaScript - Chapter 5 - Operators
JavaScript - Chapter 5 - OperatorsWebStackAcademy
 
Doma SQLテンプレートのしくみ
Doma SQLテンプレートのしくみDoma SQLテンプレートのしくみ
Doma SQLテンプレートのしくみToshihiro Nakamura
 
Azure Spring Cloud Workshop - June 17, 2020
Azure Spring Cloud Workshop - June 17, 2020Azure Spring Cloud Workshop - June 17, 2020
Azure Spring Cloud Workshop - June 17, 2020VMware Tanzu
 
Bi-temporal rdbms 2014
Bi-temporal rdbms 2014Bi-temporal rdbms 2014
Bi-temporal rdbms 2014Tomm Carr
 
Spring3.1概要 データアクセスとトランザクション処理
Spring3.1概要 データアクセスとトランザクション処理Spring3.1概要 データアクセスとトランザクション処理
Spring3.1概要 データアクセスとトランザクション処理土岐 孝平
 
Spring Interview Questions and Answers | Spring Tutorial | Spring Framework T...
Spring Interview Questions and Answers | Spring Tutorial | Spring Framework T...Spring Interview Questions and Answers | Spring Tutorial | Spring Framework T...
Spring Interview Questions and Answers | Spring Tutorial | Spring Framework T...Edureka!
 
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...Edureka!
 
Java DataBase Connectivity API (JDBC API)
Java DataBase Connectivity API (JDBC API)Java DataBase Connectivity API (JDBC API)
Java DataBase Connectivity API (JDBC API)Luzan Baral
 
Java - Exception Handling
Java - Exception HandlingJava - Exception Handling
Java - Exception HandlingPrabhdeep Singh
 
Operators , Functions and Options in VB.NET
Operators , Functions and Options in VB.NETOperators , Functions and Options in VB.NET
Operators , Functions and Options in VB.NETShyam Sir
 
RESTful API In Node Js using Express
RESTful API In Node Js using Express RESTful API In Node Js using Express
RESTful API In Node Js using Express Jeetendra singh
 
Angular Data Binding
Angular Data BindingAngular Data Binding
Angular Data BindingDuy Khanh
 

Tendances (20)

Scalable JavaScript Application Architecture
Scalable JavaScript Application ArchitectureScalable JavaScript Application Architecture
Scalable JavaScript Application Architecture
 
Spring I/O 2012: Natural Templating in Spring MVC with Thymeleaf
Spring I/O 2012: Natural Templating in Spring MVC with ThymeleafSpring I/O 2012: Natural Templating in Spring MVC with Thymeleaf
Spring I/O 2012: Natural Templating in Spring MVC with Thymeleaf
 
Modern Java web applications with Spring Boot and Thymeleaf
Modern Java web applications with Spring Boot and ThymeleafModern Java web applications with Spring Boot and Thymeleaf
Modern Java web applications with Spring Boot and Thymeleaf
 
Data Types In PHP
Data Types In PHPData Types In PHP
Data Types In PHP
 
Postgresql Federation
Postgresql FederationPostgresql Federation
Postgresql Federation
 
An introduction to SQLAlchemy
An introduction to SQLAlchemyAn introduction to SQLAlchemy
An introduction to SQLAlchemy
 
JavaScript - Chapter 5 - Operators
 JavaScript - Chapter 5 - Operators JavaScript - Chapter 5 - Operators
JavaScript - Chapter 5 - Operators
 
Hibernate
HibernateHibernate
Hibernate
 
Doma SQLテンプレートのしくみ
Doma SQLテンプレートのしくみDoma SQLテンプレートのしくみ
Doma SQLテンプレートのしくみ
 
Azure Spring Cloud Workshop - June 17, 2020
Azure Spring Cloud Workshop - June 17, 2020Azure Spring Cloud Workshop - June 17, 2020
Azure Spring Cloud Workshop - June 17, 2020
 
Bi-temporal rdbms 2014
Bi-temporal rdbms 2014Bi-temporal rdbms 2014
Bi-temporal rdbms 2014
 
Spring3.1概要 データアクセスとトランザクション処理
Spring3.1概要 データアクセスとトランザクション処理Spring3.1概要 データアクセスとトランザクション処理
Spring3.1概要 データアクセスとトランザクション処理
 
Spring Interview Questions and Answers | Spring Tutorial | Spring Framework T...
Spring Interview Questions and Answers | Spring Tutorial | Spring Framework T...Spring Interview Questions and Answers | Spring Tutorial | Spring Framework T...
Spring Interview Questions and Answers | Spring Tutorial | Spring Framework T...
 
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
Node.js Tutorial for Beginners | Node.js Web Application Tutorial | Node.js T...
 
Java DataBase Connectivity API (JDBC API)
Java DataBase Connectivity API (JDBC API)Java DataBase Connectivity API (JDBC API)
Java DataBase Connectivity API (JDBC API)
 
Java - Exception Handling
Java - Exception HandlingJava - Exception Handling
Java - Exception Handling
 
Angular 2 observables
Angular 2 observablesAngular 2 observables
Angular 2 observables
 
Operators , Functions and Options in VB.NET
Operators , Functions and Options in VB.NETOperators , Functions and Options in VB.NET
Operators , Functions and Options in VB.NET
 
RESTful API In Node Js using Express
RESTful API In Node Js using Express RESTful API In Node Js using Express
RESTful API In Node Js using Express
 
Angular Data Binding
Angular Data BindingAngular Data Binding
Angular Data Binding
 

Similaire à Doctrine with Symfony - SymfonyCon 2019

Zend Framework 2 - Basic Components
Zend Framework 2  - Basic ComponentsZend Framework 2  - Basic Components
Zend Framework 2 - Basic ComponentsMateusz Tymek
 
The Naked Bundle - Symfony Live London 2014
The Naked Bundle - Symfony Live London 2014The Naked Bundle - Symfony Live London 2014
The Naked Bundle - Symfony Live London 2014Matthias Noback
 
Debugging in drupal 8
Debugging in drupal 8Debugging in drupal 8
Debugging in drupal 8Allie Jones
 
AngularJS Architecture
AngularJS ArchitectureAngularJS Architecture
AngularJS ArchitectureEyal Vardi
 
AngularJS Internal
AngularJS InternalAngularJS Internal
AngularJS InternalEyal Vardi
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony AppsKris Wallsmith
 
The Naked Bundle - Tryout
The Naked Bundle - TryoutThe Naked Bundle - Tryout
The Naked Bundle - TryoutMatthias Noback
 
Symfony CMF - PHP Conference Brazil 2011
Symfony CMF - PHP Conference Brazil 2011Symfony CMF - PHP Conference Brazil 2011
Symfony CMF - PHP Conference Brazil 2011Jacopo Romei
 
Symfony2 - from the trenches
Symfony2 - from the trenchesSymfony2 - from the trenches
Symfony2 - from the trenchesLukas Smith
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the TrenchesJonathan Wage
 
WebCamp: Developer Day: DDD in PHP on example of Symfony - Олег Зинченко
WebCamp: Developer Day: DDD in PHP on example of Symfony - Олег ЗинченкоWebCamp: Developer Day: DDD in PHP on example of Symfony - Олег Зинченко
WebCamp: Developer Day: DDD in PHP on example of Symfony - Олег ЗинченкоGeeksLab Odessa
 
Dependency injection in Drupal 8
Dependency injection in Drupal 8Dependency injection in Drupal 8
Dependency injection in Drupal 8Alexei Gorobets
 
DDD on example of Symfony (SfCampUA14)
DDD on example of Symfony (SfCampUA14)DDD on example of Symfony (SfCampUA14)
DDD on example of Symfony (SfCampUA14)Oleg Zinchenko
 
The Naked Bundle - Symfony Usergroup Belgium
The Naked Bundle - Symfony Usergroup BelgiumThe Naked Bundle - Symfony Usergroup Belgium
The Naked Bundle - Symfony Usergroup BelgiumMatthias Noback
 
The Naked Bundle - Symfony Barcelona
The Naked Bundle - Symfony BarcelonaThe Naked Bundle - Symfony Barcelona
The Naked Bundle - Symfony BarcelonaMatthias Noback
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207patter
 
Zend Framework 1.9 Setup & Using Zend_Tool
Zend Framework 1.9 Setup & Using Zend_ToolZend Framework 1.9 Setup & Using Zend_Tool
Zend Framework 1.9 Setup & Using Zend_ToolGordon Forsythe
 

Similaire à Doctrine with Symfony - SymfonyCon 2019 (20)

Zend Framework 2 - Basic Components
Zend Framework 2  - Basic ComponentsZend Framework 2  - Basic Components
Zend Framework 2 - Basic Components
 
The Naked Bundle - Symfony Live London 2014
The Naked Bundle - Symfony Live London 2014The Naked Bundle - Symfony Live London 2014
The Naked Bundle - Symfony Live London 2014
 
Debugging in drupal 8
Debugging in drupal 8Debugging in drupal 8
Debugging in drupal 8
 
AngularJS Architecture
AngularJS ArchitectureAngularJS Architecture
AngularJS Architecture
 
AngularJS Internal
AngularJS InternalAngularJS Internal
AngularJS Internal
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
 
The Naked Bundle - Tryout
The Naked Bundle - TryoutThe Naked Bundle - Tryout
The Naked Bundle - Tryout
 
Symfony CMF - PHP Conference Brazil 2011
Symfony CMF - PHP Conference Brazil 2011Symfony CMF - PHP Conference Brazil 2011
Symfony CMF - PHP Conference Brazil 2011
 
Symfony2 - from the trenches
Symfony2 - from the trenchesSymfony2 - from the trenches
Symfony2 - from the trenches
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the Trenches
 
WebCamp: Developer Day: DDD in PHP on example of Symfony - Олег Зинченко
WebCamp: Developer Day: DDD in PHP on example of Symfony - Олег ЗинченкоWebCamp: Developer Day: DDD in PHP on example of Symfony - Олег Зинченко
WebCamp: Developer Day: DDD in PHP on example of Symfony - Олег Зинченко
 
Dependency injection in Drupal 8
Dependency injection in Drupal 8Dependency injection in Drupal 8
Dependency injection in Drupal 8
 
Drupal 8 migrate!
Drupal 8 migrate!Drupal 8 migrate!
Drupal 8 migrate!
 
DDD on example of Symfony (SfCampUA14)
DDD on example of Symfony (SfCampUA14)DDD on example of Symfony (SfCampUA14)
DDD on example of Symfony (SfCampUA14)
 
The Naked Bundle - Symfony Usergroup Belgium
The Naked Bundle - Symfony Usergroup BelgiumThe Naked Bundle - Symfony Usergroup Belgium
The Naked Bundle - Symfony Usergroup Belgium
 
The Naked Bundle - Symfony Barcelona
The Naked Bundle - Symfony BarcelonaThe Naked Bundle - Symfony Barcelona
The Naked Bundle - Symfony Barcelona
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
IOC + Javascript
IOC + JavascriptIOC + Javascript
IOC + Javascript
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207
 
Zend Framework 1.9 Setup & Using Zend_Tool
Zend Framework 1.9 Setup & Using Zend_ToolZend Framework 1.9 Setup & Using Zend_Tool
Zend Framework 1.9 Setup & Using Zend_Tool
 

Plus de julien pauli

PHP 7 OPCache extension review
PHP 7 OPCache extension reviewPHP 7 OPCache extension review
PHP 7 OPCache extension reviewjulien pauli
 
PHP Internals and Virtual Machine
PHP Internals and Virtual MachinePHP Internals and Virtual Machine
PHP Internals and Virtual Machinejulien pauli
 
Basics of Cryptography - Stream ciphers and PRNG
Basics of Cryptography - Stream ciphers and PRNGBasics of Cryptography - Stream ciphers and PRNG
Basics of Cryptography - Stream ciphers and PRNGjulien pauli
 
Mastering your home network - Do It Yourself
Mastering your home network - Do It YourselfMastering your home network - Do It Yourself
Mastering your home network - Do It Yourselfjulien pauli
 
SymfonyCon 2017 php7 performances
SymfonyCon 2017 php7 performancesSymfonyCon 2017 php7 performances
SymfonyCon 2017 php7 performancesjulien pauli
 
Php and threads ZTS
Php and threads ZTSPhp and threads ZTS
Php and threads ZTSjulien pauli
 
Symfony live 2017_php7_performances
Symfony live 2017_php7_performancesSymfony live 2017_php7_performances
Symfony live 2017_php7_performancesjulien pauli
 
Php7 extensions workshop
Php7 extensions workshopPhp7 extensions workshop
Php7 extensions workshopjulien pauli
 
Profiling php5 to php7
Profiling php5 to php7Profiling php5 to php7
Profiling php5 to php7julien pauli
 
PHP 7 performances from PHP 5
PHP 7 performances from PHP 5PHP 7 performances from PHP 5
PHP 7 performances from PHP 5julien pauli
 
Mysqlnd, an unknown powerful PHP extension
Mysqlnd, an unknown powerful PHP extensionMysqlnd, an unknown powerful PHP extension
Mysqlnd, an unknown powerful PHP extensionjulien pauli
 
Php extensions workshop
Php extensions workshopPhp extensions workshop
Php extensions workshopjulien pauli
 
Understanding PHP objects
Understanding PHP objectsUnderstanding PHP objects
Understanding PHP objectsjulien pauli
 
PHP Tips for certification - OdW13
PHP Tips for certification - OdW13PHP Tips for certification - OdW13
PHP Tips for certification - OdW13julien pauli
 

Plus de julien pauli (20)

Php engine
Php enginePhp engine
Php engine
 
PHP 7 OPCache extension review
PHP 7 OPCache extension reviewPHP 7 OPCache extension review
PHP 7 OPCache extension review
 
Dns
DnsDns
Dns
 
PHP Internals and Virtual Machine
PHP Internals and Virtual MachinePHP Internals and Virtual Machine
PHP Internals and Virtual Machine
 
Basics of Cryptography - Stream ciphers and PRNG
Basics of Cryptography - Stream ciphers and PRNGBasics of Cryptography - Stream ciphers and PRNG
Basics of Cryptography - Stream ciphers and PRNG
 
Mastering your home network - Do It Yourself
Mastering your home network - Do It YourselfMastering your home network - Do It Yourself
Mastering your home network - Do It Yourself
 
SymfonyCon 2017 php7 performances
SymfonyCon 2017 php7 performancesSymfonyCon 2017 php7 performances
SymfonyCon 2017 php7 performances
 
Php and threads ZTS
Php and threads ZTSPhp and threads ZTS
Php and threads ZTS
 
Tcpip
TcpipTcpip
Tcpip
 
Symfony live 2017_php7_performances
Symfony live 2017_php7_performancesSymfony live 2017_php7_performances
Symfony live 2017_php7_performances
 
PHP 7 new engine
PHP 7 new enginePHP 7 new engine
PHP 7 new engine
 
Php7 extensions workshop
Php7 extensions workshopPhp7 extensions workshop
Php7 extensions workshop
 
Profiling php5 to php7
Profiling php5 to php7Profiling php5 to php7
Profiling php5 to php7
 
PHP 7 performances from PHP 5
PHP 7 performances from PHP 5PHP 7 performances from PHP 5
PHP 7 performances from PHP 5
 
PHP7 is coming
PHP7 is comingPHP7 is coming
PHP7 is coming
 
Mysqlnd, an unknown powerful PHP extension
Mysqlnd, an unknown powerful PHP extensionMysqlnd, an unknown powerful PHP extension
Mysqlnd, an unknown powerful PHP extension
 
Php extensions workshop
Php extensions workshopPhp extensions workshop
Php extensions workshop
 
Understanding PHP objects
Understanding PHP objectsUnderstanding PHP objects
Understanding PHP objects
 
PHP Tips for certification - OdW13
PHP Tips for certification - OdW13PHP Tips for certification - OdW13
PHP Tips for certification - OdW13
 
PHP5.5 is Here
PHP5.5 is HerePHP5.5 is Here
PHP5.5 is Here
 

Dernier

TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 

Dernier (20)

TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 

Doctrine with Symfony - SymfonyCon 2019

  • 1. Doctrine | SymfonyCon2019 1 Doctrine - Doctrine ORM
  • 2. Doctrine | SymfonyCon2019 2 Doctrine ● Doctrine project architecture ● Common ● DBAL (Database Abstraction Layer) ● ORM (Object Relationnal Mapping) ● How to with ORM
  • 3. Doctrine | SymfonyCon2019 3 The doctrine project ● A bunch of projects ● Like Symfony Components ● Standalone ● Some dependencies around them ● https://www.doctrine-project.org/projects.html
  • 4. Doctrine | SymfonyCon2019 4 ● An organisation designing libraries for modern PHP development ● Database focused ● Database abstraction ● Universal component ● Common ● General pupose tools ● DBAL ● Database Abstract Layer ● Based on, and using PDO ● ORM ● Object Relationnal Mapper What is Doctrine ?
  • 5. Doctrine | SymfonyCon2019 5 Doctrine project
  • 6. Doctrine | SymfonyCon2019 6 What we'll study
  • 7. Doctrine | SymfonyCon2019 7 Doctrine-Annotations use DoctrineCommonAnnotationsAnnotation; class MyAnnot extends Annotation { public $something; public $hey = array(); } /** * @MyAnnot(something="foo", hey="hola") */ class Foo { } $an = new DoctrineCommonAnnotationsAnnotationReader(); var_dump($an->getClassAnnotations(new ReflectionClass('Foo'))); Annotation ::= "@" AnnotationName ["(" [Values] ")"] AnnotationName ::= QualifiedName | SimpleName QualifiedName ::= NameSpacePart "" {NameSpacePart ""}* SimpleName NameSpacePart ::= identifier | null | false | true SimpleName ::= identifier | null | false | true array 0 => object(MyAnnot)[32] public 'something' => string 'foo' public 'hey' => string 'hola' public 'value' => null
  • 8. Doctrine | SymfonyCon2019 8 Doctrine - DBAL
  • 9. Doctrine | SymfonyCon2019 9 DBAL : Database Abstraction Layer ● Uses PDO and copies its API ● You must know PDO ● DBAL : ● Allows simple CRUD ● Allows query building using OO ● Abstracts SQL types and allow mapping them on PHP types ● Allows to play with the DB Schema structure ● Can log queries
  • 10. Doctrine | SymfonyCon2019 10 DBAL : Connection ● The main element
  • 11. Doctrine | SymfonyCon2019 11 DBAL : connection ● Main element ● Allows to drive the whole DB ● Like : ● Connect() - close() ● Insert() ● Update() - executeUpdate() ● Delete() ● Query() - executeQuery()- exec() - prepare() ● fetchAll() - fetchArray() - fetchColumn() - fetchRow() ● BeginTransaction() - commit() - rollback()
  • 12. Doctrine | SymfonyCon2019 12 DBAL : Easy $stmt = $con->query("SELECT id, title, comment FROM Ratings"); while($result = $stmt->fetch()) { vprintf("%d, %s, %s", $result); } $params = array('unix_socket'=>'/var/run/mysqld/mysqld.sock', 'user'=>'foobar', 'password'=>'secret', 'driver'=>'pdo_mysql', 'dbname'=>'foobar'); $con = DoctrineDBALDriverManager::getConnection($params);
  • 13. Doctrine | SymfonyCon2019 13 DBAL looks like PDO ● DBAL has a very similar interface that PDO ● But it overpowers it significantly $stmt = $con->executeQuery("SELECT id, text FROM Ratings WHERE InsertTimeStamp > ? AND id IN(?)", [new DateTime("now"), [2,3,4]], ['datetime', DoctrineDBALConnection::PARAM_INT_ARRAY]; while($result = $stmt->fetch()) { vprintf("%d, %s", $result); } $con->delete('Ratings', ['id' => 3] ) ; $con->update('Ratings', ['comment'=>'hey !!'] ,['id' => 3] ) ; $con->insert('Ratings', [/* Columns detail */]) ;
  • 14. Doctrine | SymfonyCon2019 14 DBAL QueryBuilder ● Build and practice queries with OO API ● An ExpressionBuilder also exists $query = $con->createQueryBuilder() ; $query->from('User', 'u') ->select('u.id') ->addSelect("DATE_FORMAT(User.regdate, '%Y%m%d') as DATESORT") ->add('join', [ 'c' => [ 'joinType' => 'straight', 'joinTable' => 'Comments', 'joinAlias' => 'comment', 'joinCondition' => 'u.comment=c.id' ] ], true) /* … etc … */ echo $query; /* __toString() */
  • 15. Doctrine | SymfonyCon2019 15 DBAL and schema introspection ● API : ● listDatabases() ● listFunctions() - listSequences() ● listTableColumns($tableName) ● listTableConstraints($tableName) ● listTableDetails($tableName) ● listTableForeignKeys($tableName) ● listTableIndexes($tableName) ● listTables() platform = $con->getDatabasePlatform(); $schema = new DoctrineDBALSchemaSchema(); $myTable = $schema->createTable("Users"); $myTable->addColumn("id", "integer", ["unsigned" => true]); $myTable->addColumn("username", "string", ["length" => 32]); $myTable->setPrimaryKey(["id"]); $queries = $schema->toSql($platform);
  • 16. Doctrine | SymfonyCon2019 16 Doctrine - ORM
  • 17. Doctrine | SymfonyCon2019 17 ORM ? ● System design to adapt relationnal system to OO system ● Persistence : mecanism to flush object data into database so that data can survive across HTTP requests by being restored ● ORM makes use of metadata to bind OO model to relationnal model ● Metadata need to be in sync with the model O.R.M
  • 18. Doctrine | SymfonyCon2019 18 Doctrine ORM Entities Repository EntityManager UnitOfWork DBAL Connection PDO ClassMetadata IdentityMap DataPersisters
  • 19. Doctrine | SymfonyCon2019 19 Entity ● Business layer synchronized data object ● Describes metadata ● Annotations ● Xml ● Do not extend any class (DTO) ● Can have getters and setters ● Can have some business logic ● Can be validated by Sf Validators (Sf Form usage) ● Should not depend on any other service
  • 20. Doctrine | SymfonyCon2019 20 Entities namespace Entities; /** * @Entity * @Table(name="my_users") */ class User { /** * @Id * @GeneratedValue * @Column(type="integer", length="5") */ protected $id; /** * @Column(type="text", name="user_name") */ protected $name; public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } public function getId() { return $this->id; } } ● Can be generated from the DB schema analysis ● Mapping is usually described using annotations for metadata
  • 21. Doctrine | SymfonyCon2019 21 Types mapping ● PHP types are mapped onto RDBM types depending on the RDBM brand ● You can add your own types ● DoctrineDBALTypesType
  • 22. Doctrine | SymfonyCon2019 22 RDBM - SQL Doctrine Metadata ● Doctrine needs to read the metadata for each entity access or operation ● Metadata explain Doctrine how to convert the data between formats Entity Metadata Table 1 Entity Entity Table 2 Table 3
  • 23. Doctrine | SymfonyCon2019 23 metadata var_dump($em->getClassMetadata('FooBar::class')); object(DoctrineORMMappingClassMetadata)#321 (36) { ["name"]=> string(7) "FooBar" ["namespace"]=> string(3) "Foo" ["rootEntityName"]=> string(7) "FooBar" ["customGeneratorDefinition"]=> NULL ["customRepositoryClassName"]=> NULL ["isMappedSuperclass"]=> bool(false) ["parentClasses"]=> array(0) { } ["subClasses"]=> array(0) { } ["namedQueries"]=> array(0) { } ["namedNativeQueries"]=> array(0) { } ... ... ["sqlResultSetMappings"]=> array(0) { } ["identifier"]=> array(1) { [0]=> string(2) "id" } ["inheritanceType"]=> int(1) ["generatorType"]=> int(4) ["fieldMappings"]=> array(8) { ["id"]=>
  • 24. Doctrine | SymfonyCon2019 24 Metadata ● Metadata are taken by parsing mapping infos (annot or XML) ● They are cached ● Using Sf Cache in Sf apps ● Must be refreshed, updated, for each mapping change ● Add / remove / change type of field ● Add / remove entity or table ● Add / remove change type of association
  • 25. Doctrine | SymfonyCon2019 25 EntityManager ● find() ● clear() ● detach() ● persist() ● refresh() ● remove() ● flush() $user = new EntitiesUser; $user->setName("foo") ; $user->setAddress("somewhere") ; $em->persist($user) ; $em->flush($user) ; ● persist() attaches a new entity into the IdentityMap ● May also be used with deferred_explicit change tracking policy ● flush() persists the whole IdentityMap (sync it with the DB)
  • 26. Doctrine | SymfonyCon2019 26 EntityManager ● find() ● clear() ● detach() ● persist() ● refresh() ● remove() ● flush() $user = $em->find('EntitiesUsers', 3); $user->setName("foo") ; $em->flush($user) ; ● find() finds by PK and attaches the found entity to the IdentityMap ● find() actually SELECT * all fields, take care of that. ● flush() persists the IdentityMap (performs an "update" if some fields have been modified on entities)
  • 27. Doctrine | SymfonyCon2019 27 EntityManager ● find() ● clear() ● detach() ● persist() ● refresh() ● remove() ● flush() $user = $em->find('EntitiesUsers', 3); $em->remove($user) ; $em->flush($user) ; ● remove() marks the entity as "to be deleted" into the IdentityMap ● flush() persists the state (issues a "delete" on the DB). ● Cascading is honnored
  • 28. Doctrine | SymfonyCon2019 28 EntityManager ● find() ● clear() ● detach() ● persist() ● refresh() ● remove() ● flush() $user = $em->find('EntitiesUsers', 3); $em->detach($user) ; ● detach() deletes the entity from the IdentityMap ● Opposite to persist() ● Once detached, the entity is not tracked anymore for changes ● Cascading is honnored
  • 29. Doctrine | SymfonyCon2019 29 EntityManager ● find() ● clear() ● detach() ● persist() ● refresh() ● remove() ● flush() $user = $em->find('EntitiesUsers', 3); echo $user->getName() // "bar" $user->setName("FOO") ; echo $user->getName() // "FOO" $em->refresh($user) ; echo $user->getName() // "bar" ● refresh() Cancels any modification done to the entity so far from the IdentityMap. ● Entity is loaded, tracked for modifications and those modifications are cancelled by refresh(). ● Usually , an SQL query is not issued for that. ORM knows about the original data of any Entity ● Cascading is honnored
  • 30. Doctrine | SymfonyCon2019 30 EntityManager ● find() ● clear() ● detach() ● persist() ● refresh() ● remove() ● flush() $user = $em->find('EntitiesUsers', 3); $user = $em->find('EntitiesUsers', 4); $user = $em->find('EntitiesUsers', 5); $em->clear() ; ● clear() empties the IdentityMap in the UnitOfWork
  • 31. Doctrine | SymfonyCon2019 31 EntityManager in theory ● The IdentityMap into the UnitOfWork gets filled by entities which are queried for, or persist()ed ● UOW then tracks modifications of those entities (by default) ● Calling flush(), UOW computes a change matrix of what have changed on known tracked entities ● Computes a diff ● Orders it ● Uses a DB transaction to play the modifications ● Synchronizes with DB ● If exception is thrown, transaction is rollbacked ● Forgetting a call to flush() means forgetting a sync ● Calling flush() on a big IdentityMap will impact performances
  • 32. Doctrine | SymfonyCon2019 32 Doctrine ORM Entities Repository EntityManager UnitOfWork DBAL Connection PDO ClassMetadata IdentityMap DataPersisters
  • 33. Doctrine | SymfonyCon2019 33 EM & UOW ● You usually don't access the UOW by yourself, but may : ● UOW is the central object of the ORM . ● EntityManager is just a thin layer on top of it. ● See computeChangeSets() $em->getUnitOfWork()
  • 34. Doctrine | SymfonyCon2019 34 UOW Performances ● The more entities into the IdentityMap, the slower the computation for changes var_dump($em->getUnitOfWork()->size()); $obj = $em->find('BazOffer', 1); var_dump($em->getUnitOfWork()->size()); int(0) int(3)
  • 35. Doctrine | SymfonyCon2019 35 UOW Analysis ● getIdentityMap() returns the IdentityMap and thus the entities actually tracked by Doctrine ORM. ● All dependencies are also in $obj = $em->find('FooBar', 1); Debug::dump($em->getUnitOfWork()->getIdentityMap()); array(3) { ["FooBar"]=> array(1) { [1]=> string(8) "FooBar" } ["FooWow"]=> array(1) { [1]=> string(28) "DoctrineProxies__CG__FooWow" } ["FooUser"]=> array(1) { [10]=> string(14) "FooUser" } }
  • 36. Doctrine | SymfonyCon2019 36 UOW changeset ● getEntityChangeSet($entity) allows to see what operations are to be sent to the DB for $entity, when flush() will come var_dump($em->getUnitOfWork()->size()); $to = $em->find('FooBar', 1); $to->setCurrency('BAR'); var_dump($em->getUnitOfWork()->size()); $em->getUnitOfWork()->computeChangeSets(); dump($em->getUnitOfWork()->getEntityChangeSet($to)); int(0) int(3) array(1) { ["currency"]=> array(2) { [0]=> string(3) "foo" [1]=> string(3) "BAR" } }
  • 37. Doctrine | SymfonyCon2019 37 Change Tracking Policy ● Deferred implicit ● Default mode, the more comfortable, but the less performant ● Compare every attribute of every entities, and cascades ● Will be very heavy on big payloads ! (foreach(){foreach(){}}) ● Deferred explicit ● Only compare entities that are explicitely persisted back after modification ● The best mode, balance against performances and lines of code ● Notify ● User must notify the UOW about what changes it performed so that the UOW doesn't have to compute those by itself ● The most performant mode, but needs more code to be written
  • 38. Doctrine | SymfonyCon2019 38 Example Deferred implicit /** * @ORMTable(name="User") * @ORMEntity */ class User { $user = $em->find('User', 1); $user->setAge(30); $em->flush();
  • 39. Doctrine | SymfonyCon2019 39 Example Deferred explicit ● You tell UOW what entities to track ● Prevents the UOW from tracking a very big group of entities /** * @ORMTable(name="User") * @ORMEntity * @ORMChangeTrackingPolicy("DEFERRED_EXPLICIT") */ class User { $user = $em->find('User', 1); $user->setAge(30); $em->persist($user); $em->flush();
  • 40. Doctrine | SymfonyCon2019 40 Identity map ● Doctrine memorises entities into the IdentityMap and re-provides them when re-queried later, not performing additionnal SQL query ● Some cases bypass the identity map ● DQL queries ● Partial entities queries ● Queries not selecting using pk $u1 = $em->find('EntitiesUser', 1) ; $u1->setName('foobarbaz') ; /* ... */ $u2 = $em->find('EntitiesUser', 1) ; /* SQL is NOT re-run */ echo $u2->getName() ; /* foobarbaz */ assert($u1 === $u2) ; /* true */
  • 41. Doctrine | SymfonyCon2019 41 Repository ● Place where you write queries concerning an entity
  • 42. Doctrine | SymfonyCon2019 42 Repository API ● find(), findAll(), findBy(), findOneBy() ● Only find() makes use of the IdentityMap /* findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ $results = $repo->findBy(array('name'=>'foo'), array('name'=>'asc'), 2, 3);
  • 43. Doctrine | SymfonyCon2019 43 Association mapping ● Relations ? ● OneToOne ● OneToMany ● ManyToOne ● ManyToMany
  • 44. Doctrine | SymfonyCon2019 44 Associations ● bi-directional : ● One User can post several trips -> OneToMany ● Several trips can reference a same User -> ManyToOne namespace Entities; /** @Entity */ class User { /** @OneToMany(targetEntity="TripOffer", mappedBy="user") */ protected $tripOffer; /* ... */ } namespace Entities; /** @Entity */ class TripOffer { /**@ManyToOne(targetEntity="User", inversedBy="tripOffer") * @JoinColumn(name="Users_Id", referencedColumnName="id") */ protected $user; /* ... */ }
  • 45. Doctrine | SymfonyCon2019 45 Associations ● ArrayCollection is used to handle the "Many" part ● Otherwise the Entity itself namespace Entities; use DoctrineCommonCollectionsArrayCollection; /** @Entity */ class User { /** @OneToMany(targetEntity="TripOffer", mappedBy="user") */ protected $tripOffer; public function __construct() { $this->tripOffer = new ArrayCollection; } public function getTripOffer() { return $this->tripOffer; } public function addTripOffer(TripOffer $t) { $this->tripOffer[] = $t; } public function removeTripOffer(TripOffer $t) { $this->tripOffer->removeElement($t); } }
  • 46. Doctrine | SymfonyCon2019 46 Proxy objects ● By default, the "LAZY" fetch mode: dependancies are not loaded but replaced by Proxy classes or collections : ● Informations are read from DB only when entities are accessed ● You can ask for a proxy explicitely : $r = $em->find('EntitiesRating', 1); var_dump($r) ; object(EntitiesRating)[54] protected 'id' => int 1 protected 'creator' => object(DoctrineProxiesEntitiesUserProxy)[86] private '_entityPersister' => ... ... $realRating = $em->find('EntitiesRating', 1); $proxyRating = $em->getReference('EntitiesRating', 1);
  • 47. Doctrine | SymfonyCon2019 47 Hydration modes ● LAZY ● Default ● Uses Proxies and only loads the data when those are accessed ● EAGER ● Always loads the data (even if it is not used) ● EXTRA_LAZY ● Do not load the data for accesses : ● Collection#contains($entity) ● Collection#containsKey($key) ● Collection#count() ● Collection#get($key) ● Collection#slice($offset, $length = null)
  • 48. Doctrine | SymfonyCon2019 48 Examples hydration modes ● EAGER ● Association is always loaded ● Cascaded ● Several requests are used namespace Entities; /** @Entity */ class User { /** @OneToMany(targetEntity="TripOffer", mappedBy="user", fetch="EAGER") */ protected $tripOffer; /* ... */ } SELECT t0.id AS id1, t0.name AS name2, t0.userName AS userName3, t0.isvip AS isvip4 FROM users t0 WHERE t0.id = ? SELECT t0.id AS id1, t0.SeatsOffered AS SeatsOffered2, t0.TripOfferType AS TripOfferType3, t0.Users_Id AS Users_Id4 FROM TripOffer t0 WHERE t0.Users_Id = ? $u = $em->find('EntitiesUser', 1) ;
  • 49. Doctrine | SymfonyCon2019 49 Examples hydration modes ● EXTRA_LAZY ● Like LAZY, but does not load the data if some statistical questions are asked for it ● User, do you own some TripOffers ? ● No need to load all the trips to know that namespace Entities; /** @Entity */ class User { /** @OneToMany(targetEntity="TripOffer", mappedBy="user", fetch="EXTRA_LAZY") */ protected $tripOffer; /* ... */ } $u = $em->find('EntitiesUser', 1) ; echo $u->getTripOffer()->count() ; /* SELECT count(*) FROM TripOffer WHERE Users_Id = ? */ /* SELECT t0.id AS id1, t0.SeatsOffered AS SeatsOffered2, t0.TripOfferType AS TripOfferType3, t0.Users_Id AS Users_Id4 FROM TripOffer t0 WHERE t0.Users_Id = ? LIMIT 8 OFFSET 3 */ $someTripOffers = $u->getTripOffer()->slice(3, 8) ;
  • 50. Doctrine | SymfonyCon2019 50 Collections and hydration $u = $em->find('EntitiesUser', 1); $tripStatus = $u->getTripOffer()->get(0)->getTrips()->get(0)->getStatus();
  • 51. Doctrine | SymfonyCon2019 51 Cascades ● What to do with dependencies when acting on a root entity : ● persist ● remove ● merge ● detach ● refresh ● all ● By default, no cascades are used
  • 52. Doctrine | SymfonyCon2019 52 Example cascade (persist) namespace Entities; /** @Entity */ class User { /** @OneToMany(targetEntity="TripOffer", mappedBy="user", cascade={"persist"}) */ protected $tripOffer; /* ... */ }
  • 53. Doctrine | SymfonyCon2019 53 DQL ● Doctrine Query Language ● Looks like SQL but ● Queries Entities, not tables ● Associations are used, not foreign keys ● Mapping informations is used to convert to SQL ● INSERT does not exist ● DQL is fully extensible by the user
  • 54. Doctrine | SymfonyCon2019 54 DQL example ● You query entities, not tables ● createQuery() for a DQL query ● createNamedQuery() for a pre-recorded DQL query ● createNativeQuery() for a SQL query ● createNamedNativeQuery() for a pre-recorded SQL query $q = $em->createQuery('SELECT u FROM EntitiesUser u WHERE u.name = ?1'); $q->setParameter(1, 'foo'); $r = $q->getResult(); $q = $em->createQuery('SELECT u, r FROM EntitiesUser u LEFT JOIN u.ratings r'); $r = $q->getResult();
  • 55. Doctrine | SymfonyCon2019 55 DQL and joins namespace Entities; use DoctrineCommonCollectionsArrayCollection; /** * @Table(name="my_users") */ class User { /** * @OneToMany(targetEntity="Rating", mappedBy="userCreator") */ protected $ratings; } $q = $em->createQuery("SELECT u, r FROM EntitiesUser u JOIN u.ratings r"); $r = $q->getArrayResult();
  • 56. Doctrine | SymfonyCon2019 56 Named queries ● Queries recorded to be recalled / re-run later $dql = "SELECT ... ... ..." ; $conf = new DoctrineORMConfiguration(); $conf->addNamedQuery('search', $dql); $q = $em->createNamedQuery('search'); $r = $q->getResult();
  • 57. Doctrine | SymfonyCon2019 57 DQL and return values ● By default, the return type is an array of Entities $q = $em->createQuery('SELECT u FROM EntitiesUser u WHERE u.name = ?1'); $q->setParameter(1, 'foo'); $r = $q->getResult(); array 0 => object(DoctrineProxiesEntitiesUserProxy)[81] $q = $em->createQuery('SELECT u FROM EntitiesUser u WHERE u.name = ?1'); $q->setParameter(1, 'foo'); $r = $q->getSingleResult(); object(DoctrineProxiesEntitiesUserProxy)[81]
  • 58. Doctrine | SymfonyCon2019 58 DQL and return values $q->getArrayResult() array 0 => & array 'id' => int 2 'name' => string 'foo' (length=3) 'userName' => string 'fooname' (length=7) 'vip' => int 0 SELECT u FROM EntitiesUser u WHERE u.name = 'foo'
  • 59. Doctrine | SymfonyCon2019 59 DQL and return values ● Use the right result type you need ● single**() must be used on single results, if not : ● NoResultException ● NonUniqueResultException SELECT COUNT(u) FROM EntitiesUser u $q->getScalarResult() $q->getSingleResult() $q->getSingleScalarResult() array 1 => string '5008' array 0 => array 1 => string '5008' string '5008'
  • 60. Doctrine | SymfonyCon2019 60 DQL and return values ● Often used : getResult() ● If you select not all fields of an entity : ● An array will be returned ● You can ask for a partial entity ● select('PARTIAL alias.{col1, col2}') $q = $em->createQuery("SELECT u.name, u.userName FROM EntitiesUser u"); $r = $q->getResult(); array 0 => array 'name' => string 'bar' (length=3) 'userName' => string 'barname' (length=7) 1 => array 'name' => string 'foo' (length=3) 'userName' => string 'fooname' (length=7)
  • 61. Doctrine | SymfonyCon2019 61 DQL and return values $q = $em->createQuery("SELECT u, UPPER(r.comment), r.id, to.type FROM EntitiesUser u LEFT JOIN u.ratings r LEFT JOIN u.tripOffer to"); $results = $q->getResult(); array 0 => array 0 => object(EntitiesUser)[103] 1 => string 'THIS IS A COMMENT' (length=17) 'id' => string '1' (length=1) 'type' => null 1 => (...)
  • 62. Doctrine | SymfonyCon2019 62 DQL and Identity Map ● DQL queries store into the IdentityMap but don't read from it ● Store selected entities ● If they are full (all fields selected) ● If the result is asked to be an entity, and not an array $q = $em->createQuery('SELECT r FROM FooRating r WHERE r.id=?1'); $q->setParameter(1, 1); $result = $q->getSingleResult(); $rating1 = $em->find('FooRating', 1); /* Query is not re-played */ assert($rating1 === $result); /* that's true */ $rating1 = $em->find('FooRating', 1); $q = $em->createQuery('SELECT r FROM FooRating r WHERE r.id=?1'); $q->setParameter(1, 1); $result = $q->getSingleResult(); /* Query is re-played */ assert($rating1 === $result); /* that's true */
  • 63. Doctrine | SymfonyCon2019 63 DQL and Identity Map ● Dependancies benefit from IdentityMap $q = $em->createQuery('SELECT u, r FROM EntitiesUser u JOIN u.ratings r'); $results = $q->getResult() $rating1 = $em->find('FooRating', 1); /* No query played */ foreach ($results as $user) { $user->getRatings(); /* No query played */ } $rating1 = $em->find('FooRating', 1); $rating1->setComment('I have changed'); $q = $em->createQuery('SELECT r FROM FooRating r WHERE r.id=?1'); $q->setParameter(1, 1); $q->setHint(DoctrineORMQuery::HINT_REFRESH, 1); $result = $q->getSingleResult(); assert($rating1 === $result); assert($rating1->getComment() != 'I have changed');
  • 64. Doctrine | SymfonyCon2019 64 DQL functions $q = $em->createQuery("SELECT u, CONCAT('foo','bar') as baz FROM EntitiesUser u"); $r = $q->getResult(); array 0 => array 0 => object(EntitiesUser)[30] ... 'baz' => string 'foobar' (length=6) 1 => array ... $rating1 = $em->find('EntitiesRating', 1) ; $q = $em->createQuery("SELECT u FROM EntitiesUser u WHERE ?1 MEMBER OF u.ratings"); $q->setParameter(1, $rating1); /* A sub-select is used */
  • 65. Doctrine | SymfonyCon2019 65 Writing using DQL ● INSERT not possible ● DELETE and UPDATE are OK ● Warning, this could desync the UnitOfWork $user = $em->find('EntitiesUser', 66); $q = $em->createQuery('DELETE EntitiesUser u WHERE u.id=66'); $q->execute(); $user->setName('hello'); $em->flush(); /* UPDATE users SET name = ? WHERE id = ? */
  • 66. Doctrine | SymfonyCon2019 66 SQL ● SQL can still be used, through DBAL ● But : ● That bypasses all the Entities and the mapping done ● That can desync the UnitOfWork $results = $em->getConnection()->fetchAll("/* some query here*/") ;
  • 67. Doctrine | SymfonyCon2019 67 DQL or SQL ? ● DQL uses Entities and mapping informations, not the DB and its tables directly ● DQL is parsed and turned to SQL ● This transformation should get cached ● $query->getSQL(); ● DQL can be deeply hooked ● DQL can return Entities ● SQL returns arrays
  • 68. Doctrine | SymfonyCon2019 68 Cache ● Several caches may (must) be used ● "Query Cache" (DQL->SQL) ● Result Cache : caches a result from a query ● Metadata Cache ● Using SF, Doctrine will be bound to SF caches
  • 69. Doctrine | SymfonyCon2019 69 Query Cache ● Activated per query $conf = new DoctrineORMConfiguration(); $conf->setResultCacheImpl(new DoctrineCommonCacheApcCache()); $q = $em->createQuery('SELECT u, r, r2 FROM EntitiesUser u JOIN u.ratings r JOIN u.ratingsConcerned r2'); $q->useResultCache(1, 3600, 'foo_result') ; $r = $q->execute();
  • 70. Doctrine | SymfonyCon2019 70 Q/A ● ? ● ? ● ? ● ? ● ? ● ? ● ? ● ? ● ?
  • 71. Doctrine | SymfonyCon2019 71 Practice ● Setup symfony-demo ● Get familiar with the DB structure ● Navigate into the BlogController and the PostRepository > composer create-project symfony/symfony-demo some_project_dir > bin/console s:r
  • 72. Doctrine | SymfonyCon2019 72 Practice ● Create a query using DQL to get 3 random posts
  • 73. Doctrine | SymfonyCon2019 73 Practice ● Create a query using DQL to get 3 random posts ● See how the authors are replaced with proxies ● Access one author field ● See how N+1 query is issued by Doctrine ● Give a hint to the QueryBuilder to load partial entities ● See how dependencies are now NULLed ● Change the fetchmode of the author dependency to EAGER ● See how the authors are now gathered by Doctrine using N+1 ● In every case, dump the identity map and check the state of each entity as being STATE_MANAGED
  • 74. Doctrine | SymfonyCon2019 74 Practice ● Create a query using DQL to get 3 random posts ● Get the first post from the collection ● Modify the post content ● Compute the changeset from the UOW ● Dump the changeset ● Change the tracking policy of posts to DEFERRED_EXPLICIT ● Modify the post content ● Compute the changeset from the UOW ● Dump the changeset ● What happens ? How to do ?
  • 75. Doctrine | SymfonyCon2019 75 Practice ● Add a new "avatar" field to the User ● Play the migration ● Patch the User form to add a new type to upload an avatar ● Create an entityListener to treat the avatar ● The DB should save the avatar file path
  • 76. Doctrine | SymfonyCon2019 76 Practice ● Add a new RoleType to the Type mappings ● This type maps sf ROLE_** to an integer
  • 77. Doctrine | SymfonyCon2019 77 Practice ● Create a query to get the comments written by ROLE_ADMIN ● Delete those