PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/fuel/packages/orm/classes/hasmany.php

https://bitbucket.org/trujka/codegrounds
PHP | 271 lines | 218 code | 28 blank | 25 comment | 20 complexity | 014943b5f6dea56b00bbd2345d501073 MD5 | raw file
Possible License(s): MIT, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /**
  3. * Fuel
  4. *
  5. * Fuel is a fast, lightweight, community driven PHP5 framework.
  6. *
  7. * @package Fuel
  8. * @version 1.6
  9. * @author Fuel Development Team
  10. * @license MIT License
  11. * @copyright 2010 - 2013 Fuel Development Team
  12. * @link http://fuelphp.com
  13. */
  14. namespace Orm;
  15. class HasMany extends Relation
  16. {
  17. public function __construct($from, $name, array $config)
  18. {
  19. $this->name = $name;
  20. $this->model_from = $from;
  21. $this->model_to = array_key_exists('model_to', $config)
  22. ? $config['model_to'] : \Inflector::get_namespace($from).'Model_'.\Inflector::classify($name);
  23. $this->key_from = array_key_exists('key_from', $config)
  24. ? (array) $config['key_from'] : $this->key_from;
  25. $this->key_to = array_key_exists('key_to', $config)
  26. ? (array) $config['key_to'] : (array) \Inflector::foreign_key($this->model_from);
  27. $this->conditions = array_key_exists('conditions', $config)
  28. ? (array) $config['conditions'] : array();
  29. $this->cascade_save = array_key_exists('cascade_save', $config)
  30. ? $config['cascade_save'] : $this->cascade_save;
  31. $this->cascade_delete = array_key_exists('cascade_delete', $config)
  32. ? $config['cascade_delete'] : $this->cascade_delete;
  33. if ( ! class_exists($this->model_to))
  34. {
  35. throw new \FuelException('Related model not found by Has_Many relation "'.$this->name.'": '.$this->model_to);
  36. }
  37. $this->model_to = get_real_class($this->model_to);
  38. }
  39. public function get(Model $from)
  40. {
  41. $query = call_user_func(array($this->model_to, 'query'));
  42. reset($this->key_to);
  43. foreach ($this->key_from as $key)
  44. {
  45. // no point running a query when a key value is null
  46. if ($from->{$key} === null)
  47. {
  48. return array();
  49. }
  50. $query->where(current($this->key_to), $from->{$key});
  51. next($this->key_to);
  52. }
  53. foreach (\Arr::get($this->conditions, 'where', array()) as $key => $condition)
  54. {
  55. is_array($condition) or $condition = array($key, '=', $condition);
  56. $query->where($condition);
  57. }
  58. foreach (\Arr::get($this->conditions, 'order_by', array()) as $field => $direction)
  59. {
  60. if (is_numeric($field))
  61. {
  62. $query->order_by($direction);
  63. }
  64. else
  65. {
  66. $query->order_by($field, $direction);
  67. }
  68. }
  69. return $query->get();
  70. }
  71. public function join($alias_from, $rel_name, $alias_to_nr, $conditions = array())
  72. {
  73. $alias_to = 't'.$alias_to_nr;
  74. $model = array(
  75. 'model' => $this->model_to,
  76. 'connection' => call_user_func(array($this->model_to, 'connection')),
  77. 'table' => array(call_user_func(array($this->model_to, 'table')), $alias_to),
  78. 'primary_key' => call_user_func(array($this->model_to, 'primary_key')),
  79. 'join_type' => \Arr::get($conditions, 'join_type') ?: \Arr::get($this->conditions, 'join_type', 'left'),
  80. 'join_on' => array(),
  81. 'columns' => $this->select($alias_to),
  82. 'rel_name' => strpos($rel_name, '.') ? substr($rel_name, strrpos($rel_name, '.') + 1) : $rel_name,
  83. 'relation' => $this,
  84. 'where' => \Arr::get($conditions, 'where', array()),
  85. 'order_by' => \Arr::get($conditions, 'order_by') ?: \Arr::get($this->conditions, 'order_by', array()),
  86. );
  87. reset($this->key_to);
  88. foreach ($this->key_from as $key)
  89. {
  90. $model['join_on'][] = array($alias_from.'.'.$key, '=', $alias_to.'.'.current($this->key_to));
  91. next($this->key_to);
  92. }
  93. foreach (array(\Arr::get($this->conditions, 'where', array()), \Arr::get($conditions, 'join_on', array())) as $c)
  94. {
  95. foreach ($c as $key => $condition)
  96. {
  97. ! is_array($condition) and $condition = array($key, '=', $condition);
  98. if ( ! $condition[0] instanceof \Fuel\Core\Database_Expression and strpos($condition[0], '.') === false)
  99. {
  100. $condition[0] = $alias_to.'.'.$condition[0];
  101. }
  102. is_string($condition[2]) and $condition[2] = \Db::quote($condition[2], $model['connection']);
  103. $model['join_on'][] = $condition;
  104. }
  105. }
  106. return array($rel_name => $model);
  107. }
  108. public function save($model_from, $models_to, $original_model_ids, $parent_saved, $cascade)
  109. {
  110. if ( ! $parent_saved)
  111. {
  112. return;
  113. }
  114. if ( ! is_array($models_to) and ($models_to = is_null($models_to) ? array() : $models_to) !== array())
  115. {
  116. throw new \FuelException('Assigned relationships must be an array or null, given relationship value for '.
  117. $this->name.' is invalid.');
  118. }
  119. $original_model_ids === null and $original_model_ids = array();
  120. foreach ($models_to as $key => $model_to)
  121. {
  122. if ( ! $model_to instanceof $this->model_to)
  123. {
  124. throw new \FuelException('Invalid Model instance added to relations in this model.');
  125. }
  126. $current_model_id = ($model_to and ! $model_to->is_new()) ? $model_to->implode_pk($model_to) : null;
  127. // Check if the model was already assigned
  128. if (($model_to and $model_to->is_new()) or ! in_array($current_model_id, $original_model_ids))
  129. {
  130. // assign this object to the new objects foreign keys
  131. reset($this->key_to);
  132. $frozen = $model_to->frozen(); // only unfreeze/refreeze when it was frozen
  133. $frozen and $model_to->unfreeze();
  134. foreach ($this->key_from as $pk)
  135. {
  136. $model_to->{current($this->key_to)} = $model_from->{$pk};
  137. next($this->key_to);
  138. }
  139. $model_to->is_new() and $model_to->save(false);
  140. $frozen and $model_to->freeze();
  141. }
  142. // check if the model_to's foreign_keys match the model_from's primary keys
  143. else
  144. {
  145. // unset current model from from array
  146. unset($original_model_ids[array_search($current_model_id, $original_model_ids)]);
  147. // check if model_to still refers to this model_from
  148. $changed = false;
  149. reset($this->key_to);
  150. foreach ($this->key_from as $pk)
  151. {
  152. if ($model_to->{current($this->key_to)} != $model_from->{$pk})
  153. {
  154. $changed = true;
  155. }
  156. next($this->key_to);
  157. }
  158. // if any of the keys changed, the relationship was broken - remove model_to from loaded objects
  159. if ($changed)
  160. {
  161. $model_from->unfreeze();
  162. $rel = $model_from->_relate();
  163. unset($rel[$this->name][$key]);
  164. $model_from->_relate($rel);
  165. $model_from->freeze();
  166. // cascading this change won't work here, save just the object with cascading switched off
  167. $model_from->save(false);
  168. }
  169. }
  170. // Fix it if key isn't an imploded PK
  171. if ($key != ($current_model_id = $model_to->implode_pk($model_to)))
  172. {
  173. $model_from->unfreeze();
  174. $rel = $model_from->_relate();
  175. if ( ! empty($rel[$this->name][$key]) and $rel[$this->name][$key] === $model_to)
  176. {
  177. unset($rel[$this->name][$key]);
  178. }
  179. $rel[$this->name][$current_model_id] = $model_to;
  180. $model_from->_relate($rel);
  181. $model_from->freeze();
  182. }
  183. }
  184. // if any original ids are left over in the array, they're no longer related - break them
  185. foreach ($original_model_ids as $original_model_id)
  186. {
  187. // if still loaded set this object's old relation's foreign keys to null
  188. if ($original_model_id and $obj = call_user_func(array($this->model_to, 'find'),
  189. count($this->key_to) == 1 ? array($original_model_id) : explode('][', substr($original_model_id, 1, -1))))
  190. {
  191. $frozen = $obj->frozen(); // only unfreeze/refreeze when it was frozen
  192. $frozen and $obj->unfreeze();
  193. foreach ($this->key_to as $fk)
  194. {
  195. $obj->{$fk} = null;
  196. }
  197. $frozen and $obj->freeze();
  198. // cascading this change won't work here, save just the object with cascading switched off
  199. $obj->save(false);
  200. }
  201. }
  202. $cascade = is_null($cascade) ? $this->cascade_save : (bool) $cascade;
  203. if ($cascade and ! empty($models_to))
  204. {
  205. foreach ($models_to as $m)
  206. {
  207. $m->save();
  208. }
  209. }
  210. }
  211. public function delete($model_from, $models_to, $parent_deleted, $cascade)
  212. {
  213. if ( ! $parent_deleted)
  214. {
  215. return;
  216. }
  217. // break current relations
  218. $model_from->unfreeze();
  219. $rels = $model_from->_relate();
  220. $rels[$this->name] = array();
  221. $model_from->_relate($rels);
  222. $model_from->freeze();
  223. if ( ! empty($models_to))
  224. {
  225. $cascade = is_null($cascade) ? $this->cascade_delete : (bool) $cascade;
  226. foreach ($models_to as $m)
  227. {
  228. if ($cascade)
  229. {
  230. $m->delete();
  231. }
  232. elseif ( ! $m->frozen())
  233. {
  234. foreach ($this->key_to as $fk)
  235. {
  236. $m->{$fk} = null;
  237. }
  238. $m->is_changed() and $m->save();
  239. }
  240. }
  241. }
  242. }
  243. }