AutorÃa | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>.namespace tool_courserating\local\models;use tool_courserating\constants;use tool_courserating\helper;/*** Model for summary table** @package tool_courserating* @copyright 2022 Marina Glancy <marina.glancy@gmail.com>* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class summary extends \core\persistent {/** @var string Table name */public const TABLE = 'tool_courserating_summary';/*** Return the definition of the properties of this model.** @return array*/protected static function define_properties(): array {$props = ['courseid' => ['type' => PARAM_INT,],'cntall' => ['type' => PARAM_INT,'default' => 0,],'avgrating' => ['type' => PARAM_FLOAT,'null' => NULL_ALLOWED,'default' => null,],'sumrating' => ['type' => PARAM_INT,'default' => 0,],'cntreviews' => ['type' => PARAM_INT,'default' => 0,],'ratingmode' => ['type' => PARAM_INT,'default' => 0,],];for ($i = 1; $i <= 10; $i++) {$props[self::cntkey($i)] = ['type' => PARAM_INT,'default' => 0,];}return $props;}/*** Retrieve summary for the specified course, insert in DB if does not exist** @param int $courseid* @return summary*/public static function get_for_course(int $courseid): summary {if ($summary = self::get_record(['courseid' => $courseid])) {return $summary;} else {$summary = new static(0, (object)['courseid' => $courseid,'ratingmode' => helper::get_course_rating_mode($courseid),]);$summary->save();return $summary;}}/*** Field name for the counter of rating $i** @param int $i* @return string*/protected static function cntkey(int $i) {$i = min(max(1, $i), 10);return 'cnt' . str_pad($i, 2, "0", STR_PAD_LEFT);}/*** Update summary after a rating was added** @param int $courseid* @param rating $rating* @return static*/public static function add_rating(int $courseid, rating $rating): self {if (!$record = self::get_record(['courseid' => $courseid])) {$record = new self(0, (object)['courseid' => $courseid]);}$record->set('cntall', $record->get('cntall') + 1);$record->set('sumrating', $record->get('sumrating') + $rating->get('rating'));$record->set('avgrating', 1.0 * $record->get('sumrating') / $record->get('cntall'));if ($rating->get('hasreview')) {$record->set('cntreviews', $record->get('cntreviews') + 1);}$record->set(self::cntkey($rating->get('rating')), $record->get(self::cntkey($rating->get('rating'))) + 1);$record->save();return $record;}/*** Update summary after a rating was updated** @param int $courseid* @param rating $rating* @param \stdClass $oldrecord* @return static|null*/public static function update_rating(int $courseid, rating $rating, \stdClass $oldrecord): ?self {$ratingold = $oldrecord->rating;$summary = self::get_for_course($courseid);if (!$summary->get('cntall') || !$summary->get(self::cntkey($ratingold))) {// Sanity check, did not pass, recalculate all.return $summary->recalculate();}if ($rating == $ratingold && $rating->get('hasreview') == $oldrecord->hasreview) {// Rating did not change.return null;}$summary->set('cntreviews', $summary->get('cntreviews') + $rating->get('hasreview') - $oldrecord->hasreview);if ($rating != $ratingold) {$summary->set('sumrating', $summary->get('sumrating') + $rating->get('rating') - $ratingold);$summary->set(self::cntkey($ratingold), $summary->get(self::cntkey($ratingold)) - 1);$summary->set(self::cntkey($rating->get('rating')), (int)$summary->get(self::cntkey($rating->get('rating'))) + 1);$summary->set('avgrating', 1.0 * $summary->get('sumrating') / $summary->get('cntall'));}$summary->save();return $summary;}/*** Update summary when it has to be empty - reset all counter fields** @return void* @throws \coding_exception*/public function reset_all_counters() {foreach (['cntall', 'sumrating', 'cntreviews'] as $key) {$this->set($key, 0);}$this->set('avgrating', null);for ($i = 1; $i <= 10; $i++) {$this->set(self::cntkey($i), 0);}}/*** Recalculate summary for the specific course** @return $this|null*/public function recalculate(): ?self {global $DB;if ($this->get('ratingmode') == constants::RATEBY_NOONE) {$this->reset_all_counters();$this->save();return $this;}$isempty = $DB->sql_isempty('', 'review', false, true);$sql = 'SELECT COUNT(id) AS cntall,SUM(rating) AS sumrating,SUM(CASE WHEN '.$isempty.' THEN 0 ELSE 1 END) as cntreviews,SUM(CASE WHEN rating = 1 THEN 1 ELSE 0 END) as cnt01,SUM(CASE WHEN rating = 2 THEN 1 ELSE 0 END) as cnt02,SUM(CASE WHEN rating = 3 THEN 1 ELSE 0 END) as cnt03,SUM(CASE WHEN rating = 4 THEN 1 ELSE 0 END) as cnt04,SUM(CASE WHEN rating = 5 THEN 1 ELSE 0 END) as cnt05,SUM(CASE WHEN rating = 6 THEN 1 ELSE 0 END) as cnt06,SUM(CASE WHEN rating = 7 THEN 1 ELSE 0 END) as cnt07,SUM(CASE WHEN rating = 8 THEN 1 ELSE 0 END) as cnt08,SUM(CASE WHEN rating = 9 THEN 1 ELSE 0 END) as cnt09,SUM(CASE WHEN rating = 10 THEN 1 ELSE 0 END) as cnt10FROM {tool_courserating_rating} rWHERE r.courseid = :courseid';$params = ['courseid' => $this->get('courseid')];$result = $DB->get_record_sql($sql, $params);if (!$result->cntall) {$this->reset_all_counters();} else {$keys = ['cntall', 'sumrating', 'cntreviews'];for ($i = 1; $i <= 10; $i++) {$keys[] = self::cntkey($i);}foreach ($keys as $key) {$this->set($key, $result->$key ?? 0);}$this->set('avgrating', 1.0 * $this->get('sumrating') / $this->get('cntall'));}$this->save();return $this;}/*** Recalculate summary after an individual rating was deleted** @param \stdClass $rating snapshot of the rating record before it was deleted* @return static|null*/public static function delete_rating(\stdClass $rating): ?self {$ratingold = $rating->rating;$hasreviewold = $rating->hasreview;$record = self::get_for_course($rating->courseid);if ($record->get('cntall') <= 1 || !$record->get(self::cntkey($ratingold))) {// Sanity check did not pass or no more ratings left, recalculate all.return $record->recalculate();}if ($hasreviewold && $record->get('cntreviews') > 0) {$record->set('cntreviews', $record->get('cntreviews') - 1);}$record->set('cntall', $record->get('cntall') - 1);$record->set('sumrating', $record->get('sumrating') - $ratingold);$record->set(self::cntkey($ratingold), $record->get(self::cntkey($ratingold)) - 1);$record->set('avgrating', 1.0 * $record->get('sumrating') / $record->get('cntall'));$record->save();return $record;}}