Zend Framework 1 + Doctrine 2 - Zend Technologies

6 downloads 301 Views 2MB Size Report
Zend Framework 1 + Doctrine 2. Jonathan H. Wage. •PHP Developer for over 10 years. •Symfony Contributor. •Doctrine
Zend Framework 1 + Doctrine 2

Jonathan H. Wage •PHP Developer for over 10 years •Symfony Contributor •Doctrine Contributor •Published Author •Business Owner •Nashville, TN Resident •http://www.twitter.com/jwage •http://www.facebook.com/jwage

2

Zend Framework 1 + Doctrine 2

I work at OpenSky

•What is OpenSky? “a social commerce platform”

•Based in New York and is a major open •source software advocate •http://www.shopopensky.com

3

Zend Framework 1 + Doctrine 2

OpenSky Technologies •PHP 5.3.2 •Apache2 •Symfony2 •Doctrine2 •jQuery •mule, stomp, hornetq •MongoDB •nginx •varnish

4

Zend Framework 1 + Doctrine 2

Ralph Schindler •Software Engineer on the Zend Framework team At Zend for almost 3 years Before that TippingPoint/3Com

•Programming PHP for 12+ years •Live in New Orleans, LA. Lived in Austin, Tx for 5 years

•Where To Find Me: http://ralphschindler.com http://twitter.com/ralphschindler http://github.com/ralphschindler ralphschindler on freenode

5

Zend Framework 1 + Doctrine 2

Guilherme Blanco •Programming Experience 12+ years web development experience 9 years with PHP

•Software Engineer at Yahoo! •Open Source Evangelist Contributes regularly to the Doctrine Project,

Symfony, and Zend Framework

•Where to find me: http://twitter.com/guilhermeblanco http://github.com/guilhermeblanco

6

Zend Framework 1 + Doctrine 2

Doctrine 2

7

The Doctrine Project

What is Doctrine? • Open Source PHP Project started in 2006 • Specializes in ) */ class User { /** @Id @Column(type="integer") @GeneratedValue */ private $id; /** @Column(length=50) */ private $name; }

42

Zend Framework 1 + Doctrine 2

Map entities to RDBMS tables • Entities are just regular PHP objects: Mapped By:

•Annotations •YAML

43

Zend Framework 1 + Doctrine 2

Entities\User: type: entity table: users id: id: type: integer generator: strategy: AUTO fields: name: type: string length: 255

Map entities to RDBMS tables • Entities are just regular PHP objects: Mapped By:

•Annotations •YAML •XML



44

Zend Framework 1 + Doctrine 2

Mapping Performance • Only parsed once • Cached using configured cache driver • Subsequent requests pull mapping information from configured cache driver

45

Zend Framework 1 + Doctrine 2

Working with Objects • Use the $em to manage the persistence of your entities:

$user = new User; $user->setName('Jonathan H. Wage'); $em->persist($user); $em->flush();

46

Zend Framework 1 + Doctrine 2

Working with Objects • Updating an object:

$user = $em->getRepository('User') ->find(array('name' => 'jwage')); // modify the already managed object $user->setPassword('changed'); $em->flush(); // issues update

47

Zend Framework 1 + Doctrine 2

Working with Objects • Removing an object:

$user = $em->getRepository('User') ->find(array('name' => 'jwage')); // schedule for deletion $em->remove($user); $em->flush(); // issues delete

48

Zend Framework 1 + Doctrine 2

Transactions • Implicit:

$user = new User; $user->setName('George'); $em->persist($user); $em->flush(); •EntityManager#flush() will begin and commit/rollback a transaction

49

Zend Framework 1 + Doctrine 2

Transactions • Explicit:

// $em instanceof EntityManager $em->getConnection()->beginTransaction(); // suspend auto-commit try { //... do some work $user = new User; $user->setName('George'); $em->persist($user); $em->flush(); $em->getConnection()->commit(); } catch (Exception $e) { $em->getConnection()->rollback(); $em->close(); throw $e; }

50

Zend Framework 1 + Doctrine 2

Transactions • A more convenient explicit transaction:

// $em instanceof EntityManager $em->transactional(function($em) { //... do some work $user = new User; $user->setName('George'); $em->persist($user); });

51

Zend Framework 1 + Doctrine 2

Transactions and Performance

for ($i = 0; $i < 20; ++$i) { $user = new User; $user->name = 'Jonathan H. Wage'; $em->persist($user); } $s = microtime(true); $em->flush(); $e = microtime(true); echo $e - $s;

52

Zend Framework 1 + Doctrine 2

Transactions and Performance • How you use transactions can greatly affect performance. Here is the same thing using raw PHP code:

$s = microtime(true); for ($i = 0; $i < 20; ++$i) { mysql_query("INSERT INTO users (name) VALUES ('Jonathan H. Wage')", $link); } $e = microtime(true); echo $e - $s;

53

Zend Framework 1 + Doctrine 2

Which is faster? • The one using no ORM, and no abstraction at all? • Or the one using the Doctrine ORM?

54

Zend Framework 1 + Doctrine 2

Which is faster? • The one using no ORM, and no abstraction at all? • Or the one using the Doctrine ORM?

Doctrine2

0.0094 seconds

mysql_query 0.0165 seconds • Doctrine2 wins! How?

55

Zend Framework 1 + Doctrine 2

Not Faster • Doctrine just automatically performed the inserts inside one transaction. Here is the code updated to use transactions:

$s = microtime(true); mysql_query('START TRANSACTION', $link); for ($i = 0; $i < 20; ++$i) { mysql_query("INSERT INTO users (name) VALUES ('Jonathan H. Wage')", $link); } mysql_query('COMMIT', $link); $e = microtime(true); echo $e - $s;

56

Zend Framework 1 + Doctrine 2

Much Faster • Transactions matter and can affect performance greater than any code optimization!

Doctrine2

0.0094 seconds 0.0028

mysql_query 0.0165 seconds

57

Zend Framework 1 + Doctrine 2

Locking Support • Optimistic locking with integer:

class User { // ... /** @Version @Column(type="integer") */ private $version; // ... }

58

Zend Framework 1 + Doctrine 2

Locking Support • Optimistic locking with timestamp:

class User { // ... /** @Version @Column(type="datetime") */ private $version; // ... }

59

Zend Framework 1 + Doctrine 2

Locking Support • Verify version when finding:

use Doctrine\DBAL\LockMode; use Doctrine\ORM\OptimisticLockException; $theEntityId = 1; $expectedVersion = 184; try { $entity = $em->find('User', $theEntityId, LockMode::OPTIMISTIC, $expectedVersion); // do the work $em->flush(); } catch(OptimisticLockException $e) { echo "Sorry, but someone else has already changed this entity. Please apply the changes again!"; }

60

Zend Framework 1 + Doctrine 2

Locking Support • Example implementation:

$post = $em->find('BlogPost', 123456); echo ''; echo '';

$postId = (int) $_GET['id']; $postVersion = (int) $_GET['version']; $post = $em->find('BlogPost', $postId, \Doctrine\DBAL\LockMode::OPTIMISTIC, $postVersion);

61

Zend Framework 1 + Doctrine 2

DQL Doctrine Query Language

62

Zend Framework 1 + Doctrine 2

DQL • DQL stands for Doctrine Query Language and is an Object Query Language derivate that is very similar to the Hibernate Query Language (HQL) or the Java Persistence Query Language (JPQL). • DQL provides powerful querying capabilities over your object model. Imagine all your objects lying around in some storage (like an object ) */ private $mapped1; /** @Column(type="string") */ private $mapped2; /** * @OneToOne(targetEntity="MappedSuperclassRelated1") * @JoinColumn(name="related1_id", referencedColumnName="id") */ private $mappedRelated1; // ... more fields and methods } /** @Entity */ class EntitySubClass extends MappedSuperclassBase { /** @Id @Column(type="integer") */ private $id; /** @Column(type="string") */ private $name; // ... more fields and methods }

72

Zend Framework 1 + Doctrine 2

Single Table Inheritance /** * @Entity * @InheritanceType("SINGLE_TABLE") * @DiscriminatorColumn(name="discr", type="string") * @DiscriminatorMap({"person" = "Person", "employee" = "Employee"}) */ class Person { // ... } /** * @Entity */ class Employee extends Person { // ... }

73

Zend Framework 1 + Doctrine 2

Single Table Inheritance • All entities share one table. • To distinguish which row represents which type in the hierarchy a so-called discriminator column is used.

74

Zend Framework 1 + Doctrine 2

Class Table Inheritance /** * @Entity * @InheritanceType("JOINED") * @DiscriminatorColumn(name="discr", type="string") * @DiscriminatorMap({"person" = "Person", "employee" = "Employee"}) */ class Person { // ... } /** @Entity */ class Employee extends Person { // ... }

75

Zend Framework 1 + Doctrine 2

Class Table Inheritance • Each class in a hierarchy is mapped to several tables: its own table and the tables of all parent classes. • The table of a child class is linked to the table of a parent class through a foreign key constraint. • A discriminator column is used in the topmost table of the hierarchy because this is the easiest way to achieve polymorphic queries.

76

Zend Framework 1 + Doctrine 2

Bulk Inserts with Domain • Insert 10000 objects batches of 20:

$batchSize = 20; for ($i = 1; $i setStatus('user'); $user->setUsername('user' . $i); $user->setName('Mr.Smith-' . $i); $em->persist($user); if ($i % $batchSize == 0) { $em->flush(); $em->clear(); // Detaches all objects from Doctrine! } }

77

Zend Framework 1 + Doctrine 2

Bulk Update with DQL

$q = $em->createQuery('update Manager m set m.salary = m.salary * 0.9'); $numUpdated = $q->execute();

78

Zend Framework 1 + Doctrine 2

Bulk Update with Domain • Update objects in batches of 20:

$batchSize = 20; $i = 0; $q = $em->createQuery('select u from User u'); $iterableResult = $q->iterate(); foreach($iterableResult AS $row) { $user = $row[0]; $user->increaseCredit(); $user->calculateNewBonuses(); if (($i % $batchSize) == 0) { $em->flush(); // Executes all updates. $em->clear(); // Detaches all objects from Doctrine! } ++$i; }

79

Zend Framework 1 + Doctrine 2

Bulk Delete with DQL

$q = $em->createQuery('delete from Manager m where m.salary > 100000'); $numDeleted = $q->execute();

80

Zend Framework 1 + Doctrine 2

Bulk Delete with Domain

$batchSize = 20; $i = 0; $q = $em->createQuery('select u from User u'); $iterableResult = $q->iterate(); while (($row = $iterableResult->next()) !== false) { $em->remove($row[0]); if (($i % $batchSize) == 0) { $em->flush(); // Executes all deletions. $em->clear(); // Detaches all objects from Doctrine! } ++$i; }

81

Zend Framework 1 + Doctrine 2

Events •Doctrine triggers events throughout the •lifecycle of objects it manages:  preRemove  postRemove  prePersist  postPersist  preUpdate  postUpdate  preLoad  postLoad

82

Zend Framework 1 + Doctrine 2

Example /** * @Entity * @HasLifecycleCallbacks */ class BlogPost { // ... /** @PreUpdate */ public function prePersist() { $this->createdAt = new DateTime(); } /** @PreUpdate */ public function preUpdate() { $this->updatedAt = new DateTime(); } }

83

Zend Framework 1 + Doctrine 2

Using Raw SQL • Write a raw SQL string • Map the result set of the SQL query using a ResultSetMapping instance

84

Zend Framework 1 + Doctrine 2

Using Raw SQL $sql = 'SELECT id, name FROM users WHERE username = ?'; $rsm = new ResultSetMapping; $rsm->addEntityResult('User', 'u'); $rsm->addFieldResult('u', 'id', 'id'); $rsm->addFieldResult('u', 'name', 'name'); $query = $this->_em->createNativeQuery($sql, $rsm); $query->setParameter(1, 'jwage'); $users = $query->getResult();

85

Zend Framework 1 + Doctrine 2

Why use an object mapper?

86

Zend Framework 1 + Doctrine 2

Encapsulation Encapsulate your domain in an object oriented interface

87

Zend Framework 1 + Doctrine 2

Maintainability The organization of your domain logic in an OO way improved maintainability

88

Zend Framework 1 + Doctrine 2

Testability Keeping a clean OO domain model makes your business logic easily testable for improved stability

89

Zend Framework 1 + Doctrine 2

Portability Write portable and thin application controller code and fat models.

90

Zend Framework 1 + Doctrine 2

Demo Time

91

The Doctrine Project

What we are going to accomplish •Start with a vanilla Zend Framework Project •Ensure all dependencies are met •Configure out application to utilize Doctrine •Create an Entity (in our library) with Annotations •Generate the Database •Generate Proxies + Repositories •Create a Controller for basic crud •Talk about what would happen next

92

Zend Framework 1 + Doctrine 2

Where To Get The Code •http://github.com/ralphschindler/NOLASnowball Self contained Project Branches:

•master - Clean ZF Project, with ZF embedded in library/ folder •non-model-artifacts - Authentication service, Login form •doctrine2-managed – Has following libraries: Doctrine2, Symfony (Copied into library), Bisna (ZF1 + Doctrine2 Integration library) – Has the following entity created: NOLASnowball\Entity\Stand – Has the proper application.ini settings – Has scripts/doctrine.php setup for easy use

•doctrine2-managed-crud – Created Stand Controller, actions are complete, view scripts complete – Proxies & Repositories are generated – Assumes you’ve generated the SQL (locally would need to change db credentials) 93

Zend Framework 1 + Doctrine 2

Lets look at code! •Demo time

94

Zend Framework 1 + Doctrine 2

Exercises and Things To Implement •flush() could be a postDispatch() function call •All interaction with Entities could be moved into a Service Layer Once such implementation: https://github.com/guilhermeblanco/

ZF1-Doctrine2-ServiceLayer

•Add relationships, and alter forms accordingly

95

Zend Framework 1 + Doctrine 2

Recommended Reading •Doctrine Website & Manual (Also has download) http://www.doctrine-project.org/

•Zend Framework Web & Manual (With download) http://framework.zend.com/

•Ralph Schindler’s Sample Application https://github.com/ralphschindler/NOLASnowball

•Guilherme Blanco’s ZF1 + D2 Integration code https://github.com/guilhermeblanco/ZendFramework1-Doctrine2

96

Zend Framework 1 + Doctrine 2

Questions? •Q & A Time

97

Zend Framework 1 + Doctrine 2