hydrate.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. <?php namespace System\DB\Eloquent;
  2. class Hydrate {
  3. /**
  4. * Load the array of hydrated models.
  5. *
  6. * @param object $eloquent
  7. * @return array
  8. */
  9. public static function from($eloquent)
  10. {
  11. // -----------------------------------------------------
  12. // Load the base models.
  13. // -----------------------------------------------------
  14. $results = static::base(get_class($eloquent), $eloquent->query->get());
  15. // -----------------------------------------------------
  16. // Load all of the eager relationships.
  17. // -----------------------------------------------------
  18. if (count($results) > 0)
  19. {
  20. foreach ($eloquent->includes as $include)
  21. {
  22. if ( ! method_exists($eloquent, $include))
  23. {
  24. throw new \Exception("Attempting to eager load [$include], but the relationship is not defined.");
  25. }
  26. static::eagerly($eloquent, $include, $results);
  27. }
  28. }
  29. return $results;
  30. }
  31. /**
  32. * Hydrate the base models for a query.
  33. *
  34. * @param string $class
  35. * @param array $models
  36. * @return array
  37. */
  38. private static function base($class, $models)
  39. {
  40. $results = array();
  41. foreach ($models as $model)
  42. {
  43. $result = new $class;
  44. $result->attributes = (array) $model;
  45. $result->exists = true;
  46. // -----------------------------------------------------
  47. // The results are keyed by the ID on the record.
  48. // -----------------------------------------------------
  49. $results[$result->id] = $result;
  50. }
  51. return $results;
  52. }
  53. /**
  54. * Eagerly load a relationship.
  55. *
  56. * @param object $eloquent
  57. * @param string $include
  58. * @param array $results
  59. * @return void
  60. */
  61. private static function eagerly($eloquent, $include, &$results)
  62. {
  63. // -----------------------------------------------------
  64. // Get the relationship Eloquent model.
  65. //
  66. // We spoof the "belongs_to" key to allow the query
  67. // to be fetched without any problems.
  68. // -----------------------------------------------------
  69. $eloquent->attributes[$spoof = $include.'_id'] = 0;
  70. $model = $eloquent->$include();
  71. unset($eloquent->attributes[$spoof]);
  72. // -----------------------------------------------------
  73. // Reset the WHERE clause and bindings on the query.
  74. // -----------------------------------------------------
  75. $model->query->where = 'WHERE 1 = 1';
  76. $model->query->bindings = array();
  77. // -----------------------------------------------------
  78. // Initialize the relationship on the parent models.
  79. // -----------------------------------------------------
  80. foreach ($results as &$result)
  81. {
  82. $result->ignore[$include] = (strpos($eloquent->relating, 'has_many') === 0) ? array() : null;
  83. }
  84. if ($eloquent->relating == 'has_one' or $eloquent->relating == 'has_many')
  85. {
  86. static::eagerly_load_one_or_many($eloquent->relating_key, $eloquent->relating, $include, $model, $results);
  87. }
  88. elseif ($eloquent->relating == 'belongs_to')
  89. {
  90. static::eagerly_load_belonging($eloquent->relating_key, $include, $model, $results);
  91. }
  92. else
  93. {
  94. static::eagerly_load_many_to_many($eloquent->relating_key, $eloquent->relating_table, strtolower(get_class($eloquent)).'_id', $include, $model, $results);
  95. }
  96. }
  97. /**
  98. * Eagerly load a 1:1 or 1:* relationship.
  99. *
  100. * @param string $relating_key
  101. * @param string $relating
  102. * @param string $include
  103. * @param object $model
  104. * @param array $results
  105. * @return void
  106. */
  107. private static function eagerly_load_one_or_many($relating_key, $relating, $include, $model, &$results)
  108. {
  109. // -----------------------------------------------------
  110. // Get the related models.
  111. // -----------------------------------------------------
  112. $inclusions = $model->where_in($relating_key, array_keys($results))->get();
  113. // -----------------------------------------------------
  114. // Match the child models with their parent.
  115. // -----------------------------------------------------
  116. foreach ($inclusions as $key => $inclusion)
  117. {
  118. if ($relating == 'has_one')
  119. {
  120. $results[$inclusion->$relating_key]->ignore[$include] = $inclusion;
  121. }
  122. else
  123. {
  124. $results[$inclusion->$relating_key]->ignore[$include][$inclusion->id] = $inclusion;
  125. }
  126. }
  127. }
  128. /**
  129. * Eagerly load a 1:1 belonging relationship.
  130. *
  131. * @param string $relating_key
  132. * @param string $include
  133. * @param object $model
  134. * @param array $results
  135. * @return void
  136. */
  137. private static function eagerly_load_belonging($relating_key, $include, $model, &$results)
  138. {
  139. // -----------------------------------------------------
  140. // Gather the keys from the parent models.
  141. // -----------------------------------------------------
  142. $keys = array();
  143. foreach ($results as &$result)
  144. {
  145. $keys[] = $result->$relating_key;
  146. }
  147. // -----------------------------------------------------
  148. // Get the related models.
  149. // -----------------------------------------------------
  150. $inclusions = $model->where_in('id', array_unique($keys))->get();
  151. // -----------------------------------------------------
  152. // Match the child models with their parent.
  153. // -----------------------------------------------------
  154. foreach ($results as &$result)
  155. {
  156. $result->ignore[$include] = $inclusions[$result->$relating_key];
  157. }
  158. }
  159. /**
  160. * Eagerly load a many-to-many relationship.
  161. *
  162. * @param string $relating_key
  163. * @param string $relating_table
  164. * @param string $foreign_key
  165. * @param string $include
  166. * @param object $model
  167. * @param array $results
  168. * @return void
  169. */
  170. private static function eagerly_load_many_to_many($relating_key, $relating_table, $foreign_key, $include, $model, &$results)
  171. {
  172. // -----------------------------------------------------
  173. // Reset the SELECT clause.
  174. // -----------------------------------------------------
  175. $model->query->select = null;
  176. // -----------------------------------------------------
  177. // Retrieve the raw results as stdClasses.
  178. //
  179. // We also add the foreign key to the select which will allow us
  180. // to match the models back to their parents.
  181. // -----------------------------------------------------
  182. $inclusions = $model->query->where_in($relating_key, array_keys($results))->get(Meta::table(get_class($model)).'.*', $relating_table.'.'.$foreign_key);
  183. // -----------------------------------------------------
  184. // Get the class name of the related model.
  185. // -----------------------------------------------------
  186. $class = get_class($model);
  187. // -----------------------------------------------------
  188. // Create the related models.
  189. // -----------------------------------------------------
  190. foreach ($inclusions as $inclusion)
  191. {
  192. $related = new $class;
  193. $related->exists = true;
  194. $related->attributes = (array) $inclusion;
  195. // -----------------------------------------------------
  196. // Remove the foreign key from the attributes since it
  197. // was only added to the query to help us match the models.
  198. // -----------------------------------------------------
  199. unset($related->attributes[$foreign_key]);
  200. // -----------------------------------------------------
  201. // Add the related model to the parent model's array.
  202. // -----------------------------------------------------
  203. $results[$inclusion->$foreign_key]->ignore[$include][$inclusion->id] = $related;
  204. }
  205. }
  206. }