Differences Between: [Versions 310 and 402] [Versions 310 and 403]
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 /** 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; 27 28 defined('MOODLE_INTERNAL') || die(); 29 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 { 42 43 /** @var favourite_repository_interface $repo the favourite repository object. */ 44 protected $repo; 45 46 /** @var int $userid the id of the user to which this favourites service is scoped. */ 47 protected $userid; 48 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 } 59 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. 74 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 } 79 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 } 84 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 } 112 113 /** 114 * Find a list of favourites, by multiple types within a component. 115 * 116 * E.g. "Find all favourites in the activity chooser" might result in: 117 * $favcourses = find_all_favourites('core_course', ['contentitem_mod_assign','contentitem_mod_assignment'); 118 * 119 * @param string $component the frankenstyle component name. 120 * @param array $itemtypes optional the type of the favourited item. 121 * @param int $limitfrom optional pagination control for returning a subset of records, starting at this point. 122 * @param int $limitnum optional pagination control for returning a subset comprising this many records. 123 * @return array the list of favourites found. 124 * @throws \moodle_exception if the component name is invalid, or if the repository encounters any errors. 125 */ 126 public function find_all_favourites(string $component, array $itemtypes = [], int $limitfrom = 0, int $limitnum = 0) : array { 127 if (!in_array($component, \core_component::get_component_names())) { 128 throw new \moodle_exception("Invalid component name '$component'"); 129 } 130 $params = [ 131 'userid' => $this->userid, 132 'component' => $component, 133 ]; 134 if ($itemtypes) { 135 $params['itemtype'] = $itemtypes; 136 } 137 138 return $this->repo->find_by( 139 $params, 140 $limitfrom, 141 $limitnum 142 ); 143 } 144 145 /** 146 * Returns the SQL required to include favourite information for a given component/itemtype combination. 147 * 148 * Generally, find_favourites_by_type() is the recommended way to fetch favourites. 149 * 150 * This method is used to include favourite information in external queries, for items identified by their 151 * component and itemtype, matching itemid to the $joinitemid, and for the user to which this service is scoped. 152 * 153 * It uses a LEFT JOIN to preserve the original records. If you wish to restrict your records, please consider using a 154 * "WHERE {$tablealias}.id IS NOT NULL" in your query. 155 * 156 * Example usage: 157 * 158 * list($sql, $params) = $service->get_join_sql_by_type('core_message', 'message_conversations', 'myfavouritetablealias', 159 * 'conv.id'); 160 * Results in $sql: 161 * "LEFT JOIN {favourite} fav 162 * ON fav.component = :favouritecomponent 163 * AND fav.itemtype = :favouriteitemtype 164 * AND fav.userid = 1234 165 * AND fav.itemid = conv.id" 166 * and $params: 167 * ['favouritecomponent' => 'core_message', 'favouriteitemtype' => 'message_conversations'] 168 * 169 * @param string $component the frankenstyle component name. 170 * @param string $itemtype the type of the favourited item. 171 * @param string $tablealias the desired alias for the favourites table. 172 * @param string $joinitemid the table and column identifier which the itemid is joined to. E.g. conversation.id. 173 * @return array the list of sql and params, in the format [$sql, $params]. 174 */ 175 public function get_join_sql_by_type(string $component, string $itemtype, string $tablealias, string $joinitemid) : array { 176 $sql = " LEFT JOIN {favourite} {$tablealias} 177 ON {$tablealias}.component = :favouritecomponent 178 AND {$tablealias}.itemtype = :favouriteitemtype 179 AND {$tablealias}.userid = {$this->userid} 180 AND {$tablealias}.itemid = {$joinitemid} "; 181 182 $params = [ 183 'favouritecomponent' => $component, 184 'favouriteitemtype' => $itemtype, 185 ]; 186 187 return [$sql, $params]; 188 } 189 190 /** 191 * Delete a favourite item from an area and from within a context. 192 * 193 * E.g. delete a favourite course from the area 'core_course', 'course' with itemid 3 and from within the CONTEXT_USER context. 194 * 195 * @param string $component the frankenstyle component name. 196 * @param string $itemtype the type of the favourited item. 197 * @param int $itemid the id of the item which was favourited (not the favourite's id). 198 * @param \context $context the context of the item which was favourited. 199 * @throws \moodle_exception if the user does not control the favourite, or it doesn't exist. 200 */ 201 public function delete_favourite(string $component, string $itemtype, int $itemid, \context $context) { 202 if (!in_array($component, \core_component::get_component_names())) { 203 throw new \moodle_exception("Invalid component name '$component'"); 204 } 205 206 // Business logic: check the user owns the favourite. 207 try { 208 $favourite = $this->repo->find_favourite($this->userid, $component, $itemtype, $itemid, $context->id); 209 } catch (\moodle_exception $e) { 210 throw new \moodle_exception("Favourite does not exist for the user. Cannot delete."); 211 } 212 213 $this->repo->delete($favourite->id); 214 } 215 216 /** 217 * Check whether an item has been marked as a favourite in the respective area. 218 * 219 * @param string $component the frankenstyle component name. 220 * @param string $itemtype the type of the favourited item. 221 * @param int $itemid the id of the item which was favourited (not the favourite's id). 222 * @param \context $context the context of the item which was favourited. 223 * @return bool true if the item is favourited, false otherwise. 224 */ 225 public function favourite_exists(string $component, string $itemtype, int $itemid, \context $context) : bool { 226 return $this->repo->exists_by( 227 [ 228 'userid' => $this->userid, 229 'component' => $component, 230 'itemtype' => $itemtype, 231 'itemid' => $itemid, 232 'contextid' => $context->id 233 ] 234 ); 235 } 236 237 /** 238 * Get the favourite. 239 * 240 * @param string $component the frankenstyle component name. 241 * @param string $itemtype the type of the favourited item. 242 * @param int $itemid the id of the item which was favourited (not the favourite's id). 243 * @param \context $context the context of the item which was favourited. 244 * @return favourite|null 245 */ 246 public function get_favourite(string $component, string $itemtype, int $itemid, \context $context) { 247 try { 248 return $this->repo->find_favourite( 249 $this->userid, 250 $component, 251 $itemtype, 252 $itemid, 253 $context->id 254 ); 255 } catch (\dml_missing_record_exception $e) { 256 return null; 257 } 258 } 259 260 /** 261 * Count the favourite by item type. 262 * 263 * @param string $component the frankenstyle component name. 264 * @param string $itemtype the type of the favourited item. 265 * @param \context|null $context the context of the item which was favourited. 266 * @return int 267 */ 268 public function count_favourites_by_type(string $component, string $itemtype, \context $context = null) { 269 $criteria = [ 270 'userid' => $this->userid, 271 'component' => $component, 272 'itemtype' => $itemtype 273 ]; 274 275 if ($context) { 276 $criteria['contextid'] = $context->id; 277 } 278 279 return $this->repo->count_by($criteria); 280 } 281 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body