Slide 1

Slide 1 text

Karsten Dambekalns Persistence in FLOW3 with Doctrine 2 FLOW3 Experience 2012 1

Slide 2

Slide 2 text

co-lead of TYPO3 5.0 and FLOW3 34 years old lives in Lübeck, Germany 1 wife, 3 sons, 1 espresso machine likes canoeing and climbing Karsten Dambekalns 2

Slide 3

Slide 3 text

Persistence in FLOW3 with Doctrine 2 Object Persistence in the Flow • Based on Doctrine 2 • Seamless integration into FLOW3 • Provides the great Doctrine 2 features • Uses UUIDs • Our low-level persistence API • Allows for own, custom persistence backends (instead of Doctrine 2) • CouchDB is supported natively 3

Slide 4

Slide 4 text

Basic Object Persistence // Create a new customer and persist it: $customer = new Customer("Robert"); $this->customerRepository->add($customer); // Update a customer: $customer->setName("I, Robot"); $this->customerRepository->update($customer); // Find an existing customer: $otherCustomer = $this->customerRepository->findByFirstName("Karsten"); // … and delete it: $this->customerRepository->remove($otherCustomer); • Get your repository injected conveniently • Handle your objects (almost) like you had no framework 4

Slide 5

Slide 5 text

Persistence in FLOW3 with Doctrine 2 Differences to plain Doctrine 2 in modeling • Identifier properties are added transparently • FLOW3 does autodetection for • repository class names, column types, referenced column names • target entity types, cascade attributes • All Doctrine annotations work as usual • Whatever you specify wins over automation • Allows for full flexibility 5

Slide 6

Slide 6 text

Purely Doctrine 2 use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity(repositoryClass="BugRepository") */ class Bug { /** * @var integer * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue */ protected $id; /** * @var string * @ORM\Column(type="string") */ protected $description; /** * @var \DateTime * @ORM\Column(type="datetime") */ 6

Slide 7

Slide 7 text

use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity(repositoryClass="BugRepository") */ class Bug { /** * @var integer * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue */ protected $id; /** * @var string * @ORM\Column(type="string") */ protected $description; /** * @var \DateTime * @ORM\Column(type="datetime") */ Doctrine 2 in FLOW3 7

Slide 8

Slide 8 text

Purely Doctrine 2 /** * @var \DateTime * @ORM\Column(type="datetime") */ protected $created; /** * @var Example\User * @ORM\ManyToOne(targetEntity="Example\User", inversedBy="assignedBugs") */ protected $engineer; /** * @var \Doctrine\Common\Collections\Collection * @ORM\ManyToMany(targetEntity="Example\Product") */ protected $products; } 8

Slide 9

Slide 9 text

/** * @var \DateTime * @ORM\Column(type="datetime") */ protected $created; /** * @var Example\User * @ORM\ManyToOne(targetEntity="Example\User", inversedBy="assignedBugs") */ protected $engineer; /** * @var \Doctrine\Common\Collections\Collection * @ORM\ManyToMany(targetEntity="Example\Product") */ protected $products; } Doctrine 2 in FLOW3 9

Slide 10

Slide 10 text

Using Repositories Choose between the generic base repository to support any backend or the Doctrine base repository to access advanced Doctrine functionality. Extending the base repositories of FLOW3 • Provides basic methods like: findAll(), countAll(), remove(), removeAll() • Provides automatic finder methods to retrieve by property: findByPropertyName($value), findOneByPropertyName($value) Add specialized finder methods to your own repository. 10

Slide 11

Slide 11 text

Advanced Queries using the QOM class PostRepository extends \FLOW3\Persistence\Repository { /** * Finds most recent posts excluding the given post * * @param \TYPO3\Blog\Domain\Model\Post $post Post to exclude from result * @param integer $limit The number of posts to return at max * @return array All posts of the $post's blog except for $post */ public function findRecentExceptThis(\TYPO3\Blog\Domain\Model\Post $post, $limit = 20) { $query = $this->createQuery(); $posts = $query->matching($query->equals('blog', $post->getBlog())) ->setOrderings(array( 'date' => \TYPO3\FLOW3\Persistence\QueryInterface::ORDER_DESCENDING )) ->setLimit($limit) ->execute() ->toArray(); unset($posts[array_search($post, $posts)]); return $posts; } } PostRepository.php 11

Slide 12

Slide 12 text

Advanced Queries using DQL class PostRepository extends \FLOW3\Persistence\Doctrine\Repository { /** * Finds most recent posts excluding the given post * * @param \TYPO3\Blog\Domain\Model\Post $post Post to exclude from result * @param integer $limit The number of posts to return at max * @return array All posts of the $post's blog except for $post */ public function findRecentExceptThis(\TYPO3\Blog\Domain\Model\Post $post, $limit = 20) { // this is an alternative way of doing this when extending the Doctrine 2 // specific repository and using DQL. $query = $this->entityManager->createQuery( 'SELECT p FROM \TYPO3\Blog\Domain\Model\Post p WHERE p.blog = :blog AND NOT p = :excludedPost ORDER BY p.date DESC' ); return $query ->setMaxResults($limit) ->execute(array('blog' => $post->getBlog(), 'excludedPost' => $post)); } } PostRepository.php 12

Slide 13

Slide 13 text

Modeling Associations • Modeling associations is hard for many people • Start with the model, not the data • Read the Doctrine documentation on associations • Put a printed list of possible association on your wall • Always remember: The owning side of a relationship determines the updates to the relationship in the database 13

Slide 14

Slide 14 text

Modeling Associations How FLOW3 helps you with associations • Cascade attributes are managed by FLOW3 • based on aggregate boundaries • Target entity can be left out • Join columns and tables have automagic defaults • No, not only if your identifier column is named id • Check your mapping with flow3 doctrine:validate All magic can be overridden by using annotations! 14

Slide 15

Slide 15 text

Schema Management Doctrine 2 Migrations • Migrations allow schema versioning and change deployment • Migrations are the recommended way for schema updates • Can also be used to deploy predefined and update existing data • Tools to create and deploy migrations are integrated with FLOW3 15

Slide 16

Slide 16 text

Schema Management Migrations Workflow • Develop until your model is ready for a first “freeze” • Create a migration and move / check / customize it • Migrate to create the tables $ ./flow3 doctrine:migrationgenerate Generated new migration class! Next Steps: - Move /…/DoctrineMigrations/Version20120328152041.php to YourPackage/Migrations/Mysql/ - Review and adjust the generated migration. - (optional) execute the migration using ./flow3 doctrine:migrate $ ./flow3 doctrine:migrate 16

Slide 17

Slide 17 text

Schema Management /** * Rename FLOW3 tables to follow FQCN */ class Version20110824124835 extends AbstractMigration { /** * @param Schema $schema * @return void */ public function up(Schema $schema) { $this->abortIf($this->connection->getDatabasePlatform()->getName() != "mysql"); $this->addSql("RENAME TABLE flow3_policy_role TO typo3_flow3_security_policy_role"); $this->addSql("RENAME TABLE flow3_resource_resource TO typo3_flow3_resource_resource"); $this->addSql("RENAME TABLE flow3_resource_resourcepointer TO typo3_flow3_resource_resourcepointer"); $this->addSql("RENAME TABLE flow3_resource_securitypublishingconfiguration TO typo3_flow3_security_authorization_resource_securitypublis_6180a"); $this->addSql("RENAME TABLE flow3_security_account TO typo3_flow3_security_account"); } Migration files are usually very simple code 17

Slide 18

Slide 18 text

Schema Management $ ./flow3 doctrine:migrationstatus == Configuration >> Name: Doctrine Database Migrations >> Database Driver: pdo_mysql >> Database Name: blog >> Configuration Source: manually configured >> Version Table Name: flow3_doctrine_migrationstatus >> Migrations Namespace: TYPO3\FLOW3\Persistence\Doctrine\Migrations >> Migrations Directory: /…/Configuration/Doctrine/Migrations >> Current Version: 2011-06-08 07:43:24 (20110608074324) >> Latest Version: 2011-06-08 07:43:24 (20110608074324) >> Executed Migrations: 1 >> Available Migrations: 1 >> New Migrations: 0 == Migration Versions >> 2011-06-08 07:43:24 (20110608074324) migrated Checking the migration status on the console 18

Slide 19

Slide 19 text

Schema Management Migrations Workflow • Rinse and repeat: from now on create a new migration whenever you changed your model classes • Generated migrations most probably need to be adjusted: • Renaming a model means renaming a table, not dropping and creating • Data migration might need to be added • Sometimes the generated changes are useless Good migrations make your user’s day 19

Slide 20

Slide 20 text

Schema Management Manual database updates • For simple situations this can be good enough: • Useful when • You need to use an existing database dump • No migrations exist for your database of choice (send patches!) • Using SQLite (due to limited schema change functionality) $ ./flow3 doctrine:create $ ./flow3 doctrine:update 20

Slide 21

Slide 21 text

Integrating existing database tables Use existing data from TYPO3 or other applications Two principal approaches • Accessing raw data in a specialized repository • Use your own database connection and SQL • Does not use the default persistence layer • Creating a clean model mapped to the existing structure • FLOW3 will use the same database as the existing application • Uses the default persistence layer 21

Slide 22

Slide 22 text

Mapping fe_users to a model /** * @FLOW3\Entity * @ORM\Table(name=”fe_users”) */ class FrontendUser { /** * @var integer * @ORM\Id * @ORM\Column(name="uid") * @ORM\GeneratedValue */ protected $identifier; /** * @var string */ protected $username; 22

Slide 23

Slide 23 text

Mapping fe_users to a model /** * @var string */ protected $username; /** * @var string * @ORM\Column(name="first_name") */ protected $firstName; /** * @var \Doctrine\Common\Collections\Collection * @ORM\ManyToMany(mappedBy="users") * @ORM\JoinTable(name="user_groups_mm", …) */ protected $groups; 23

Slide 24

Slide 24 text

Integrating existing database tables Pitfalls • Migrations will try to drop existing tables and columns! • Data type mismatches break FK constraints • integer vs. unsigned integer • Real data can be bad data • No FK constraints on legacy data • Missing entries break associations • Watch out for specifics like deleted and hidden flags 24

Slide 25

Slide 25 text

Persistence in FLOW3 with Doctrine 2 Questions? 25

Slide 26

Slide 26 text

Thank You! • These slides can be found at: http://speakerdeck.com/u/kdambekalns | http://slideshare.net/kfish • Give me feedback: [email protected] | [email protected] • Download FLOW3: http://flow3.typo3.org • Follow me on twitter: @kdambekalns • Support me using 26