Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 517 lines (408 sloc) 16.624 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
e35592c @mariano Doc updates
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.
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity …
authored
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 …
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 …
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 …
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 …
authored
318 methods the session adapter will expect it to have.
319
e35592c @mariano Doc updates
authored
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.
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity …
authored
329
330 Once the model is created, create its database table using the doctrine
331 console. Finally, edit your `app/config/bootstrap/session.php` file and use the
332 following to configure the session:
dcc154b @mariano Documentation updates
authored
333
334 ```php
335 Session::config(array(
336 'default' => array(
337 'adapter' => 'li3_doctrine2\extensions\adapter\session\Entity',
338 'model' => 'app\models\Session'
339 )
340 ));
341 ```
342
343 If you wish to override session INI settings, use the `ini` setting. For
344 example, if you wish your session data to be valid across all subdomains,
345 replace the session definition with the following:
346
347 ```php
348 $host = $_SERVER['HTTP_HOST'];
349 if (strpos($host, '.') !== false) {
350 $host = preg_replace('/^.*?([^\.]+\.[^\.]+)$/', '\\1', $host);
351 }
352 Session::config(array(
353 'default' => array(
354 'adapter' => 'li3_doctrine2\extensions\adapter\session\Entity',
355 'model' => 'app\models\Session',
356 'ini' => array(
357 'cookie_domain' => '.' . $host
358 )
359 )
360 ));
361 ```
362
363 ## Authentication ##
364
365 Even when you could easily build your own authentication library, using
366 [lithium's implementation] [lithium-authentication] is highly recommended. If
367 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 …
authored
368 authentication, since it allows it to interact with Doctrine models. The model
369 you wish to use should extend from `BaseEntity`.
370
e35592c @mariano Doc updates
authored
371 > If you still want to use the Form adapter but do not wish to extend
372 > `BaseEntity`, then your model should implement the
373 > `li3_doctrine2\models\IUser` interface. You should also note that if you do
374 > not pass the `entityManager` setting to the auth configuration, then your
375 > model should implement a static method named `getEntityManager()` that
376 > should return Doctrine's entity manager for the model. This method is not
377 > part of the interface signature because it is optional, and is only used if
378 > you don't set the `entityManager` session configuration setting.
dcc154b @mariano Documentation updates
authored
379
380 Once you have your model, you need to configure `Auth`. Edit your
381 `app/config/bootstrap/session.php` and add the following to the end:
382
383 ```php
384 use lithium\security\Auth;
385
386 Auth::config(array(
387 'default' => array(
388 'adapter' => 'li3_doctrine2\extensions\adapter\security\auth\Form',
389 'model' => 'app\models\User',
390 'fields' => array('email', 'password')
391 )
392 ));
393 ```
394
395 Once this is done, you can use `Auth` as usual.
396
3a96489 @mariano Adding how to log queries to li3_perf in the documentation
authored
397 # Integrating libraries #
1bfb208 @mariano README updates
authored
398
399 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 …
authored
400 how to integrate them with li3\_doctrine2, and how to let li3\_doctrine2 work
401 with other lithium libraries that might be of use for your application.
1bfb208 @mariano README updates
authored
402
403 ## DoctrineExtensions ##
b7bce75 @mariano Adding README
authored
404
405 If there is one tool I would recommend you checkout for your Doctrine models,
e811e60 @mariano Fixing link reference in README
authored
406 that would be [DoctrineExtensions] [DoctrineExtensions]. It provides with a set
b7bce75 @mariano Adding README
authored
407 of behavioral extensions to the Doctrine core that will simplify your
408 development.
409
410 To use DoctrineExtensions, you should first add it as GIT submodule. To do so,
411 switch to the core directory holding your lithium application, and do:
412
413 ```bash
a6ef188 @mariano Recommending installation on main library folder
authored
414 $ git submodule add https://github.com/l3pp4rd/DoctrineExtensions.git libraries/_source/DoctrineExtensions
b7bce75 @mariano Adding README
authored
415 ```
416
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity …
authored
417 You would then use your connection configuration (in
418 `app/config/connections.php`) to configure Doctrine with your desired behaviors.
419 For example, if you wish to use Timestampable and Sluggable, you would first add
420 the library in `app/config/libraries.php`:
b7bce75 @mariano Adding README
authored
421
422 ```php
423 Libraries::add('Gedmo', array(
a6ef188 @mariano Recommending installation on main library folder
authored
424 'path' => LITHIUM_LIBRARY_PATH . '/_source/DoctrineExtensions/lib/Gedmo'
b7bce75 @mariano Adding README
authored
425 ));
426 ```
427
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity …
authored
428 And then you would filter the `createEntityManager()` method in the `Doctrine`
a165763 @mariano Making the creation of entity manager filterable
authored
429 datasource to add the behaviors. Edit your `app/config/connections.php` file
430 and add the following right below the connection definition:
b7bce75 @mariano Adding README
authored
431
432 ```php
a165763 @mariano Making the creation of entity manager filterable
authored
433 Connections::get('default')->applyFilter('createEntityManager',
434 function($self, $params, $chain) {
435 $params['eventManager']->addEventSubscriber(
436 new \Gedmo\Timestampable\TimestampableListener()
b7bce75 @mariano Adding README
authored
437 );
a165763 @mariano Making the creation of entity manager filterable
authored
438 $params['eventManager']->addEventSubscriber(
439 new \Gedmo\Sluggable\SluggableListener()
440 );
441 return $chain->next($self, $params, $chain);
b7bce75 @mariano Adding README
authored
442 }
a165763 @mariano Making the creation of entity manager filterable
authored
443 );
b7bce75 @mariano Adding README
authored
444 ```
445
3a96489 @mariano Adding how to log queries to li3_perf in the documentation
authored
446 ## Li3Perf ##
447
448 [li3_perf] [li3_perf] is a handy utility that you should use (only when the
449 development environment is activated, though) to keep track of bottlenecks,
450 and potential performance problems.
451
c7d9147 @mariano Fix docs
authored
452 One of the features it offers is the ability to show all the database queries
453 that were executed as part of a request. In order to use that functionality
454 with li3\_doctrine2, a little work has to be done. Fortunately, it's quite
455 easy.
3a96489 @mariano Adding how to log queries to li3_perf in the documentation
authored
456
457 Create a file named `Li3PerfSQLLogger.php` and place it in your
458 `app/libraries/_source` folder with the following contents:
459
460 ```php
461 <?php
462 namespace app\libraries\_source;
463
464 use Doctrine\DBAL\Logging\SQLLogger;
465 use li3_perf\extensions\util\Data;
466
c7d9147 @mariano Fix docs
authored
467 class Li3PerfSQLLogger implements SQLLogger {
c9bbec8 @mariano Calculating ellapsed time for query on example logger
authored
468 protected $query;
469 protected $start;
470
3a96489 @mariano Adding how to log queries to li3_perf in the documentation
authored
471 public function startQuery($sql, array $params = null, array $types = null) {
c9bbec8 @mariano Calculating ellapsed time for query on example logger
authored
472 $this->start = microtime(true);
473 $this->query = compact('sql', 'params', 'types');
3a96489 @mariano Adding how to log queries to li3_perf in the documentation
authored
474 }
475
476 public function stopQuery() {
c9bbec8 @mariano Calculating ellapsed time for query on example logger
authored
477 $ellapsed = (microtime(true) - $this->start) * 1000;
478 Data::append('queries', array(array_merge(
479 array('explain' => array('millis' => $ellapsed)),
480 $this->query
481 )));
3a96489 @mariano Adding how to log queries to li3_perf in the documentation
authored
482 }
483 }
484
485 ?>
486 ```
487
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity …
authored
488 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
489 datasource. Edit your `app/config/connections.php` file and add the following
490 right below the connection definition:
491
492 ```php
493 Connections::get('default')->applyFilter('createEntityManager',
494 function($self, $params, $chain) {
495 if (\lithium\core\Libraries::get('li3_perf')) {
496 $params['configuration']->setSQLLogger(
d8eaf7b @mariano Using interfaces to give an alternative to extending from BaseEntity …
authored
497 new \app\libraries\_source\Li3PerfSQLLogger()
498 );
3a96489 @mariano Adding how to log queries to li3_perf in the documentation
authored
499 }
500 return $chain->next($self, $params, $chain);
501 }
502 );
503 ```
504
505 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 …
authored
506 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
507
b7bce75 @mariano Adding README
authored
508 [lithium]: http://lithify.me
509 [doctrine2]: http://www.doctrine-project.org
510 [license]: http://www.opensource.org/licenses/bsd-license.php
511 [DoctrineExtensions]: https://github.com/l3pp4rd/DoctrineExtensions
b9bef23 @mariano README updates
authored
512 [doctrine-mapping-guide]: http://www.doctrine-project.org/docs/orm/2.1/en/reference/basic-mapping.html
889e90b @mariano Documentation updates
authored
513 [doctrine-querying-guide]: http://www.doctrine-project.org/docs/orm/2.1/en/reference/working-with-objects.html#querying
514 [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
515 [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
516 [li3_perf]: https://github.com/tmaiaroto/li3_perf
Something went wrong with that request. Please try again.