[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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 * A scheduled task. 19 * 20 * @package core 21 * @copyright 2013 onwards Martin Dougiamas http://dougiamas.com 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 namespace core\task; 25 26 use core_tag_collection, core_tag_tag, core_tag_area, stdClass; 27 28 /** 29 * Simple task to run the tag cron. 30 */ 31 class tag_cron_task extends scheduled_task { 32 33 /** 34 * Get a descriptive name for this task (shown to admins). 35 * 36 * @return string 37 */ 38 public function get_name() { 39 return get_string('tasktagcron', 'admin'); 40 } 41 42 /** 43 * Do the job. 44 * Throw exceptions on errors (the job will be retried). 45 */ 46 public function execute() { 47 global $CFG; 48 49 if (!empty($CFG->usetags)) { 50 $this->compute_correlations(); 51 $this->cleanup(); 52 } 53 } 54 55 /** 56 * Calculates and stores the correlated tags of all tags. 57 * 58 * The correlations are stored in the 'tag_correlation' table. 59 * 60 * Two tags are correlated if they appear together a lot. Ex.: Users tagged with "computers" 61 * will probably also be tagged with "algorithms". 62 * 63 * The rationale for the 'tag_correlation' table is performance. It works as a cache 64 * for a potentially heavy load query done at the 'tag_instance' table. So, the 65 * 'tag_correlation' table stores redundant information derived from the 'tag_instance' table. 66 * 67 * @param int $mincorrelation Only tags with more than $mincorrelation correlations will be identified. 68 */ 69 public function compute_correlations($mincorrelation = 2) { 70 global $DB; 71 72 // This mighty one line query fetches a row from the database for every 73 // individual tag correlation. We then need to process the rows collecting 74 // the correlations for each tag id. 75 // The fields used by this query are as follows: 76 // tagid : This is the tag id, there should be at least $mincorrelation 77 // rows for each tag id. 78 // correlation : This is the tag id that correlates to the above tagid field. 79 // correlationid : This is the id of the row in the tag_correlation table that 80 // relates to the tagid field and will be NULL if there are no 81 // existing correlations. 82 $sql = 'SELECT pairs.tagid, pairs.correlation, pairs.ocurrences, co.id AS correlationid 83 FROM ( 84 SELECT ta.tagid, tb.tagid AS correlation, COUNT(*) AS ocurrences 85 FROM {tag_instance} ta 86 JOIN {tag} tga ON ta.tagid = tga.id 87 JOIN {tag_instance} tb ON (ta.itemtype = tb.itemtype AND ta.component = tb.component 88 AND ta.itemid = tb.itemid AND ta.tagid <> tb.tagid) 89 JOIN {tag} tgb ON tb.tagid = tgb.id AND tgb.tagcollid = tga.tagcollid 90 GROUP BY ta.tagid, tb.tagid 91 HAVING COUNT(*) > :mincorrelation 92 ) pairs 93 LEFT JOIN {tag_correlation} co ON co.tagid = pairs.tagid 94 ORDER BY pairs.tagid ASC, pairs.ocurrences DESC, pairs.correlation ASC'; 95 $rs = $DB->get_recordset_sql($sql, array('mincorrelation' => $mincorrelation)); 96 97 // Set up an empty tag correlation object. 98 $tagcorrelation = new stdClass; 99 $tagcorrelation->id = null; 100 $tagcorrelation->tagid = null; 101 $tagcorrelation->correlatedtags = array(); 102 103 // We store each correlation id in this array so we can remove any correlations 104 // that no longer exist. 105 $correlations = array(); 106 107 // Iterate each row of the result set and build them into tag correlations. 108 // We add all of a tag's correlations to $tagcorrelation->correlatedtags[] 109 // then save the $tagcorrelation object. 110 foreach ($rs as $row) { 111 if ($row->tagid != $tagcorrelation->tagid) { 112 // The tag id has changed so we have all of the correlations for this tag. 113 $tagcorrelationid = $this->process_computed_correlation($tagcorrelation); 114 if ($tagcorrelationid) { 115 $correlations[] = $tagcorrelationid; 116 } 117 // Now we reset the tag correlation object so we can reuse it and set it 118 // up for the current record. 119 $tagcorrelation = new stdClass; 120 $tagcorrelation->id = $row->correlationid; 121 $tagcorrelation->tagid = $row->tagid; 122 $tagcorrelation->correlatedtags = array(); 123 } 124 // Save the correlation on the tag correlation object. 125 $tagcorrelation->correlatedtags[] = $row->correlation; 126 } 127 // Update the current correlation after the last record. 128 $tagcorrelationid = $this->process_computed_correlation($tagcorrelation); 129 if ($tagcorrelationid) { 130 $correlations[] = $tagcorrelationid; 131 } 132 133 // Close the recordset. 134 $rs->close(); 135 136 // Remove any correlations that weren't just identified. 137 if (empty($correlations)) { 138 // There are no tag correlations. 139 $DB->delete_records('tag_correlation'); 140 } else { 141 list($sql, $params) = $DB->get_in_or_equal($correlations, 142 SQL_PARAMS_NAMED, 'param0000', false); 143 $DB->delete_records_select('tag_correlation', 'id '.$sql, $params); 144 } 145 } 146 147 /** 148 * Clean up the tag tables, making sure all tagged object still exists. 149 * 150 * This method is called from cron. 151 * 152 * This should normally not be necessary, but in case related tags are not deleted 153 * when the tagged record is removed, this should be done once in a while, perhaps 154 * on an occasional cron run. On a site with lots of tags, this could become an 155 * expensive function to call. 156 */ 157 public function cleanup() { 158 global $DB; 159 160 // Get ids to delete from instances where the tag has been deleted. This should never happen apparently. 161 $sql = "SELECT ti.id 162 FROM {tag_instance} ti 163 LEFT JOIN {tag} t ON t.id = ti.tagid 164 WHERE t.id IS null"; 165 $tagids = $DB->get_records_sql($sql); 166 $tagarray = array(); 167 foreach ($tagids as $tagid) { 168 $tagarray[] = $tagid->id; 169 } 170 171 // Next get ids from instances that have an owner that has been deleted. 172 $sql = "SELECT ti.id 173 FROM {tag_instance} ti, {user} u 174 WHERE ti.itemid = u.id 175 AND ti.itemtype = 'user' 176 AND ti.component = 'core' 177 AND u.deleted = 1"; 178 $tagids = $DB->get_records_sql($sql); 179 foreach ($tagids as $tagid) { 180 $tagarray[] = $tagid->id; 181 } 182 183 // Get the other itemtypes. 184 $sql = "SELECT DISTINCT component, itemtype 185 FROM {tag_instance} 186 WHERE itemtype <> 'user' or component <> 'core'"; 187 $tagareas = $DB->get_records_sql($sql); 188 foreach ($tagareas as $tagarea) { 189 $sql = 'SELECT ti.id 190 FROM {tag_instance} ti 191 LEFT JOIN {' . $tagarea->itemtype . '} it ON it.id = ti.itemid 192 WHERE it.id IS null 193 AND ti.itemtype = ? AND ti.component = ?'; 194 $tagids = $DB->get_records_sql($sql, array($tagarea->itemtype, $tagarea->component)); 195 foreach ($tagids as $tagid) { 196 $tagarray[] = $tagid->id; 197 } 198 } 199 200 // Get instances for each of the ids to be deleted. 201 if (count($tagarray) > 0) { 202 list($sqlin, $params) = $DB->get_in_or_equal($tagarray); 203 $sql = "SELECT ti.*, COALESCE(t.name, 'deleted') AS name, COALESCE(t.rawname, 'deleted') AS rawname 204 FROM {tag_instance} ti 205 LEFT JOIN {tag} t ON t.id = ti.tagid 206 WHERE ti.id $sqlin"; 207 $instances = $DB->get_records_sql($sql, $params); 208 $this->bulk_delete_instances($instances); 209 } 210 211 core_tag_collection::cleanup_unused_tags(); 212 } 213 214 /** 215 * This function processes a tag correlation and makes changes in the database as required. 216 * 217 * The tag correlation object needs have both a tagid property and a correlatedtags property that is an array. 218 * 219 * @param stdClass $tagcorrelation 220 * @return int/bool The id of the tag correlation that was just processed or false. 221 */ 222 public function process_computed_correlation(stdClass $tagcorrelation) { 223 global $DB; 224 225 // You must provide a tagid and correlatedtags must be set and be an array. 226 if (empty($tagcorrelation->tagid) || !isset($tagcorrelation->correlatedtags) || 227 !is_array($tagcorrelation->correlatedtags)) { 228 return false; 229 } 230 231 $tagcorrelation->correlatedtags = join(',', $tagcorrelation->correlatedtags); 232 if (!empty($tagcorrelation->id)) { 233 // The tag correlation already exists so update it. 234 $DB->update_record('tag_correlation', $tagcorrelation); 235 } else { 236 // This is a new correlation to insert. 237 $tagcorrelation->id = $DB->insert_record('tag_correlation', $tagcorrelation); 238 } 239 return $tagcorrelation->id; 240 } 241 242 /** 243 * This function will delete numerous tag instances efficiently. 244 * This removes tag instances only. It doesn't check to see if it is the last use of a tag. 245 * 246 * @param array $instances An array of tag instance objects with the addition of the tagname and tagrawname 247 * (used for recording a delete event). 248 */ 249 public function bulk_delete_instances($instances) { 250 global $DB; 251 252 $instanceids = array(); 253 foreach ($instances as $instance) { 254 $instanceids[] = $instance->id; 255 } 256 257 // This is a multi db compatible method of creating the correct sql when using the 'IN' value. 258 // $insql is the sql statement, $params are the id numbers. 259 list($insql, $params) = $DB->get_in_or_equal($instanceids); 260 $sql = 'id ' . $insql; 261 $DB->delete_records_select('tag_instance', $sql, $params); 262 263 // Now go through and record each tag individually with the event system. 264 foreach ($instances as $instance) { 265 // Trigger tag removed event (i.e. The tag instance has been removed). 266 \core\event\tag_removed::create_from_tag_instance($instance, $instance->name, 267 $instance->rawname, true)->trigger(); 268 } 269 } 270 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |