From 8ffbe9c1634ba832da9a812f7c67221ac83f5575 Mon Sep 17 00:00:00 2001 From: Jake Dallimore Date: Tue, 9 Oct 2018 11:10:21 +0800 Subject: [PATCH] MDL-63658 core_favourites: adding paging support to the service layer --- .../local/repository/crud_repository.php | 24 ++--- .../repository/favourites_repository.php | 16 ++-- .../local/service/user_favourites_service.php | 36 +++++--- favourites/tests/repository_test.php | 88 +++++++++++++++++++ favourites/tests/service_test.php | 38 +++++++- 5 files changed, 172 insertions(+), 30 deletions(-) diff --git a/favourites/classes/local/repository/crud_repository.php b/favourites/classes/local/repository/crud_repository.php index 503a3213dd8..3a515d908d7 100644 --- a/favourites/classes/local/repository/crud_repository.php +++ b/favourites/classes/local/repository/crud_repository.php @@ -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. diff --git a/favourites/classes/local/repository/favourites_repository.php b/favourites/classes/local/repository/favourites_repository.php index 799b8e9346b..9e0fbdbe237 100644 --- a/favourites/classes/local/repository/favourites_repository.php +++ b/favourites/classes/local/repository/favourites_repository.php @@ -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; diff --git a/favourites/classes/local/service/user_favourites_service.php b/favourites/classes/local/service/user_favourites_service.php index 37da62349e0..c9026aa535f 100644 --- a/favourites/classes/local/service/user_favourites_service.php +++ b/favourites/classes/local/service/user_favourites_service.php @@ -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 + ); } /** diff --git a/favourites/tests/repository_test.php b/favourites/tests/repository_test.php index e815cf95379..1910931e19a 100644 --- a/favourites/tests/repository_test.php +++ b/favourites/tests/repository_test.php @@ -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. */ diff --git a/favourites/tests/service_test.php b/favourites/tests/service_test.php index 60f8fab768b..d02b353f508 100644 --- a/favourites/tests/service_test.php +++ b/favourites/tests/service_test.php @@ -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. */ -- 2.43.0