PageRenderTime 52ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

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

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