Skip to Content

Chapitre 12.2 — Cheatsheets

Partie 12 : Annexes — À épingler pendant que vous codez. Snippets copiables, alignés sur Moodle 5.2 (PHP 8.3+, racine web public/). Les points qui varient selon les versions sont signalés.


1. DML — les méthodes $DB (base de données)

global $DB; puis :

// LECTURE $record = $DB->get_record('table', ['id' => $id], '*', MUST_EXIST); // 1 ligne (exception si absente) $record = $DB->get_record('table', ['id' => $id]); // 1 ligne ou false $records = $DB->get_records('table', ['userid' => $uid], 'timecreated DESC'); // tableau indexé par id $records = $DB->get_records_sql($sql, $params); // SQL libre $rs = $DB->get_recordset('table', ['userid' => $uid]); // STREAMING (gros volumes) foreach ($rs as $r) { /* … */ } $rs->close(); // TOUJOURS close() $value = $DB->get_field('table', 'name', ['id' => $id]); // 1 colonne $list = $DB->get_fieldset_select('table', 'id', 'userid = ?', [$uid]); // 1 colonne, plusieurs lignes $n = $DB->count_records('table', ['completed' => 1]); $exists = $DB->record_exists('table', ['id' => $id]); // bool // ÉCRITURE $id = $DB->insert_record('table', (object)['name' => 'x', 'userid' => $uid]); // retourne l'id $DB->insert_records('table', $arrayofobjects); // insertion en masse $DB->update_record('table', $object); // $object->id requis $DB->set_field('table', 'completed', 1, ['id' => $id]); $DB->delete_records('table', ['id' => $id]); $DB->delete_records_select('table', 'timecreated < ?', [$cutoff]); $DB->execute($sql, $params); // SQL arbitraire // TRANSACTIONS $tx = $DB->start_delegated_transaction(); // … opérations … $tx->allow_commit();

⚠️ Toujours des placeholders (? ou :name), jamais de concaténation SQL. Nom de table sans préfixe mdl_ (Moodle l’ajoute) ; en SQL libre, {table}.


2. Le pattern de page sécurisée

<?php require(__DIR__ . '/../../config.php'); // bootstrap : $CFG,$DB,$USER,$PAGE,$OUTPUT require_login(); // + $course, $cm pour une activité $context = context_system::instance(); // ou context_module::instance($cm->id) require_capability('component/xxx:cap', $context); $PAGE->set_context($context); $PAGE->set_url(new moodle_url('/local/xxx/index.php')); $PAGE->set_pagelayout('standard'); $PAGE->set_title(get_string('x', 'component')); $PAGE->set_heading(get_string('x', 'component')); // Actions mutantes : jeton CSRF obligatoire $action = optional_param('action', '', PARAM_ALPHA); if ($action) { require_sesskey(); /* … */ redirect($url); } // pattern PRG echo $OUTPUT->header(); echo $OUTPUT->render_from_template('component/tpl', $context_array); echo $OUTPUT->footer();

Types courants : required_param('id', PARAM_INT), optional_param('q', '', PARAM_TEXT). Voir §7 pour les PARAM_*.


3. Squelettes de plugins

local (public/local/todo/) :

version.php · lib.php · index.php · settings.php db/{access.php, install.xml, upgrade.php} · lang/en/local_todo.php classes/{manager.php, form/x_form.php, output/{renderable.php, renderer.php}} templates/x.mustache

block (public/blocks/xp/) :

version.php · block_xp.php (class block_xp extends block_base) · edit_form.php db/access.php (:addinstance + :myaddinstance) · lang/en/block_xp.php

mod (public/mod/xxx/) :

version.php · lib.php (FEATURE_*, _add/_update/_delete_instance, grades) mod_form.php · view.php · index.php · lang/en/xxx.php (SANS mod_) db/{access.php, install.xml, upgrade.php} classes/{completion/custom_completion.php, event/*, output/*} backup/moodle2/*

version.php minimal :

$plugin->component = 'local_todo'; $plugin->version = 2026070800; // AAAAMMJJXX $plugin->requires = 2026042000; // Moodle 5.2 $plugin->supported = [502, 503]; $plugin->maturity = MATURITY_STABLE; $plugin->release = 'v1.0';

4. CLI admin (les commandes du quotidien)

php admin/cli/upgrade.php --non-interactive # jouer les upgrades (core + plugins) php admin/cli/purge_caches.php # LE geste le plus fréquent php admin/cli/maintenance.php --enable # / --disable php admin/cli/cron.php # lancer le cron manuellement php admin/cli/cfg.php --name=theme --set=boost # lire/écrire un réglage php admin/cli/install_database.php ... # install en CLI php admin/tool/behat/cli/init.php # init Behat php admin/tool/phpunit/cli/init.php # init PHPUnit

Débogage (config.php) :

$CFG->debug = (E_ALL | E_STRICT); // DEBUG_DEVELOPER $CFG->debugdisplay = 1; $CFG->cachejs = false; // servir amd/src sans rebuild $CFG->themedesignermode = true; // SCSS recompilé à chaque page (DEV ONLY)

5. moodle-docker (dev local)

export MOODLE_DOCKER_WWWROOT=/path/to/moodle export MOODLE_DOCKER_DB=pgsql bin/moodle-docker-compose up -d bin/moodle-docker-wait-for-db bin/moodle-docker-compose exec webserver php admin/cli/install_database.php \ --agree-license --fullname="Dev" --shortname="dev" \ --adminpass="Test1234!" --adminemail="a@example.com" # lancer une commande : bin/moodle-docker-compose exec webserver php admin/cli/purge_caches.php bin/moodle-docker-compose down

6. moodle-plugin-ci (CI)

composer create-project moodlehq/moodle-plugin-ci ci ^4 export PATH="$(pwd)/ci/bin:$PATH" moodle-plugin-ci install --plugin ./plugin --db-host=127.0.0.1 moodle-plugin-ci phplint moodle-plugin-ci phpcs --max-warnings 0 # moodle-cs moodle-plugin-ci phpdoc moodle-plugin-ci validate moodle-plugin-ci savepoints moodle-plugin-ci mustache moodle-plugin-ci grunt moodle-plugin-ci phpunit moodle-plugin-ci behat --profile chrome

7. Les PARAM_* les plus utilisés

TypeUsage
PARAM_INTentier
PARAM_FLOATdécimal
PARAM_BOOL0/1
PARAM_ALPHAlettres a-z
PARAM_ALPHANUMlettres + chiffres
PARAM_TEXTtexte sans HTML (nettoyé)
PARAM_RAWbrut (à échapper à la sortie soi-même !)
PARAM_NOTAGSHTML supprimé
PARAM_URLURL valide
PARAM_EMAILe-mail
PARAM_FILEnom de fichier sûr
PARAM_PATHchemin sûr
PARAM_COMPONENTfrankenstyle (mod_forum)
PARAM_SEQUENCEliste d’ids 1,2,3

8. Web Services — REST

# Handshake / test du token curl 'https://moodle.example.com/webservice/rest/server.php' \ --data-urlencode 'wstoken=TOKEN' \ --data-urlencode 'wsfunction=core_webservice_get_site_info' \ --data-urlencode 'moodlewsrestformat=json' # Paramètres imbriqués (notation à crochets) --data-urlencode 'options[ids][0]=2' --data-urlencode 'enrolments[0][userid]=123'

Client TypeScript (côté serveur Next.js) — voir ch. 9.5 pour callMoodle() complet :

const body = new URLSearchParams({ wstoken: TOKEN, wsfunction, moodlewsrestformat: 'json', ...flatten(args) }); const res = await fetch(`${URL}/webservice/rest/server.php`, { method: 'POST', body }); const data = await res.json(); if ('exception' in data) throw new MoodleError(data.errorcode, data.message); // HTTP 200 trompeur !

Fonctions WS core utiles :

FonctionRôle
core_webservice_get_site_infohandshake, fonctions autorisées
core_course_get_courseslister les cours
core_enrol_get_users_coursescours d’un utilisateur
enrol_manual_enrol_usersinscrire
core_user_get_usersrechercher des utilisateurs
gradereport_user_get_grade_itemsnotes d’un utilisateur
core_course_get_contentssections/activités d’un cours

9. Frontend — Mustache / AMD / core-ajax

Mustache :

{{name}} {{! échappé (sûr) }} {{{formattedhtml}}} {{! brut — HTML nettoyé côté PHP uniquement }} {{#items}}…{{/items}} {{! section / boucle }} {{^items}}Vide{{/items}} {{! inversion (else) }} {{> component/partial }} {{! partial }} {{#str}}save, core{{/str}} {{! chaîne de langue }} {{#pix}}t/edit, core{{/pix}} {{! icône }} id="x-{{uniqid}}" {{! id unique (instances multiples) }} {{#js}} require([...], m => m.init("x-{{uniqid}}")); {{/js}}

AMD (amd/src/x.js, build : grunt amd) :

import Ajax from 'core/ajax'; import Templates from 'core/templates'; import Notification from 'core/notification'; export const init = (rootId) => { const root = document.getElementById(rootId); root.addEventListener('click', async (e) => { // délégation const btn = e.target.closest('[data-action="save"]'); if (!btn) return; try { const res = await Ajax.call([{ methodname: 'component_x', args: {id: 1} }])[0]; // [0] ! const {html, js} = await Templates.renderForPromise('component/tpl', res); Templates.replaceNodeContents(root, html, js); } catch (err) { Notification.exception(err); } }); };

Appel depuis PHP : $PAGE->requires->js_call_amd('component/x', 'init', [$id]);

Rendu PHP → Mustache :

echo $OUTPUT->render_from_template('component/tpl', ['name' => format_string($x->name)]);

10. External function (créer un WS) — squelette

namespace component\external; use core_external\{external_api, external_function_parameters, external_value, external_single_structure}; class get_x extends external_api { public static function execute_parameters(): external_function_parameters { return new external_function_parameters(['id' => new external_value(PARAM_INT, 'x id')]); } public static function execute(int $id): array { ['id' => $id] = self::validate_parameters(self::execute_parameters(), ['id' => $id]); $context = \context_system::instance(); self::validate_context($context); require_capability('component/xxx:view', $context); // … métier (filtrer par $USER->id !) … return ['name' => \core_external\util::format_string($name, $context)]; } public static function execute_returns(): external_single_structure { return new external_single_structure(['name' => new external_value(PARAM_TEXT, 'name')]); } }

Déclarer dans db/services.php (classname, type, ajax => true, capabilities), bumper version.php.


11. Branches git

BrancheVersion
maindéveloppement (future version)
MOODLE_502_STABLEMoodle 5.2
MOODLE_501_STABLEMoodle 5.1
MOODLE_500_STABLEMoodle 5.0

Plugin : une branche par version majeure supportée ; correctifs cherry-pickés entre branches (ch. 6.7).


12. Thème — pipeline SCSS (rappel)

  • pre-SCSS (prescsscallback) → variables Bootstrap ($primary, $font-family-base) — AVANT.
  • preset (scss) → Bootstrap + Boost.
  • extra-SCSS (extrascsscallback) → règles finales — APRÈS (peut utiliser $primary).

Variables → pre ; règles → extra. Réglage → set_updatedcallback('theme_reset_all_caches').


Chapitre suivant : 03 — Pièges courants et FAQ.