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éfixemdl_(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.mustacheblock (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.phpmod (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 PHPUnitDé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 down6. 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 chrome7. Les PARAM_* les plus utilisés
| Type | Usage |
|---|---|
PARAM_INT | entier |
PARAM_FLOAT | décimal |
PARAM_BOOL | 0/1 |
PARAM_ALPHA | lettres a-z |
PARAM_ALPHANUM | lettres + chiffres |
PARAM_TEXT | texte sans HTML (nettoyé) |
PARAM_RAW | brut (à échapper à la sortie soi-même !) |
PARAM_NOTAGS | HTML supprimé |
PARAM_URL | URL valide |
PARAM_EMAIL | |
PARAM_FILE | nom de fichier sûr |
PARAM_PATH | chemin sûr |
PARAM_COMPONENT | frankenstyle (mod_forum) |
PARAM_SEQUENCE | liste 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 :
| Fonction | Rôle |
|---|---|
core_webservice_get_site_info | handshake, fonctions autorisées |
core_course_get_courses | lister les cours |
core_enrol_get_users_courses | cours d’un utilisateur |
enrol_manual_enrol_users | inscrire |
core_user_get_users | rechercher des utilisateurs |
gradereport_user_get_grade_items | notes d’un utilisateur |
core_course_get_contents | sections/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
| Branche | Version |
|---|---|
main | développement (future version) |
MOODLE_502_STABLE | Moodle 5.2 |
MOODLE_501_STABLE | Moodle 5.1 |
MOODLE_500_STABLE | Moodle 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.