PageRenderTime 44ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/fuel/packages/orm/classes/manymany.php

https://bitbucket.org/codeyash/bootstrap
PHP | 353 lines | 275 code | 46 blank | 32 comment | 17 complexity | a308ced3fc775c43ec4bae66113297cb MD5 | raw file
Possible License(s): MIT, Apache-2.0
  1. <?php
  2. /**
  3. * Fuel is a fast, lightweight, community driven PHP5 framework.
  4. *
  5. * @package Fuel
  6. * @version 1.5
  7. * @author Fuel Development Team
  8. * @license MIT License
  9. * @copyright 2010 - 2013 Fuel Development Team
  10. * @link http://fuelphp.com
  11. */
  12. namespace Orm;
  13. class ManyMany extends Relation
  14. {
  15. protected $key_from = array('id');
  16. protected $key_to = array('id');
  17. /**
  18. * @var string classname of model to use as connection
  19. */
  20. protected $model_through;
  21. /**
  22. * @var string table name of table to use as connection, alternative to $model_through setting
  23. */
  24. protected $table_through;
  25. /**
  26. * @var string foreign key of from model in connection table
  27. */
  28. protected $key_through_from;
  29. /**
  30. * @var string foreign key of to model in connection table
  31. */
  32. protected $key_through_to;
  33. public function __construct($from, $name, array $config)
  34. {
  35. $this->name = $name;
  36. $this->model_from = $from;
  37. $this->model_to = array_key_exists('model_to', $config)
  38. ? $config['model_to'] : \Inflector::get_namespace($from).'Model_'.\Inflector::classify($name);
  39. $this->key_from = array_key_exists('key_from', $config)
  40. ? (array) $config['key_from'] : $this->key_from;
  41. $this->key_to = array_key_exists('key_to', $config)
  42. ? (array) $config['key_to'] : $this->key_to;
  43. $this->conditions = array_key_exists('conditions', $config)
  44. ? (array) $config['conditions'] : array();
  45. if ( ! empty($config['table_through']))
  46. {
  47. $this->table_through = $config['table_through'];
  48. }
  49. else
  50. {
  51. $table_name = array($this->model_from, $this->model_to);
  52. natcasesort($table_name);
  53. $table_name = array_merge($table_name);
  54. $this->table_through = \Inflector::tableize($table_name[0]).'_'.\Inflector::tableize($table_name[1]);
  55. }
  56. $this->key_through_from = ! empty($config['key_through_from'])
  57. ? (array) $config['key_through_from'] : (array) \Inflector::foreign_key($this->model_from);
  58. $this->key_through_to = ! empty($config['key_through_to'])
  59. ? (array) $config['key_through_to'] : (array) \Inflector::foreign_key($this->model_to);
  60. $this->cascade_save = array_key_exists('cascade_save', $config)
  61. ? $config['cascade_save'] : $this->cascade_save;
  62. $this->cascade_delete = array_key_exists('cascade_delete', $config)
  63. ? $config['cascade_delete'] : $this->cascade_delete;
  64. if ( ! class_exists($this->model_to))
  65. {
  66. throw new \FuelException('Related model not found by Many_Many relation "'.$this->name.'": '.$this->model_to);
  67. }
  68. $this->model_to = get_real_class($this->model_to);
  69. }
  70. public function get(Model $from)
  71. {
  72. // Create the query on the model_through
  73. $query = call_user_func(array($this->model_to, 'query'));
  74. // set the model_from's keys as where conditions for the model_through
  75. $join = array(
  76. 'table' => array($this->table_through, 't0_through'),
  77. 'join_type' => null,
  78. 'join_on' => array(),
  79. 'columns' => $this->select_through('t0_through')
  80. );
  81. reset($this->key_from);
  82. foreach ($this->key_through_from as $key)
  83. {
  84. if ($from->{current($this->key_from)} === null)
  85. {
  86. return array();
  87. }
  88. $query->where('t0_through.'.$key, $from->{current($this->key_from)});
  89. next($this->key_from);
  90. }
  91. reset($this->key_to);
  92. foreach ($this->key_through_to as $key)
  93. {
  94. $join['join_on'][] = array('t0_through.'.$key, '=', 't0.'.current($this->key_to));
  95. next($this->key_to);
  96. }
  97. foreach (\Arr::get($this->conditions, 'where', array()) as $key => $condition)
  98. {
  99. is_array($condition) or $condition = array($key, '=', $condition);
  100. $query->where($condition);
  101. }
  102. foreach (\Arr::get($this->conditions, 'order_by', array()) as $field => $direction)
  103. {
  104. if (is_numeric($field))
  105. {
  106. $query->order_by($direction);
  107. }
  108. else
  109. {
  110. $query->order_by($field, $direction);
  111. }
  112. }
  113. $query->_join($join);
  114. return $query->get();
  115. }
  116. public function select_through($table)
  117. {
  118. foreach ($this->key_through_to as $to)
  119. {
  120. $properties[] = $table.'.'.$to;
  121. }
  122. foreach ($this->key_through_from as $from)
  123. {
  124. $properties[] = $table.'.'.$from;
  125. }
  126. return $properties;
  127. }
  128. public function join($alias_from, $rel_name, $alias_to_nr, $conditions = array())
  129. {
  130. $alias_to = 't'.$alias_to_nr;
  131. $models = array(
  132. $rel_name.'_through' => array(
  133. 'model' => null,
  134. 'connection' => call_user_func(array($this->model_to, 'connection')),
  135. 'table' => array($this->table_through, $alias_to.'_through'),
  136. 'primary_key' => null,
  137. 'join_type' => \Arr::get($conditions, 'join_type') ?: \Arr::get($this->conditions, 'join_type', 'left'),
  138. 'join_on' => array(),
  139. 'columns' => $this->select_through($alias_to.'_through'),
  140. 'rel_name' => $this->model_through,
  141. 'relation' => $this
  142. ),
  143. $rel_name => array(
  144. 'model' => $this->model_to,
  145. 'connection' => call_user_func(array($this->model_to, 'connection')),
  146. 'table' => array(call_user_func(array($this->model_to, 'table')), $alias_to),
  147. 'primary_key' => call_user_func(array($this->model_to, 'primary_key')),
  148. 'join_type' => \Arr::get($conditions, 'join_type') ?: \Arr::get($this->conditions, 'join_type', 'left'),
  149. 'join_on' => array(),
  150. 'columns' => $this->select($alias_to),
  151. 'rel_name' => strpos($rel_name, '.') ? substr($rel_name, strrpos($rel_name, '.') + 1) : $rel_name,
  152. 'relation' => $this,
  153. 'where' => \Arr::get($conditions, 'where', array()),
  154. 'order_by' => \Arr::get($conditions, 'order_by') ?: \Arr::get($this->conditions, 'order_by', array()),
  155. )
  156. );
  157. reset($this->key_from);
  158. foreach ($this->key_through_from as $key)
  159. {
  160. $models[$rel_name.'_through']['join_on'][] = array($alias_from.'.'.current($this->key_from), '=', $alias_to.'_through.'.$key);
  161. next($this->key_from);
  162. }
  163. reset($this->key_to);
  164. foreach ($this->key_through_to as $key)
  165. {
  166. $models[$rel_name]['join_on'][] = array($alias_to.'_through.'.$key, '=', $alias_to.'.'.current($this->key_to));
  167. next($this->key_to);
  168. }
  169. foreach (\Arr::get($this->conditions, 'where', array()) as $key => $condition)
  170. {
  171. ! is_array($condition) and $condition = array($key, '=', $condition);
  172. if ( ! $condition[0] instanceof \Fuel\Core\Database_Expression and strpos($condition[0], '.') === false)
  173. {
  174. $condition[0] = $alias_to.'.'.$condition[0];
  175. }
  176. is_string($condition[2]) and $condition[2] = \Db::quote($condition[2], $models[$rel_name]['connection']);
  177. $models[$rel_name]['join_on'][] = $condition;
  178. }
  179. return $models;
  180. }
  181. public function save($model_from, $models_to, $original_model_ids, $parent_saved, $cascade)
  182. {
  183. if ( ! $parent_saved)
  184. {
  185. return;
  186. }
  187. if ( ! is_array($models_to) and ($models_to = is_null($models_to) ? array() : $models_to) !== array())
  188. {
  189. throw new \FuelException('Assigned relationships must be an array or null, given relationship value for '.
  190. $this->name.' is invalid.');
  191. }
  192. $original_model_ids === null and $original_model_ids = array();
  193. $del_rels = $original_model_ids;
  194. foreach ($models_to as $key => $model_to)
  195. {
  196. if ( ! $model_to instanceof $this->model_to)
  197. {
  198. throw new \FuelException('Invalid Model instance added to relations in this model.');
  199. }
  200. // Save if it's a yet unsaved object
  201. if ($model_to->is_new())
  202. {
  203. $model_to->save(false);
  204. }
  205. $current_model_id = $model_to ? $model_to->implode_pk($model_to) : null;
  206. // Check if the model was already assigned, if not INSERT relationships:
  207. if ( ! in_array($current_model_id, $original_model_ids))
  208. {
  209. $ids = array();
  210. reset($this->key_from);
  211. foreach ($this->key_through_from as $pk)
  212. {
  213. $ids[$pk] = $model_from->{current($this->key_from)};
  214. next($this->key_from);
  215. }
  216. reset($this->key_to);
  217. foreach ($this->key_through_to as $pk)
  218. {
  219. $ids[$pk] = $model_to->{current($this->key_to)};
  220. next($this->key_to);
  221. }
  222. \DB::insert($this->table_through)->set($ids)->execute(call_user_func(array($model_from, 'connection')));
  223. $original_model_ids[] = $current_model_id; // prevents inserting it a second time
  224. }
  225. else
  226. {
  227. // unset current model from from array of new relations
  228. unset($del_rels[array_search($current_model_id, $original_model_ids)]);
  229. }
  230. // ensure correct pk assignment
  231. if ($key != $current_model_id)
  232. {
  233. $model_from->unfreeze();
  234. $rel = $model_from->_relate();
  235. if ( ! empty($rel[$this->name][$key]) and $rel[$this->name][$key] === $model_to)
  236. {
  237. unset($rel[$this->name][$key]);
  238. }
  239. $rel[$this->name][$current_model_id] = $model_to;
  240. $model_from->_relate($rel);
  241. $model_from->freeze();
  242. }
  243. }
  244. // If any ids are left in $del_rels they are no longer assigned, DELETE the relationships:
  245. foreach ($del_rels as $original_model_id)
  246. {
  247. $query = \DB::delete($this->table_through);
  248. reset($this->key_from);
  249. foreach ($this->key_through_from as $key)
  250. {
  251. $query->where($key, '=', $model_from->{current($this->key_from)});
  252. next($this->key_from);
  253. }
  254. $to_keys = count($this->key_to) == 1 ? array($original_model_id) : explode('][', substr($original_model_id, 1, -1));
  255. reset($to_keys);
  256. foreach ($this->key_through_to as $key)
  257. {
  258. $query->where($key, '=', current($to_keys));
  259. next($to_keys);
  260. }
  261. $query->execute(call_user_func(array($model_from, 'connection')));
  262. }
  263. $cascade = is_null($cascade) ? $this->cascade_save : (bool) $cascade;
  264. if ($cascade and ! empty($models_to))
  265. {
  266. foreach ($models_to as $m)
  267. {
  268. $m->save();
  269. }
  270. }
  271. }
  272. public function delete($model_from, $models_to, $parent_deleted, $cascade)
  273. {
  274. if ( ! $parent_deleted)
  275. {
  276. return;
  277. }
  278. // Remove relations
  279. $model_from->unfreeze();
  280. $rels = $model_from->_relate();
  281. $rels[$this->name] = array();
  282. $model_from->_relate($rels);
  283. $model_from->freeze();
  284. // Delete all relationship entries for the model_from
  285. $this->delete_related($model_from);
  286. $cascade = is_null($cascade) ? $this->cascade_delete : (bool) $cascade;
  287. if ($cascade and ! empty($model_to))
  288. {
  289. foreach ($models_to as $m)
  290. {
  291. $m->delete();
  292. }
  293. }
  294. }
  295. public function delete_related($model_from)
  296. {
  297. // Delete all relationship entries for the model_from
  298. $query = \DB::delete($this->table_through);
  299. reset($this->key_from);
  300. foreach ($this->key_through_from as $key)
  301. {
  302. $query->where($key, '=', $model_from->{current($this->key_from)});
  303. next($this->key_from);
  304. }
  305. $query->execute(call_user_func(array($model_from, 'connection')));
  306. }
  307. }