Handle all search results through SearchResult object.
This is a required step toward implementing a BookmarkService based on SQL database.
Related to #953
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');
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');
} 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);
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);
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);
/**
* @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;
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;
}
$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
+ );
}
/**
*/
public function count(string $visibility = null): int
{
- return count($this->search([], $visibility));
+ return $this->search([], $visibility)->getResultCount();
}
/**
*/
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)
$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()) {
*/
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;
}
* @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.
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace Shaarli\Bookmark;
+
+/**
+ * Read-only class used to represent search result, including pagination.
+ */
+class SearchResult
+{
+ /** @var Bookmark[] List of result bookmarks with pagination applied */
+ protected $bookmarks;
+
+ /** @var int number of Bookmarks found, with pagination applied */
+ protected $resultCount;
+
+ /** @var int total number of result found */
+ protected $totalCount;
+
+ /** @var int pagination: limit number of result bookmarks */
+ protected $limit;
+
+ /** @var int pagination: offset to apply to complete result list */
+ protected $offset;
+
+ public function __construct(array $bookmarks, int $totalCount, int $offset, ?int $limit)
+ {
+ $this->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;
+ }
+}
$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);
$data['self_link'] = $pageaddr . $requestUri;
$data['index_url'] = $pageaddr;
$data['usepermalinks'] = $this->usePermalinks === true;
- $data['links'] = $linkDisplayed;
+ $data['links'] = $links;
return $data;
}
* 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']);
}
// 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 {
$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),
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;
$searchTags = normalize_spaces($request->getParam('searchtags') ?? '');
$searchTerm = escape(normalize_spaces($request->getParam('searchterm') ?? ''));
- ;
// Filter bookmarks according search parameters.
$visibility = $this->container->sessionManager->getSessionParameter('visibility');
'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) {
// 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));
$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,
]
);
$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
);
// 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) {
}
$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) {
{
$updated = false;
- foreach ($this->bookmarkService->search() as $bookmark) {
+ foreach ($this->bookmarkService->search()->getBookmarks() as $bookmark) {
if (
$bookmark->isNote()
&& startsWith($bookmark->getUrl(), '?')
$request = ['searchtags' => $tags];
$this->assertEquals(
2,
- count($this->privateLinkDB->search($request, null, true))
+ count($this->privateLinkDB->search($request, null, true)->getBookmarks())
);
}
$request = ['searchtags' => $tags];
$this->assertEquals(
2,
- count($this->privateLinkDB->search($request, null, true))
+ count($this->privateLinkDB->search($request, null, true)->getBookmarks())
);
}
$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())
);
}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace Shaarli\Bookmark;
+
+use Shaarli\TestCase;
+
+/**
+ * Test SearchResult class.
+ */
+class SearchResultTest extends TestCase
+{
+ /** Create a SearchResult without any pagination parameter. */
+ public function testResultNoParameters(): void
+ {
+ $searchResult = SearchResult::getSearchResult($data = ['a', 'b', 'c', 'd', 'e', 'f']);
+
+ static::assertSame($data, $searchResult->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());
+ }
+}
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;
->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
->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
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
+use Shaarli\Bookmark\SearchResult;
use Shaarli\TestCase;
use Shaarli\Thumbnailer;
use Slim\Http\Request;
$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);
use Shaarli\Bookmark\Bookmark;
use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
+use Shaarli\Bookmark\SearchResult;
use Shaarli\Config\ConfigManager;
use Shaarli\Security\LoginManager;
use Shaarli\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
['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
['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);
$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))
namespace Shaarli\Front\Controller\Visitor;
use Shaarli\Bookmark\Bookmark;
+use Shaarli\Bookmark\SearchResult;
use Shaarli\Feed\CachedPage;
use Shaarli\TestCase;
use Slim\Http\Request;
$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())
$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 = [];
});
$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 = [];
});
$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 = [];
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;
$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'),
- ];
+ ]);
}
})
;