MDL-63303 core_favourites: add get and count functions
[moodle.git] / favourites / classes / local / service / user_favourite_service.php
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/>.
17 /**
18  * Contains the user_favourite_service class, part of the service layer for the favourites subsystem.
19  *
20  * @package   core_favourites
21  * @copyright 2018 Jake Dallimore <jrhdallimore@gmail.com>
22  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
24 namespace core_favourites\local\service;
25 use \core_favourites\local\entity\favourite;
26 use \core_favourites\local\repository\favourite_repository_interface;
28 defined('MOODLE_INTERNAL') || die();
30 /**
31  * Class service, providing an single API for interacting with the favourites subsystem for a SINGLE USER.
32  *
33  * This class is responsible for exposing key operations (add, remove, find) and enforces any business logic necessary to validate
34  * authorization/data integrity for these operations.
35  *
36  * All object persistence is delegated to the favourite_repository_interface object.
37  *
38  * @copyright 2018 Jake Dallimore <jrhdallimore@gmail.com>
39  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40  */
41 class user_favourite_service {
43     /** @var favourite_repository_interface $repo the favourite repository object. */
44     protected $repo;
46     /** @var int $userid the id of the user to which this favourites service is scoped. */
47     protected $userid;
49     /**
50      * The user_favourite_service constructor.
51      *
52      * @param \context_user $usercontext The context of the user to which this service operations are scoped.
53      * @param \core_favourites\local\repository\favourite_repository_interface $repository a favourites repository.
54      */
55     public function __construct(\context_user $usercontext, favourite_repository_interface $repository) {
56         $this->repo = $repository;
57         $this->userid = $usercontext->instanceid;
58     }
60     /**
61      * Favourite an item defined by itemid/context, in the area defined by component/itemtype.
62      *
63      * @param string $component the frankenstyle component name.
64      * @param string $itemtype the type of the item being favourited.
65      * @param int $itemid the id of the item which is to be favourited.
66      * @param \context $context the context in which the item is to be favourited.
67      * @param int|null $ordering optional ordering integer used for sorting the favourites in an area.
68      * @return favourite the favourite, once created.
69      * @throws \moodle_exception if the component name is invalid, or if the repository encounters any errors.
70      */
71     public function create_favourite(string $component, string $itemtype, int $itemid, \context $context,
72             int $ordering = null) : favourite {
73         // Access: Any component can ask to favourite something, we can't verify access to that 'something' here though.
75         // Validate the component name.
76         if (!in_array($component, \core_component::get_component_names())) {
77             throw new \moodle_exception("Invalid component name '$component'");
78         }
80         $favourite = new favourite($component, $itemtype, $itemid, $context->id, $this->userid);
81         $favourite->ordering = $ordering > 0 ? $ordering : null;
82         return $this->repo->add($favourite);
83     }
85     /**
86      * Find a list of favourites, by type, where type is the component/itemtype pair.
87      *
88      * E.g. "Find all favourite courses" might result in:
89      * $favcourses = find_favourites_by_type('core_course', 'course');
90      *
91      * @param string $component the frankenstyle component name.
92      * @param string $itemtype the type of the favourited item.
93      * @param int $limitfrom optional pagination control for returning a subset of records, starting at this point.
94      * @param int $limitnum optional pagination control for returning a subset comprising this many records.
95      * @return array the list of favourites found.
96      * @throws \moodle_exception if the component name is invalid, or if the repository encounters any errors.
97      */
98     public function find_favourites_by_type(string $component, string $itemtype, int $limitfrom = 0, int $limitnum = 0) : array {
99         if (!in_array($component, \core_component::get_component_names())) {
100             throw new \moodle_exception("Invalid component name '$component'");
101         }
102         return $this->repo->find_by(
103             [
104                 'userid' => $this->userid,
105                 'component' => $component,
106                 'itemtype' => $itemtype
107             ],
108             $limitfrom,
109             $limitnum
110         );
111     }
113     /**
114      * Delete a favourite item from an area and from within a context.
115      *
116      * E.g. delete a favourite course from the area 'core_course', 'course' with itemid 3 and from within the CONTEXT_USER context.
117      *
118      * @param string $component the frankenstyle component name.
119      * @param string $itemtype the type of the favourited item.
120      * @param int $itemid the id of the item which was favourited (not the favourite's id).
121      * @param \context $context the context of the item which was favourited.
122      * @throws \moodle_exception if the user does not control the favourite, or it doesn't exist.
123      */
124     public function delete_favourite(string $component, string $itemtype, int $itemid, \context $context) {
125         if (!in_array($component, \core_component::get_component_names())) {
126             throw new \moodle_exception("Invalid component name '$component'");
127         }
129         // Business logic: check the user owns the favourite.
130         try {
131             $favourite = $this->repo->find_favourite($this->userid, $component, $itemtype, $itemid, $context->id);
132         } catch (\moodle_exception $e) {
133             throw new \moodle_exception("Favourite does not exist for the user. Cannot delete.");
134         }
136         $this->repo->delete($favourite->id);
137     }
139     /**
140      * Check whether an item has been marked as a favourite in the respective area.
141      *
142      * @param string $component the frankenstyle component name.
143      * @param string $itemtype the type of the favourited item.
144      * @param int $itemid the id of the item which was favourited (not the favourite's id).
145      * @param \context $context the context of the item which was favourited.
146      * @return bool true if the item is favourited, false otherwise.
147      */
148     public function favourite_exists(string $component, string $itemtype, int $itemid, \context $context) : bool {
149         return $this->repo->exists_by(
150             [
151                 'userid' => $this->userid,
152                 'component' => $component,
153                 'itemtype' => $itemtype,
154                 'itemid' => $itemid,
155                 'contextid' => $context->id
156             ]
157         );
158     }
160     /**
161      * Get the favourite.
162      *
163      * @param string $component the frankenstyle component name.
164      * @param string $itemtype the type of the favourited item.
165      * @param int $itemid the id of the item which was favourited (not the favourite's id).
166      * @param \context $context the context of the item which was favourited.
167      * @return favourite|null
168      */
169     public function get_favourite(string $component, string $itemtype, int $itemid, \context $context) {
170         try {
171             return $this->repo->find_favourite(
172                 $this->userid,
173                 $component,
174                 $itemtype,
175                 $itemid,
176                 $context->id
177             );
178         } catch (\dml_missing_record_exception $e) {
179             return null;
180         }
181     }
183     /**
184      * Count the favourite by item type.
185      *
186      * @param string $component the frankenstyle component name.
187      * @param string $itemtype the type of the favourited item.
188      * @param \context|null $context the context of the item which was favourited.
189      * @return favourite|null
190      */
191     public function count_favourites_by_type(string $component, string $itemtype, \context $context = null) {
192         $criteria = [
193             'userid' => $this->userid,
194             'component' => $component,
195             'itemtype' => $itemtype
196         ];
198         if ($context) {
199             $criteria['contextid'] = $context->id;
200         }
202         return $this->repo->count_by($criteria);
203     }