Symfony 1.3 + Doctrine 1.2

103
Symfony 1.3 + Doctrine 1.2 Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 1 Symfony 1.3 + Doctrine 1.2

description

Presentation on Symfony 1.3 and Doctrine 1.2 for Symfony Day Cologne 2009.

Transcript of Symfony 1.3 + Doctrine 1.2

Page 1: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 1

Symfony 1.3+

Doctrine 1.2

Page 2: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 2

Continued Evolutionof Symfony 1.x

Page 3: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 3

Lots of new features, enhancements and bug fixes.

Page 4: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 4

Out with the old and in with the new!

Page 5: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 5

Doctrine is the default ORM

Page 6: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 6

Propel is deprecated

Page 7: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 7

Propel is deprecated in 1.3 but not removed. It will be dropped

completely in symfony 2.0

Page 8: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 8

Still want to use Propel? :)Generate your project with the new --orm option

$ php /path/to/symfony generate:project foo --orm=Propel

Page 9: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 9

New Symfony Features

Page 10: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 10

Use the --installer option to execute Symfony installer

scripts

Page 11: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 11

$ php /path/to/symfony generate:project --installer=my_installer.php

Page 12: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 12

Now in your my_installer.php you can do things like...if (!$this->askConfirmation('Are you sure you want to run this installer?')){ return;} $this->installDir(dirname(__FILE__).'/skeleton');

$this->runTask('plugin:publish-assets');

$validator = new sfValidatorEmail(array(), array('invalid' => 'hmmm, it does not look like an email!'));$email = $this->askAndValidate('Please, give me your email:', $validator);

$this->runTask('configure:author', sprintf("'%s'", $email));

$secret = $this->ask('Give a unique string for the CSRF secret:');

$this->runTask('generate:app', 'frontend --escaping-strategy=true --csrf-secret='.$secret);

$this->runTask('plugin:install', 'sfDoctrineGuardPlugin');$this->reloadTasks();

$this->runTask('guard:create-user', 'jwage changeme');$this->runTask('cache:clear');

Page 13: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 13

It replaces the need to maintain your own skeleton symfony project. Just write your own custom installer to

generate a new project the way you want it.

Page 14: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 14

Form Framework

Page 15: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 15

Recognize this?

class LoginForm extends BaseUserForm{ public function configure() { unset( $this['first_name'], $this['last_name'], $this['email_address'] ); }}

Page 16: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 16

Using unset() to remove fields

Page 17: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 17

Instead, lets say what form fields we WANT to use instead

of the ones we don’t want

Page 18: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 18

Now it is possible with the useFields() method.

class LoginForm extends BaseUserForm{ public function configure() { $this->useFields(array( 'username', 'password' )); }}

Page 19: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 19

Easier to maintain

Page 20: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 20

Does not require keeping unset() list updated when we

add new fields to model

Page 21: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 21

Forms + Events

Page 22: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 22

We now have a sfFormSymfony in core and BaseForm class in

each project.

Page 23: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 23

Sits on top of the agnostic sfForm and couples symfony

dispatcher with the form framework.

Page 24: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 24

Allows us to dispatch symfony events from within the form

framework. YES!

Page 25: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 25

What events can I use?

Page 26: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 26

• form.post_configure - Notified after every form is configured

• form.filter_values - Filters tainted parameters and files array just prior to binding

• form.validation_error - Notified whenever form validation fails.

• form.method_not_found - Notified whenever an unknown method is called

Page 27: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 27

Example Form Event

Add easy ReCaptcha functionality to all forms

Page 28: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 28

Define a regular form that extends sfFormSymfony

class MyForm extends BaseForm{ public function configure() { $this->widgetSchema['title'] = new sfWidgetFormInputText(); $this->validatorSchema['title'] = new sfValidatorString();

$this->widgetSchema->setNameFormat('my_form[%s]'); }}

Page 29: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 29

Connect events form.post_configure and form.filter_values

class ProjectConfiguration extends sfProjectConfiguration{ public function setup() { // ...

$this->dispatcher->connect('form.post_configure', array( $this, 'formPostConfigure' ));

$this->dispatcher->connect('form.filter_values', array( $this, 'formFilterValues' )); }}

Page 30: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 30

Define formPostConfigure() method.

public function formPostConfigure(sfEvent $event) { $form = $event->getSubject();

if ($form->reCaptcha) { $widgetSchema = $form->getWidgetSchema(); $validatorSchema = $form->getValidatorSchema(); $widgetSchema['captcha'] = new sfWidgetFormReCaptcha(array( 'public_key' => '6Ld2DgQAAAAAAApXLteupHPcbSxbSHkhNTuYLChX' ));

$validatorSchema['captcha'] = new sfValidatorReCaptcha(array( 'private_key' => '6Ld2DgQAAAAAANIbaXJsFEBOyg56CL_ljy3APlPb' )); } }

Page 31: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 31

Define formFilterValues() method.

public function formFilterValues(sfEvent $event, $values) { $form = $event->getSubject();

if ($form->reCaptcha) { $request = sfContext::getInstance()->getRequest();

$captcha = array( 'captcha' => array( 'recaptcha_challenge_field' => $request->getParameter('recaptcha_challenge_field'), 'recaptcha_response_field' => $request->getParameter('recaptcha_response_field') ) ); $values = array_merge($values, $captcha); } return $values; }

Page 32: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 32

Now to enable recaptcha for any of your forms just add a public property to your forms

with a value of true

Page 33: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 33

Enable recaptcha on MyForm.

class MyForm extends BaseForm{ public $reCaptcha = true;

public function configure() { $this->widgetSchema['title'] = new sfWidgetFormInputText(); $this->validatorSchema['title'] = new sfValidatorString();

$this->widgetSchema->setNameFormat('my_form[%s]'); }}

Page 34: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 34

Now MyForm has recaptcha enabled. Enable it on any of

your other forms.

Page 35: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 35

The previous example requires that sfFormExtraPlugin be installed in your project.

Page 36: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 36

Of course we could have implemented this right in

BaseForm without events. This would make more sense if implemented via a plugin.

Page 37: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 37

Testing Improvements

Page 38: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 38

sfTesterResponse adds matches() method

Page 39: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 39

Runs a regex on the entire response content.

Page 40: Symfony 1.3 + Doctrine 1.2

$browser->with('response')->begin()-> matches('/I have \d+ apples/')-> // it takes a regex as an argument matches('!/I have \d+ apples/')-> // a ! at the beginning means that the regex must not match matches('!/I have \d+ apples/i')-> // you can also add regex modifiersend();

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 40

$browser->with('response')->begin()-> matches('/I have \d+ apples/')-> matches('!/I have \d+ apples/')-> matches('!/I have \d+ apples/i')->end();

Page 41: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 41

More powerful than contains() and is useful for non-XML type

responses where checkElement() cannot be used

Page 42: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2

$ php symfony test:all --xml=log.xml

42

Tests now have JUnit Compatible XML Output

Page 43: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 43

$ php symfony test:all -t

Output exception traces when running all tests

Useful for debugging failing tests

Page 44: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 44

New checkForm() method on sfTesterResponse

Tests that all form fields are rendered properly in the HTML response.

Page 45: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 45

$browser->with('response')->begin()-> checkForm('ArticleForm')->end();

Page 46: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 46

Use CSS selectors when dealing with multiple forms

$browser->with('response')->begin()-> checkForm('ArticleForm', '#articleForm')->end();

Page 47: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 47

Improve Developer Testing Efficiency

Page 48: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 48

When you have a large test suite it is time consuming to run all tests every time you

make a change.

Page 49: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 49

Use the --only-failed option

Page 50: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 50

After you run the test suite once, re-run with the

--only-failed option to execute only the failed tests from the

previous run.

Page 51: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 51

Now you can re-run this over and over until you have zero

failures again.

Page 52: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 52

Interactive CLI Tasks

Ask the user for some input and use it in your task

Page 53: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 53

In your task class you can use some code like below.

$anwser = $this->askAndValidate( 'What is you email?', new sfValidatorEmail());

Page 54: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 54

Improved Doctrine Integration

Page 55: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 55

Disable Form Generation for Certain Models

Page 56: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 56

For models like many to many join tables we don’t need form classes so we can disable the generation of them.

UserGroup: options: symfony: form: false filter: false columns: user_id: type: integer primary: true group_id: type: integer primary: true

Page 57: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 57

Generated forms now follow model inheritance hierarchy

Page 58: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 58

Now your form classes will generate the same inheritance structure that your models define.

class Moderator extends User{ // ...}

class ModeratorForm extends UserForm{ // ...}

Page 59: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 59

New Doctrine Tasks

Page 60: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 60

$ php symfony doctrine:create-model-tables Model1 Model2 Model3

Create Tables for Set of ModelsTask will drop and recreate specified tables.

Useful in development mode when you want to rebuild a set of models in the database over and over while you’re working on it. Instead of having to rebuild the entire database just to test.

Page 61: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 61

Delete Model FilesDelete associated generated files for a model like forms and filter classes.

$ php symfony doctrine:delete-model-files ModelName

This is useful if you remove a model or rename a model, before you would have to manually clean up these classes and it was a pain. This task automates it for you.

Page 62: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 62

Clean Model FilesThis task is combined with the previous task to clean up orphaned model files.

$ php symfony doctrine:clean-model-files

It detects what model all model files exist on disk but cannot be found in your YAML schema files. It will find these files and report them to you asking you to confirm whether or not they should be deleted.

Page 63: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 63

Reload Data$ php symfony doctrine:reload-data

The above is the same as doing the following

$ php symfony doctrine:drop-db $ php symfony doctrine:build-db $ php symfony doctrine:insert-sql $ php symfony doctrine:data-load

This is commonly used to prepare a test database before each test is run.

Page 64: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 64

The New Build TaskThe new doctrine:build task allows you to specify what exactly you would like symfony and Doctrine to build. This task replicates the functionality in many of the existing combination-tasks, which have all been deprecated in favor of this more flexible solution.

Page 65: Symfony 1.3 + Doctrine 1.2

This will drop and create the database, create the tables configured in your YAML schema and load the fixture data.

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 65

$ php symfony doctrine:build --db --and-load

Build Examples

Page 66: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 66

This will build the model, forms, form filters and run any pending migrations.

$ php symfony doctrine:build --all-classes --and-migrate

Build Examples

Page 67: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 67

This will build the model, migrate the database and append category fixtures data.

$ php symfony doctrine:build --model --and-migrate --and-append=data/fixtures/categories.yml

Build Examples

Page 68: Symfony 1.3 + Doctrine 1.2

We've added two new methods for retrieving Doctrine date/time values as PHP DateTime object instances.

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 68

Dealing with Doctrine Dates

Page 69: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 69

If you prefer to work with DateTime objects then you can use these methods to get and set your Doctrine date values.

echo $article->getDateTimeObject('created_at')->format('m/d/Y');

$article->setDateTimeObject('created_at', new DateTime('09/01/1985'));

www.php.net/DateTime

Page 70: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 70

Alternative doctrine:dql output

$ ./symfony doctrine:dql "FROM Article a" --table>> doctrine executing dql queryDQL: FROM Article a+----+-----------+----------------+---------------------+---------------------+| id | author_id | is_on_homepage | created_at | updated_at |+----+-----------+----------------+---------------------+---------------------+| 1 | 1 | | 2009-07-07 18:02:24 | 2009-07-07 18:02:24 || 2 | 2 | | 2009-07-07 18:02:24 | 2009-07-07 18:02:24 |+----+-----------+----------------+---------------------+---------------------+(2 results)

You probably recognize this as it is how the mysql command line outputs SQL queries.

Page 71: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 71

Doctrine 1.2

Page 72: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 72

Easy to upgrade from 1.1

Page 73: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 73

Some deprecated functions, methods, etc. removed but the core of things have remained mostly the same. Only new

features and flexibilities have been added.

Page 74: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 74

Custom Query Class

Page 75: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 75

Specify the query class to use in your project somewhere in your configuration.

$manager->setAttribute( Doctrine::ATTR_QUERY_CLASS, 'MyQuery');

Page 76: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 76

Define the MyQuery class which extends Doctrine_Query

class MyQuery extends Doctrine_Query{ }

Page 77: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 77

Now you can add new methods and override functionality in

the Doctrine_Query class for all your query objects in your

application

Page 78: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 78

Custom Collection Class

Page 79: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 79

Specify the collection class to use in your project somewhere in your configuration.

$manager->setAttribute( Doctrine::ATTR_COLLECTION_CLASS, 'MyCollection');

Page 80: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 80

Define the MyCollection class which extends Doctrine_Collection

class MyCollection extends Doctrine_Collection{ }

Page 81: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 81

Now you can add new methods and override functionality in

your collections.

Page 82: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 82

Add new method to get the sum of a field in a collection.

class MyCollection extends Doctrine_Collection{ public function getSum($field) { $sum = 0; foreach ($this as $record) { $sum = $sum + $record->get($field); } return $sum; }}

Page 83: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 83

Now it can be used like the following

$transactions = Doctrine::getTable('Transaction') ->retrieveAllUserTransactions('jwage');

echo get_class($transactions); // MyCollection

// Output the total amount billed for all transactions for jwageecho $transactions->getSum('total_billed');

Page 84: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 84

Custom Data Hydrators

Page 85: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 85

It is now possible to write and register your own custom data hydrators which can be used from your query objects.

$manager->registerHydrator('MyHydrator', ‘MyHydratorDriver’);

Page 86: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 86

Define the MyHydrator class

class MyHydratorDriver extends Doctrine_Hydrator_Abstract{ public function hydrateResultSet($stmt) { return $stmt->fetchAll(PDO::FETCH_ASSOC); }}

Page 87: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 87

Custom hydrators must extend Doctrine_Hydrator_Abstract

and must implement thehydrateResultSet() method.

Page 88: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 88

The core hydrators execute the PDO statement and build a

special structure to be returned. You can execute the

statement and do whatever you want with the data before

returning it.

Page 89: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 89

Using Custom Hydrator

$q->execute(array(), 'MyHydrator');

Page 90: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 90

Custom Connection Classes

Page 91: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 91

Sometimes you may have some proprietary database type that Doctrine

does not support. The custom connections allow you to implement your own database drivers to use.

Page 92: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 92

Register Connection Driver

$manager->registerConnectionDriver( 'test', 'Doctrine_Connection_Test');

Page 93: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 93

Define Connection Classes class Doctrine_Connection_Test extends Doctrine_Connection_Common{}

class Doctrine_Adapter_Test implements Doctrine_Adapter_Interface{ // ... all the methods defined in the interface // ... mimics the PDO interface which connects // ... to your database}

Page 94: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 94

Open a New Connection $conn = $manager->openConnection('test://username:password@localhost/dbname');

echo get_class($conn); // Doctrine_Connection_Testecho get_class($conn->getDbh()); // Doctrine_Adapter_Test

Page 95: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 95

Improve Save Performance

$manager->setAttribute(Doctrine::ATTR_CASCADE_SAVES, false);

Disabling cascading saves to improve performance when saving lots of objects. The negative affect of this change is that you can’t save dirty objects that are more than one level deep in the object hierarchy.

Page 96: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 96

New Convenience Magic Finders

Page 97: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 97

$user = $table->findOneByUsernameAndPassword('jwage', md5('changeme'));$users = $table->findByIsAdminOrIsModerator(true, true);

Please be aware these finders are very limited and are only meant for quickly prototyping code. It is almost always recommended to write your own DQL queries which select and join the exact resultset you need.

Method name is converted in to a DQL query. Combine AND/OR

conditions.

Page 98: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 98

Doctrine Extensions Repository

With the start of Doctrine 1.2 development we opened a special repository for users to contribute extensions to Doctrine to share with the community.

Page 99: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 99

Similar to Symfony Plugins

Page 100: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 100

Outside of Symfony Doctrine has a mechanism for autoloading and registering extensions. This is not necessary in Symfony because the native autoloading will load the code for the extension. We just need to download the code and put it somewhere in Symfony.

Using the Extensions

Page 101: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 101

$ cd /path/to/symfony/project$ mkdir lib/doctrine_extensions$ svn co http://svn.doctrine-project.org/extensions/Taggable/branches/1.2-1.0/ Taggable$ ./symfony cc

Using the Extensions

Page 102: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 102

Using the Extensions

---Article: actAs: [Taggable] columns: title: string(255) body: clob

Page 103: Symfony 1.3 + Doctrine 1.2

Symfony 1.3 + Doctrine 1.2

Jonathan H. Wage: Symfony 1.3 + Doctrine 1.2 103

You can contact Jonathan about Doctrine and Open-Source or for training, consulting, application development, or business

related questions at [email protected]

Jonathan H. [email protected]+1 415 992 5468

sensiolabs.com | doctrine-project.org | sympalphp.org | jwage.com

Questions?