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 mod_questionnaire\feedback;use invalid_parameter_exception;use coding_exception;/*** Class for describing a feedback section.** @package mod_questionnaire* @copyright 2018 onward Mike Churchward (mike.churchward@poetopensource.org)* @author Mike Churchward* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class section {/** @var int */public $id = 0;/** @var int */public $surveyid = 0;/** @var int */public $section = 1;/** @var array */public $scorecalculation = [];/** @var string */public $sectionlabel = '';/** @var string */public $sectionheading = '';/** @var string */public $sectionheadingformat = FORMAT_HTML;/** @var array */public $sectionfeedback = [];/** @var array */public $questions = [];/** The table name. */const TABLE = 'questionnaire_fb_sections';/** Represents the "no score" setting. */const NOSCORE = -1;/*** Section constructor.* if $params is provided, loads the entire feedback section record from the specified parameters. Parameters can be:* 'id' - the id field of the fb_sections table (required if no 'surveyid' field),* 'surveyid' - the surveyid field of the fb_sections table (required if no 'id' field),* 'sectionnum' - the section field of the fb_sections table (ignored if 'id' is present; defaults to 1).** @param array $questions Array of mod_questionnaire\question objects.* @param array $params As above* @throws \dml_exception* @throws coding_exception* @throws invalid_parameter_exception*/public function __construct($questions, $params = []) {if (!is_array($params) || !is_array($questions)) {throw new coding_exception('Invalid data provided.');}$this->questions = $questions;// Return a new section based on the data parameters if present.if (isset($params['id']) || isset($params['surveyid'])) {$this->load_section($params);}}/*** Factory method to create a new, empty section and return an instance.* @param int $surveyid* @param string $sectionlabel* @return section*/public static function new_section($surveyid, $sectionlabel = '') {global $DB;$newsection = new self([], []);if (empty($sectionlabel)) {$sectionlabel = get_string('feedbackdefaultlabel', 'questionnaire');}$maxsection = $DB->get_field(self::TABLE, 'MAX(section)', ['surveyid' => $surveyid]);$newsection->surveyid = $surveyid;$newsection->section = $maxsection + 1;$newsection->sectionlabel = $sectionlabel;$newsection->scorecalculation = $newsection->encode_scorecalculation([]);$newsecid = $DB->insert_record(self::TABLE, $newsection);$newsection->id = $newsecid;$newsection->scorecalculation = [];return $newsection;}/*** Loads the entire feedback section record from the specified parameters. Parameters can be:* 'id' - the id field of the fb_sections table (required if no 'surveyid' field),* 'surveyid' - the surveyid field of the fb_sections table (required if no 'id' field),* 'sectionnum' - the section field of the fb_sections table (ignored if 'id' is present; defaults to 1).** @param array $params* @throws \dml_exception* @throws coding_exception* @throws invalid_parameter_exception*/public function load_section($params) {global $DB;if (!is_array($params)) {throw new coding_exception('Invalid data provided.');} else if (isset($params['id'])) {$where = 'WHERE fs.id = :id ';} else if (isset($params['surveyid'])) {$where = 'WHERE fs.surveyid = :surveyid AND fs.section = :sectionnum ';if (!isset($params['sectionnum'])) {$params['sectionnum'] = 1;}} else {throw new coding_exception('No valid data parameters provided.');}$select = 'SELECT f.id as fbid, fs.*, f.feedbacklabel, f.feedbacktext, f.feedbacktextformat, f.minscore, f.maxscore ';$from = 'FROM {' . self::TABLE . '} fs LEFT JOIN {' . sectionfeedback::TABLE . '} f ON fs.id = f.sectionid ';$order = 'ORDER BY minscore DESC';if (!($feedbackrecs = $DB->get_records_sql($select . $from . $where . $order, $params))) {throw new invalid_parameter_exception('No feedback sections exists for that data.');}foreach ($feedbackrecs as $fbid => $feedbackrec) {if (empty($this->id)) {$this->id = $feedbackrec->id;$this->surveyid = $feedbackrec->surveyid;$this->section = $feedbackrec->section;$this->scorecalculation = $this->get_valid_scorecalculation($feedbackrec->scorecalculation);$this->sectionlabel = $feedbackrec->sectionlabel;$this->sectionheading = $feedbackrec->sectionheading;$this->sectionheadingformat = $feedbackrec->sectionheadingformat;}if (!empty($fbid)) {$feedbackrec->id = $fbid;$this->sectionfeedback[$fbid] = new sectionfeedback(0, $feedbackrec);}}}/*** Loads the section feedback record into the proper array location.** @param \stdClass $feedbackrec* @return int The id of the section feedback record.*/public function load_sectionfeedback($feedbackrec) {if (!isset($feedbackrec->id) || empty($feedbackrec->id)) {$sectionfeedback = sectionfeedback::new_sectionfeedback($feedbackrec);$this->sectionfeedback[$sectionfeedback->id] = $sectionfeedback;return $sectionfeedback->id;} else {$this->sectionfeedback[$feedbackrec->id] = new sectionfeedback(0, $feedbackrec);return $feedbackrec->id;}}/*** Updates the object and data record with a new scorecalculation. If no new score provided, uses what's in the object.** @param array $scorecalculation* @throws coding_exception*/public function set_new_scorecalculation($scorecalculation = null) {global $DB;if ($scorecalculation == null) {$scorecalculation = $this->scorecalculation;}if (is_array($scorecalculation)) {$newscore = $this->encode_scorecalculation($scorecalculation);$DB->set_field(self::TABLE, 'scorecalculation', $newscore, ['id' => $this->id]);$this->scorecalculation = $scorecalculation;} else {throw new coding_exception('Invalid scorecalculation format.');}}/*** Removes the question from this section and updates the database.** @param int $qid The question id and index.* @throws \dml_exception* @throws coding_exception*/public function remove_question($qid) {if (isset($this->scorecalculation[$qid])) {unset($this->scorecalculation[$qid]);$this->set_new_scorecalculation();}}/*** Deletes this section from the database. Object is invalid after that.* This will also adjust the section numbers so that they are sequential and begin at 1.*/public function delete() {global $DB;$this->delete_sectionfeedback();$DB->delete_records(self::TABLE, ['id' => $this->id]);// Resequence the section numbers as necessary.if ($allsections = $DB->get_records(self::TABLE, ['surveyid' => $this->surveyid], 'section ASC')) {$count = 1;foreach ($allsections as $id => $section) {if ($section->section != $count) {$DB->set_field(self::TABLE, 'section', $count, ['id' => $id]);}$count++;}}}/*** Deletes the section feedback records from the database and clears the object array.** @throws \dml_exception*/public function delete_sectionfeedback() {global $DB;// It's quicker to delete all of the records at once then to go through the array and delete each object.$DB->delete_records(sectionfeedback::TABLE, ['sectionid' => $this->id]);$this->sectionfeedback = [];}/*** Updates the data record with what is currently in the object instance.** @throws \dml_exception* @throws coding_exception*/public function update() {global $DB;$this->scorecalculation = $this->encode_scorecalculation($this->scorecalculation);$DB->update_record(self::TABLE, $this);$this->scorecalculation = $this->get_valid_scorecalculation($this->scorecalculation);foreach ($this->sectionfeedback as $sectionfeedback) {$sectionfeedback->update();}}/*** Decode and ensure scorecalculation is what we expect.* @param string|null $codedstring* @return array* @throws coding_exception*/public static function decode_scorecalculation(?string $codedstring): array {// Expect a serialized data string.if (($codedstring == null)) {$codedstring = '';}if (!is_string($codedstring)) {throw new coding_exception('Invalid scorecalculation format.');}if (!empty($codedstring)) {$scorecalculation = unserialize_array($codedstring) ?: [];} else {$scorecalculation = [];}if (!is_array($scorecalculation)) {throw new coding_exception('Invalid scorecalculation format.');}foreach ($scorecalculation as $score) {if (!empty($score) && !is_numeric($score)) {throw new coding_exception('Invalid scorecalculation format.');}}return $scorecalculation;}/*** Return the decoded and validated calculation array.* @param string $codedstring* @return mixed* @throws coding_exception*/protected function get_valid_scorecalculation($codedstring) {$scorecalculation = static::decode_scorecalculation($codedstring);// Check for deleted questions and questions that don't support scores.foreach ($scorecalculation as $qid => $score) {if (!isset($this->questions[$qid])) {unset($scorecalculation[$qid]);} else if (!$this->questions[$qid]->supports_feedback_scores()) {$scorecalculation[$qid] = self::NOSCORE;}}return $scorecalculation;}/*** Return the encoded score array as a serialized string.* @param string $scorearray* @return mixed* @throws coding_exception*/protected function encode_scorecalculation($scorearray) {// Expect an array.if (!is_array($scorearray)) {throw new coding_exception('Invalid scorearray format.');}$scorecalculation = serialize($scorearray);return $scorecalculation;}}