···988988 }
989989990990 /**
991991+ * Add a model to the given named ($name) relationship.
992992+ *
993993+ * @internal This should <strong>only</strong> be used by eager load
994994+ * @param Model $model
995995+ * @param $name of relationship for this table
996996+ * @return void
997997+ */
998998+ public function set_relationship_from_eager_load(Model $model=null, $name)
999999+ {
10001000+ $table = static::table();
10011001+10021002+ if (($rel = $table->get_relationship($name)))
10031003+ {
10041004+ if ($rel->is_poly())
10051005+ {
10061006+ // if the related model is null and it is a poly then we should have an empty array
10071007+ if (is_null($model))
10081008+ return $this->__relationships[$name] = array();
10091009+ else
10101010+ return $this->__relationships[$name][] = $model;
10111011+ }
10121012+ else
10131013+ return $this->__relationships[$name] = $model;
10141014+ }
10151015+10161016+ throw new RelationshipException("Relationship named $name has not been declared for class: {$table->class->getName()}");
10171017+ }
10181018+10191019+ /**
9911020 * Reloads the attributes and relationships of this object from the database.
9921021 *
9931022 * @return Model
···10001029 $this->set_attributes($this->find($pk)->attributes);
10011030 $this->reset_dirty();
1002103110321032+ return $this;
10331033+ }
10341034+10351035+ public function __clone()
10361036+ {
10371037+ $this->__relationships = array();
10381038+ $this->reset_dirty();
10031039 return $this;
10041040 }
10051041
+83
lib/php-activerecord/lib/Relationship.php
···9797 }
98989999 /**
100100+ * What is this relationship's cardinality?
101101+ *
102102+ * @return bool
103103+ */
104104+ public function is_poly()
105105+ {
106106+ return $this->poly_relationship;
107107+ }
108108+109109+ /**
110110+ * Eagerly loads relationships for $models.
111111+ *
112112+ * This method takes an array of models, collects PK or FK (whichever is needed for relationship), then queries
113113+ * the related table by PK/FK and attaches the array of returned relationships to the appropriately named relationship on
114114+ * $models.
115115+ *
116116+ * @param Table $table
117117+ * @param $models array of model objects
118118+ * @param $attributes array of attributes from $models
119119+ * @param $includes array of eager load directives
120120+ * @param $query_keys -> key(s) to be queried for on included/related table
121121+ * @param $model_values_keys -> key(s)/value(s) to be used in query from model which is including
122122+ * @return void
123123+ */
124124+ protected function query_and_attach_related_models_eagerly(Table $table, $models, $attributes, $includes=array(), $query_keys=array(), $model_values_keys=array())
125125+ {
126126+ $values = array();
127127+ $options = array();
128128+ $query_key = $query_keys[0];
129129+ $model_values_key = $model_values_keys[0];
130130+131131+ foreach ($attributes as $column => $value)
132132+ $values[] = $value[$model_values_key];
133133+134134+ $values = array($values);
135135+ $options['conditions'] = SQLBuilder::create_conditions_from_underscored_string($table->conn,$query_key,$values);
136136+137137+ if (!empty($includes))
138138+ $options['include'] = $includes;
139139+140140+ $class = $this->class_name;
141141+142142+ $related_models = $class::find('all', $options);
143143+ $used_models = array();
144144+145145+ foreach ($models as $model)
146146+ {
147147+ $matches = 0;
148148+ $key_to_match = $model->$model_values_key;
149149+150150+ foreach ($related_models as $related)
151151+ {
152152+ if ($related->$query_key == $key_to_match)
153153+ {
154154+ $hash = spl_object_hash($related);
155155+156156+ if (in_array($hash, $used_models))
157157+ $model->set_relationship_from_eager_load(clone($related), $this->attribute_name);
158158+ else
159159+ $model->set_relationship_from_eager_load($related, $this->attribute_name);
160160+161161+ $used_models[] = $hash;
162162+ $matches++;
163163+ }
164164+ }
165165+166166+ if (0 === $matches)
167167+ $model->set_relationship_from_eager_load(null, $this->attribute_name);
168168+ }
169169+ }
170170+171171+ /**
100172 * Creates a new instance of specified {@link Model} with the attributes pre-loaded.
101173 *
102174 * @param Model $model The model which holds this association
···420492 $attributes = $this->inject_foreign_key_for_new_association($model, $attributes);
421493 return parent::create_association($model, $attributes);
422494 }
495495+496496+ public function load_eagerly($models=array(), $attributes=array(), $includes, Table $table)
497497+ {
498498+ $this->set_keys($table->class->name);
499499+ $this->query_and_attach_related_models_eagerly($table,$models,$attributes,$includes,$this->foreign_key, $table->pk);
500500+ }
423501};
424502425503/**
···539617 $options['conditions'] = $conditions;
540618 $class = $this->class_name;
541619 return $class::first($options);
620620+ }
621621+622622+ public function load_eagerly($models=array(), $attributes, $includes, Table $table)
623623+ {
624624+ $this->query_and_attach_related_models_eagerly($table,$models,$attributes,$includes, $this->primary_key,$this->foreign_key);
542625 }
543626};
544627?>
+70-5
lib/php-activerecord/lib/Table.php
···178178 {
179179 $sql = $this->options_to_sql($options);
180180 $readonly = (array_key_exists('readonly',$options) && $options['readonly']) ? true : false;
181181- return $this->find_by_sql($sql->to_s(),$sql->get_where_values(), $readonly);
181181+ $eager_load = array_key_exists('include',$options) ? $options['include'] : null;
182182+183183+ return $this->find_by_sql($sql->to_s(),$sql->get_where_values(), $readonly, $eager_load);
182184 }
183185184184- public function find_by_sql($sql, $values=null, $readonly=false)
186186+ public function find_by_sql($sql, $values=null, $readonly=false, $includes=null)
185187 {
186188 $this->last_sql = $sql;
187189188188- $list = array();
190190+ $collect_attrs_for_includes = is_null($includes) ? false : true;
191191+ $list = $attrs = array();
192192+189193 $sth = $this->conn->query($sql,$values);
190194191195 while (($row = $sth->fetch()))
···195199 if ($readonly)
196200 $model->readonly();
197201202202+ if ($collect_attrs_for_includes)
203203+ $attrs[] = $model->attributes();
204204+198205 $list[] = $model;
199206 }
207207+208208+ if ($collect_attrs_for_includes)
209209+ $this->execute_eager_load($list, $attrs, $includes);
210210+200211 return $list;
201212 }
202213214214+ /**
215215+ * Executes an eager load of a given named relationship for this table.
216216+ *
217217+ * @param $models array found modesl for this table
218218+ * @param $attrs array of attrs from $models
219219+ * @param $includes array eager load directives
220220+ * @return void
221221+ */
222222+ private function execute_eager_load($models=array(), $attrs=array(), $includes=array())
223223+ {
224224+ if (!is_array($includes))
225225+ $includes = array($includes);
226226+227227+ foreach ($includes as $index => $name)
228228+ {
229229+ // nested include
230230+ if (is_array($name))
231231+ {
232232+ $nested_includes = count($name) > 1 ? $name : $name[0];
233233+ $name = $index;
234234+ }
235235+ else
236236+ $nested_includes = array();
237237+238238+ $rel = $this->get_relationship($name, true);
239239+ $rel->load_eagerly($models, $attrs, $nested_includes, $this);
240240+ }
241241+ }
242242+203243 public function get_column_by_inflected_name($inflected_name)
204244 {
205245 foreach ($this->columns as $raw_name => $column)
···220260 return $table;
221261 }
222262223223- public function get_relationship($name)
263263+ /**
264264+ * Retrieve a relationship object for this table. Strict as true will throw an error
265265+ * if the relationship name does not exist.
266266+ *
267267+ * @param $name string name of Relationship
268268+ * @param $strict bool
269269+ * @throws RelationshipException
270270+ * @return Relationship or null
271271+ */
272272+ public function get_relationship($name, $strict=false)
224273 {
225225- if (isset($this->relationships[$name]))
274274+ if ($this->has_relationship($name))
226275 return $this->relationships[$name];
276276+277277+ if ($strict)
278278+ throw new RelationshipException("Relationship named $name has not been declared for class: {$this->class->getName()}");
279279+280280+ return null;
281281+ }
282282+283283+ /**
284284+ * Does a given relationship exist?
285285+ *
286286+ * @param $name string name of Relationship
287287+ * @return bool
288288+ */
289289+ public function has_relationship($name)
290290+ {
291291+ return array_key_exists($name, $this->relationships);
227292 }
228293229294 public function insert(&$data)
···11book_id,author_id,secondary_author_id,name,special
22-1,1,2,"Ancient Art of Main Tanking",022+1,1,2,"Ancient Art of Main Tanking",0
33+2,2,2,"Another Book",0