From 9b8c0a4560fa1d87cab1529099b1b4677e92e265 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Wed, 20 Jan 2021 14:45:59 +0100 Subject: Handle pagination through BookmarkService Handle all search results through SearchResult object. This is a required step toward implementing a BookmarkService based on SQL database. Related to #953 --- application/api/controllers/Links.php | 38 +++--- application/api/controllers/Tags.php | 8 +- application/bookmark/BookmarkFileService.php | 32 +++-- application/bookmark/BookmarkServiceInterface.php | 8 +- application/bookmark/SearchResult.php | 136 +++++++++++++++++++++ application/feed/FeedBuilder.php | 25 ++-- .../front/controller/admin/ManageTagController.php | 11 +- .../controller/admin/ThumbnailsController.php | 2 +- .../controller/visitor/BookmarkListController.php | 63 ++++------ .../front/controller/visitor/DailyController.php | 2 +- .../controller/visitor/PictureWallController.php | 12 +- application/netscape/NetscapeBookmarkUtils.php | 2 +- application/updater/Updater.php | 2 +- tests/bookmark/BookmarkFileServiceTest.php | 8 +- tests/bookmark/SearchResultTest.php | 125 +++++++++++++++++++ .../controller/admin/ManageTagControllerTest.php | 9 +- .../controller/admin/ThumbnailsControllerTest.php | 5 +- .../visitor/BookmarkListControllerTest.php | 29 +++-- .../controller/visitor/DailyControllerTest.php | 45 ++++--- .../visitor/PictureWallControllerTest.php | 7 +- 20 files changed, 420 insertions(+), 149 deletions(-) create mode 100644 application/bookmark/SearchResult.php create mode 100644 tests/bookmark/SearchResultTest.php diff --git a/application/api/controllers/Links.php b/application/api/controllers/Links.php index b83b2260..fe4bdc9f 100644 --- a/application/api/controllers/Links.php +++ b/application/api/controllers/Links.php @@ -36,13 +36,6 @@ class Links extends ApiController public function getLinks($request, $response) { $private = $request->getParam('visibility'); - $bookmarks = $this->bookmarkService->search( - [ - 'searchtags' => $request->getParam('searchtags', ''), - 'searchterm' => $request->getParam('searchterm', ''), - ], - $private - ); // Return bookmarks from the {offset}th link, starting from 0. $offset = $request->getParam('offset'); @@ -50,9 +43,6 @@ class Links extends ApiController throw new ApiBadParametersException('Invalid offset'); } $offset = ! empty($offset) ? intval($offset) : 0; - if ($offset > count($bookmarks)) { - return $response->withJson([], 200, $this->jsonStyle); - } // limit parameter is either a number of bookmarks or 'all' for everything. $limit = $request->getParam('limit'); @@ -61,23 +51,33 @@ class Links extends ApiController } elseif (ctype_digit($limit)) { $limit = intval($limit); } elseif ($limit === 'all') { - $limit = count($bookmarks); + $limit = null; } else { throw new ApiBadParametersException('Invalid limit'); } + $searchResult = $this->bookmarkService->search( + [ + 'searchtags' => $request->getParam('searchtags', ''), + 'searchterm' => $request->getParam('searchterm', ''), + ], + $private, + false, + false, + false, + [ + 'limit' => $limit, + 'offset' => $offset, + 'allowOutOfBounds' => true, + ] + ); + // 'environment' is set by Slim and encapsulate $_SERVER. $indexUrl = index_url($this->ci['environment']); $out = []; - $index = 0; - foreach ($bookmarks as $bookmark) { - if (count($out) >= $limit) { - break; - } - if ($index++ >= $offset) { - $out[] = ApiUtils::formatLink($bookmark, $indexUrl); - } + foreach ($searchResult->getBookmarks() as $bookmark) { + $out[] = ApiUtils::formatLink($bookmark, $indexUrl); } return $response->withJson($out, 200, $this->jsonStyle); diff --git a/application/api/controllers/Tags.php b/application/api/controllers/Tags.php index e60e00a7..5a23f6db 100644 --- a/application/api/controllers/Tags.php +++ b/application/api/controllers/Tags.php @@ -122,12 +122,12 @@ class Tags extends ApiController throw new ApiBadParametersException('New tag name is required in the request body'); } - $bookmarks = $this->bookmarkService->search( + $searchResult = $this->bookmarkService->search( ['searchtags' => $args['tagName']], BookmarkFilter::$ALL, true ); - foreach ($bookmarks as $bookmark) { + foreach ($searchResult->getBookmarks() as $bookmark) { $bookmark->renameTag($args['tagName'], $data['name']); $this->bookmarkService->set($bookmark, false); $this->history->updateLink($bookmark); @@ -157,12 +157,12 @@ class Tags extends ApiController throw new ApiTagNotFoundException(); } - $bookmarks = $this->bookmarkService->search( + $searchResult = $this->bookmarkService->search( ['searchtags' => $args['tagName']], BookmarkFilter::$ALL, true ); - foreach ($bookmarks as $bookmark) { + foreach ($searchResult->getBookmarks() as $bookmark) { $bookmark->deleteTag($args['tagName']); $this->bookmarkService->set($bookmark, false); $this->history->updateLink($bookmark); diff --git a/application/bookmark/BookmarkFileService.php b/application/bookmark/BookmarkFileService.php index 6666a251..8ea37427 100644 --- a/application/bookmark/BookmarkFileService.php +++ b/application/bookmark/BookmarkFileService.php @@ -55,8 +55,12 @@ class BookmarkFileService implements BookmarkServiceInterface /** * @inheritDoc */ - public function __construct(ConfigManager $conf, History $history, Mutex $mutex, bool $isLoggedIn) - { + public function __construct( + ConfigManager $conf, + History $history, + Mutex $mutex, + bool $isLoggedIn + ) { $this->conf = $conf; $this->history = $history; $this->mutex = $mutex; @@ -129,8 +133,9 @@ class BookmarkFileService implements BookmarkServiceInterface string $visibility = null, bool $caseSensitive = false, bool $untaggedOnly = false, - bool $ignoreSticky = false - ) { + bool $ignoreSticky = false, + array $pagination = [] + ): SearchResult { if ($visibility === null) { $visibility = $this->isLoggedIn ? BookmarkFilter::$ALL : BookmarkFilter::$PUBLIC; } @@ -143,13 +148,20 @@ class BookmarkFileService implements BookmarkServiceInterface $this->bookmarks->reorder('DESC', true); } - return $this->bookmarkFilter->filter( + $bookmarks = $this->bookmarkFilter->filter( BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT, [$searchTags, $searchTerm], $caseSensitive, $visibility, $untaggedOnly ); + + return SearchResult::getSearchResult( + $bookmarks, + $pagination['offset'] ?? 0, + $pagination['limit'] ?? null, + $pagination['allowOutOfBounds'] ?? false + ); } /** @@ -282,7 +294,7 @@ class BookmarkFileService implements BookmarkServiceInterface */ public function count(string $visibility = null): int { - return count($this->search([], $visibility)); + return $this->search([], $visibility)->getResultCount(); } /** @@ -305,10 +317,10 @@ class BookmarkFileService implements BookmarkServiceInterface */ public function bookmarksCountPerTag(array $filteringTags = [], string $visibility = null): array { - $bookmarks = $this->search(['searchtags' => $filteringTags], $visibility); + $searchResult = $this->search(['searchtags' => $filteringTags], $visibility); $tags = []; $caseMapping = []; - foreach ($bookmarks as $bookmark) { + foreach ($searchResult->getBookmarks() as $bookmark) { foreach ($bookmark->getTags() as $tag) { if ( empty($tag) @@ -357,7 +369,7 @@ class BookmarkFileService implements BookmarkServiceInterface $previous = null; $next = null; - foreach ($this->search([], null, false, false, true) as $bookmark) { + foreach ($this->search([], null, false, false, true)->getBookmarks() as $bookmark) { if ($to < $bookmark->getCreated()) { $next = $bookmark->getCreated(); } elseif ($from < $bookmark->getCreated() && $to > $bookmark->getCreated()) { @@ -378,7 +390,7 @@ class BookmarkFileService implements BookmarkServiceInterface */ public function getLatest(): ?Bookmark { - foreach ($this->search([], null, false, false, true) as $bookmark) { + foreach ($this->search([], null, false, false, true)->getBookmarks() as $bookmark) { return $bookmark; } diff --git a/application/bookmark/BookmarkServiceInterface.php b/application/bookmark/BookmarkServiceInterface.php index 08cdbb4e..4b1f0daa 100644 --- a/application/bookmark/BookmarkServiceInterface.php +++ b/application/bookmark/BookmarkServiceInterface.php @@ -44,16 +44,18 @@ interface BookmarkServiceInterface * @param bool $caseSensitive * @param bool $untaggedOnly * @param bool $ignoreSticky + * @param array $pagination This array can contain the following keys for pagination: limit, offset. * - * @return Bookmark[] + * @return SearchResult */ public function search( array $request = [], string $visibility = null, bool $caseSensitive = false, bool $untaggedOnly = false, - bool $ignoreSticky = false - ); + bool $ignoreSticky = false, + array $pagination = [] + ): SearchResult; /** * Get a single bookmark by its ID. diff --git a/application/bookmark/SearchResult.php b/application/bookmark/SearchResult.php new file mode 100644 index 00000000..c0bce311 --- /dev/null +++ b/application/bookmark/SearchResult.php @@ -0,0 +1,136 @@ +bookmarks = $bookmarks; + $this->resultCount = count($bookmarks); + $this->totalCount = $totalCount; + $this->limit = $limit; + $this->offset = $offset; + } + + /** + * Build a SearchResult from provided full result set and pagination settings. + * + * @param Bookmark[] $bookmarks Full set of result which will be filtered + * @param int $offset Start recording results from $offset + * @param int|null $limit End recording results after $limit bookmarks is reached + * @param bool $allowOutOfBounds Set to false to display the last page if the offset is out of bound, + * return empty result set otherwise (default: false) + * + * @return SearchResult + */ + public static function getSearchResult( + $bookmarks, + int $offset = 0, + ?int $limit = null, + bool $allowOutOfBounds = false + ): self { + $totalCount = count($bookmarks); + if (!$allowOutOfBounds && $offset > $totalCount) { + $offset = $limit === null ? 0 : $limit * -1; + } + + if ($bookmarks instanceof BookmarkArray) { + $buffer = []; + foreach ($bookmarks as $key => $value) { + $buffer[$key] = $value; + } + $bookmarks = $buffer; + } + + return new static( + array_slice($bookmarks, $offset, $limit, true), + $totalCount, + $offset, + $limit + ); + } + + /** @return Bookmark[] List of result bookmarks with pagination applied */ + public function getBookmarks(): array + { + return $this->bookmarks; + } + + /** @return int number of Bookmarks found, with pagination applied */ + public function getResultCount(): int + { + return $this->resultCount; + } + + /** @return int total number of result found */ + public function getTotalCount(): int + { + return $this->totalCount; + } + + /** @return int pagination: limit number of result bookmarks */ + public function getLimit(): ?int + { + return $this->limit; + } + + /** @return int pagination: offset to apply to complete result list */ + public function getOffset(): int + { + return $this->offset; + } + + /** @return int Current page of result set in complete results */ + public function getPage(): int + { + if (empty($this->limit)) { + return $this->offset === 0 ? 1 : 2; + } + $base = $this->offset >= 0 ? $this->offset : $this->totalCount + $this->offset; + + return (int) ceil($base / $this->limit) + 1; + } + + /** @return int Get the # of the last page */ + public function getLastPage(): int + { + if (empty($this->limit)) { + return $this->offset === 0 ? 1 : 2; + } + + return (int) ceil($this->totalCount / $this->limit); + } + + /** @return bool Either the current page is the last one or not */ + public function isLastPage(): bool + { + return $this->getPage() === $this->getLastPage(); + } + + /** @return bool Either the current page is the first one or not */ + public function isFirstPage(): bool + { + return $this->offset === 0; + } +} diff --git a/application/feed/FeedBuilder.php b/application/feed/FeedBuilder.php index ed62af26..d5d74fd1 100644 --- a/application/feed/FeedBuilder.php +++ b/application/feed/FeedBuilder.php @@ -102,22 +102,16 @@ class FeedBuilder $userInput['searchtags'] = false; } - // Optionally filter the results: - $linksToDisplay = $this->linkDB->search($userInput ?? [], null, false, false, true); - - $nblinksToDisplay = $this->getNbLinks(count($linksToDisplay), $userInput); + $limit = $this->getLimit($userInput); - // Can't use array_keys() because $link is a LinkDB instance and not a real array. - $keys = []; - foreach ($linksToDisplay as $key => $value) { - $keys[] = $key; - } + // Optionally filter the results: + $searchResult = $this->linkDB->search($userInput ?? [], null, false, false, true, ['limit' => $limit]); $pageaddr = escape(index_url($this->serverInfo)); $this->formatter->addContextData('index_url', $pageaddr); - $linkDisplayed = []; - for ($i = 0; $i < $nblinksToDisplay && $i < count($keys); $i++) { - $linkDisplayed[$keys[$i]] = $this->buildItem($feedType, $linksToDisplay[$keys[$i]], $pageaddr); + $links = []; + foreach ($searchResult->getBookmarks() as $key => $bookmark) { + $links[$key] = $this->buildItem($feedType, $bookmark, $pageaddr); } $data['language'] = $this->getTypeLanguage($feedType); @@ -128,7 +122,7 @@ class FeedBuilder $data['self_link'] = $pageaddr . $requestUri; $data['index_url'] = $pageaddr; $data['usepermalinks'] = $this->usePermalinks === true; - $data['links'] = $linkDisplayed; + $data['links'] = $links; return $data; } @@ -268,19 +262,18 @@ class FeedBuilder * If 'nb' not set or invalid, default value: $DEFAULT_NB_LINKS. * If 'nb' is set to 'all', display all filtered bookmarks (max parameter). * - * @param int $max maximum number of bookmarks to display. * @param array $userInput $_GET. * * @return int number of bookmarks to display. */ - protected function getNbLinks($max, ?array $userInput) + protected function getLimit(?array $userInput) { if (empty($userInput['nb'])) { return self::$DEFAULT_NB_LINKS; } if ($userInput['nb'] == 'all') { - return $max; + return null; } $intNb = intval($userInput['nb']); diff --git a/application/front/controller/admin/ManageTagController.php b/application/front/controller/admin/ManageTagController.php index 8675a0c5..1333cce7 100644 --- a/application/front/controller/admin/ManageTagController.php +++ b/application/front/controller/admin/ManageTagController.php @@ -57,9 +57,12 @@ class ManageTagController extends ShaarliAdminController } // TODO: move this to bookmark service - $count = 0; - $bookmarks = $this->container->bookmarkService->search(['searchtags' => $fromTag], BookmarkFilter::$ALL, true); - foreach ($bookmarks as $bookmark) { + $searchResult = $this->container->bookmarkService->search( + ['searchtags' => $fromTag], + BookmarkFilter::$ALL, + true + ); + foreach ($searchResult->getBookmarks() as $bookmark) { if (false === $isDelete) { $bookmark->renameTag($fromTag, $toTag); } else { @@ -68,11 +71,11 @@ class ManageTagController extends ShaarliAdminController $this->container->bookmarkService->set($bookmark, false); $this->container->history->updateLink($bookmark); - $count++; } $this->container->bookmarkService->save(); + $count = $searchResult->getResultCount(); if (true === $isDelete) { $alert = sprintf( t('The tag was removed from %d bookmark.', 'The tag was removed from %d bookmarks.', $count), diff --git a/application/front/controller/admin/ThumbnailsController.php b/application/front/controller/admin/ThumbnailsController.php index 94d97d4b..5dfea096 100644 --- a/application/front/controller/admin/ThumbnailsController.php +++ b/application/front/controller/admin/ThumbnailsController.php @@ -22,7 +22,7 @@ class ThumbnailsController extends ShaarliAdminController public function index(Request $request, Response $response): Response { $ids = []; - foreach ($this->container->bookmarkService->search() as $bookmark) { + foreach ($this->container->bookmarkService->search()->getBookmarks() as $bookmark) { // A note or not HTTP(S) if ($bookmark->isNote() || !startsWith(strtolower($bookmark->getUrl()), 'http')) { continue; diff --git a/application/front/controller/visitor/BookmarkListController.php b/application/front/controller/visitor/BookmarkListController.php index fe8231be..321ca813 100644 --- a/application/front/controller/visitor/BookmarkListController.php +++ b/application/front/controller/visitor/BookmarkListController.php @@ -36,7 +36,6 @@ class BookmarkListController extends ShaarliVisitorController $searchTags = normalize_spaces($request->getParam('searchtags') ?? ''); $searchTerm = escape(normalize_spaces($request->getParam('searchterm') ?? '')); - ; // Filter bookmarks according search parameters. $visibility = $this->container->sessionManager->getSessionParameter('visibility'); @@ -44,39 +43,26 @@ class BookmarkListController extends ShaarliVisitorController 'searchtags' => $searchTags, 'searchterm' => $searchTerm, ]; - $linksToDisplay = $this->container->bookmarkService->search( - $search, - $visibility, - false, - !!$this->container->sessionManager->getSessionParameter('untaggedonly') - ) ?? []; - - // ---- Handle paging. - $keys = []; - foreach ($linksToDisplay as $key => $value) { - $keys[] = $key; - } - - $linksPerPage = $this->container->sessionManager->getSessionParameter('LINKS_PER_PAGE', 20) ?: 20; // Select articles according to paging. - $pageCount = (int) ceil(count($keys) / $linksPerPage) ?: 1; - $page = (int) $request->getParam('page') ?? 1; + $page = (int) ($request->getParam('page') ?? 1); $page = $page < 1 ? 1 : $page; - $page = $page > $pageCount ? $pageCount : $page; + $linksPerPage = $this->container->sessionManager->getSessionParameter('LINKS_PER_PAGE', 20) ?: 20; - // Start index. - $i = ($page - 1) * $linksPerPage; - $end = $i + $linksPerPage; + $searchResult = $this->container->bookmarkService->search( + $search, + $visibility, + false, + !!$this->container->sessionManager->getSessionParameter('untaggedonly'), + false, + ['offset' => $linksPerPage * ($page - 1), 'limit' => $linksPerPage] + ) ?? []; - $linkDisp = []; $save = false; - while ($i < $end && $i < count($keys)) { - $save = $this->updateThumbnail($linksToDisplay[$keys[$i]], false) || $save; - $link = $formatter->format($linksToDisplay[$keys[$i]]); - - $linkDisp[$keys[$i]] = $link; - $i++; + $links = []; + foreach ($searchResult->getBookmarks() as $key => $bookmark) { + $save = $this->updateThumbnail($bookmark, false) || $save; + $links[$key] = $formatter->format($bookmark); } if ($save) { @@ -86,15 +72,10 @@ class BookmarkListController extends ShaarliVisitorController // Compute paging navigation $searchtagsUrl = $searchTags === '' ? '' : '&searchtags=' . urlencode($searchTags); $searchtermUrl = $searchTerm === '' ? '' : '&searchterm=' . urlencode($searchTerm); + $page = $searchResult->getPage(); - $previous_page_url = ''; - if ($i !== count($keys)) { - $previous_page_url = '?page=' . ($page + 1) . $searchtermUrl . $searchtagsUrl; - } - $next_page_url = ''; - if ($page > 1) { - $next_page_url = '?page=' . ($page - 1) . $searchtermUrl . $searchtagsUrl; - } + $previousPageUrl = !$searchResult->isLastPage() ? '?page=' . ($page + 1) . $searchtermUrl . $searchtagsUrl : ''; + $nextPageUrl = !$searchResult->isFirstPage() ? '?page=' . ($page - 1) . $searchtermUrl . $searchtagsUrl : ''; $tagsSeparator = $this->container->conf->get('general.tags_separator', ' '); $searchTagsUrlEncoded = array_map('urlencode', tags_str2array($searchTags, $tagsSeparator)); @@ -104,16 +85,16 @@ class BookmarkListController extends ShaarliVisitorController $data = array_merge( $this->initializeTemplateVars(), [ - 'previous_page_url' => $previous_page_url, - 'next_page_url' => $next_page_url, + 'previous_page_url' => $previousPageUrl, + 'next_page_url' => $nextPageUrl, 'page_current' => $page, - 'page_max' => $pageCount, - 'result_count' => count($linksToDisplay), + 'page_max' => $searchResult->getLastPage(), + 'result_count' => $searchResult->getTotalCount(), 'search_term' => escape($searchTerm), 'search_tags' => escape($searchTags), 'search_tags_url' => $searchTagsUrlEncoded, 'visibility' => $visibility, - 'links' => $linkDisp, + 'links' => $links, ] ); diff --git a/application/front/controller/visitor/DailyController.php b/application/front/controller/visitor/DailyController.php index 29492a5f..3739ec16 100644 --- a/application/front/controller/visitor/DailyController.php +++ b/application/front/controller/visitor/DailyController.php @@ -100,7 +100,7 @@ class DailyController extends ShaarliVisitorController $days = []; $format = DailyPageHelper::getFormatByType($type); $length = DailyPageHelper::getRssLengthByType($type); - foreach ($this->container->bookmarkService->search() as $bookmark) { + foreach ($this->container->bookmarkService->search()->getBookmarks() as $bookmark) { $day = $bookmark->getCreated()->format($format); // Stop iterating after DAILY_RSS_NB_DAYS entries diff --git a/application/front/controller/visitor/PictureWallController.php b/application/front/controller/visitor/PictureWallController.php index 23553ee6..9c8f07d7 100644 --- a/application/front/controller/visitor/PictureWallController.php +++ b/application/front/controller/visitor/PictureWallController.php @@ -30,19 +30,19 @@ class PictureWallController extends ShaarliVisitorController ); // Optionally filter the results: - $links = $this->container->bookmarkService->search($request->getQueryParams()); - $linksToDisplay = []; + $bookmarks = $this->container->bookmarkService->search($request->getQueryParams())->getBookmarks(); + $links = []; // Get only bookmarks which have a thumbnail. // Note: we do not retrieve thumbnails here, the request is too heavy. $formatter = $this->container->formatterFactory->getFormatter('raw'); - foreach ($links as $key => $link) { - if (!empty($link->getThumbnail())) { - $linksToDisplay[] = $formatter->format($link); + foreach ($bookmarks as $key => $bookmark) { + if (!empty($bookmark->getThumbnail())) { + $links[] = $formatter->format($bookmark); } } - $data = ['linksToDisplay' => $linksToDisplay]; + $data = ['linksToDisplay' => $links]; $this->executePageHooks('render_picwall', $data, TemplatePage::PICTURE_WALL); foreach ($data as $key => $value) { diff --git a/application/netscape/NetscapeBookmarkUtils.php b/application/netscape/NetscapeBookmarkUtils.php index 2d97b4c8..20715bd0 100644 --- a/application/netscape/NetscapeBookmarkUtils.php +++ b/application/netscape/NetscapeBookmarkUtils.php @@ -64,7 +64,7 @@ class NetscapeBookmarkUtils } $bookmarkLinks = []; - foreach ($this->bookmarkService->search([], $selection) as $bookmark) { + foreach ($this->bookmarkService->search([], $selection)->getBookmarks() as $bookmark) { $link = $formatter->format($bookmark); $link['taglist'] = implode(',', $bookmark->getTags()); if ($bookmark->isNote() && $prependNoteUrl) { diff --git a/application/updater/Updater.php b/application/updater/Updater.php index 4f557d0f..11b6c051 100644 --- a/application/updater/Updater.php +++ b/application/updater/Updater.php @@ -152,7 +152,7 @@ class Updater { $updated = false; - foreach ($this->bookmarkService->search() as $bookmark) { + foreach ($this->bookmarkService->search()->getBookmarks() as $bookmark) { if ( $bookmark->isNote() && startsWith($bookmark->getUrl(), '?') diff --git a/tests/bookmark/BookmarkFileServiceTest.php b/tests/bookmark/BookmarkFileServiceTest.php index f619aff3..d1af3fb0 100644 --- a/tests/bookmark/BookmarkFileServiceTest.php +++ b/tests/bookmark/BookmarkFileServiceTest.php @@ -807,7 +807,7 @@ class BookmarkFileServiceTest extends TestCase $request = ['searchtags' => $tags]; $this->assertEquals( 2, - count($this->privateLinkDB->search($request, null, true)) + count($this->privateLinkDB->search($request, null, true)->getBookmarks()) ); } @@ -820,7 +820,7 @@ class BookmarkFileServiceTest extends TestCase $request = ['searchtags' => $tags]; $this->assertEquals( 2, - count($this->privateLinkDB->search($request, null, true)) + count($this->privateLinkDB->search($request, null, true)->getBookmarks()) ); } @@ -834,12 +834,12 @@ class BookmarkFileServiceTest extends TestCase $request = ['searchtags' => $tags]; $this->assertEquals( 1, - count($this->privateLinkDB->search($request, 'all', true)) + count($this->privateLinkDB->search($request, 'all', true)->getBookmarks()) ); $this->assertEquals( 0, - count($this->publicLinkDB->search($request, 'public', true)) + count($this->publicLinkDB->search($request, 'public', true)->getBookmarks()) ); } diff --git a/tests/bookmark/SearchResultTest.php b/tests/bookmark/SearchResultTest.php new file mode 100644 index 00000000..12854c1f --- /dev/null +++ b/tests/bookmark/SearchResultTest.php @@ -0,0 +1,125 @@ +getBookmarks()); + static::assertSame(6, $searchResult->getResultCount()); + static::assertSame(6, $searchResult->getTotalCount()); + static::assertSame(null, $searchResult->getLimit()); + static::assertSame(0, $searchResult->getOffset()); + static::assertSame(1, $searchResult->getPage()); + static::assertSame(1, $searchResult->getLastPage()); + static::assertTrue($searchResult->isFirstPage()); + static::assertTrue($searchResult->isLastPage()); + } + + /** Create a SearchResult with only an offset parameter */ + public function testResultWithOffset(): void + { + $searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 2); + + static::assertSame([2 => 'c', 3 => 'd', 4 => 'e', 5 => 'f'], $searchResult->getBookmarks()); + static::assertSame(4, $searchResult->getResultCount()); + static::assertSame(6, $searchResult->getTotalCount()); + static::assertSame(null, $searchResult->getLimit()); + static::assertSame(2, $searchResult->getOffset()); + static::assertSame(2, $searchResult->getPage()); + static::assertSame(2, $searchResult->getLastPage()); + static::assertFalse($searchResult->isFirstPage()); + static::assertTrue($searchResult->isLastPage()); + } + + /** Create a SearchResult with only a limit parameter */ + public function testResultWithLimit(): void + { + $searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 0, 2); + + static::assertSame([0 => 'a', 1 => 'b'], $searchResult->getBookmarks()); + static::assertSame(2, $searchResult->getResultCount()); + static::assertSame(6, $searchResult->getTotalCount()); + static::assertSame(2, $searchResult->getLimit()); + static::assertSame(0, $searchResult->getOffset()); + static::assertSame(1, $searchResult->getPage()); + static::assertSame(3, $searchResult->getLastPage()); + static::assertTrue($searchResult->isFirstPage()); + static::assertFalse($searchResult->isLastPage()); + } + + /** Create a SearchResult with offset and limit parameters */ + public function testResultWithLimitAndOffset(): void + { + $searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 2, 2); + + static::assertSame([2 => 'c', 3 => 'd'], $searchResult->getBookmarks()); + static::assertSame(2, $searchResult->getResultCount()); + static::assertSame(6, $searchResult->getTotalCount()); + static::assertSame(2, $searchResult->getLimit()); + static::assertSame(2, $searchResult->getOffset()); + static::assertSame(2, $searchResult->getPage()); + static::assertSame(3, $searchResult->getLastPage()); + static::assertFalse($searchResult->isFirstPage()); + static::assertFalse($searchResult->isLastPage()); + } + + /** Create a SearchResult with offset and limit parameters displaying the last page */ + public function testResultWithLimitAndOffsetLastPage(): void + { + $searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 4, 2); + + static::assertSame([4 => 'e', 5 => 'f'], $searchResult->getBookmarks()); + static::assertSame(2, $searchResult->getResultCount()); + static::assertSame(6, $searchResult->getTotalCount()); + static::assertSame(2, $searchResult->getLimit()); + static::assertSame(4, $searchResult->getOffset()); + static::assertSame(3, $searchResult->getPage()); + static::assertSame(3, $searchResult->getLastPage()); + static::assertFalse($searchResult->isFirstPage()); + static::assertTrue($searchResult->isLastPage()); + } + + /** Create a SearchResult with offset and limit parameters out of bound (display the last page) */ + public function testResultWithLimitAndOffsetOutOfBounds(): void + { + $searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 12, 2); + + static::assertSame([4 => 'e', 5 => 'f'], $searchResult->getBookmarks()); + static::assertSame(2, $searchResult->getResultCount()); + static::assertSame(6, $searchResult->getTotalCount()); + static::assertSame(2, $searchResult->getLimit()); + static::assertSame(-2, $searchResult->getOffset()); + static::assertSame(3, $searchResult->getPage()); + static::assertSame(3, $searchResult->getLastPage()); + static::assertFalse($searchResult->isFirstPage()); + static::assertTrue($searchResult->isLastPage()); + } + + /** Create a SearchResult with offset and limit parameters out of bound (no result) */ + public function testResultWithLimitAndOffsetOutOfBoundsNoResult(): void + { + $searchResult = SearchResult::getSearchResult(['a', 'b', 'c', 'd', 'e', 'f'], 12, 2, true); + + static::assertSame([], $searchResult->getBookmarks()); + static::assertSame(0, $searchResult->getResultCount()); + static::assertSame(6, $searchResult->getTotalCount()); + static::assertSame(2, $searchResult->getLimit()); + static::assertSame(12, $searchResult->getOffset()); + static::assertSame(7, $searchResult->getPage()); + static::assertSame(3, $searchResult->getLastPage()); + static::assertFalse($searchResult->isFirstPage()); + static::assertFalse($searchResult->isLastPage()); + } +} diff --git a/tests/front/controller/admin/ManageTagControllerTest.php b/tests/front/controller/admin/ManageTagControllerTest.php index af6f273f..56a64cbb 100644 --- a/tests/front/controller/admin/ManageTagControllerTest.php +++ b/tests/front/controller/admin/ManageTagControllerTest.php @@ -6,6 +6,7 @@ namespace Shaarli\Front\Controller\Admin; use Shaarli\Bookmark\Bookmark; use Shaarli\Bookmark\BookmarkFilter; +use Shaarli\Bookmark\SearchResult; use Shaarli\Config\ConfigManager; use Shaarli\Front\Exception\WrongTokenException; use Shaarli\Security\SessionManager; @@ -100,11 +101,11 @@ class ManageTagControllerTest extends TestCase ->expects(static::once()) ->method('search') ->with(['searchtags' => 'old-tag'], BookmarkFilter::$ALL, true) - ->willReturnCallback(function () use ($bookmark1, $bookmark2): array { + ->willReturnCallback(function () use ($bookmark1, $bookmark2): SearchResult { $bookmark1->expects(static::once())->method('renameTag')->with('old-tag', 'new-tag'); $bookmark2->expects(static::once())->method('renameTag')->with('old-tag', 'new-tag'); - return [$bookmark1, $bookmark2]; + return SearchResult::getSearchResult([$bookmark1, $bookmark2]); }) ; $this->container->bookmarkService @@ -153,11 +154,11 @@ class ManageTagControllerTest extends TestCase ->expects(static::once()) ->method('search') ->with(['searchtags' => 'old-tag'], BookmarkFilter::$ALL, true) - ->willReturnCallback(function () use ($bookmark1, $bookmark2): array { + ->willReturnCallback(function () use ($bookmark1, $bookmark2): SearchResult { $bookmark1->expects(static::once())->method('deleteTag')->with('old-tag'); $bookmark2->expects(static::once())->method('deleteTag')->with('old-tag'); - return [$bookmark1, $bookmark2]; + return SearchResult::getSearchResult([$bookmark1, $bookmark2]); }) ; $this->container->bookmarkService diff --git a/tests/front/controller/admin/ThumbnailsControllerTest.php b/tests/front/controller/admin/ThumbnailsControllerTest.php index e5749654..0c9b63c3 100644 --- a/tests/front/controller/admin/ThumbnailsControllerTest.php +++ b/tests/front/controller/admin/ThumbnailsControllerTest.php @@ -6,6 +6,7 @@ namespace Shaarli\Front\Controller\Admin; use Shaarli\Bookmark\Bookmark; use Shaarli\Bookmark\Exception\BookmarkNotFoundException; +use Shaarli\Bookmark\SearchResult; use Shaarli\TestCase; use Shaarli\Thumbnailer; use Slim\Http\Request; @@ -40,12 +41,12 @@ class ThumbnailsControllerTest extends TestCase $this->container->bookmarkService ->expects(static::once()) ->method('search') - ->willReturn([ + ->willReturn(SearchResult::getSearchResult([ (new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'), (new Bookmark())->setId(2)->setUrl('?abcdef')->setTitle('Note 1'), (new Bookmark())->setId(3)->setUrl('http://url2.tld')->setTitle('Title 2'), (new Bookmark())->setId(4)->setUrl('ftp://domain.tld', ['ftp'])->setTitle('FTP'), - ]) + ])) ; $result = $this->controller->index($request, $response); diff --git a/tests/front/controller/visitor/BookmarkListControllerTest.php b/tests/front/controller/visitor/BookmarkListControllerTest.php index dec938f2..0fbab9d4 100644 --- a/tests/front/controller/visitor/BookmarkListControllerTest.php +++ b/tests/front/controller/visitor/BookmarkListControllerTest.php @@ -6,6 +6,7 @@ namespace Shaarli\Front\Controller\Visitor; use Shaarli\Bookmark\Bookmark; use Shaarli\Bookmark\Exception\BookmarkNotFoundException; +use Shaarli\Bookmark\SearchResult; use Shaarli\Config\ConfigManager; use Shaarli\Security\LoginManager; use Shaarli\TestCase; @@ -45,13 +46,15 @@ class BookmarkListControllerTest extends TestCase ['searchtags' => '', 'searchterm' => ''], null, false, - false + false, + false, + ['offset' => 0, 'limit' => 2] ) - ->willReturn([ + ->willReturn(SearchResult::getSearchResult([ (new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'), (new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'), (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'), - ] + ], 0, 2) ); $this->container->sessionManager @@ -119,13 +122,15 @@ class BookmarkListControllerTest extends TestCase ['searchtags' => '', 'searchterm' => ''], null, false, - false + false, + false, + ['offset' => 2, 'limit' => 2] ) - ->willReturn([ + ->willReturn(SearchResult::getSearchResult([ (new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'), (new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'), (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'), - ]) + ], 2, 2)) ; $this->container->sessionManager @@ -207,13 +212,15 @@ class BookmarkListControllerTest extends TestCase ['searchtags' => 'abc@def', 'searchterm' => 'ghi jkl'], 'private', false, - true + true, + false, + ['offset' => 0, 'limit' => 2] ) - ->willReturn([ + ->willReturn(SearchResult::getSearchResult([ (new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'), (new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'), (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'), - ]) + ], 0, 2)) ; $result = $this->controller->index($request, $response); @@ -358,13 +365,13 @@ class BookmarkListControllerTest extends TestCase $this->container->bookmarkService ->expects(static::once()) ->method('search') - ->willReturn([ + ->willReturn(SearchResult::getSearchResult([ (new Bookmark())->setId(1)->setUrl('https://url1.tld')->setTitle('Title 1')->setThumbnail(false), $b1 = (new Bookmark())->setId(2)->setUrl('https://url2.tld')->setTitle('Title 2'), (new Bookmark())->setId(3)->setUrl('https://url3.tld')->setTitle('Title 3')->setThumbnail(false), $b2 = (new Bookmark())->setId(2)->setUrl('https://url4.tld')->setTitle('Title 4'), (new Bookmark())->setId(2)->setUrl('ftp://url5.tld', ['ftp'])->setTitle('Title 5'), - ]) + ])) ; $this->container->bookmarkService ->expects(static::exactly(2)) diff --git a/tests/front/controller/visitor/DailyControllerTest.php b/tests/front/controller/visitor/DailyControllerTest.php index 70fbce54..821ba321 100644 --- a/tests/front/controller/visitor/DailyControllerTest.php +++ b/tests/front/controller/visitor/DailyControllerTest.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Shaarli\Front\Controller\Visitor; use Shaarli\Bookmark\Bookmark; +use Shaarli\Bookmark\SearchResult; use Shaarli\Feed\CachedPage; use Shaarli\TestCase; use Slim\Http\Request; @@ -347,13 +348,15 @@ class DailyControllerTest extends TestCase $request = $this->createMock(Request::class); $response = new Response(); - $this->container->bookmarkService->expects(static::once())->method('search')->willReturn([ - (new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'), - (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'), - (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'), - (new Bookmark())->setId(4)->setCreated($dates[2])->setUrl('http://domain.tld/4'), - (new Bookmark())->setId(5)->setCreated($dates[3])->setUrl('http://domain.tld/5'), - ]); + $this->container->bookmarkService->expects(static::once())->method('search')->willReturn( + SearchResult::getSearchResult([ + (new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'), + (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'), + (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'), + (new Bookmark())->setId(4)->setCreated($dates[2])->setUrl('http://domain.tld/4'), + (new Bookmark())->setId(5)->setCreated($dates[3])->setUrl('http://domain.tld/5'), + ]) + ); $this->container->pageCacheManager ->expects(static::once()) @@ -454,7 +457,9 @@ class DailyControllerTest extends TestCase $request = $this->createMock(Request::class); $response = new Response(); - $this->container->bookmarkService->expects(static::once())->method('search')->willReturn([]); + $this->container->bookmarkService + ->expects(static::once())->method('search') + ->willReturn(SearchResult::getSearchResult([])); // Save RainTPL assigned variables $assignedVariables = []; @@ -613,11 +618,13 @@ class DailyControllerTest extends TestCase }); $response = new Response(); - $this->container->bookmarkService->expects(static::once())->method('search')->willReturn([ - (new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'), - (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'), - (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'), - ]); + $this->container->bookmarkService->expects(static::once())->method('search')->willReturn( + SearchResult::getSearchResult([ + (new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'), + (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'), + (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'), + ]) + ); // Save RainTPL assigned variables $assignedVariables = []; @@ -674,11 +681,13 @@ class DailyControllerTest extends TestCase }); $response = new Response(); - $this->container->bookmarkService->expects(static::once())->method('search')->willReturn([ - (new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'), - (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'), - (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'), - ]); + $this->container->bookmarkService->expects(static::once())->method('search')->willReturn( + SearchResult::getSearchResult([ + (new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'), + (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'), + (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'), + ]) + ); // Save RainTPL assigned variables $assignedVariables = []; diff --git a/tests/front/controller/visitor/PictureWallControllerTest.php b/tests/front/controller/visitor/PictureWallControllerTest.php index b868231d..429e99a2 100644 --- a/tests/front/controller/visitor/PictureWallControllerTest.php +++ b/tests/front/controller/visitor/PictureWallControllerTest.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Shaarli\Front\Controller\Visitor; use Shaarli\Bookmark\Bookmark; +use Shaarli\Bookmark\SearchResult; use Shaarli\Config\ConfigManager; use Shaarli\Front\Exception\ThumbnailsDisabledException; use Shaarli\TestCase; @@ -50,17 +51,17 @@ class PictureWallControllerTest extends TestCase $this->container->bookmarkService ->expects(static::once()) ->method('search') - ->willReturnCallback(function (array $parameters, ?string $visibility): array { + ->willReturnCallback(function (array $parameters, ?string $visibility): SearchResult { // Visibility is set through the container, not the call static::assertNull($visibility); // No query parameters if (count($parameters) === 0) { - return [ + return SearchResult::getSearchResult([ (new Bookmark())->setId(1)->setUrl('http://url.tld')->setThumbnail('thumb1'), (new Bookmark())->setId(2)->setUrl('http://url2.tld'), (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setThumbnail('thumb2'), - ]; + ]); } }) ; -- cgit v1.2.3