MDL-63658 core_favourites: adding paging support to the service layer
[moodle.git] / favourites / classes / local / repository / favourites_repository.php
CommitLineData
d4e98ee5
JD
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16/**
17 * Contains the user_favourites_repository class, responsible for CRUD operations for user favourites.
18 *
19 * @package core_favourites
20 * @copyright 2018 Jake Dallimore <jrhdallimore@gmail.com>
21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22 */
4a02aae5 23namespace core_favourites\local\repository;
d4e98ee5
JD
24
25defined('MOODLE_INTERNAL') || die();
26
27/**
28 * Class favourites_repository.
29 *
30 * This class handles persistence of favourites. Favourites from all areas are supported by this repository.
31 *
32 * @copyright 2018 Jake Dallimore <jrhdallimore@gmail.com>
33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34 */
35class favourites_repository implements ifavourites_repository {
36
37 /**
38 * @var string the name of the table which favourites are stored in.
39 */
40 protected $favouritetable = 'favourite';
41
42 /**
43 * The favourites_repository constructor.
44 */
45 public function __construct() {
46 }
47
48 /**
49 * Add a favourite to the repository.
50 *
51 * @param \stdClass $favourite the favourite to add.
52 * @return \stdClass the favourite which has been stored.
53 * @throws \dml_exception if any database errors are encountered.
54 * @throws \moodle_exception if the favourite has missing or invalid properties.
55 */
8ffbe9c1 56 public function add($favourite) : \stdClass {
d4e98ee5
JD
57 global $DB;
58 $this->validate($favourite);
59 $favourite = (array)$favourite;
60 $time = time();
61 $favourite['timecreated'] = $time;
62 $favourite['timemodified'] = $time;
63 $id = $DB->insert_record($this->favouritetable, $favourite);
64 return $this->find($id);
65 }
66
67 /**
68 * Add a collection of favourites to the repository.
69 *
70 * @param array $items the list of favourites to add.
71 * @return array the list of favourites which have been stored.
72 * @throws \dml_exception if any database errors are encountered.
73 * @throws \moodle_exception if any of the favourites have missing or invalid properties.
74 */
75 public function add_all(array $items) : array {
76 global $DB;
77 $time = time();
78 foreach ($items as $item) {
79 $this->validate($item);
80 $favourite = (array)$item;
81 $favourite['timecreated'] = $time;
82 $favourite['timemodified'] = $time;
83 $ids[] = $DB->insert_record($this->favouritetable, $favourite);
84 }
85 list($insql, $params) = $DB->get_in_or_equal($ids);
86 return $DB->get_records_select($this->favouritetable, "id $insql", $params);
87 }
88
89 /**
90 * Find a favourite by id.
91 *
92 * @param int $id the id of the favourite.
93 * @return \stdClass the favourite.
94 * @throws \dml_exception if any database errors are encountered.
95 */
96 public function find(int $id) : \stdClass {
97 global $DB;
98 return $DB->get_record($this->favouritetable, ['id' => $id], '*', MUST_EXIST);
99 }
100
101 /**
102 * Return all items matching the supplied criteria (a [key => value,..] list).
103 *
104 * @param array $criteria the list of key/value criteria pairs.
8ffbe9c1
JD
105 * @param int $limitfrom optional pagination control for returning a subset of records, starting at this point.
106 * @param int $limitnum optional pagination control for returning a subset comprising this many records.
d4e98ee5
JD
107 * @return array the list of favourites matching the criteria.
108 * @throws \dml_exception if any database errors are encountered.
109 */
8ffbe9c1 110 public function find_by(array $criteria, int $limitfrom = 0, int $limitnum = 0) : array {
d4e98ee5 111 global $DB;
8ffbe9c1 112 return $DB->get_records($this->favouritetable, $criteria, '', '*', $limitfrom, $limitnum);
d4e98ee5
JD
113 }
114
115 /**
116 * Return all items in this repository, as an array, indexed by id.
117 *
8ffbe9c1
JD
118 * @param int $limitfrom optional pagination control for returning a subset of records, starting at this point.
119 * @param int $limitnum optional pagination control for returning a subset comprising this many records.
d4e98ee5
JD
120 * @return array the list of all favourites stored within this repository.
121 * @throws \dml_exception if any database errors are encountered.
122 */
8ffbe9c1 123 public function find_all(int $limitfrom = 0, int $limitnum = 0) : array {
d4e98ee5 124 global $DB;
8ffbe9c1 125 return $DB->get_records($this->favouritetable, null, '', '*', $limitfrom, $limitnum);
d4e98ee5
JD
126 }
127
128 /**
129 * Find a specific favourite, based on the properties known to identify it.
130 *
131 * Used if we don't know its id.
132 *
133 * @param int $userid the id of the user to which the favourite belongs.
134 * @param string $component the frankenstyle component name.
135 * @param string $itemtype the type of the favourited item.
136 * @param int $itemid the id of the item which was favourited (not the favourite's id).
137 * @param int $contextid the contextid of the item which was favourited.
138 * @return \stdClass the favourite.
139 * @throws \dml_exception if any database errors are encountered or if the record could not be found.
140 */
141 public function find_favourite(int $userid, string $component, string $itemtype, int $itemid, int $contextid) : \stdClass {
142 global $DB;
143 // Favourites model: We know that only one favourite can exist based on these properties.
144 return $DB->get_record($this->favouritetable, [
145 'userid' => $userid,
146 'component' => $component,
147 'itemtype' => $itemtype,
148 'itemid' => $itemid,
149 'contextid' => $contextid
150 ], '*', MUST_EXIST);
151 }
152
153 /**
154 * Check whether a favourite exists in this repository, based on its id.
155 *
156 * @param int $id the id to search for.
157 * @return bool true if the favourite exists, false otherwise.
158 * @throws \dml_exception if any database errors are encountered.
159 */
160 public function exists(int $id) : bool {
161 global $DB;
162 return $DB->record_exists($this->favouritetable, ['id' => $id]);
163 }
164
165 /**
166 * Update a favourite.
167 *
168 * @param \stdClass $favourite the favourite to update.
169 * @return \stdClass the updated favourite.
170 * @throws \dml_exception if any database errors are encountered.
171 */
8ffbe9c1 172 public function update($favourite) : \stdClass {
d4e98ee5
JD
173 global $DB;
174 $time = time();
175 $favourite->timemodified = $time;
176 $DB->update_record($this->favouritetable, $favourite);
177 return $this->find($favourite->id);
178 }
179
180 /**
181 * Delete a favourite, by id.
182 *
183 * @param int $id the id of the favourite to delete.
184 * @throws \dml_exception if any database errors are encountered.
185 */
186 public function delete(int $id) {
187 global $DB;
188 $DB->delete_records($this->favouritetable, ['id' => $id]);
189 }
190
191 /**
192 * Return the total number of favourites in this repository.
193 *
194 * @return int the total number of items.
195 * @throws \dml_exception if any database errors are encountered.
196 */
197 public function count() : int {
198 global $DB;
199 return $DB->count_records($this->favouritetable);
200 }
201
202 /**
203 * Check for the existence of a favourite item in the specified area.
204 *
205 * A favourite item is identified by the itemid/contextid pair.
206 * An area is identified by the component/itemtype pair.
207 *
208 * @param int $userid the id of user to whom the favourite belongs.
209 * @param string $component the frankenstyle component name.
210 * @param string $itemtype the type of the favourited item.
211 * @param int $itemid the id of the item which was favourited (not the favourite's id).
212 * @param int $contextid the contextid of the item which was favourited.
213 * @return bool true if the favourited item exists, false otherwise.
214 * @throws \dml_exception if any database errors are encountered.
215 */
216 public function exists_by_area(int $userid, string $component, string $itemtype, int $itemid, int $contextid) : bool {
217 global $DB;
218 return $DB->record_exists($this->favouritetable,
219 [
220 'userid' => $userid,
221 'component' => $component,
222 'itemtype' => $itemtype,
223 'itemid' => $itemid,
224 'contextid' => $contextid
225 ]
226 );
227 }
228
229 /**
230 * Delete all favourites within the component/itemtype.
231 *
232 * @param int $userid the id of the user to whom the favourite belongs.
233 * @param string $component the frankenstyle component name.
234 * @param string $itemtype the type of the favourited item.
235 * @throws \dml_exception if any database errors are encountered.
236 */
237 public function delete_by_area(int $userid, string $component, string $itemtype) {
238 global $DB;
239 $DB->delete_records($this->favouritetable,
240 [
241 'userid' => $userid,
242 'component' => $component,
243 'itemtype' => $itemtype
244 ]
245 );
246 }
247
248 /**
249 * Return the number of user favourites matching the specified criteria.
250 *
251 * @param array $criteria the list of key/value criteria pairs.
252 * @return int the number of favourites matching the criteria.
253 * @throws \dml_exception if any database errors are encountered.
254 */
255 public function count_by(array $criteria) : int {
256 global $DB;
257 return $DB->count_records($this->favouritetable, $criteria);
258 }
259
260 /**
261 * Basic validation, confirming we have the minimum field set needed to save a record to the store.
262 *
263 * @param \stdClass $favourite the favourite record to validate.
264 * @throws \moodle_exception if the supplied favourite has missing or unsupported fields.
265 */
266 protected function validate(\stdClass $favourite) {
267
268 $favourite = (array)$favourite;
269
270 // The allowed fields, and whether or not each is required.
271 // The timecreated field is generated during create/update, and cannot be specified either.
272 $allowedfields = [
273 'userid' => true,
274 'component' => true,
275 'itemtype' => true,
276 'itemid' => true,
277 'contextid' => true,
278 'ordering' => false
279 ];
280
281 $requiredfields = array_filter($allowedfields, function($field) {
282 return $field;
283 });
284
285 if ($missingfields = array_keys(array_diff_key($requiredfields, $favourite))) {
286 throw new \moodle_exception("Missing object property(s) '" . join(', ', $missingfields) . "'.");
287 }
288
289 // If the record contains fields we don't allow, throw an exception.
290 if ($unsupportedfields = array_keys(array_diff_key($favourite, $allowedfields))) {
291 throw new \moodle_exception("Unexpected object property(s) '" . join(', ', $unsupportedfields) . "'.");
292 }
293 }
294}