Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 518 lines (408 sloc) 16.663 kb
1bfb208 @mariano README updates
authored
1 li3\_doctrine2 offers integration between [the most RAD PHP framework] [lithium]
b7bce75 @mariano Adding README
authored
2 and possibly the best PHP 5.3 ORM out there: [Doctrine2] [doctrine2]
3
4 # License #
5
1bfb208 @mariano README updates
authored
6 li3\_doctrine2 is released under the [BSD License] [license].
b7bce75 @mariano Adding README
authored
7
8 # Installation #
9
1bfb208 @mariano README updates
authored
10 It is recommended that you install li3\_doctrine2 as a GIT submodule, in order
b7bce75 @mariano Adding README
authored
11 to keep up with the latest upgrades. To do so, switch to the core directory
12 holding your lithium application, and do:
13
14 ```bash
a6ef188 @mariano Recommending installation on main library folder
authored
15 $ git submodule add https://github.com/mariano/li3_doctrine2.git libraries/li3_doctrine2
1f9e666 @mariano Adding recursive call to git submodule in docs
authored
16 $ cd libraries/li3_doctrine2
17 $ git submodule update --init --recursive
b7bce75 @mariano Adding README
authored
18 ```
19
1bfb208 @mariano README updates
authored
20 # Usage #
21
b9bef23 @mariano README updates
authored
22 ## Adding the li3\_doctrine2 library ##
1bfb208 @mariano README updates
authored
23
a6ef188 @mariano Recommending installation on main library folder
authored
24 Once you have downloaded li3\_doctrine2 and placed it in your main `libraries`
25 folder, or your `app/libraries` folder, you need to enable it by placing the
26 following at the end of your `app/config/bootstrap/libraries.php` file:
1bfb208 @mariano README updates
authored
27
28 ```php
29 Libraries::add('li3_doctrine2');
30 ```
31
32 ## Defining a connection ##
33
b9bef23 @mariano README updates
authored
34 Setting up a connection with li3\_doctrine2 is easy. All you need to do is
35 add the following to your `app/config/bootstrap/connections.php` file (make
36 sure to edit the settings to match your host, without altering the `type`
37 setting):
38
39 ```php
40 Connections::add('default', array(
41 'type' => 'Doctrine',
42 'driver' => 'pdo_mysql',
43 'host' => 'localhost',
44 'user' => 'root',
45 'password' => 'password',
707fc8e @mariano Documentation updates
authored
46 'dbname' => 'my_db'
b9bef23 @mariano README updates
authored
47 ));
48 ```
1bfb208 @mariano README updates
authored
49
707fc8e @mariano Documentation updates
authored
50 ## Working with models ##
51
52 ### Creating models ###
1bfb208 @mariano README updates
authored
53
54 When looking to create your doctrine models, you have two choices: you can
889e90b @mariano Documentation updates
authored
55 have them follow your custom class hierarchy (or none at all), or you could
1bfb208 @mariano README updates
authored
56 have them extend from the `BaseEntity` class provided by this library. The
b9bef23 @mariano README updates
authored
57 advantage of choosing the later is that your models will have lithium's
889e90b @mariano Documentation updates
authored
58 validation support, and can be better integrated with the custom adapters
59 provided by this library (such as for session management or for authorization.)
b9bef23 @mariano README updates
authored
60
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity and...
authored
61 If you still want validation support but do not wish to extend `BaseEntity`
62 your models should implement the `li3_doctrine2\models\IModel` interface.
63
b9bef23 @mariano README updates
authored
64 Let us create a `User` model. Following doctrine's [basic mapping guide]
72f1e8b @mariano Documentation updates
authored
65 [doctrine-mapping-guide] we'll use annotations to define the properties, and
66 we will also include lithium validation rules (that's why we are choosing to
67 extend this model from `BaseEntity`):
b9bef23 @mariano README updates
authored
68
69 ```php
72f1e8b @mariano Documentation updates
authored
70 <?php
71 namespace app\models;
72
73 use Doctrine\ORM\Mapping\Column;
74 use Doctrine\ORM\Mapping\Entity;
75 use Doctrine\ORM\Mapping\GeneratedValue;
76 use Doctrine\ORM\Mapping\Id;
77 use Doctrine\ORM\Mapping\Table;
78 use lithium\security\Password;
79
80 /**
81 * @Entity
82 * @Table(name="users")
83 */
84 class User extends \li3_doctrine2\models\BaseEntity {
85 /**
86 * @Id
87 * @GeneratedValue
88 * @Column(type="integer")
89 */
5e3b410 @mariano Documentation updates
authored
90 private $id;
72f1e8b @mariano Documentation updates
authored
91
92 /**
93 * @Column(type="string",unique=true)
94 */
5e3b410 @mariano Documentation updates
authored
95 private $email;
72f1e8b @mariano Documentation updates
authored
96
97 /**
98 * @Column(type="text")
99 */
5e3b410 @mariano Documentation updates
authored
100 private $password;
72f1e8b @mariano Documentation updates
authored
101
102 /**
103 * @Column(type="string")
104 */
5e3b410 @mariano Documentation updates
authored
105 private $name;
72f1e8b @mariano Documentation updates
authored
106
107 /**
108 * Validation rules
109 */
f6dc2ee @mariano Only mapped fields need to be protected
authored
110 protected $validates = array(
72f1e8b @mariano Documentation updates
authored
111 'email' => array(
112 'required' => array('notEmpty', 'message' => 'Email is required'),
113 'valid' => array('email', 'message' => 'You must specify a valid email address', 'skipEmpty' => true)
114 ),
115 'password' => array('notEmpty', 'message' => 'Password must not be blank'),
116 'name' => array('notEmpty', 'message' => 'Please provide your full name')
117
118 );
119
120 public function getId() {
121 return $this->id;
122 }
123
124 public function getEmail() {
125 return $this->email;
126 }
127
128 public function setEmail($email) {
129 $this->email = $email;
130 }
131
96bca82 @mariano Adding note to README
authored
132 public function getPassword() {
133 return $this->password;
134 }
135
72f1e8b @mariano Documentation updates
authored
136 public function setPassword($password) {
137 $this->password = !empty($password) ? Password::hash($password) : null;
138 }
139
140 public function getName() {
141 return $this->name;
142 }
143
144 public function setName($name) {
145 $this->name = $name;
146 }
147 }
148 ?>
b9bef23 @mariano README updates
authored
149 ```
1bfb208 @mariano README updates
authored
150
96bca82 @mariano Adding note to README
authored
151 You should note that if you make your model properties private, each property
2bc7b6e @mariano README improvement
authored
152 **must have** a getter and a setter method, otherwise validation and other
96bca82 @mariano Adding note to README
authored
153 features provided by `BaseEntity` won't work.
154
889e90b @mariano Documentation updates
authored
155 ### Using the Doctrine shell to generate the schema ###
707fc8e @mariano Documentation updates
authored
156
623f2ae @mariano Documentation updates
authored
157 Once you have your model(s) created, you can use doctrine's shell to generate
158 the schema. li3\_doctrine2 offers a wrapper for doctrine's shell that
889e90b @mariano Documentation updates
authored
159 reutilizes lithium's connection details. To run the access the core directory
160 of your application and do:
623f2ae @mariano Documentation updates
authored
161
162 ```bash
a6ef188 @mariano Recommending installation on main library folder
authored
163 $ libraries/li3_doctrine2/bin/doctrine
623f2ae @mariano Documentation updates
authored
164 ```
165
166 That will give you all the available commands. For example, to get the SQL
167 you should run to create the schema for your models, do:
168
169 ```bash
a6ef188 @mariano Recommending installation on main library folder
authored
170 $ libraries/li3_doctrine2/bin/doctrine orm:schema-tool:create --dump-sql
623f2ae @mariano Documentation updates
authored
171 ```
172
173 which will give an output similar to the following:
174
175 ```sql
176 CREATE TABLE users (
177 id INT AUTO_INCREMENT NOT NULL,
178 email VARCHAR(255) NOT NULL,
179 password LONGTEXT NOT NULL,
180 name VARCHAR(255) NOT NULL,
181 UNIQUE INDEX UNIQ_1483A5E9E7927C74 (email),
182 PRIMARY KEY(id)
183 ) ENGINE = InnoDB
184 ```
707fc8e @mariano Documentation updates
authored
185
889e90b @mariano Documentation updates
authored
186 ### Getting the entity manager ###
187
188 Doctrine's `EntityManager` is the way we have to interact with the underlying
189 database, which means we'll always need to obtain it. You can do so by
190 running the following code (change `default` to the name of your connection
191 as defined in `app/config/connections.php`):
192
193 ```php
194 $em = \lithium\data\Connections::get('default')->getEntityManager();
195 ```
196
197 If your models extend from `BaseEntity`, then all of them have a static method
198 named `getEntityManager()` (which uses a static property inherited from
199 `BaseEntity` named `$connectionName` to figure out what connection to use):
200
201 ```php
202 $em = User::getEntityManager();
203 ```
204
c23d0e8 @mariano Adding BaseEntity::getRepository() method
authored
205 `BaseEntity` also offers a `getRepository()` method which will return the
206 repository for the model (see the section *Fetching record* below.)
207
889e90b @mariano Documentation updates
authored
208 ### Fetching records ###
209
210 Once you have the entity manager, you can fetch a user with ID 1 (notice how
211 we use the fully qualified class name for the model) using the entity
212 manager:
213
214 ```php
215 $user = $em->find('app\models\User', 1);
216 ```
217
218 or using model repositories:
219
220 ```php
221 $user = $em->getRepository('app\models\User')->findOneById(1);
222 ```
223
c23d0e8 @mariano Adding BaseEntity::getRepository() method
authored
224 If your model extends from `BaseEntity`, then the above could be retwritten
225 as:
226
227 ```php
228 $user = User::getRepository()->findOneById(1);
229 ```
230
889e90b @mariano Documentation updates
authored
231 If you want to find out more about querying models with Doctrine, go through
232 its [Querying guide] [doctrine-querying-guide].
233
234 ### Creating/Updating/Deleting records ###
235
236 Records are persisted (or removed) though the entity manager, as shown in
237 Doctrine's [Persisting guide] [doctrine-persisting-guide].
238
239 One thing to note is that if your models extend from `BaseEntity`, you have
240 validation rules defined for them, and the data you provide does not validate,
241 persisting it will throw a `ValidateException` (the following example uses
242 the `User` model we defined earlier):
243
244 ```php
245 $user = new User();
246 $user->setName('John Doe');
247 $user->setEmail('bademail@');
248
249 try {
250 $em->persist($user);
251 $em->flush();
252 } catch(\li3_doctrine2\models\ValidateException $e) {
253 echo $e->getMessage();
254 }
255 ```
256
257 You should also note that `BaseEntity` provides a method named `set()` which
258 comes very handy if the user data is to be populated from a form submission.
259 If so, the above code could be rewritten as:
260
261 ```php
262 $user = new User();
263 $user->set($this->request->data);
264
265 try {
266 $em->persist($user);
267 $em->flush();
268 } catch(\li3_doctrine2\models\ValidateException $e) {
269 echo $e->getMessage();
270 }
271 ```
272
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity and...
authored
273 In this last example, if lithium's Form helper is bound to the record instance,
889e90b @mariano Documentation updates
authored
274 it will properly show validation errors. The following view code uses the
275 `$user` variable from the example above to bind the form to its validation
276 errors:
277
278 ```php
279 <?php echo $this->form->create(isset($user) ? $user : null); ?>
280 <?php echo $this->form->field('email'); ?>
281 <?php echo $this->form->field('password', array('type' => 'password')); ?>
282 <?php echo $this->form->field('name'); ?>
283 <?php echo $this->form->submit('Signup'); ?>
284 <?php echo $this->form->end(); ?>
285 ```
286
dcc154b @mariano Documentation updates
authored
287 # Extensions #
288
289 li3\_doctrine2 also offers a set of extensions to integrate different parts
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity and...
authored
290 of your lithium application with your doctrine models.
dcc154b @mariano Documentation updates
authored
291
292 ## Session ##
293
294 Some installations require session data to be stored on a centralized location.
295 While there are powerful, storage-centric solutions for session storage, using
296 the database is still a popular choice.
297
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity and...
authored
298 If you wish to store your session data on the database using Doctrine models,
dcc154b @mariano Documentation updates
authored
299 then you will need to use li3\_doctrine2's session adapter. You start by
300 creating the model that the library will use to represent a session record.
301 For example, create a file named `Session.php` and place it in your
302 `app/models` folder with the following contents:
303
304 ```php
305 <?php
306 namespace app\models;
307
308 /**
309 * @Entity
310 * @Table(name="sessions")
311 */
312 class Session extends \li3_doctrine2\models\BaseSession {
313 }
314 ?>
315 ```
316
317 We are extending from `BaseSession` since it provides us with the needed
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity and...
authored
318 methods the session adapter will expect it to have.
319
320 If you still want to use the session adapter with your own Doctrine models,
321 but do not wish to extend `BaseSession`, then your session model should
322 implement the `li3_doctrine2\models\ISession` interface. You should also
323 note that if you do not pass the `entityManager` setting to the session
324 configuration, then your session model should implement a static method
325 named `getEntityManager()` that should return Doctrine's entity manager
326 for the session model. This method is not part of the interface signature
327 because it is optional, and is only used if you don't set the
328 `entityManager` session configuration setting.
dcc154b @mariano Documentation updates
authored
329
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity and...
authored
330
331 Once the model is created, create its database table using the doctrine
332 console. Finally, edit your `app/config/bootstrap/session.php` file and use the
333 following to configure the session:
dcc154b @mariano Documentation updates
authored
334
335 ```php
336 Session::config(array(
337 'default' => array(
338 'adapter' => 'li3_doctrine2\extensions\adapter\session\Entity',
339 'model' => 'app\models\Session'
340 )
341 ));
342 ```
343
344 If you wish to override session INI settings, use the `ini` setting. For
345 example, if you wish your session data to be valid across all subdomains,
346 replace the session definition with the following:
347
348 ```php
349 $host = $_SERVER['HTTP_HOST'];
350 if (strpos($host, '.') !== false) {
351 $host = preg_replace('/^.*?([^\.]+\.[^\.]+)$/', '\\1', $host);
352 }
353 Session::config(array(
354 'default' => array(
355 'adapter' => 'li3_doctrine2\extensions\adapter\session\Entity',
356 'model' => 'app\models\Session',
357 'ini' => array(
358 'cookie_domain' => '.' . $host
359 )
360 )
361 ));
362 ```
363
364 ## Authentication ##
365
366 Even when you could easily build your own authentication library, using
367 [lithium's implementation] [lithium-authentication] is highly recommended. If
368 you wish to go this route, you'll need li3\_doctrine's Form adapter for
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity and...
authored
369 authentication, since it allows it to interact with Doctrine models. The model
370 you wish to use should extend from `BaseEntity`.
371
372 If you still want to use the Form adapter but do not wish to extend
373 `BaseEntity`, then your model should implement the
374 `li3_doctrine2\models\IUser` interface. You should also note that if you do
375 not pass the `entityManager` setting to the auth configuration, then your
376 model should implement a static method named `getEntityManager()` that
377 should return Doctrine's entity manager for the model. This method is not
378 part of the interface signature because it is optional, and is only used if
379 you don't set the `entityManager` session configuration setting.
dcc154b @mariano Documentation updates
authored
380
381 Once you have your model, you need to configure `Auth`. Edit your
382 `app/config/bootstrap/session.php` and add the following to the end:
383
384 ```php
385 use lithium\security\Auth;
386
387 Auth::config(array(
388 'default' => array(
389 'adapter' => 'li3_doctrine2\extensions\adapter\security\auth\Form',
390 'model' => 'app\models\User',
391 'fields' => array('email', 'password')
392 )
393 ));
394 ```
395
396 Once this is done, you can use `Auth` as usual.
397
3a96489 @mariano Adding how to log queries to li3_perf in the documentation
authored
398 # Integrating libraries #
1bfb208 @mariano README updates
authored
399
400 In this section I'll cover some of the doctrine extension libraries out there,
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity and...
authored
401 how to integrate them with li3\_doctrine2, and how to let li3\_doctrine2 work
402 with other lithium libraries that might be of use for your application.
1bfb208 @mariano README updates
authored
403
404 ## DoctrineExtensions ##
b7bce75 @mariano Adding README
authored
405
406 If there is one tool I would recommend you checkout for your Doctrine models,
e811e60 @mariano Fixing link reference in README
authored
407 that would be [DoctrineExtensions] [DoctrineExtensions]. It provides with a set
b7bce75 @mariano Adding README
authored
408 of behavioral extensions to the Doctrine core that will simplify your
409 development.
410
411 To use DoctrineExtensions, you should first add it as GIT submodule. To do so,
412 switch to the core directory holding your lithium application, and do:
413
414 ```bash
a6ef188 @mariano Recommending installation on main library folder
authored
415 $ git submodule add https://github.com/l3pp4rd/DoctrineExtensions.git libraries/_source/DoctrineExtensions
b7bce75 @mariano Adding README
authored
416 ```
417
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity and...
authored
418 You would then use your connection configuration (in
419 `app/config/connections.php`) to configure Doctrine with your desired behaviors.
420 For example, if you wish to use Timestampable and Sluggable, you would first add
421 the library in `app/config/libraries.php`:
b7bce75 @mariano Adding README
authored
422
423 ```php
424 Libraries::add('Gedmo', array(
a6ef188 @mariano Recommending installation on main library folder
authored
425 'path' => LITHIUM_LIBRARY_PATH . '/_source/DoctrineExtensions/lib/Gedmo'
b7bce75 @mariano Adding README
authored
426 ));
427 ```
428
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity and...
authored
429 And then you would filter the `createEntityManager()` method in the `Doctrine`
a165763 @mariano Making the creation of entity manager filterable
authored
430 datasource to add the behaviors. Edit your `app/config/connections.php` file
431 and add the following right below the connection definition:
b7bce75 @mariano Adding README
authored
432
433 ```php
a165763 @mariano Making the creation of entity manager filterable
authored
434 Connections::get('default')->applyFilter('createEntityManager',
435 function($self, $params, $chain) {
436 $params['eventManager']->addEventSubscriber(
437 new \Gedmo\Timestampable\TimestampableListener()
b7bce75 @mariano Adding README
authored
438 );
a165763 @mariano Making the creation of entity manager filterable
authored
439 $params['eventManager']->addEventSubscriber(
440 new \Gedmo\Sluggable\SluggableListener()
441 );
442 return $chain->next($self, $params, $chain);
b7bce75 @mariano Adding README
authored
443 }
a165763 @mariano Making the creation of entity manager filterable
authored
444 );
b7bce75 @mariano Adding README
authored
445 ```
446
3a96489 @mariano Adding how to log queries to li3_perf in the documentation
authored
447 ## Li3Perf ##
448
449 [li3_perf] [li3_perf] is a handy utility that you should use (only when the
450 development environment is activated, though) to keep track of bottlenecks,
451 and potential performance problems.
452
c7d9147 @mariano Fix docs
authored
453 One of the features it offers is the ability to show all the database queries
454 that were executed as part of a request. In order to use that functionality
455 with li3\_doctrine2, a little work has to be done. Fortunately, it's quite
456 easy.
3a96489 @mariano Adding how to log queries to li3_perf in the documentation
authored
457
458 Create a file named `Li3PerfSQLLogger.php` and place it in your
459 `app/libraries/_source` folder with the following contents:
460
461 ```php
462 <?php
463 namespace app\libraries\_source;
464
465 use Doctrine\DBAL\Logging\SQLLogger;
466 use li3_perf\extensions\util\Data;
467
c7d9147 @mariano Fix docs
authored
468 class Li3PerfSQLLogger implements SQLLogger {
c9bbec8 @mariano Calculating ellapsed time for query on example logger
authored
469 protected $query;
470 protected $start;
471
3a96489 @mariano Adding how to log queries to li3_perf in the documentation
authored
472 public function startQuery($sql, array $params = null, array $types = null) {
c9bbec8 @mariano Calculating ellapsed time for query on example logger
authored
473 $this->start = microtime(true);
474 $this->query = compact('sql', 'params', 'types');
3a96489 @mariano Adding how to log queries to li3_perf in the documentation
authored
475 }
476
477 public function stopQuery() {
c9bbec8 @mariano Calculating ellapsed time for query on example logger
authored
478 $ellapsed = (microtime(true) - $this->start) * 1000;
479 Data::append('queries', array(array_merge(
480 array('explain' => array('millis' => $ellapsed)),
481 $this->query
482 )));
3a96489 @mariano Adding how to log queries to li3_perf in the documentation
authored
483 }
484 }
485
486 ?>
487 ```
488
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity and...
authored
489 Now, we need to filter the `createEntityManager()` method of the `Doctrine`
3a96489 @mariano Adding how to log queries to li3_perf in the documentation
authored
490 datasource. Edit your `app/config/connections.php` file and add the following
491 right below the connection definition:
492
493 ```php
494 Connections::get('default')->applyFilter('createEntityManager',
495 function($self, $params, $chain) {
496 if (\lithium\core\Libraries::get('li3_perf')) {
497 $params['configuration']->setSQLLogger(
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity and...
authored
498 new \app\libraries\_source\Li3PerfSQLLogger()
499 );
3a96489 @mariano Adding how to log queries to li3_perf in the documentation
authored
500 }
501 return $chain->next($self, $params, $chain);
502 }
503 );
504 ```
505
506 Notice how we are only using the logger we created if the li3\_perf library
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity and...
authored
507 is activated. You should now see your queries on the performance toolbar.
3a96489 @mariano Adding how to log queries to li3_perf in the documentation
authored
508
b7bce75 @mariano Adding README
authored
509 [lithium]: http://lithify.me
510 [doctrine2]: http://www.doctrine-project.org
511 [license]: http://www.opensource.org/licenses/bsd-license.php
512 [DoctrineExtensions]: https://github.com/l3pp4rd/DoctrineExtensions
b9bef23 @mariano README updates
authored
513 [doctrine-mapping-guide]: http://www.doctrine-project.org/docs/orm/2.1/en/reference/basic-mapping.html
889e90b @mariano Documentation updates
authored
514 [doctrine-querying-guide]: http://www.doctrine-project.org/docs/orm/2.1/en/reference/working-with-objects.html#querying
515 [doctrine-persisting-guide]: http://www.doctrine-project.org/docs/orm/2.1/en/reference/working-with-objects.html#persisting-entities
0db1df1 @mariano Documentation updates
authored
516 [lithium-authentication]: http://lithify.me/docs/manual/auth/simple-authentication.wiki
3a96489 @mariano Adding how to log queries to li3_perf in the documentation
authored
517 [li3_perf]: https://github.com/tmaiaroto/li3_perf
Something went wrong with that request. Please try again.