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

/src/site/components/com_stories/domains/behaviors/aggregatable.php

https://github.com/bhar1red/anahita
PHP | 344 lines | 197 code | 41 blank | 106 comment | 25 complexity | 1afaebd3e0772eb6fd43b47b33f9ecd2 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. <?php
  2. /**
  3. * LICENSE: ##LICENSE##
  4. *
  5. * @category Anahita
  6. * @package Com_Stories
  7. * @subpackage Domain_Repository
  8. * @author Arash Sanieyan <ash@anahitapolis.com>
  9. * @author Rastin Mehr <rastin@anahitapolis.com>
  10. * @copyright 2008 - 2010 rmdStudio Inc./Peerglobe Technology Inc
  11. * @license GNU GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
  12. * @version SVN: $Id$
  13. * @link http://www.anahitapolis.com
  14. */
  15. /**
  16. * Aggregatable behavior
  17. *
  18. * @category Anahita
  19. * @package Com_Stories
  20. * @subpackage Domain_Repository
  21. * @author Arash Sanieyan <ash@anahitapolis.com>
  22. * @author Rastin Mehr <rastin@anahitapolis.com>
  23. * @license GNU GPLv3 <http://www.gnu.org/licenses/gpl-3.0.html>
  24. * @link http://www.anahitapolis.com
  25. */
  26. class ComStoriesDomainBehaviorAggregatable extends AnDomainBehaviorAbstract
  27. {
  28. /**
  29. * Loaded nodes
  30. *
  31. * @var array
  32. */
  33. protected $_loaded_nodes;
  34. /**
  35. * After fetch
  36. *
  37. * @param KCommandContext $context
  38. * @return void
  39. */
  40. protected function _beforeQuerySelect(KCommandContext $context)
  41. {
  42. $query = $context->query;
  43. if ( $query->aggregate_keys ) {
  44. $this->_buildAggregateQuery($query, $query->aggregate_keys);
  45. }
  46. }
  47. /**
  48. * After fetch
  49. *
  50. * @param KCommandContext $context
  51. * @return void
  52. */
  53. protected function _afterRepositoryFetch(KCommandContext $context)
  54. {
  55. $query = $context->query;
  56. if ( $context->mode != AnDomain::FETCH_VALUE && $query->aggregate_keys ) {
  57. $this->_preloadData($context->data);
  58. }
  59. }
  60. /**
  61. * Summerize a story query
  62. *
  63. * @param AnDomainQuery $query Query object
  64. * @param array $config Summerization configuration
  65. *
  66. * @return AnDomainEntityset
  67. */
  68. protected function _buildAggregateQuery($query, $config)
  69. {
  70. $cases = array();
  71. $config = KConfig::unbox($config);
  72. $config[] = array(
  73. 'WHEN @col(name) LIKE "avatar_edit" THEN IF(@col(subject.id) = @col(target.id), "", @col(id))',
  74. 'WHEN @col(name) LIKE "actor_follow" THEN @col(subject.id)'
  75. );
  76. foreach($config as $component => $keys)
  77. {
  78. foreach($keys as $name => $key)
  79. {
  80. if ( !is_numeric($name) )
  81. {
  82. $names = explode(',', $name);
  83. $keys = explode(',', $key);
  84. foreach($keys as $i => $key)
  85. $keys[$i] = '@col('.$key.'.id)';
  86. foreach($names as $name)
  87. {
  88. $cases[]='WHEN CONCAT(@col(name),@col(component)) LIKE \''.$name.$component.'\' THEN CONCAT('.implode(',',$keys).')';
  89. }
  90. } else
  91. {
  92. $cases[] = $key;
  93. }
  94. }
  95. }
  96. $cases = array_unique($cases);
  97. $keys = array();
  98. //always group by date, name and component
  99. $date = '@col(name),@col(component),DATE(@col(creationTime))';
  100. $keys[] = $date;
  101. if ( !empty($cases) )
  102. {
  103. $case = 'CASE TRUE ';
  104. $case .= implode(' ',$cases).' ';
  105. $case .= 'ELSE CONCAT(@col(target.id),@col(subject.id),@col(object.id)) ';
  106. $case .= 'END';
  107. $keys[] = $case;
  108. } else
  109. $keys[] = '@col(target.id),@col(subject.id),@col(object.id)';
  110. $keys = implode(',', $keys);
  111. $comment_if = "IF(@col(comment.id) IS NOT NULL AND @col(object.id) IS NOT NULL , CONCAT_WS(',',$date, @col(object.id)), CONCAT_WS(',',$keys))";
  112. //don't group if a story has a body or it has directly been commented on
  113. $bundle = "IF (@col(body) <> '' AND @col(body) IS NOT NULL,@col(id),$comment_if) AS bundle_key";
  114. $query->select($bundle);
  115. $query->select('GROUP_CONCAT(DISTINCT @col(id)) AS ids');
  116. $query->select('GROUP_CONCAT(DISTINCT @col(owner.id)) AS owner_ids');
  117. $query->select('GROUP_CONCAT(DISTINCT @col(target.id)) AS target_ids');
  118. $query->select('GROUP_CONCAT(DISTINCT @col(comment.id)) AS comment_ids');
  119. $query->select('GROUP_CONCAT(DISTINCT @col(object.id)) AS object_ids');
  120. $query->select('GROUP_CONCAT(DISTINCT @col(subject.id)) AS subject_ids');
  121. $query->select(array('id'=>'MAX(@col(id))'));
  122. $query->select(array('creationTime'=>'MAX(@col(creationTime))'));
  123. $query->select(array('updateTime'=>'MAX(@col(updateTime))'));
  124. $viewer = get_viewer();
  125. $query->group('bundle_key');
  126. $query->order = array();
  127. $query->order('modified_on', 'DESC');
  128. }
  129. /**
  130. * Loads all the necessary objects after each collection fetch
  131. *
  132. * @param AnDomainEntityset $stories
  133. * @return void
  134. */
  135. protected function _preloadData($stories)
  136. {
  137. $node_ids = array();
  138. $comment_ids = array();
  139. foreach($stories as $story)
  140. {
  141. $columns = $story->getRowData();
  142. $node_ids = array_merge($node_ids,
  143. $story->getIds('owner'),
  144. $story->getIds('subject'),
  145. $story->getIds('target'),
  146. $story->getIds('object')
  147. );
  148. $comment_ids = array_merge($comment_ids, $story->getIds('comment'));
  149. }
  150. $node_ids = array_unique($node_ids);
  151. $query = $this->getService('repos://site/base.comment')->getQuery()->where('parent.id','IN',$node_ids);
  152. $author_ids = $query->fetchValues('author.id');
  153. $node_ids = array_unique(array_merge($node_ids, $author_ids, $comment_ids));
  154. //we don't any behavior messes around with the fetched stories
  155. $query = $this->getService('repos://site/base.node')->getQuery()->id($node_ids)->disableChain();
  156. $query->columns('*');
  157. $nodes = $query->fetchSet();
  158. $nodes = AnHelperArray::indexBy($nodes, 'id');
  159. $this->_loaded_nodes = $nodes;
  160. }
  161. /**
  162. * Return the object of the node
  163. *
  164. * @return ComBaseDomainEntityNode
  165. */
  166. public function getObject()
  167. {
  168. return $this->_getPreloadedNode('object');
  169. }
  170. /**
  171. * Return the object of the node
  172. *
  173. * @return ComActorsDomainEntityActor
  174. */
  175. public function getSubject()
  176. {
  177. return $this->_getPreloadedNode('subject');
  178. }
  179. /**
  180. * Return the object of the node
  181. *
  182. * @return ComActorsDomainEntityActor
  183. */
  184. public function getTarget()
  185. {
  186. return $this->_getPreloadedNode('target');
  187. }
  188. /**
  189. * Since nodes related to an aggregated set have been preloaded
  190. * we just fetch them from the cache list _loaded_nodes
  191. *
  192. * @TODO we need to this in the _preloadData method after the nodes
  193. * have been loaded
  194. *
  195. * @return ComBaseDomainEntityNode
  196. */
  197. protected function _getPreloadedNode($data)
  198. {
  199. if ( !$this->aggregated() ) {
  200. return $this->_mixer->get($data);
  201. }
  202. if ( !isset($this->_mixer->__ids) ) {
  203. $this->_mixer->__ids = array();
  204. }
  205. if ( !isset($this->_mixer->__ids[$data]) )
  206. {
  207. $nodes = array();
  208. $ids = $this->getIds($data) ;
  209. foreach($ids as $id) {
  210. if ( isset($this->_loaded_nodes[$id]) ) {
  211. $nodes[] = $this->_loaded_nodes[$id];
  212. }
  213. }
  214. if ( count($nodes) < 2 ) {
  215. $nodes = array_pop($nodes);
  216. }
  217. $this->_mixer->__ids[$data] = $nodes;
  218. }
  219. return $this->_mixer->__ids[$data];
  220. }
  221. /**
  222. * Return an array of previously loaded nodes
  223. *
  224. * @return array
  225. */
  226. public function getLoadedNodes()
  227. {
  228. return $this->_loaded_nodes;
  229. }
  230. /**
  231. * Return an array of aggregated IDs
  232. *
  233. * @param string
  234. * @return array
  235. */
  236. public function getIds($key = null)
  237. {
  238. if ( !isset($this->_mixer->__ids) ) {
  239. $this->_mixer->__ids = array();
  240. }
  241. $prop = $key ? $key : 'id';
  242. $key = $key ? $key.'_ids' : 'ids';
  243. if ( !isset($this->_mixer->__ids[$key]) )
  244. {
  245. $columns = $this->getRowData();
  246. if ( !empty($columns[$key]) )
  247. $ids = explode(',',$columns[$key]);
  248. else {
  249. $ids = isset($this->$prop) ? ($prop == 'id' ? array($this->id) : array($this->$prop->id)) : array();
  250. }
  251. $this->_mixer->__ids[$key] = $ids;
  252. }
  253. return $this->_mixer->__ids[$key];
  254. }
  255. /**
  256. * Return an array of aggregated comments of the object
  257. *
  258. * @return array
  259. */
  260. public function getComments()
  261. {
  262. //setup the comments
  263. $comment_ids = $this->getIds('comment');
  264. $comments = array();
  265. //only shows comments if there are comment_ids in the story
  266. //body or if the story has directly been commented on
  267. if ( !empty($comment_ids) )
  268. {
  269. sort($comment_ids);
  270. $size = 0;
  271. foreach($comment_ids as $id)
  272. {
  273. if ( isset($this->_loaded_nodes[$id]) )
  274. {
  275. $comment = $this->_loaded_nodes[$id];
  276. if ( $comment instanceof ComBaseDomainEntityComment )
  277. {
  278. $comments[$id] = $comment;
  279. $size++;
  280. }
  281. }
  282. if ( $size == 10 )
  283. break;
  284. }
  285. }
  286. return $comments;
  287. }
  288. /**
  289. * Return if a story is an aggregation of multiple stories
  290. *
  291. * @return boolean
  292. */
  293. public function aggregated()
  294. {
  295. if ( $this->getEntityState() & AnDomain::STATE_NEW ) {
  296. return false;
  297. }
  298. if ( $this->getRowData('bundle_key') == null ) {
  299. return false;
  300. }
  301. return count($this->getIds()) > 1;
  302. }
  303. }