doctrine in flow3

Post on 01-Nov-2014

5.741 Views

Category:

Technology

2 Downloads

Preview:

Click to see full reader

DESCRIPTION

Presentation given at F3X 2012 on the integration of Doctrine 2 into the PHP framework FLOW3.

TRANSCRIPT

Karsten Dambekalns

Persistence in FLOW3

with Doctrine 2

FLOW3 Experience 2012

1

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

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

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

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

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") */ protected $created;

/** * @var User * @ORM\ManyToOne(targetEntity="Example\User", inversedBy="assignedBugs") */ protected $engineer;

/** * @var \Doctrine\Common\Collections\Collection<Product> * @ORM\ManyToMany(targetEntity="Example\Product") */ protected $products;}

6

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") */ protected $created;

/** * @var User * @ORM\ManyToOne(targetEntity="Example\User", inversedBy="assignedBugs") */ protected $engineer;

/** * @var \Doctrine\Common\Collections\Collection<Product> * @ORM\ManyToMany(targetEntity="Example\Product") */ protected $products;}

Doctrine 2 in FLOW3

7

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<Example\Product> * @ORM\ManyToMany(targetEntity="Example\Product") */ protected $products;}

8

/** * @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<Example\Product> * @ORM\ManyToMany(targetEntity="Example\Product") */ protected $products;}

Doctrine 2 in FLOW3

9

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

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

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

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

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

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

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:migrationgenerateGenerated 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

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

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

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

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

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

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

Mapping fe_users to a model

/** * @var string */ protected $username;

/** * @var string * @ORM\Column(name="first_name") */ protected $firstName;

/** * @var \Doctrine\Common\Collections\Collection<My \Example\FrontendUserGroup> * @ORM\ManyToMany(mappedBy="users") * @ORM\JoinTable(name="user_groups_mm", …) */ protected $groups;

23

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

Persistence in FLOW3 with Doctrine 2

Questions?

25

Thank You!

• These slides can be found at:http://speakerdeck.com/u/kdambekalns | http://slideshare.net/kfish

• Give me feedback:karsten@typo3.org | karsten@dambekalns.de

• Download FLOW3: http://flow3.typo3.org

• Follow me on twitter: @kdambekalns

• Support me using

26

top related