[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