Skip to content

Commit

Permalink
EZP-29851: As a developer I want to be able to load several Locations…
Browse files Browse the repository at this point in the history
… at once, fast (#2493)

* EZP-29851: As a developer I want to be able to load several Locations at once, fast

Motivation
----------
Like the other bulk loading API's added recently this is to optimize usage in:
- Page builder
- Cronjobs / Commands
- GraphQL
- REST
- Admin UI
- Internally in Repository itself
- ...

These changes allows for less PHP code to execute, less SQL lookups, and less cache lookups by taking advantage of
Symfony Cache's multi get support.

Biggest benfit will be on setups where cache server is on a different machine, this can save ~5ms
for each and every lookup saved. Which can on complex landing pages mean saving up-to several seconds
of load time when HTTPCache is cold or disabled.

Design
------
Like other multi load API's return iterable list of objects, in this case Locations.
_Iterable as we might want to introduce custom collection in the future to expose more info, hence avoid hardcoding usage of plain PHP array type._

```
     /**
      * Return list of unique Locations, with location id as key.
      *
      * Missing items (NotFound) & those user does not have access to (Unauthorized), will be missing from the
      * list and not cause any exception. It's up to calling logic to determine if this should cause exception or not.
      *
      * @param array $locationIds
      * @param string[]|null $prioritizedLanguages Filter on and use as prioritized language code on translated properties of returned objects.
      * @param bool|null $useAlwaysAvailable Respect always available flag on content when filtering on $prioritizedLanguages.
      *
      * @return \eZ\Publish\API\Repository\Values\Content\Location[]|iterable
      */
     public function loadLocationList(array $locationIds, array $prioritizedLanguages = null, bool $useAlwaysAvailable = null): iterable;
```

* Fix review comment in regards to REST Client fix

* Fix review comment on SiteAccessAware repo tests return value usage
  • Loading branch information
andrerom authored and Łukasz Serwatka committed Dec 20, 2018
1 parent 6e491ce commit 953d7b1
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 0 deletions.
13 changes: 13 additions & 0 deletions Repository/LocationService.php
Expand Up @@ -51,6 +51,19 @@ public function copySubtree(Location $subtree, Location $targetParentLocation);
*/
public function loadLocation($locationId, array $prioritizedLanguages = null, bool $useAlwaysAvailable = null);

/**
* Loads several location objects from its $locationIds.
*
* Returned list of Locations will be filtered by what is found and what current user has access to.
*
* @param array $locationIds
* @param string[]|null $prioritizedLanguages Filter on and use as prioritized language code on translated properties of returned objects.
* @param bool|null $useAlwaysAvailable Respect always available flag on content when filtering on $prioritizedLanguages.
*
* @return \eZ\Publish\API\Repository\Values\Content\Location[]|iterable
*/
public function loadLocationList(array $locationIds, array $prioritizedLanguages = null, bool $useAlwaysAvailable = null): iterable;

/**
* Loads a location object from its $remoteId.
*
Expand Down
21 changes: 21 additions & 0 deletions Repository/Tests/LocationServiceAuthorizationTest.php
Expand Up @@ -146,6 +146,27 @@ public function testLoadLocationThrowsUnauthorizedException()
/* END: Use Case */
}

/**
* Test for the loadLocationList() method.
*
* @covers \eZ\Publish\API\Repository\LocationService::loadLocationList
*/
public function testLoadLocationListFiltersUnauthorizedLocations(): void
{
$repository = $this->getRepository();
$locationService = $repository->getLocationService();

// Set current user to newly created user (with no rights)
$repository->getPermissionResolver()->setCurrentUserReference(
$this->createUserVersion1()
);

$locations = $locationService->loadLocationList([13]);

self::assertInternalType('iterable', $locations);
self::assertCount(0, $locations);
}

/**
* Test for the loadLocationByRemoteId() method.
*
Expand Down
63 changes: 63 additions & 0 deletions Repository/Tests/LocationServiceTest.php
Expand Up @@ -548,6 +548,69 @@ public function testLoadLocationThrowsNotFoundException()
/* END: Use Case */
}

/**
* Test for the loadLocationList() method.
*
* @covers \eZ\Publish\API\Repository\LocationService::loadLocationList
*/
public function testLoadLocationList(): void
{
$repository = $this->getRepository();

// 5 is the ID of an existing location, 442 is a non-existing id
$locationService = $repository->getLocationService();
$locations = $locationService->loadLocationList([5, 442]);

self::assertInternalType('iterable', $locations);
self::assertCount(1, $locations);
self::assertEquals([5], array_keys($locations));
self::assertInstanceOf(Location::class, $locations[5]);
self::assertEquals(5, $locations[5]->id);
}

/**
* Test for the loadLocationList() method.
*
* @covers \eZ\Publish\API\Repository\LocationService::loadLocationList
* @depends testLoadLocationList
*/
public function testLoadLocationListPrioritizedLanguagesFallback(): void
{
$repository = $this->getRepository();

$this->createLanguage('pol-PL', 'Polski');

// 5 is the ID of an existing location, 442 is a non-existing id
$locationService = $repository->getLocationService();
$locations = $locationService->loadLocationList([5, 442], ['pol-PL'], false);

self::assertInternalType('iterable', $locations);
self::assertCount(0, $locations);
}

/**
* Test for the loadLocationList() method.
*
* @covers \eZ\Publish\API\Repository\LocationService::loadLocationList
* @depends testLoadLocationListPrioritizedLanguagesFallback
*/
public function testLoadLocationListPrioritizedLanguagesFallbackAndAlwaysAvailable(): void
{
$repository = $this->getRepository();

$this->createLanguage('pol-PL', 'Polski');

// 5 is the ID of an existing location, 442 is a non-existing id
$locationService = $repository->getLocationService();
$locations = $locationService->loadLocationList([5, 442], ['pol-PL'], true);

self::assertInternalType('iterable', $locations);
self::assertCount(1, $locations);
self::assertEquals([5], array_keys($locations));
self::assertInstanceOf(Location::class, $locations[5]);
self::assertEquals(5, $locations[5]->id);
}

/**
* Test for the loadLocationByRemoteId() method.
*
Expand Down

0 comments on commit 953d7b1

Please sign in to comment.