[Solar-svn] Revision 3107

pmjones at solarphp.com pmjones at solarphp.com
Sun Apr 13 08:06:54 CDT 2008


Solar_Sql_Model_Related, _Belongs_To, _Has_Many, and _Has_One
-------------------------------------------------------------

Added more "self-knowledge" to relation objects so they can take over the
heavy-lifting current handled inside the model object.

* [ADD] Method modSelectCountPages() to modify the count-pages SELECT from a
  native model.  This helps to make sure that eager-joins are honored when
  counting pages.

* [ADD] Abstract method modSelectEager() to modify the fetch*() SELECT from a
  native model.  This pulls the logic for eager-joins out of the model object
  and puts it in the relation object.

* [ADD] Support method _modSelectEager() to handle most types of eager joins,
  with (has-one/belongs-to) or without (has-many) columns.




Modified: trunk/Solar/Sql/Model/Related/BelongsTo.php
===================================================================
--- trunk/Solar/Sql/Model/Related/BelongsTo.php	2008-04-13 01:05:44 UTC (rev 3106)
+++ trunk/Solar/Sql/Model/Related/BelongsTo.php	2008-04-13 13:06:52 UTC (rev 3107)
@@ -19,6 +19,31 @@
     
     /**
      * 
+     * When the native model is doing a select and an eager-join is requested
+     * for this relation, this method modifies the select to add the eager
+     * join.
+     * 
+     * Automatically adds the foreign columns to the select.
+     * 
+     * @param Solar_Sql_Select $select The SELECT to be modified.
+     * 
+     * @return void The SELECT is modified in place.
+     * 
+     */
+    public function modSelectEager($select)
+    {
+        // build column names as "name__col" so that we can extract the
+        // the related data later.
+        $cols = array();
+        foreach ($this->cols as $col) {
+            $cols[] = "$col AS {$this->name}__$col";
+        }
+        
+        $this->_modSelectEager($select, $cols);
+    }
+    
+    /**
+     * 
      * Sets the relationship type.
      * 
      * @return void

Modified: trunk/Solar/Sql/Model/Related/HasMany.php
===================================================================
--- trunk/Solar/Sql/Model/Related/HasMany.php	2008-04-13 01:05:44 UTC (rev 3106)
+++ trunk/Solar/Sql/Model/Related/HasMany.php	2008-04-13 13:06:52 UTC (rev 3107)
@@ -20,6 +20,108 @@
     
     /**
      * 
+     * When the native model is doing a select and an eager-join is requested
+     * for this relation, this method modifies the select to add the eager
+     * join.
+     * 
+     * **Does not** add the foreign columns to the select, because that would
+     * result in really large result tables. Note that we fetch rows from the
+     * has-many relation separately, so not adding columns here is OK.
+     * 
+     * @param Solar_Sql_Select $select The SELECT to be modified.
+     * 
+     * @return void The SELECT is modified in place.
+     * 
+     */
+    public function modSelectEager($select)
+    {
+        if (empty($this->through)) {
+            // less-complex "has many" relationship.
+            $this->_modSelectEager($select);
+            
+            // make the rows distinct, so we only get one row regardless of
+            // the number of related rows (since we're not selecting cols).
+            $select->distinct(true);
+            
+            // done!
+            return;
+        }
+        
+        // more-complex "has many through" relationship.
+        // join the native table to the mapping table.
+        $join_table = "{$this->through_table} AS {$this->through_alias}";
+        $join_where = "{$this->native_alias}.{$this->native_col} = "
+                    . "{$this->through_alias}.{$this->through_native_col}";
+        
+        $select->leftJoin($join_table, $join_where);
+        
+        // join the mapping table to the foreign table.
+        $join_table = "{$this->foreign_table} AS {$this->foreign_alias}";
+        $join_where = "{$this->through_alias}.{$this->through_foreign_col} = "
+                    . "{$this->foreign_alias}.{$this->foreign_col}";
+        
+        $select->leftJoin($join_table, $join_where);
+        
+        // make the rows distinct, so we only get one row regardless of
+        // the number of related rows (since we're not selecting cols).
+        $select->distinct(true);
+        
+        // honor foreign inheritance
+        if ($this->foreign_inherit_col) {
+            $select->where(
+                "{$this->foreign_alias}.{$this->foreign_inherit_col} = ?",
+                $this->foreign_inherit_val
+            );
+        }
+    }
+    
+    /**
+     * 
+     * Modifies the SELECT from a native model countPages() call to join
+     * with the foreign model (especially on eager fetches).
+     * 
+     * While a regular "has many" relation uses the standard parent method,
+     * a "has many through" relation requires this override.
+     * 
+     * @param Solar_Sql_Select $select The SELECT from the native model
+     * countPages() method.
+     * 
+     * @return void The SELECT is modified in place.
+     * 
+     */
+    public function modSelectCountPages($select)
+    {
+        if (empty($this->through)) {
+            // less-complex "has many" relationship.
+            return parent::modSelectCountPages($select);
+        }
+        
+        // more-complex "has many through" relationship.
+        // join the native table to the mapping table.
+        $join_table = "{$this->through_table} AS {$this->through_alias}";
+        $join_where = "{$this->native_alias}.{$this->native_col} = "
+                    . "{$this->through_alias}.{$this->through_native_col}";
+        
+        $select->leftJoin($join_table, $join_where);
+        
+        // join the mapping table to the foreign table.
+        $join_table = "{$this->foreign_table} AS {$this->foreign_alias}";
+        $join_where = "{$this->through_alias}.{$this->through_foreign_col} = "
+                    . "{$this->foreign_alias}.{$this->foreign_col}";
+        
+        $select->leftJoin($join_table, $join_where);
+        
+        // honor foreign inheritance
+        if ($this->foreign_inherit_col) {
+            $select->where(
+                "{$this->foreign_alias}.{$this->foreign_inherit_col} = ?",
+                $this->foreign_inherit_val
+            );
+        }
+    }
+    
+    /**
+     * 
      * Sets the relationship type.
      * 
      * @return void

Modified: trunk/Solar/Sql/Model/Related/HasOne.php
===================================================================
--- trunk/Solar/Sql/Model/Related/HasOne.php	2008-04-13 01:05:44 UTC (rev 3106)
+++ trunk/Solar/Sql/Model/Related/HasOne.php	2008-04-13 13:06:52 UTC (rev 3107)
@@ -19,6 +19,31 @@
     
     /**
      * 
+     * When the native model is doing a select and an eager-join is requested
+     * for this relation, this method modifies the select to add the eager
+     * join.
+     * 
+     * Automatically adds the foreign columns to the select.
+     * 
+     * @param Solar_Sql_Select $select The SELECT to be modified.
+     * 
+     * @return void The SELECT is modified in place.
+     * 
+     */
+    public function modSelectEager($select)
+    {
+        // build column names as "name__col" so that we can extract the
+        // the related data later.
+        $cols = array();
+        foreach ($this->cols as $col) {
+            $cols[] = "$col AS {$this->name}__$col";
+        }
+        
+        $this->_modSelectEager($select, $cols);
+    }
+    
+    /**
+     * 
      * Sets the relationship type.
      * 
      * @return void

Modified: trunk/Solar/Sql/Model/Related.php
===================================================================
--- trunk/Solar/Sql/Model/Related.php	2008-04-13 01:05:44 UTC (rev 3106)
+++ trunk/Solar/Sql/Model/Related.php	2008-04-13 13:06:52 UTC (rev 3107)
@@ -381,7 +381,7 @@
     
     /**
      * 
-     * Creates a new selection object for records on this relationship.
+     * Creates a new selection object for fetching records from this relation.
      * 
      * @param mixed $spec If an array, treated as params for a select 
      * statement (where, group, having, etc) for finding the native-model IDs.
@@ -446,6 +446,41 @@
     
     /**
      * 
+     * Modifies the SELECT from a native model countPages() call to join
+     * with the foreign model (especially on eager fetches).
+     * 
+     * @param Solar_Sql_Select $select The SELECT from the native model
+     * countPages() method.
+     * 
+     * @return void The SELECT is modified in place.
+     * 
+     */
+    public function modSelectCountPages($select)
+    {
+        // primary-key join condition on foreign table
+        $cond = "{$this->native_alias}.{$this->native_col} = "
+              . "{$this->foreign_alias}.{$this->foreign_col}";
+        
+        // add the join, no columns.
+        $select->leftJoin(
+            "{$this->foreign_table} AS {$this->foreign_alias}",
+            $cond
+        );
+        
+        // inheritance for foreign model
+        if ($this->foreign_inherit_col) {
+            $select->where(
+                "{$this->foreign_alias}.{$this->foreign_inherit_col} = ?",
+                $this->foreign_inherit_val
+            );
+        }
+        
+        // added where conditions for the join
+        $select->multiWhere($this->where);
+    }
+    
+    /**
+     * 
      * Modifies the base select statement for the relationship type.
      * 
      * @param Solar_Sql_Select $select The selection object to modify.
@@ -507,6 +542,44 @@
     
     /**
      * 
+     * Support method for modSelectEager().  This implementation works for
+     * belongs_to and has_one (with columns) and has_many (without columns).
+     * The "has_many through" relation needs its own implementation.
+     * 
+     * @param Solar_Sql_Select $select The SELECT to be modified.
+     * 
+     * @param array $cols Any columns to add to the SELECT.
+     * 
+     * @return void The SELECT is modified in place.
+     * 
+     */
+    protected function _modSelectEager($select, $cols = null)
+    {
+        // primary-key join condition on foreign table
+        $cond = "{$this->native_alias}.{$this->native_col} = "
+              . "{$this->foreign_alias}.{$this->foreign_col}";
+        
+        // add the join
+        $select->leftJoin(
+            "{$this->foreign_table} AS {$this->foreign_alias}",
+            $cond,
+            $cols
+        );
+        
+        // inheritance for foreign model
+        if ($this->foreign_inherit_col) {
+            $select->where(
+                "{$this->foreign_alias}.{$this->foreign_inherit_col} = ?",
+                $this->foreign_inherit_val
+            );
+        }
+        
+        // added where conditions for the join
+        $select->multiWhere($this->where);
+    }
+    
+    /**
+     * 
      * Sets the foreign model instance based on user-defined relationship
      * options.
      * 
@@ -688,6 +761,19 @@
     
     /**
      * 
+     * When the native model is doing a select and an eager-join is requested
+     * for this relation, this method modifies the select to add the eager
+     * join.
+     * 
+     * @param Solar_Sql_Select $select The SELECT to be modified.
+     * 
+     * @return void The SELECT is modified in place.
+     * 
+     */
+    abstract public function modSelectEager($select);
+    
+    /**
+     * 
      * Sets the relationship type.
      * 
      * @return void




More information about the Solar-svn mailing list