diff --git a/Timeline/TimelineBuilder.php b/Timeline/TimelineBuilder.php index 2081a8762..eb95d6c1c 100644 --- a/Timeline/TimelineBuilder.php +++ b/Timeline/TimelineBuilder.php @@ -20,6 +20,7 @@ namespace Chill\MainBundle\Timeline; use Doctrine\ORM\Query\ResultSetMapping; +use Doctrine\DBAL\Types\Type; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\DependencyInjection\ContainerAwareInterface; @@ -52,33 +53,60 @@ class TimelineBuilder implements ContainerAwareInterface /** * return an HTML string with timeline - * + * * This function must be called from controller - * + * * @example https://redmine.champs-libres.coop/projects/chillperson/repository/revisions/bd2e1b1808f73e39532e9538413025df5487cad0/entry/Controller/TimelinePersonController.php#L47 the implementation in person bundle - * + * * @param string $context * @param array $args arguments defined by the bundle which create the context - * @param int $page first page = 0 + * @param int $firstItem first item number * @param int $number number of items by page * @return string an HTML representation, must be included using `|raw` filter */ - public function getTimelineHTML($context, array $args, $page = 0, $number = 20) + public function getTimelineHTML($context, array $args, $firstItem = 0, $number = 20) { - $query = $this->buildUnionQuery($context, $args, $page, $number); - $fetched = $this->runQuery($query); + $union = $this->buildUnionQuery($context, $args); + + //add ORDER BY clause and LIMIT + $query = $union . sprintf(' ORDER BY date DESC LIMIT %d OFFSET %d', + $number, $firstItem); + + // run query and handle results + $fetched = $this->runUnionQuery($query); $entitiesByKey = $this->getEntities($fetched, $context); return $this->render($fetched, $entitiesByKey, $context, $args); } + /** + * Return the number of items for the given context and args + * + * @param unknown $context + * @param array $args + * @return mixed|\Doctrine\DBAL\Driver\Statement|NULL + */ + public function countItems($context, array $args) + { + $union = $this->buildUnionQuery($context, $args); + + // embed the union query inside a count query + $count = sprintf('SELECT COUNT(sq.id) AS total FROM (%s) as sq', $union); + + $rsm = (new ResultSetMapping()) + ->addScalarResult('total', 'total', Type::INTEGER); + + return $this->em->createNativeQuery($count, $rsm) + ->getSingleScalarResult(); + } + /** * add a provider id - * + * * @internal This function is called by the TimelineCompilerClass - * + * * @param string $context the context of the service - * @param string $id the + * @param string $id the */ public function addProvider($context, $id) { @@ -87,7 +115,7 @@ class TimelineBuilder implements ContainerAwareInterface /** * Get providers by context - * + * * @param string $context * @return TimelineProviderInterface[] */ @@ -104,9 +132,9 @@ class TimelineBuilder implements ContainerAwareInterface /** * build the UNION query with all providers - * + * * @uses self::buildSelectQuery to build individual SELECT queries - * + * * @param string $context * @param mixed $args * @param int $page @@ -114,7 +142,7 @@ class TimelineBuilder implements ContainerAwareInterface * @return string * @throws \LogicException if no builder have been defined for this context */ - private function buildUnionQuery($context, array $args, $page, $number) + private function buildUnionQuery($context, array $args) { //throw an exception if no provider have been defined for this context if (!array_key_exists($context, $this->providers)) { @@ -124,21 +152,18 @@ class TimelineBuilder implements ContainerAwareInterface //append SELECT queries with UNION keyword between them $union = ''; - foreach($this->getProvidersByContext($context) as $provider) { - $select = $this->buildSelectQuery($provider, $context, $args); + foreach($this->getProvidersByContext($context) as $provider) { + $select = $this->buildSelectQuery($provider, $context, $args); $append = ($union === '') ? $select : ' UNION '.$select; $union .= $append; } - //add ORDER BY clause and LIMIT - $union .= sprintf(' ORDER BY date DESC LIMIT %d OFFSET %d', - $number, $page * $number); return $union; } /** * return the SQL SELECT query as a string, - * + * * @uses TimelineProfiderInterface::fetchQuery use the fetchQuery function * @param \Chill\MainBundle\Timeline\TimelineProviderInterface $provider * @param string $context @@ -164,11 +189,11 @@ class TimelineBuilder implements ContainerAwareInterface /** * run the UNION query and return result as an array - * + * * @param string $query * @return array */ - private function runQuery($query) + private function runUnionQuery($query) { $resultSetMapping = (new ResultSetMapping()) ->addScalarResult('id', 'id') @@ -180,14 +205,14 @@ class TimelineBuilder implements ContainerAwareInterface } /** - * + * * @param array $queriedIds * @param string $context * @return array with the form array($type => [$entity, $entity, $entity]) */ private function getEntities(array $queriedIds, $context) { - //gather entities by type to pass all id with same type to the TimelineProvider. + //gather entities by type to pass all id with same type to the TimelineProvider. $idsByType = array(); foreach($queriedIds as $result) { @@ -211,7 +236,7 @@ class TimelineBuilder implements ContainerAwareInterface /** * render the timeline as HTML - * + * * @param array $fetched * @param array $entitiesByType * @param string $context @@ -224,7 +249,7 @@ class TimelineBuilder implements ContainerAwareInterface $timelineEntries = array(); foreach ($fetched as $result) { $data = $this->getTemplateData( - $result['type'], + $result['type'], $entitiesByType[$result['type']][$result['id']], //the entity $context, $args); @@ -244,7 +269,7 @@ class TimelineBuilder implements ContainerAwareInterface /** * get the template data from the provider for the given entity, by type. - * + * * @param string $type * @param mixed $entity * @param string $context