Skip to content

Commit

Permalink
IBX-8726: Added support for IsBookmarked criterion (#75)
Browse files Browse the repository at this point in the history
For more details see https://issues.ibexa.co/browse/IBX-8726 and #75

Key changes:

* Added IsBookmarked criterion visitor

* Added location_bookmarked_user_ids search field

* [Tests] Added IsBookmarkedTest

---------

Co-Authored-By: Konrad Oboza <[email protected]>
Co-Authored-By: Paweł Niedzielski <[email protected]>
  • Loading branch information
3 people authored Sep 4, 2024
1 parent c2333a0 commit 6dbe045
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
namespace Ibexa\Solr\FieldMapper\ContentFieldMapper;

use Ibexa\Contracts\Core\Persistence\Bookmark\Handler as BookmarkHandler;
use Ibexa\Contracts\Core\Persistence\Content;
use Ibexa\Contracts\Core\Persistence\Content\Location;
use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler;
Expand All @@ -23,8 +24,13 @@ class ContentDocumentLocationFields extends ContentFieldMapper
*/
protected $locationHandler;

public function __construct(LocationHandler $locationHandler)
{
private BookmarkHandler $bookmarkHandler;

public function __construct(
BookmarkHandler $bookmarkHandler,
LocationHandler $locationHandler
) {
$this->bookmarkHandler = $bookmarkHandler;
$this->locationHandler = $locationHandler;
}

Expand Down Expand Up @@ -89,6 +95,12 @@ public function mapFields(Content $content)
$locationData['ancestors'],
new FieldType\MultipleIdentifierField()
);

$fields[] = new Field(
'location_bookmarked_user_ids',
$this->bookmarkHandler->loadUserIdsByLocation($location),
new FieldType\MultipleIdentifierField()
);
}

if ($mainLocation !== null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
namespace Ibexa\Solr\FieldMapper\LocationFieldMapper;

use Ibexa\Contracts\Core\Persistence\Bookmark\Handler as BookmarkHandler;
use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler;
use Ibexa\Contracts\Core\Persistence\Content\Location;
use Ibexa\Contracts\Core\Persistence\Content\Type\Handler as ContentTypeHandler;
Expand All @@ -26,10 +27,14 @@ class LocationDocumentBaseFields extends LocationFieldMapper

protected ContentTypeHandler $contentTypeHandler;

private BookmarkHandler $bookmarkHandler;

public function __construct(
BookmarkHandler $bookmarkHandler,
ContentHandler $contentHandler,
ContentTypeHandler $contentTypeHandler
) {
$this->bookmarkHandler = $bookmarkHandler;
$this->contentHandler = $contentHandler;
$this->contentTypeHandler = $contentTypeHandler;
}
Expand Down Expand Up @@ -121,6 +126,11 @@ public function mapFields(Location $location)
$contentType->isContainer,
new FieldType\BooleanField()
),
new Field(
'location_bookmarked_user_ids',
$this->bookmarkHandler->loadUserIdsByLocation($location),
new FieldType\MultipleIdentifierField()
),
];
}

Expand Down
57 changes: 57 additions & 0 deletions src/lib/Query/Location/CriterionVisitor/Location/IsBookmarked.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Solr\Query\Location\CriterionVisitor\Location;

use Ibexa\Contracts\Core\Repository\PermissionResolver;
use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion;
use Ibexa\Contracts\Solr\Query\CriterionVisitor;
use LogicException;

final class IsBookmarked extends CriterionVisitor
{
private const SEARCH_FIELD = 'location_bookmarked_user_ids_mid';

private PermissionResolver $permissionResolver;

public function __construct(PermissionResolver $permissionResolver)
{
$this->permissionResolver = $permissionResolver;
}

public function canVisit(Criterion $criterion): bool
{
return $criterion instanceof Criterion\Location\IsBookmarked
&& $criterion->operator === Criterion\Operator::EQ;
}

public function visit(
Criterion $criterion,
CriterionVisitor $subVisitor = null
): string {
if (!is_array($criterion->value)) {
throw new LogicException(sprintf(
'Expected %s Criterion value to be an array, received %s',
Criterion\Location\IsBookmarked::class,
get_debug_type($criterion->value),
));
}

$userId = $this->permissionResolver
->getCurrentUserReference()
->getUserId();

$query = self::SEARCH_FIELD . ':"' . $userId . '"';

if (!$criterion->value[0]) {
$query = 'NOT ' . $query;
}

return $query;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,12 @@ services:
tags:
- { name: ibexa.search.solr.query.location.criterion.visitor }

Ibexa\Solr\Query\Location\CriterionVisitor\Location\IsBookmarked:
arguments:
$permissionResolver: '@Ibexa\Contracts\Core\Repository\PermissionResolver'
tags:
- { name: ibexa.search.solr.query.location.criterion.visitor }

Ibexa\Solr\Query\Location\CriterionVisitor\Factory\LocationFullTextFactory:
parent: Ibexa\Solr\Query\Common\CriterionVisitor\Factory\FullTextFactoryAbstract

Expand Down
8 changes: 5 additions & 3 deletions src/lib/Resources/config/container/solr/field_mappers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ services:

Ibexa\Solr\FieldMapper\ContentFieldMapper\ContentDocumentLocationFields:
arguments:
- '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler'
$bookmarkHandler: '@Ibexa\Contracts\Core\Persistence\Bookmark\Handler'
$locationHandler: '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler'
tags:
- {name: ibexa.search.solr.field.mapper.content}

Expand All @@ -57,8 +58,9 @@ services:

Ibexa\Solr\FieldMapper\LocationFieldMapper\LocationDocumentBaseFields:
arguments:
- '@Ibexa\Contracts\Core\Persistence\Content\Handler'
- '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler'
$bookmarkHandler: '@Ibexa\Contracts\Core\Persistence\Bookmark\Handler'
$contentHandler: '@Ibexa\Contracts\Core\Persistence\Content\Handler'
$contentTypeHandler: '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler'
tags:
- {name: ibexa.search.solr.field.mapper.location}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Tests\Solr\Search\Query\Location\CriterionVisitor\Location;

use Ibexa\Contracts\Core\Repository\PermissionResolver;
use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion;
use Ibexa\Contracts\Solr\Query\CriterionVisitor;
use Ibexa\Core\Repository\Values\User\UserReference;
use Ibexa\Solr\Query\Location\CriterionVisitor\Location\IsBookmarked;
use PHPUnit\Framework\TestCase;

/**
* @covers \Ibexa\Solr\Query\Location\CriterionVisitor\Location\IsBookmarked
*/
final class IsBookmarkedTest extends TestCase
{
private const USER_ID = 123;

private CriterionVisitor $visitor;

/** @var \Ibexa\Contracts\Core\Repository\PermissionResolver&\PHPUnit\Framework\MockObject\MockObject */
private PermissionResolver $permissionResolver;

protected function setUp(): void
{
$this->permissionResolver = $this->createMock(PermissionResolver::class);
$this->visitor = new IsBookmarked($this->permissionResolver);
}

/**
* @dataProvider provideDataForTestCanVisit
*/
public function testCanVisit(
bool $expected,
Criterion $criterion
): void {
self::assertSame(
$expected,
$this->visitor->canVisit($criterion)
);
}

/**
* @return iterable<array{
* bool,
* \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion
* }>
*/
public function provideDataForTestCanVisit(): iterable
{
yield 'Not supported criterion' => [
false,
new Criterion\ContentId(123),
];

yield 'Supported criterion' => [
true,
new Criterion\Location\IsBookmarked(),
];
}

/**
* @dataProvider provideDataForTestVisit
*/
public function testVisit(
string $expected,
Criterion $criterion
): void {
$this->mockPermissionResolverGetCurrentUserReference();

self::assertSame(
$expected,
$this->visitor->visit($criterion)
);
}

/**
* @return iterable<array{
* string,
* \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion
* }>
*/
public function provideDataForTestVisit(): iterable
{
yield 'Query for bookmarked locations' => [
'location_bookmarked_user_ids_mid:"123"',
new Criterion\Location\IsBookmarked(),
];

yield 'Query for not bookmarked locations' => [
'NOT location_bookmarked_user_ids_mid:"123"',
new Criterion\Location\IsBookmarked(false),
];
}

private function mockPermissionResolverGetCurrentUserReference(): void
{
$this->permissionResolver
->method('getCurrentUserReference')
->willReturn(new UserReference(self::USER_ID));
}
}

0 comments on commit 6dbe045

Please sign in to comment.