From 85146e56763d216946ffc8475fc19de37592f63b Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2016 23:02:12 +0200 Subject: [PATCH 1/3] Added support to order short URL lists --- .../src/Repository/ShortUrlRepository.php | 37 +++++++++++++++---- module/Core/src/Service/ShortUrlService.php | 5 ++- .../src/Service/ShortUrlServiceInterface.php | 3 +- .../Rest/src/Action/ListShortcodesAction.php | 1 + 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/module/Core/src/Repository/ShortUrlRepository.php b/module/Core/src/Repository/ShortUrlRepository.php index a287fe0b..0d7677ea 100644 --- a/module/Core/src/Repository/ShortUrlRepository.php +++ b/module/Core/src/Repository/ShortUrlRepository.php @@ -20,21 +20,42 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI $qb = $this->createListQueryBuilder($searchTerm, $tags); $qb->select('s'); + // Set limit and offset if (isset($limit)) { $qb->setMaxResults($limit); } if (isset($offset)) { $qb->setFirstResult($offset); } + + // In case the ordering has been specified, the query could be more complex. Process it if (isset($orderBy)) { - if (is_string($orderBy)) { - $qb->orderBy($orderBy); - } elseif (is_array($orderBy)) { - $key = key($orderBy); - $qb->orderBy($key, $orderBy[$key]); - } - } else { - $qb->orderBy('s.dateCreated'); + return $this->processOrderByForList($qb, $orderBy); + } + + // With no order by, order by date and just return the list of ShortUrls + $qb->orderBy('s.dateCreated'); + return $qb->getQuery()->getResult(); + } + + protected function processOrderByForList(QueryBuilder $qb, $orderBy) + { + $fieldName = is_array($orderBy) ? key($orderBy) : $orderBy; + $order = is_array($orderBy) ? $orderBy[$fieldName] : 'ASC'; + + if ($fieldName === 'visits') { + $qb->addSelect('COUNT(v) AS totalVisits') + ->leftJoin('s.visits', 'v') + ->groupBy('s') + ->orderBy('totalVisits', $order); + + return array_column($qb->getQuery()->getResult(), 0); + } elseif (in_array($fieldName, [ + 'originalUrl', + 'shortCode', + 'dateCreated', + ])) { + $qb->orderBy('s.' . $fieldName, $order); } return $qb->getQuery()->getResult(); diff --git a/module/Core/src/Service/ShortUrlService.php b/module/Core/src/Service/ShortUrlService.php index faf778b1..59c76565 100644 --- a/module/Core/src/Service/ShortUrlService.php +++ b/module/Core/src/Service/ShortUrlService.php @@ -34,13 +34,14 @@ class ShortUrlService implements ShortUrlServiceInterface * @param int $page * @param string $searchQuery * @param array $tags + * @param null $orderBy * @return ShortUrl[]|Paginator */ - public function listShortUrls($page = 1, $searchQuery = null, array $tags = []) + public function listShortUrls($page = 1, $searchQuery = null, array $tags = [], $orderBy = null) { /** @var ShortUrlRepository $repo */ $repo = $this->em->getRepository(ShortUrl::class); - $paginator = new Paginator(new PaginableRepositoryAdapter($repo, $searchQuery, $tags)); + $paginator = new Paginator(new PaginableRepositoryAdapter($repo, $searchQuery, $tags, $orderBy)); $paginator->setItemCountPerPage(PaginableRepositoryAdapter::ITEMS_PER_PAGE) ->setCurrentPageNumber($page); diff --git a/module/Core/src/Service/ShortUrlServiceInterface.php b/module/Core/src/Service/ShortUrlServiceInterface.php index cdf9a45f..bc9b8daf 100644 --- a/module/Core/src/Service/ShortUrlServiceInterface.php +++ b/module/Core/src/Service/ShortUrlServiceInterface.php @@ -11,9 +11,10 @@ interface ShortUrlServiceInterface * @param int $page * @param string $searchQuery * @param array $tags + * @param null $orderBy * @return ShortUrl[]|Paginator */ - public function listShortUrls($page = 1, $searchQuery = null, array $tags = []); + public function listShortUrls($page = 1, $searchQuery = null, array $tags = [], $orderBy = null); /** * @param string $shortCode diff --git a/module/Rest/src/Action/ListShortcodesAction.php b/module/Rest/src/Action/ListShortcodesAction.php index dd08c071..aa987c2f 100644 --- a/module/Rest/src/Action/ListShortcodesAction.php +++ b/module/Rest/src/Action/ListShortcodesAction.php @@ -75,6 +75,7 @@ class ListShortcodesAction extends AbstractRestAction isset($query['page']) ? $query['page'] : 1, isset($query['searchTerm']) ? $query['searchTerm'] : null, isset($query['tags']) ? $query['tags'] : [], + isset($query['orderBy']) ? $query['orderBy'] : null, ]; } } From 9ac48bfbc5dfb883d4bfa8842c38e0ac1f9bbbdf Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2016 23:10:30 +0200 Subject: [PATCH 2/3] Added support for ordering in shortcode:list command --- .../Shortcode/ListShortcodesCommand.php | 22 ++++++++++++++++++- .../Shortcode/ListShortcodesCommandTest.php | 12 +++++----- .../src/Repository/ShortUrlRepository.php | 6 ++++- .../test/Action/ListShortcodesActionTest.php | 8 +++---- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/module/CLI/src/Command/Shortcode/ListShortcodesCommand.php b/module/CLI/src/Command/Shortcode/ListShortcodesCommand.php index bfe2ae6f..a8594db7 100644 --- a/module/CLI/src/Command/Shortcode/ListShortcodesCommand.php +++ b/module/CLI/src/Command/Shortcode/ListShortcodesCommand.php @@ -70,6 +70,14 @@ class ListShortcodesCommand extends Command InputOption::VALUE_OPTIONAL, $this->translator->translate('A comma-separated list of tags to filter results') ) + ->addOption( + 'orderBy', + 'o', + InputOption::VALUE_OPTIONAL, + $this->translator->translate( + 'The field from which we want to order by. Pass ASC or DESC separated by a comma' + ) + ) ->addOption( 'showTags', null, @@ -85,12 +93,13 @@ class ListShortcodesCommand extends Command $tags = $input->getOption('tags'); $tags = ! empty($tags) ? explode(',', $tags) : []; $showTags = $input->getOption('showTags'); + $orderBy = $input->getOption('orderBy'); /** @var QuestionHelper $helper */ $helper = $this->getHelper('question'); do { - $result = $this->shortUrlService->listShortUrls($page, $searchTerm, $tags); + $result = $this->shortUrlService->listShortUrls($page, $searchTerm, $tags, $this->processOrderBy($input)); $page++; $table = new Table($output); @@ -136,4 +145,15 @@ class ListShortcodesCommand extends Command } } while ($continue); } + + protected function processOrderBy(InputInterface $input) + { + $orderBy = $input->getOption('orderBy'); + if (empty($orderBy)) { + return null; + } + + $orderBy = explode(',', $orderBy); + return count($orderBy) === 1 ? $orderBy[0] : [$orderBy[0] => $orderBy[1]]; + } } diff --git a/module/CLI/test/Command/Shortcode/ListShortcodesCommandTest.php b/module/CLI/test/Command/Shortcode/ListShortcodesCommandTest.php index 306cb87b..03ac0e0e 100644 --- a/module/CLI/test/Command/Shortcode/ListShortcodesCommandTest.php +++ b/module/CLI/test/Command/Shortcode/ListShortcodesCommandTest.php @@ -46,8 +46,8 @@ class ListShortcodesCommandTest extends TestCase public function noInputCallsListJustOnce() { $this->questionHelper->setInputStream($this->getInputStream('\n')); - $this->shortUrlService->listShortUrls(1, null, [])->willReturn(new Paginator(new ArrayAdapter())) - ->shouldBeCalledTimes(1); + $this->shortUrlService->listShortUrls(1, null, [], null)->willReturn(new Paginator(new ArrayAdapter())) + ->shouldBeCalledTimes(1); $this->commandTester->execute(['command' => 'shortcode:list']); } @@ -103,8 +103,8 @@ class ListShortcodesCommandTest extends TestCase { $page = 5; $this->questionHelper->setInputStream($this->getInputStream('\n')); - $this->shortUrlService->listShortUrls($page, null, [])->willReturn(new Paginator(new ArrayAdapter())) - ->shouldBeCalledTimes(1); + $this->shortUrlService->listShortUrls($page, null, [], null)->willReturn(new Paginator(new ArrayAdapter())) + ->shouldBeCalledTimes(1); $this->commandTester->execute([ 'command' => 'shortcode:list', @@ -118,8 +118,8 @@ class ListShortcodesCommandTest extends TestCase public function ifTagsFlagIsProvidedTagsColumnIsIncluded() { $this->questionHelper->setInputStream($this->getInputStream('\n')); - $this->shortUrlService->listShortUrls(1, null, [])->willReturn(new Paginator(new ArrayAdapter())) - ->shouldBeCalledTimes(1); + $this->shortUrlService->listShortUrls(1, null, [], null)->willReturn(new Paginator(new ArrayAdapter())) + ->shouldBeCalledTimes(1); $this->commandTester->execute([ 'command' => 'shortcode:list', diff --git a/module/Core/src/Repository/ShortUrlRepository.php b/module/Core/src/Repository/ShortUrlRepository.php index 0d7677ea..256cb985 100644 --- a/module/Core/src/Repository/ShortUrlRepository.php +++ b/module/Core/src/Repository/ShortUrlRepository.php @@ -43,7 +43,11 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI $fieldName = is_array($orderBy) ? key($orderBy) : $orderBy; $order = is_array($orderBy) ? $orderBy[$fieldName] : 'ASC'; - if ($fieldName === 'visits') { + if (in_array($fieldName, [ + 'visits', + 'visitsCount', + 'visitCount', + ])) { $qb->addSelect('COUNT(v) AS totalVisits') ->leftJoin('s.visits', 'v') ->groupBy('s') diff --git a/module/Rest/test/Action/ListShortcodesActionTest.php b/module/Rest/test/Action/ListShortcodesActionTest.php index df15bb0c..d17740be 100644 --- a/module/Rest/test/Action/ListShortcodesActionTest.php +++ b/module/Rest/test/Action/ListShortcodesActionTest.php @@ -34,8 +34,8 @@ class ListShortcodesActionTest extends TestCase public function properListReturnsSuccessResponse() { $page = 3; - $this->service->listShortUrls($page, null, [])->willReturn(new Paginator(new ArrayAdapter())) - ->shouldBeCalledTimes(1); + $this->service->listShortUrls($page, null, [], null)->willReturn(new Paginator(new ArrayAdapter())) + ->shouldBeCalledTimes(1); $response = $this->action->__invoke( ServerRequestFactory::fromGlobals()->withQueryParams([ @@ -52,8 +52,8 @@ class ListShortcodesActionTest extends TestCase public function anExceptionsReturnsErrorResponse() { $page = 3; - $this->service->listShortUrls($page, null, [])->willThrow(\Exception::class) - ->shouldBeCalledTimes(1); + $this->service->listShortUrls($page, null, [], null)->willThrow(\Exception::class) + ->shouldBeCalledTimes(1); $response = $this->action->__invoke( ServerRequestFactory::fromGlobals()->withQueryParams([ From 0b9753582d5fc7f660922089f393dfaa5f52eb7a Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 22 Oct 2016 23:13:54 +0200 Subject: [PATCH 3/3] Documented how to order results --- docs/swagger.json | 13 +++++++++++++ module/CLI/lang/es.mo | Bin 6541 -> 6739 bytes module/CLI/lang/es.po | 10 ++++++++-- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/swagger.json b/docs/swagger.json index 262b0cfc..b3b7a5c5 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -90,6 +90,19 @@ } } }, + { + "name": "orderBy", + "in": "query", + "description": "The field from which you want to order the result. (Since v1.3.0)", + "enum": [ + "originalUrl", + "shortCode", + "dateCreated", + "visits" + ], + "required": false, + "type": "string" + }, { "$ref": "#/parameters/Authorization" } diff --git a/module/CLI/lang/es.mo b/module/CLI/lang/es.mo index 06f0a3db516154e7b2427015304e07c32a01489a..3365694abb1e2e8bd51fda869dbb18fc3c7e86c0 100644 GIT binary patch delta 1600 zcmYk+OKeP09LMp0tEKv4s%7eV+Il}qr=Ioh%Oe^ZB1nX*bETs5Fl`kfvmv^X2q-EC?GR5|OZwU?uUkknsK8TjFHS{hXf8z5jFn|8wSC{jwMPjM$kMw+!^CpP1C)bqX$8yOYeUyJLRZ^b6F zto1V}W#S2z;VV?^k64UfupF(xj96BIV{j5q!g;7b8!&=#)boyFKc2y?D>F-%c}7Dlg(}e1?4N7Z>ie0-`A+ z*oY0NU4067;bm;YZ@3hjD#MzeK)uif)ckc+;CHA@W`8nJii#+_!x-kEnpUhYD>G`# zLV^qo4}|D5v&@-u7?HWw3^>k}MydtilyK|7tq-`cYAfh1e=o>eX}{ z)pUOUHth$cR%dw$eGz>=U2S@ZEkH^iw0f>8;S9QdxXi6OlpJcy-#coP-xYTUGpU5@ z>paxa>H2)v=OuewPs*h-aX;e@9ItiTyzXwdetQ#BuDJ!f{j`_ydi*&5cAo1un8a*+iTSvUrT76^!`877H*hy*ZAmXwg#o_DQ1hL^TR4oB_!Up1Z)33&_XbbKx2LjEcw_R^tzB#u9d+)Fd#0!*~Gaa67J`LjJ|O z{|7Z+9lK*_5p*L@KVLfi3PFg{}*t11Y z)!dEya21E}FKVNMtg7`MB73!GrNmzgFL6WF`UVxkbyTRzn7tiqP$3&Z<$N3`a1s^z zZ>SA4kPjZU7{+k`mD*|K1+cfs;q4<1;F`lA%%ForY2vG>iKb9RcLx>9`*<8*psLx& zG0tNx9>MF#Pxchu*Ur12LM<2yrX$;oicBwt(3xP6!JwL6L)U4kFWXCRr1P^KqpPxY zGDShfsQ(K!=69P8p++FwraEZy#``^mURiC_YmS@87~hoF7P_LK6X;dc&ULydT~Sa| z-qaKwo#ZfGDN|Fi{%;`$ncf$IY)9!Ta!STJ=xS{$OBEXb4emRso2rLH8qkyq)d7X? z=1-wlYo^!IBXnxZTIp^}Uh!SdO;#5KGJN4kd$?sL>aTG}t;tdUY+kY_Sd{S(%RzNh diff --git a/module/CLI/lang/es.po b/module/CLI/lang/es.po index f2c806c7..dab5bdb7 100644 --- a/module/CLI/lang/es.po +++ b/module/CLI/lang/es.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: Shlink 1.0\n" -"POT-Creation-Date: 2016-10-22 13:14+0200\n" -"PO-Revision-Date: 2016-10-22 13:15+0200\n" +"POT-Creation-Date: 2016-10-22 23:12+0200\n" +"PO-Revision-Date: 2016-10-22 23:13+0200\n" "Last-Translator: Alejandro Celaya \n" "Language-Team: \n" "Language: es_ES\n" @@ -172,6 +172,12 @@ msgstr "" msgid "A comma-separated list of tags to filter results" msgstr "Una lista de etiquetas separadas por coma para filtrar el resultado" +msgid "" +"The field from which we want to order by. Pass ASC or DESC separated by a " +"comma" +msgstr "" +"El campo por el cual queremos ordernar. Pasa ASC o DESC separado por una coma" + msgid "Whether to display the tags or not" msgstr "Si se desea mostrar las etiquetas o no"