MDL-63658 core_favourites: adding paging support to the service layer

This commit is contained in:
Jake Dallimore 2018-10-09 11:10:21 +08:00
parent ac9138db1c
commit 8ffbe9c163
5 changed files with 172 additions and 30 deletions

View File

@ -31,10 +31,10 @@ interface crud_repository {
/**
* Add one item to this repository.
*
* @param \stdClass $item the item to add.
* @return \stdClass the item which was added.
* @param object $item the item to add.
* @return object the item which was added.
*/
public function add(\stdClass $item) : \stdClass;
public function add($item);
/**
* Add all the items in the list to this repository.
@ -48,24 +48,28 @@ interface crud_repository {
* Find an item in this repository based on its id.
*
* @param int $id the id of the item.
* @return \stdClass the item.
* @return object the item.
*/
public function find(int $id) : \stdClass;
public function find(int $id);
/**
* Find all items in this repository.
*
* @param int $limitfrom optional pagination control for returning a subset of records, starting at this point.
* @param int $limitnum optional pagination control for returning a subset comprising this many records.
* @return array list of all items in this repository.
*/
public function find_all() : array;
public function find_all(int $limitfrom = 0, int $limitnum = 0) : array;
/**
* Find all items with attributes matching certain values.
*
* @param array $criteria the array of attribute/value pairs.
* @param int $limitfrom optional pagination control for returning a subset of records, starting at this point.
* @param int $limitnum optional pagination control for returning a subset comprising this many records.
* @return array the list of items matching the criteria.
*/
public function find_by(array $criteria) : array;
public function find_by(array $criteria, int $limitfrom = 0, int $limitnum = 0) : array;
/**
* Check whether an item exists in this repository, based on its id.
@ -85,10 +89,10 @@ interface crud_repository {
/**
* Update an item within this repository.
*
* @param \stdClass $item the item to update.
* @return \stdClass the updated item.
* @param object $item the item to update.
* @return object the updated item.
*/
public function update(\stdClass $item) : \stdClass;
public function update($item);
/**
* Delete an item by id.

View File

@ -53,7 +53,7 @@ class favourites_repository implements ifavourites_repository {
* @throws \dml_exception if any database errors are encountered.
* @throws \moodle_exception if the favourite has missing or invalid properties.
*/
public function add(\stdClass $favourite) : \stdClass {
public function add($favourite) : \stdClass {
global $DB;
$this->validate($favourite);
$favourite = (array)$favourite;
@ -102,23 +102,27 @@ class favourites_repository implements ifavourites_repository {
* Return all items matching the supplied criteria (a [key => value,..] list).
*
* @param array $criteria the list of key/value criteria pairs.
* @param int $limitfrom optional pagination control for returning a subset of records, starting at this point.
* @param int $limitnum optional pagination control for returning a subset comprising this many records.
* @return array the list of favourites matching the criteria.
* @throws \dml_exception if any database errors are encountered.
*/
public function find_by(array $criteria) : array {
public function find_by(array $criteria, int $limitfrom = 0, int $limitnum = 0) : array {
global $DB;
return $DB->get_records($this->favouritetable, $criteria);
return $DB->get_records($this->favouritetable, $criteria, '', '*', $limitfrom, $limitnum);
}
/**
* Return all items in this repository, as an array, indexed by id.
*
* @param int $limitfrom optional pagination control for returning a subset of records, starting at this point.
* @param int $limitnum optional pagination control for returning a subset comprising this many records.
* @return array the list of all favourites stored within this repository.
* @throws \dml_exception if any database errors are encountered.
*/
public function find_all() : array {
public function find_all(int $limitfrom = 0, int $limitnum = 0) : array {
global $DB;
return $DB->get_records($this->favouritetable);
return $DB->get_records($this->favouritetable, null, '', '*', $limitfrom, $limitnum);
}
/**
@ -165,7 +169,7 @@ class favourites_repository implements ifavourites_repository {
* @return \stdClass the updated favourite.
* @throws \dml_exception if any database errors are encountered.
*/
public function update(\stdClass $favourite) : \stdClass {
public function update($favourite) : \stdClass {
global $DB;
$time = time();
$favourite->timemodified = $time;

View File

@ -44,17 +44,6 @@ class user_favourites_service {
/** @var int $userid the id of the user to which this favourites service is scoped. */
protected $userid;
/**
* Helper, returning a flat list of component names.
*
* @return array the array of component names.
*/
protected function get_component_list() {
return array_keys(array_reduce(\core_component::get_component_list(), function($carry, $item) {
return array_merge($carry, $item);
}, []));
}
/**
* The user_favourites_service constructor.
*
@ -66,6 +55,17 @@ class user_favourites_service {
$this->userid = $usercontext->instanceid;
}
/**
* Helper, returning a flat list of component names.
*
* @return array the array of component names.
*/
protected function get_component_list() {
return array_keys(array_reduce(\core_component::get_component_list(), function($carry, $item) {
return array_merge($carry, $item);
}, []));
}
/**
* Favourite an item defined by itemid/context, in the area defined by component/itemtype.
*
@ -105,14 +105,24 @@ class user_favourites_service {
*
* @param string $component the frankenstyle component name.
* @param string $itemtype the type of the favourited item.
* @param int $limitfrom optional pagination control for returning a subset of records, starting at this point.
* @param int $limitnum optional pagination control for returning a subset comprising this many records.
* @return array the list of favourites found.
* @throws \moodle_exception if the component name is invalid, or if the repository encounters any errors.
*/
public function find_favourites_by_type(string $component, string $itemtype) : array {
public function find_favourites_by_type(string $component, string $itemtype, int $limitfrom = 0, int $limitnum = 0) : array {
if (!in_array($component, $this->get_component_list())) {
throw new \moodle_exception("Invalid component name '$component'");
}
return $this->repo->find_by(['userid' => $this->userid, 'component' => $component, 'itemtype' => $itemtype]);
return $this->repo->find_by(
[
'userid' => $this->userid,
'component' => $component,
'itemtype' => $itemtype
],
$limitfrom,
$limitnum
);
}
/**

View File

@ -235,6 +235,48 @@ class favourites_repository_testcase extends advanced_testcase {
}
}
/**
* Testing the pagination of the find_all method.
*/
public function test_find_all_pagination() {
list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
$favouritesrepo = new favourites_repository($user1context);
// Verify that for an empty repository, find_all with any combination of page options returns an empty array.
$this->assertEquals([], $favouritesrepo->find_all(0, 0));
$this->assertEquals([], $favouritesrepo->find_all(0, 10));
$this->assertEquals([], $favouritesrepo->find_all(1, 0));
$this->assertEquals([], $favouritesrepo->find_all(1, 10));
// Save 10 arbitrary favourites to the repo.
foreach (range(1, 10) as $i) {
$favourite = (object) [
'userid' => $user1context->instanceid,
'component' => 'core_course',
'itemtype' => 'course',
'itemid' => $i,
'contextid' => $course1context->id
];
$favouritesrepo->add($favourite);
}
// Verify we have 10 favourites.
$this->assertEquals(10, $favouritesrepo->count());
// Verify we can fetch the first page of 5 records.
$favourites = $favouritesrepo->find_all(0, 5);
$this->assertCount(5, $favourites);
// Verify we can fetch the second page.
$favourites = $favouritesrepo->find_all(5, 5);
$this->assertCount(5, $favourites);
// Verify the third page request ends with an empty array.
$favourites = $favouritesrepo->find_all(10, 5);
$this->assertCount(0, $favourites);
}
/**
* Test retrieval of a user's favourites for a given criteria, in this case, area.
*/
@ -263,6 +305,52 @@ class favourites_repository_testcase extends advanced_testcase {
$this->assertCount(0, $userfavourites);
}
/**
* Testing the pagination of the find_by method.
*/
public function test_find_by_pagination() {
list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
$favouritesrepo = new favourites_repository($user1context);
// Verify that for an empty repository, find_all with any combination of page options returns an empty array.
$this->assertEquals([], $favouritesrepo->find_by([], 0, 0));
$this->assertEquals([], $favouritesrepo->find_by([], 0, 10));
$this->assertEquals([], $favouritesrepo->find_by([], 1, 0));
$this->assertEquals([], $favouritesrepo->find_by([], 1, 10));
// Save 10 arbitrary favourites to the repo.
foreach (range(1, 10) as $i) {
$favourite = (object) [
'userid' => $user1context->instanceid,
'component' => 'core_course',
'itemtype' => 'course',
'itemid' => $i,
'contextid' => $course1context->id
];
$favouritesrepo->add($favourite);
}
// Verify we have 10 favourites.
$this->assertEquals(10, $favouritesrepo->count());
// Verify a request for a page, when no criteria match, results in an empty array.
$favourites = $favouritesrepo->find_by(['component' => 'core_message'], 0, 5);
$this->assertCount(0, $favourites);
// Verify we can fetch a the first page of 5 records.
$favourites = $favouritesrepo->find_by(['component' => 'core_course'], 0, 5);
$this->assertCount(5, $favourites);
// Verify we can fetch the second page.
$favourites = $favouritesrepo->find_by(['component' => 'core_course'], 5, 5);
$this->assertCount(5, $favourites);
// Verify the third page request ends with an empty array.
$favourites = $favouritesrepo->find_by(['component' => 'core_course'], 10, 5);
$this->assertCount(0, $favourites);
}
/**
* Test the count_by() method.
*/

View File

@ -84,7 +84,7 @@ class user_favourites_service_testcase extends advanced_testcase {
);
$mockrepo->expects($this->any())
->method('find_by')
->will($this->returnCallback(function(array $criteria) use (&$mockstore) {
->will($this->returnCallback(function(array $criteria, int $limitfrom = 0, int $limitnum = 0) use (&$mockstore) {
// Check the mockstore for all objects with properties matching the key => val pairs in $criteria.
foreach ($mockstore as $index => $mockrow) {
$mockrowarr = (array)$mockrow;
@ -92,6 +92,11 @@ class user_favourites_service_testcase extends advanced_testcase {
$returns[$index] = $mockrow;
}
}
// Return a subset of the records, according to the paging options, if set.
if ($limitnum != 0) {
return array_slice($returns, $limitfrom, $limitnum);
}
// Otherwise, just return the full set.
return $returns;
})
);
@ -245,6 +250,37 @@ class user_favourites_service_testcase extends advanced_testcase {
$service->find_favourites_by_type('cccore_notreal', 'something');
}
/**
* Test confirming the pagination support for the find_favourites_by_type() method.
*/
public function test_find_favourites_by_type_pagination() {
list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
// Get a user_favourites_service for the user.
$repo = $this->get_mock_repository([]);
$service = new \core_favourites\local\service\user_favourites_service($user1context, $repo);
// Favourite 10 arbitrary items.
foreach (range(1, 10) as $i) {
$service->create_favourite('core_course', 'course', $i, $course1context);
}
// Verify we have 10 favourites.
$this->assertCount(10, $service->find_favourites_by_type('core_course', 'course'));
// Verify we get back 5 favourites for page 1.
$favourites = $service->find_favourites_by_type('core_course', 'course', 0, 5);
$this->assertCount(5, $favourites);
// Verify we get back 5 favourites for page 2.
$favourites = $service->find_favourites_by_type('core_course', 'course', 5, 5);
$this->assertCount(5, $favourites);
// Verify we get back an empty array if querying page 3.
$favourites = $service->find_favourites_by_type('core_course', 'course', 10, 5);
$this->assertCount(0, $favourites);
}
/**
* Test confirming the basic deletion behaviour.
*/