SlideShare a Scribd company logo
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

More Related Content

What's hot

Java
JavaJava
Les intents sous Android
Les intents sous Android Les intents sous Android
Les intents sous Android
Houssem Lahiani
 
Formation python
Formation pythonFormation python
Formation python
j_lipaz
 
Tp n 3 linux
Tp n 3 linuxTp n 3 linux
Tp n 3 linux
Amir Souissi
 
Chap 6 : classes et interfaces
Chap 6 : classes et interfacesChap 6 : classes et interfaces
Chap 6 : classes et interfaces
Aziz Darouichi
 
Models for hierarchical data
Models for hierarchical dataModels for hierarchical data
Models for hierarchical data
Karwin Software Solutions LLC
 
POO Java Chapitre 1 Classe & Objet
POO Java Chapitre 1 Classe & ObjetPOO Java Chapitre 1 Classe & Objet
POO Java Chapitre 1 Classe & Objet
Mouna Torjmen
 
Clips basics how to make expert system in clips | facts adding | rules makin...
Clips basics  how to make expert system in clips | facts adding | rules makin...Clips basics  how to make expert system in clips | facts adding | rules makin...
Clips basics how to make expert system in clips | facts adding | rules makin...
NaumanMalik30
 
Constructors in Java (2).pdf
Constructors in Java (2).pdfConstructors in Java (2).pdf
Constructors in Java (2).pdf
kumari36
 
Examen principal- php - correction
Examen principal- php - correctionExamen principal- php - correction
Examen principal- php - correction
Ines Ouaz
 
Generics in java
Generics in javaGenerics in java
Generics in java
suraj pandey
 
Corrige tp java
Corrige tp javaCorrige tp java
Corrige tp java
Maya Medjdoub
 
Exercice 2 java Héritage
Exercice 2  java HéritageExercice 2  java Héritage
Exercice 2 java Héritage
NadaBenLatifa
 
Android presentation - Gradle ++
Android presentation - Gradle ++Android presentation - Gradle ++
Android presentation - Gradle ++
Javier de Pedro López
 
Polymorphisme (cours, résumé)
Polymorphisme (cours, résumé)Polymorphisme (cours, résumé)
Polymorphisme (cours, résumé)
Anis Bouhachem Djer
 
Design pattern bridgepatern
Design pattern bridgepaternDesign pattern bridgepatern
Design pattern bridgepatern
Kombe Khalfan
 
Developing for Node.JS with MySQL and NoSQL
Developing for Node.JS with MySQL and NoSQLDeveloping for Node.JS with MySQL and NoSQL
Developing for Node.JS with MySQL and NoSQL
John David Duncan
 
Inheritance in oops
Inheritance in oopsInheritance in oops
Inheritance in oops
Hirra Sultan
 
Cours algo: Les pointeurs
Cours algo: Les pointeursCours algo: Les pointeurs
Cours algo: Les pointeurs
Sana REFAI
 
Fondamentaux java
Fondamentaux javaFondamentaux java
Fondamentaux java
Ines Ouaz
 

What's hot (20)

Java
JavaJava
Java
 
Les intents sous Android
Les intents sous Android Les intents sous Android
Les intents sous Android
 
Formation python
Formation pythonFormation python
Formation python
 
Tp n 3 linux
Tp n 3 linuxTp n 3 linux
Tp n 3 linux
 
Chap 6 : classes et interfaces
Chap 6 : classes et interfacesChap 6 : classes et interfaces
Chap 6 : classes et interfaces
 
Models for hierarchical data
Models for hierarchical dataModels for hierarchical data
Models for hierarchical data
 
POO Java Chapitre 1 Classe & Objet
POO Java Chapitre 1 Classe & ObjetPOO Java Chapitre 1 Classe & Objet
POO Java Chapitre 1 Classe & Objet
 
Clips basics how to make expert system in clips | facts adding | rules makin...
Clips basics  how to make expert system in clips | facts adding | rules makin...Clips basics  how to make expert system in clips | facts adding | rules makin...
Clips basics how to make expert system in clips | facts adding | rules makin...
 
Constructors in Java (2).pdf
Constructors in Java (2).pdfConstructors in Java (2).pdf
Constructors in Java (2).pdf
 
Examen principal- php - correction
Examen principal- php - correctionExamen principal- php - correction
Examen principal- php - correction
 
Generics in java
Generics in javaGenerics in java
Generics in java
 
Corrige tp java
Corrige tp javaCorrige tp java
Corrige tp java
 
Exercice 2 java Héritage
Exercice 2  java HéritageExercice 2  java Héritage
Exercice 2 java Héritage
 
Android presentation - Gradle ++
Android presentation - Gradle ++Android presentation - Gradle ++
Android presentation - Gradle ++
 
Polymorphisme (cours, résumé)
Polymorphisme (cours, résumé)Polymorphisme (cours, résumé)
Polymorphisme (cours, résumé)
 
Design pattern bridgepatern
Design pattern bridgepaternDesign pattern bridgepatern
Design pattern bridgepatern
 
Developing for Node.JS with MySQL and NoSQL
Developing for Node.JS with MySQL and NoSQLDeveloping for Node.JS with MySQL and NoSQL
Developing for Node.JS with MySQL and NoSQL
 
Inheritance in oops
Inheritance in oopsInheritance in oops
Inheritance in oops
 
Cours algo: Les pointeurs
Cours algo: Les pointeursCours algo: Les pointeurs
Cours algo: Les pointeurs
 
Fondamentaux java
Fondamentaux javaFondamentaux java
Fondamentaux java
 

Similar to Doctrine with Symfony - SymfonyCon 2019

Zend Framework 2 - Basic Components
Zend Framework 2  - Basic ComponentsZend Framework 2  - Basic Components
Zend Framework 2 - Basic Components
Mateusz 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 2014
Matthias Noback
 
Debugging in drupal 8
Debugging in drupal 8Debugging in drupal 8
Debugging in drupal 8
Allie Jones
 
AngularJS Architecture
AngularJS ArchitectureAngularJS Architecture
AngularJS Architecture
Eyal Vardi
 
AngularJS Internal
AngularJS InternalAngularJS Internal
AngularJS Internal
Eyal Vardi
 
How Kris Writes Symfony Apps
How Kris Writes Symfony AppsHow Kris Writes Symfony Apps
How Kris Writes Symfony Apps
Kris Wallsmith
 
The Naked Bundle - Tryout
The Naked Bundle - TryoutThe Naked Bundle - Tryout
The Naked Bundle - Tryout
Matthias Noback
 
Symfony CMF - PHP Conference Brazil 2011
Symfony CMF - PHP Conference Brazil 2011Symfony CMF - PHP Conference Brazil 2011
Symfony CMF - PHP Conference Brazil 2011
Jacopo Romei
 
Symfony2 - from the trenches
Symfony2 - from the trenchesSymfony2 - from the trenches
Symfony2 - from the trenches
Lukas Smith
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the Trenches
Jonathan 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
 
Drupal 8 migrate!
Drupal 8 migrate!Drupal 8 migrate!
Drupal 8 migrate!
Pavel Makhrinsky
 
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 Belgium
Matthias Noback
 
The Naked Bundle - Symfony Barcelona
The Naked Bundle - Symfony BarcelonaThe Naked Bundle - Symfony Barcelona
The Naked Bundle - Symfony Barcelona
Matthias Noback
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
Lars Jankowfsky
 
IOC + Javascript
IOC + JavascriptIOC + Javascript
IOC + Javascript
Brian Cavalier
 
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_Tool
Gordon Forsythe
 

Similar to 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
 

More from julien pauli

Php engine
Php enginePhp engine
Php engine
julien pauli
 
PHP 7 OPCache extension review
PHP 7 OPCache extension reviewPHP 7 OPCache extension review
PHP 7 OPCache extension review
julien pauli
 
PHP Internals and Virtual Machine
PHP Internals and Virtual MachinePHP Internals and Virtual Machine
PHP Internals and Virtual Machine
julien 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 PRNG
julien 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 Yourself
julien pauli
 
SymfonyCon 2017 php7 performances
SymfonyCon 2017 php7 performancesSymfonyCon 2017 php7 performances
SymfonyCon 2017 php7 performances
julien pauli
 
Php and threads ZTS
Php and threads ZTSPhp and threads ZTS
Php and threads ZTS
julien pauli
 
Tcpip
TcpipTcpip
Symfony live 2017_php7_performances
Symfony live 2017_php7_performancesSymfony live 2017_php7_performances
Symfony live 2017_php7_performances
julien pauli
 
PHP 7 new engine
PHP 7 new enginePHP 7 new engine
PHP 7 new engine
julien pauli
 
Php7 extensions workshop
Php7 extensions workshopPhp7 extensions workshop
Php7 extensions workshop
julien pauli
 
Profiling php5 to php7
Profiling php5 to php7Profiling php5 to php7
Profiling php5 to php7
julien pauli
 
PHP 7 performances from PHP 5
PHP 7 performances from PHP 5PHP 7 performances from PHP 5
PHP 7 performances from PHP 5
julien pauli
 
PHP7 is coming
PHP7 is comingPHP7 is coming
PHP7 is coming
julien pauli
 
Mysqlnd, an unknown powerful PHP extension
Mysqlnd, an unknown powerful PHP extensionMysqlnd, an unknown powerful PHP extension
Mysqlnd, an unknown powerful PHP extension
julien 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
 
PHP5.5 is Here
PHP5.5 is HerePHP5.5 is Here
PHP5.5 is Here
julien pauli
 

More from 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
 

Recently uploaded

Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
Alan Dix
 
Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
Adtran
 
Video Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the FutureVideo Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the Future
Alpen-Adria-Universität
 
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
Neo4j
 
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
Neo4j
 
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
Neo4j
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
Kari Kakkonen
 
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdfObservability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Paige Cruz
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance
 
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Albert Hoitingh
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
DanBrown980551
 
GraphSummit Singapore | The Art of the Possible with Graph - Q2 2024
GraphSummit Singapore | The Art of the  Possible with Graph - Q2 2024GraphSummit Singapore | The Art of the  Possible with Graph - Q2 2024
GraphSummit Singapore | The Art of the Possible with Graph - Q2 2024
Neo4j
 
Removing Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software FuzzingRemoving Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software Fuzzing
Aftab Hussain
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
KatiaHIMEUR1
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
BookNet Canada
 
Introduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - CybersecurityIntroduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - Cybersecurity
mikeeftimakis1
 
National Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practicesNational Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practices
Quotidiano Piemontese
 
Generative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to ProductionGenerative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to Production
Aggregage
 
Uni Systems Copilot event_05062024_C.Vlachos.pdf
Uni Systems Copilot event_05062024_C.Vlachos.pdfUni Systems Copilot event_05062024_C.Vlachos.pdf
Uni Systems Copilot event_05062024_C.Vlachos.pdf
Uni Systems S.M.S.A.
 
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdfSAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
Peter Spielvogel
 

Recently uploaded (20)

Epistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI supportEpistemic Interaction - tuning interfaces to provide information for AI support
Epistemic Interaction - tuning interfaces to provide information for AI support
 
Pushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 daysPushing the limits of ePRTC: 100ns holdover for 100 days
Pushing the limits of ePRTC: 100ns holdover for 100 days
 
Video Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the FutureVideo Streaming: Then, Now, and in the Future
Video Streaming: Then, Now, and in the Future
 
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
GraphSummit Singapore | The Future of Agility: Supercharging Digital Transfor...
 
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
GraphSummit Singapore | Graphing Success: Revolutionising Organisational Stru...
 
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
GraphSummit Singapore | Enhancing Changi Airport Group's Passenger Experience...
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
 
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdfObservability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
Observability Concepts EVERY Developer Should Know -- DeveloperWeek Europe.pdf
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
 
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
Encryption in Microsoft 365 - ExpertsLive Netherlands 2024
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
 
GraphSummit Singapore | The Art of the Possible with Graph - Q2 2024
GraphSummit Singapore | The Art of the  Possible with Graph - Q2 2024GraphSummit Singapore | The Art of the  Possible with Graph - Q2 2024
GraphSummit Singapore | The Art of the Possible with Graph - Q2 2024
 
Removing Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software FuzzingRemoving Uninteresting Bytes in Software Fuzzing
Removing Uninteresting Bytes in Software Fuzzing
 
Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !Securing your Kubernetes cluster_ a step-by-step guide to success !
Securing your Kubernetes cluster_ a step-by-step guide to success !
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
 
Introduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - CybersecurityIntroduction to CHERI technology - Cybersecurity
Introduction to CHERI technology - Cybersecurity
 
National Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practicesNational Security Agency - NSA mobile device best practices
National Security Agency - NSA mobile device best practices
 
Generative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to ProductionGenerative AI Deep Dive: Advancing from Proof of Concept to Production
Generative AI Deep Dive: Advancing from Proof of Concept to Production
 
Uni Systems Copilot event_05062024_C.Vlachos.pdf
Uni Systems Copilot event_05062024_C.Vlachos.pdfUni Systems Copilot event_05062024_C.Vlachos.pdf
Uni Systems Copilot event_05062024_C.Vlachos.pdf
 
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdfSAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
SAP Sapphire 2024 - ASUG301 building better apps with SAP Fiori.pdf
 

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