Documentation


SEARCH

TABLE OF CONTENT

    1. Getting started 2. Basic concepts 3. Request dispatching 4. Context container 5. Dual MVC 6. Component model: 7. Security 8. Configuration 9. Session handling 10. I18n 11. Cache 12. Logging 13. Error handling 14. Advanced Topics 15. API reference

      Tutorials: Frequently Asqued Questions

      See also:


      Integrating an ORM

      Isolating the data access logic

      Table of Contents

      Introduction

      Nowadays, ORM is one of the best paradigms to manage the persistence vs. maintaining SQL and handling persistence via code directly.

      ORMs make transparent the database as well as how instances are loaded and stored on it.
      In this section, we're going to show some practices to integrate an ORM within the model layer.

      Doctrine

      Doctrine is a PHP ORM (object relational mapper) for PHP 5.2.3+ that sits on top of a powerful PHP DBAL (database abstraction layer). One of its key features is the ability to optionally write database queries in an OO (object oriented) SQL-dialect called DQL inspired by Hibernates HQL. This provides developers with a powerful alternative to SQL that maintains a maximum of flexibility without requiring needless code duplication.

      Including Doctrine

      Doctrine has the following directory structure out of the box:

      The only file that we're going to include is the Doctrine.php, placed within the doctrine lib directory. The Doctrine.php contains the main class Doctrine, which has an autoload method in charge of include the rest of files as soon as they are needed.

      First of all, we need to place Doctrine inside our project. A good practice is to use a directory to place all the third-party libraries. We usually place our libraries within a directory thrdparty under the libs (/libs/thrdparty/) just to keep a placing convention, but it could be placed wherever

      Second, we need to include the Doctrine.php file, so we can do that easily by adding an entry within the app/config/includepath.xml file:

      1. <?xml version "1.0" standalone="yes"?>
      2. <classes>
      3.  
      4.   ...
      5.  
      6.   <cluster name="Doctrine classes" path="/libs/thrdparty/doctrine/lib">
      7.     <class name="Doctrine" file="Doctrine.php"/>
      8.   </cluster>
      9.  
      10.   ...
      11.   
      12. </classes>
      So, the Doctrine.php file will be included just when needed, more like on demand, it is, just when the Doctrine class is used.

      Now let's register the Doctrine autoload function that we have told about within our configuration file as following:

      1. <?xml version "1.0" standalone="yes"?>
      2. <configuration>
      3.  
      4.   <configuration-directives>
      5.     <autoload class="Doctrine" method="autoload"/>
      6.   </configuration-directives>
      7.  
      8. </configuration>
      This will register the Doctrine autoload callback instead of ussing the spl_autoload_register function directly within our code.

      Now we are able to consume doctrine API directly from our model.


      Configuring Doctrine

      We recommends to use Lion's dependency injection capabilities to configure Doctrine. It eases and centralizes the doctrine configuration. Let's see it with some examples:

      The following code has been extracted from doctrine documentation, explaining how to use PDO to create a connection:
      1. <?php
      2.  
      3. $dsn 'mysql:dbname=testdb;host=127.0.0.1';
      4. $user 'dbuser';
      5. $password 'dbpass';
      6.  
      7. try {
      8.     $dbh new PDO($dsn$user$password);
      9.     $conn Doctrine_Manager::connection($dbh);
      10. catch (PDOException $e{
      11.     echo 'Connection failed: ' $e->getMessage();
      12. }
      13.  
      14. ?>
      Well, we can use dependency injection to setup a connection, let's see it in terms of context instance definitions:
      1. <?xml version "1.0" standalone="yes"?>
      2. <configuration>
      3.  
      4.   <context-instances>
      5.  
      6.     <!-- Setup the dbh instance: -->
      7.     <context-instance id = "dbh" class = "PDO">
      8.         <constructor-arg>
      9.            <value>mysql:dbname=testdb;host=127.0.0.1</value>
      10.         </constructor-arg>
      11.         <constructor-arg><value>dbuser</value></constructor-arg>
      12.         <constructor-arg><value>dbpass</value></constructor-arg>
      13.     </context-instance>      
      14.     
      15.     <!-- define the doctrine connection instance as the result
      16.          of calling the connection factory method within the
      17.          Doctrine_Manager class: -->
      18.     <context-instance id = "conn"
      19.                    class = "Doctrine_Manager"
      20.           factory-method = "connection"/>
      21.         <constructor-arg><ref id="dbh"/></constructor-arg>
      22.     </context-instance>
      23.  
      24.   </context-instances>
      25.   
      26. </configuration>

      Now it's as easy as get the conn instance by asking the application context:

      1. <?php
      2.  
      3.     //gets the conn instance from the application context:
      4.     $conn __ApplicationContext::getInstance()->
      5.                                   getContextInstance('conn');
      Take the followin benefits of configuring Doctrine with Lion's dependency injection:
      • Maintain a configuration file declarativelly (vs. maintain a PHP source code programatically)
      • Let Lion to discover and resolve dependency between instances (vs. keep a concrete order to create instances programatically)
      • The connection instance is managed by the application context: it can be retrieved from whatever part within our code (vs. implementing singleton mechanism to make available our connection instance)

      Let's try now with a more complex example:

      1. <?php
      2.  
      3. $conn $manager->openConnection(
      4. 'mysql://username:password@localhost/dbname''connection 1');
      5.  
      6. $conn2 $manager->openConnection(
      7. 'mysql://username2:password2@localhost/dbname2''connection 2');
      8.  
      9. $manager->bindComponent('User''connection 1');
      10.  
      11. $manager->bindComponent('Group''connection 2');
      12.  
      13. $q Doctrine_Query::create();
      14.  
      15. // Doctrine uses 'connection 1' for fetching here
      16. $users $q->from('User u')->where('u.id IN (1,2,3)')->execute();
      17.  
      18. // Doctrine uses 'connection 2' for fetching here
      19. $groups $q->from('Group g')->where('g.id IN (1,2,3)')->execute();
      20.  
      21. ?>
      In this example, we are going to setup 2 connections, each one associated to a model class: One to handle User's persistence and the other one to handle Group's persistence. Each one connects to a different data source.

      First thing to do will be to setup connection by ussing dependency injection, as shown in the following code:

      1. <?xml version "1.0" standalone="yes"?>
      2. <configuration>
      3.  
      4.   <context-instances>
      5.  
      6.     <context-instance id = "doctrine_manager" class="Doctrine_Manager"/>
      7.  
      8.     <context-instance id = "conn"
      9.         factory-instance = "doctrine_manager"
      10.           factory-method = "openConnection">
      11.         <constructor-arg>
      12.             <value>mysql://username:password@localhost/dbname</value>
      13.         </constructor-arg>
      14.         <constructor-arg>
      15.             <value>connection 1</value>
      16.         </constructor-arg>
      17.     </context-instance>
      18.  
      19.     <context-instance id = "conn2"
      20.         factory-instance = "doctrine_manager"
      21.           factory-method = "openConnection">
      22.         <constructor-arg>
      23.             <value>mysql://username2:password2@localhost/dbname2</value>
      24.         </constructor-arg>
      25.         <constructor-arg>
      26.             <value>connection 2</value>
      27.         </constructor-arg>
      28.     </context-instance>
      29.  
      30.  
      31.  
      32.   </context-instances>
      33.   
      34. </configuration>

      Next, we'll associate each connection to each model class.
      The way to do that could be by ussing the same context.xml file, the only thing to take into account is the fact that we should need a manager::bindComponent method to get an array of pairs class, connection ids. Unfortunatelly we don't have this method within the original Doctrine API, so we could solve it by subclassing the Doctrine_Manager in order to add this method. i.e.:

      1. <?php
      2.  
      3. class LionDoctrine_Manager extends Doctrine_Manager {
      4.  
      5.     public function bindComponents(array $components{
      6.         foreach($components as $component_class => $component_connection_id{
      7.             $this->bindComponent($component_class$component_connection_id);
      8.         }
      9.     }
      10.  
      11. }
      Now that we have the bindComponents method allowing to set an array of pairs, we are able to associate connections declarativelly within our context.xml. i.e.:
      1. <?xml version "1.0" standalone="yes"?>
      2. <configuration>
      3.  
      4.   <context-instances>
      5.  
      6.     <!-- We have changed the doctrine_manager class to point to our 
      7.          new LionDoctrine_Manager class -->
      8.     <context-instance id = "doctrine_manager" class="LionDoctrine_Manager">
      9.       <property name="bindComponents">
      10.         <list>
      11.           <entry key="User"><value>connection 1</value></entry>
      12.           <entry key="Group"><value>connection 2</value></entry>
      13.         </list>
      14.       </property>
      15.     </context-instance>
      16.     
      17.     <context-instance id = "conn"
      18.         factory-instance = "doctrine_manager"
      19.           factory-method = "openConnection">
      20.         <constructor-arg>
      21.             <value>mysql://username:password@localhost/dbname</value>
      22.         </constructor-arg>
      23.         <constructor-arg>
      24.             <value>connection 1</value>
      25.         </constructor-arg>
      26.     </context-instance>
      27.  
      28.     <context-instance id = "conn2"
      29.         factory-instance = "doctrine_manager"
      30.           factory-method = "openConnection">
      31.         <constructor-arg>
      32.             <value>mysql://username2:password2@localhost/dbname2</value>
      33.         </constructor-arg>
      34.         <constructor-arg>
      35.             <value>connection 2</value>
      36.         </constructor-arg>
      37.     </context-instance>
      38.  
      39.  
      40.  
      41.   </context-instances>
      42.   
      43. </configuration>

      Now we have everything configure in order to use the Doctrine API:

      1. <?php
      2.  
      3.     $q Doctrine_Query::create();
      4.     
      5.     // Doctrine uses 'connection 1' for fetching here
      6.     $users $q->from('User u')->where('u.id IN (1,2,3)')->execute();
      7.     
      8.     // Doctrine uses 'connection 2' for fetching here
      9.     $groups $q->from('Group g')->where('g.id IN (1,2,3)')->execute();

      Again, the summary is to let the framework handle the configuration to just focus on code.


      Propel

      Propel is an Object-Relational Mapping (ORM) framework for PHP5. It allows you to access your database using a set of objects, providing a simple API for storing and retrieving data.

      Including Propel

      Propel has the following directory structure out of the box:

      The only file that we're going to include is the Propel.php, placed within the propel runtime/classes/propel directory. The Propel.php contains the main class Propel, which has an autoload method in charge of include the rest of files as soon as they are needed.

      First of all, we need to place Propel inside our project. A good practice is to use a directory to place all the third-party libraries. We usually place our libraries within a directory thrdparty under the libs (/libs/thrdparty/) just to keep a placing convention, but it could be placed wherever

      Second, we need to include the Propel.php file, so we can do that easily by adding an entry within the app/config/includepath.xml file:

      1. <?xml version "1.0" standalone="yes"?>
      2. <classes>
      3.  
      4.   ...
      5.  
      6.   <cluster name = "Propel classes" 
      7.            path="/libs/thrdparty/Propel/runtime/classes/propel">
      8.     <class name="Propel" file="Propel.php"/>
      9.   </cluster>
      10.  
      11.   ...
      12.   
      13. </classes>
      So, the Propel.php file will be included just when needed, more like on demand, it is, just when the Propel class is used.

      Now let's register the Propel autoload function that we have told about within our configuration file as following:

      1. <?xml version "1.0" standalone="yes"?>
      2. <configuration>
      3.  
      4.   <configuration-directives>
      5.     <autoload class="Propel" method="autoload"/>
      6.   </configuration-directives>
      7.  
      8. </configuration>
      This will register the Propel autoload callback instead of ussing the spl_autoload_register function directly within our code.

      Now we are able to consume propel API directly from our model.