MDL-64057 core_favourites: add get_join_sql_by_type() to service layer
authorJake Dallimore <jake@moodle.com>
Thu, 22 Nov 2018 03:23:21 +0000 (11:23 +0800)
committerJake Dallimore <jake@moodle.com>
Fri, 23 Nov 2018 03:02:18 +0000 (11:02 +0800)
This provides a way for external queries to include information about
favourited items.

favourites/classes/local/service/user_favourite_service.php
favourites/tests/service_test.php

index a63692a..e569523 100644 (file)
@@ -110,6 +110,51 @@ class user_favourite_service {
         );
     }
 
+    /**
+     * Returns the SQL required to include favourite information for a given component/itemtype combination.
+     *
+     * Generally, find_favourites_by_type() is the recommended way to fetch favourites.
+     *
+     * This method is used to include favourite information in external queries, for items identified by their
+     * component and itemtype, matching itemid to the $joinitemid, and for the user to which this service is scoped.
+     *
+     * It uses a LEFT JOIN to preserve the original records. If you wish to restrict your records, please consider using a
+     * "WHERE {$tablealias}.id IS NOT NULL" in your query.
+     *
+     * Example usage:
+     *
+     * list($sql, $params) = $service->get_join_sql_by_type('core_message', 'message_conversations', 'myfavouritetablealias',
+     *                                                      'conv.id');
+     * Results in $sql:
+     *     "LEFT JOIN {favourite} fav
+     *             ON fav.component = :favouritecomponent
+     *            AND fav.itemtype = :favouriteitemtype
+     *            AND fav.userid = 1234
+     *            AND fav.itemid = conv.id"
+     * and $params:
+     *     ['favouritecomponent' => 'core_message', 'favouriteitemtype' => 'message_conversations']
+     *
+     * @param string $component the frankenstyle component name.
+     * @param string $itemtype the type of the favourited item.
+     * @param string $tablealias the desired alias for the favourites table.
+     * @param string $joinitemid the table and column identifier which the itemid is joined to. E.g. conversation.id.
+     * @return array the list of sql and params, in the format [$sql, $params].
+     */
+    public function get_join_sql_by_type(string $component, string $itemtype, string $tablealias, string $joinitemid) : array {
+        $sql = " LEFT JOIN {favourite} {$tablealias}
+                        ON {$tablealias}.component = :favouritecomponent
+                       AND {$tablealias}.itemtype = :favouriteitemtype
+                       AND {$tablealias}.userid = {$this->userid}
+                       AND {$tablealias}.itemid = {$joinitemid} ";
+
+        $params = [
+            'favouritecomponent' => $component,
+            'favouriteitemtype' => $itemtype,
+        ];
+
+        return [$sql, $params];
+    }
+
     /**
      * Delete a favourite item from an area and from within a context.
      *
@@ -186,7 +231,7 @@ class user_favourite_service {
      * @param string $component the frankenstyle component name.
      * @param string $itemtype the type of the favourited item.
      * @param \context|null $context the context of the item which was favourited.
-     * @return favourite|null
+     * @return int
      */
     public function count_favourites_by_type(string $component, string $itemtype, \context $context = null) {
         $criteria = [
index 5ead879..c48e81f 100644 (file)
@@ -429,4 +429,35 @@ class user_favourite_service_testcase extends advanced_testcase {
         // Gets counted if we include all contexts.
         $this->assertEquals(3, $service->count_favourites_by_type('core_course', 'course'));
     }
+
+    /**
+     * Verify that the join sql generated by get_join_sql_by_type is valid and can be used to include favourite information.
+     */
+    public function test_get_join_sql_by_type() {
+        global $DB;
+        list($user1context, $user2context, $course1context, $course2context) = $this->setup_users_and_courses();
+
+        // Get a user_favourite_service for the user.
+        // We need to use a real (DB) repository, as we want to run the SQL.
+        $repo = new \core_favourites\local\repository\favourite_repository();
+        $service = new \core_favourites\local\service\user_favourite_service($user1context, $repo);
+
+        // Favourite the first course only.
+        $service->create_favourite('core_course', 'course', $course1context->instanceid, $course1context);
+
+        // Generate the join snippet.
+        list($favsql, $favparams) = $service->get_join_sql_by_type('core_course', 'course', 'favalias', 'c.id');
+
+        // Join against a simple select, including the 2 courses only.
+        $params = ['courseid1' => $course1context->instanceid, 'courseid2' => $course2context->instanceid];
+        $params = $params + $favparams;
+        $records = $DB->get_records_sql("SELECT c.id, favalias.component
+                                           FROM {course} c $favsql
+                                          WHERE c.id = :courseid1 OR c.id = :courseid2", $params);
+
+        // Verify the favourite information is returned, but only for the favourited course.
+        $this->assertCount(2, $records);
+        $this->assertEquals('core_course', $records[$course1context->instanceid]->component);
+        $this->assertEmpty($records[$course2context->instanceid]->component);
+    }
 }