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

/framework/classes/orm/twinnable/manymany.php

https://github.com/jay3/core
PHP | 222 lines | 181 code | 31 blank | 10 comment | 13 complexity | 005abf491d0793681786651970ae4bb7 MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. /**
  3. * NOVIUS OS - Web OS for digital communication
  4. *
  5. * @copyright 2011 Novius
  6. * @license GNU Affero General Public License v3 or (at your option) any later version
  7. * http://www.gnu.org/licenses/agpl-3.0.html
  8. * @link http://www.novius-os.org
  9. */
  10. namespace Nos;
  11. use \DB;
  12. class Orm_Twinnable_ManyMany extends \Orm\ManyMany
  13. {
  14. protected $column_context_from = 'context';
  15. protected $column_context_common_id_to = 'context_common_id';
  16. protected $column_context_to = 'context';
  17. protected $column_context_is_main_to = 'context_is_main';
  18. public function __construct($from, $name, array $config)
  19. {
  20. $to = array_key_exists('model_to', $config) ? $config['model_to'] : \Inflector::get_namespace($from).'Model_'.\Inflector::classify($name);
  21. if (!class_exists($to)) {
  22. throw new \FuelException('The related model ‘'.$this->model_to.'’ cannot be found by the many_many relation ‘'.$this->name.'’.');
  23. }
  24. $to_behaviour = $to::behaviours('Nos\Orm_Behaviour_Twinnable', false);
  25. if (!$to_behaviour) {
  26. throw new \FuelException('The twinnable_many_many relation ‘'.$name.'’ of the model ‘'.$from.'’ refers to a model which doesn’t have the Twinnable behaviour.');
  27. }
  28. $from_behaviour = $from::behaviours('Nos\Orm_Behaviour_Twinnable', false);
  29. if (!$from_behaviour) {
  30. throw new \FuelException('The model ‘'.$from.'’ has a twinnable_many_many relation but no Twinnable behaviour. How strange.');
  31. }
  32. $config['key_from'] = array_key_exists('key_from', $config) ? (array) $config['key_from'] : $from_behaviour['common_id_property'];
  33. $config['key_to'] = array_key_exists('key_to', $config) ? (array) $config['key_to'] : $to_behaviour['common_id_property'];
  34. parent::__construct($from, $name, $config);
  35. $this->column_context_from = array_key_exists('column_context_from', $config) ? $config['column_context_from'] : $from_behaviour['context_property'];
  36. $this->column_context_common_id_to = array_key_exists('column_context_common_id_to', $config) ? $config['column_context_common_id_to'] : $to_behaviour['common_id_property'];
  37. $this->column_context_to = array_key_exists('column_context_to', $config) ? $config['column_context_to'] : $to_behaviour['context_property'];
  38. $this->column_context_is_main_to = array_key_exists('column_context_is_main_to', $config) ? $config['column_context_is_main_to'] : $to_behaviour['is_main_property'];
  39. }
  40. public function get(\Orm\Model $from)
  41. {
  42. // Create the query on the model_through
  43. $query = call_user_func(array($this->model_to, 'query'));
  44. // set the model_from's keys as where conditions for the model_through
  45. $join = array(
  46. 'table' => array($this->table_through, 't0_through'),
  47. 'join_type' => null,
  48. 'join_on' => array(),
  49. 'columns' => $this->select_through('t0_through')
  50. );
  51. reset($this->key_from);
  52. foreach ($this->key_through_from as $key) {
  53. if ($from->{current($this->key_from)} === null) {
  54. return array();
  55. }
  56. $query->where('t0_through.'.$key, $from->{current($this->key_from)});
  57. next($this->key_from);
  58. }
  59. reset($this->key_to);
  60. foreach ($this->key_through_to as $key) {
  61. $join['join_on'][] = array('t0_through.'.$key, '=', 't0.'.current($this->key_to));
  62. next($this->key_to);
  63. }
  64. $query->and_where_open();
  65. $query->where($this->column_context_to, $from->{$this->column_context_from});
  66. $query->or_where($this->column_context_is_main_to, 1);
  67. $query->and_where_close();
  68. foreach (\Arr::get($this->conditions, 'where', array()) as $key => $condition) {
  69. is_array($condition) or $condition = array($key, '=', $condition);
  70. $query->where($condition);
  71. }
  72. foreach (\Arr::get($this->conditions, 'order_by', array()) as $field => $direction) {
  73. if (is_numeric($field)) {
  74. $query->order_by($direction);
  75. } else {
  76. $query->order_by($field, $direction);
  77. }
  78. }
  79. $query->_join($join);
  80. $result = array();
  81. $result_context = array();
  82. foreach ($query->get() as $pk => $model) {
  83. if (isset($result_context[$model->{$this->column_context_common_id_to}])) {
  84. if ($model->{$this->column_context_to} !== $from->{$this->column_context_from}) {
  85. continue;
  86. } else {
  87. unset($result[$result_context[$model->{$this->column_context_common_id_to}]]);
  88. }
  89. }
  90. $result_context[$model->{$this->column_context_common_id_to}] = $pk;
  91. $result[$pk] = $model;
  92. }
  93. return $result;
  94. }
  95. public function join($alias_from, $rel_name, $alias_to_nr, $conditions = array())
  96. {
  97. $alias_to = 't'.$alias_to_nr;
  98. $alias_through = array($this->table_through, $alias_to.'_through');
  99. $alias_to_table = array(call_user_func(array($this->model_to, 'table')), $alias_to);
  100. $props_pks = $this->_selectFallback($alias_to, $alias_to.'_fallback');
  101. $models = array(
  102. $rel_name.'_through' => array(
  103. 'model' => null,
  104. 'connection' => call_user_func(array($this->model_to, 'connection')),
  105. 'table' => $alias_through,
  106. 'primary_key' => null,
  107. 'join_type' => \Arr::get($conditions, 'join_type') ?: \Arr::get($this->conditions, 'join_type', 'left'),
  108. 'join_on' => array(),
  109. 'columns' => $this->select_through($alias_to.'_through'),
  110. 'rel_name' => $this->model_through,
  111. 'relation' => $this
  112. ),
  113. $rel_name => array(
  114. 'model' => $this->model_to,
  115. 'connection' => call_user_func(array($this->model_to, 'connection')),
  116. 'table' => $alias_to_table,
  117. 'primary_key' => $props_pks['primary_key'],
  118. 'join_type' => \Arr::get($conditions, 'join_type') ?: \Arr::get($this->conditions, 'join_type', 'left'),
  119. 'join_on' => array(),
  120. 'columns' => $props_pks['columns'],
  121. 'rel_name' => strpos($rel_name, '.') ? substr($rel_name, strrpos($rel_name, '.') + 1) : $rel_name,
  122. 'relation' => $this,
  123. 'where' => \Arr::get($conditions, 'where', array()),
  124. ),
  125. $rel_name.'_fallback' => array(
  126. 'model' => null,
  127. 'connection' => call_user_func(array($this->model_to, 'connection')),
  128. 'table' => array(call_user_func(array($this->model_to, 'table')), $alias_to.'_fallback'),
  129. 'primary_key' => null,
  130. 'join_type' => \Arr::get($conditions, 'join_type') ? : \Arr::get($this->conditions, 'join_type', 'left'),
  131. 'join_on' => array(),
  132. 'columns' => array(),
  133. 'rel_name' => (strpos($rel_name, '.') ? substr($rel_name, strrpos($rel_name, '.') + 1) : $rel_name).'_fallback',
  134. 'relation' => $this,
  135. 'where' => \Arr::get($conditions, 'where', array()),
  136. ),
  137. );
  138. reset($this->key_from);
  139. foreach ($this->key_through_from as $key) {
  140. $models[$rel_name.'_through']['join_on'][] = array($alias_from.'.'.current($this->key_from), '=', $alias_to.'_through.'.$key);
  141. next($this->key_from);
  142. }
  143. reset($this->key_to);
  144. foreach ($this->key_through_to as $key) {
  145. $models[$rel_name]['join_on'][] = array($alias_to.'_through.'.$key, '=', $alias_to.'.'.current($this->key_to));
  146. $models[$rel_name.'_fallback']['join_on'][] = array($alias_to.'_through.'.$key, '=', $alias_to.'_fallback'.'.'.current($this->key_to));
  147. next($this->key_to);
  148. }
  149. $models[$rel_name]['join_on'][] = array($alias_to.'.'.$this->column_context_is_main_to, '=', DB::expr(1));
  150. $models[$rel_name.'_fallback']['join_on'][] = array($alias_from.'.'.$this->column_context_from, '=', $alias_to.'_fallback'.'.'.$this->column_context_to);
  151. foreach (array(\Arr::get($this->conditions, 'where', array()), \Arr::get($conditions, 'join_on', array())) as $c) {
  152. foreach ($c as $key => $condition) {
  153. ! is_array($condition) and $condition = array($key, '=', $condition);
  154. if (!$condition[0] instanceof \Fuel\Core\Database_Expression and strpos($condition[0], '.') === false) {
  155. $condition[0] = $alias_to.'.'.$condition[0];
  156. }
  157. is_string($condition[2]) and $condition[2] = \Db::quote($condition[2], $models[$rel_name]['connection']);
  158. $models[$rel_name]['join_on'][] = $condition;
  159. $models[$rel_name.'_fallback']['join_on'][] = $condition;
  160. }
  161. }
  162. $order_by = \Arr::get($conditions, 'order_by') ?: \Arr::get($this->conditions, 'order_by', array());
  163. foreach ($order_by as $key => $direction) {
  164. if (!$key instanceof \Fuel\Core\Database_Expression and strpos($key, '.') === false) {
  165. $key = $alias_to.'.'.$key;
  166. } else {
  167. $key = str_replace(array($alias_through[0],$alias_to_table[0]), array($alias_through[1],$alias_to_table[1]), $key);
  168. }
  169. $models[$rel_name]['order_by'][$key] = $direction;
  170. $models[$rel_name.'_fallback']['order_by'][$key] = $direction;
  171. }
  172. return $models;
  173. }
  174. public function _selectFallback($table, $table_fallback)
  175. {
  176. $pks = call_user_func(array($this->model_to, 'primary_key'));
  177. $props = call_user_func(array($this->model_to, 'properties'));
  178. $properties = array();
  179. $primary_key = array();
  180. foreach ($props as $pk => $pv) {
  181. $properties[] = array(\DB::expr('COALESCE('.$table_fallback.'.'.$pk.','.$table.'.'.$pk.')'), $table.'_'.$pk);
  182. if (in_array($pk, $pks)) {
  183. $primary_key[$table.'_'.$pk] = $pk;
  184. }
  185. }
  186. return array(
  187. 'columns' => $properties,
  188. 'primary_key' => $primary_key,
  189. );
  190. }
  191. }