Como vimos en la Guía Definitiva: Crea tu primer bloque en Moodle 4.5 desde cero (Parte 1), ya sentamos las bases de nuestro desarrollo. De hecho, logramos crear un bloque que se instalaba correctamente, aparecía en la barra lateral del curso y saludaba al mundo. Para un proyecto personal o una prueba de concepto, eso suele ser más que suficiente.
Sin embargo, tenemos que ser sinceros: en el desarrollo de software profesional, «que funcione» es solo el 20% del trabajo.
Por ejemplo, si trabajas en una universidad con 40.000 alumnos activos, o en una multinacional que utiliza Moodle para su formación corporativa, tu bloque tiene que ser robusto como una roca. Además, no puede ralentizar la carga del sitio bajo ninguna circunstancia. Por todo ello, debe respetar escrupulosamente los roles de seguridad y permitir que los administradores y profesores lo adapten sin tener que tocar una sola línea de código fuente.
Así que, en esta segunda entrega para Moodle 4.5+, vamos a diseccionar la anatomía avanzada de un plugin. Vamos a dejar atrás el código estático y nos adentraremos en la configuración multinivel, la seguridad por contextos, la optimización extrema de base de datos con caché y la arquitectura limpia.

Prepárate un buen café y abre tu IDE, porque hoy vamos a profundizar de verdad en las entrañas de la plataforma para crear bloques Moodle a un nivel verdaderamente profesional.
Qué aprenderás en esta segunda parte
En la Parte 1 construimos la base: creamos un bloque funcional en Moodle 4.5, entendimos su estructura mínima, lo instalamos correctamente y comprobamos cómo se integra en el curso.
Pero en proyectos reales, eso es solo el punto de partida.
En esta segunda entrega damos el salto hacia un desarrollo orientado a producción. Aquí ya no se trata de que el bloque aparezca en pantalla, sino de que esté diseñado para convivir con miles de usuarios, respetar el modelo de seguridad de Moodle y no comprometer el rendimiento del sistema.
En esta Parte 2 aprenderás a:
- Diseñar una configuración multinivel coherente: ajustes globales para administración y configuración específica por instancia.
- Definir capacidades propias y aplicar correctamente la Access API respetando el sistema de contextos.
- Implementar controles de seguridad explícitos para evitar accesos indebidos y comportamientos no deseados.
- Optimizar el rendimiento utilizando la Moodle Universal Cache (MUC) y eliminando consultas costosas en cada carga de página.
- Separar lógica y presentación mediante plantillas Mustache para mantener una arquitectura limpia y mantenible.
- Aplicar internacionalización conforme a los estándares oficiales de Moodle.
Si la primera parte te permitió crear un bloque que funciona, esta segunda te permitirá crear un bloque que puede mantenerse en producción con garantías técnicas.
1. La filosofía de configuración al crear bloques Moodle
Uno de los errores más comunes —y peligrosos— cuando empezamos a desarrollar en Moodle es introducir valores fijos (hardcoded) directamente en el código PHP. Por ejemplo, si quieres que el bloque muestre los últimos 5 cursos de un alumno, puede ser tentador escribir un 5 en la consulta SQL. Aunque parezca inofensivo, esto a la larga es un grave error. La realidad es que mañana un profesor querrá ver 10 elementos y otro preferirá ver solo 3.

Afortunadamente, al crear bloques Moodle, el sistema nos ofrece una configuración extremadamente potente y granular que se divide en dos grandes mundos: los ajustes globales del administrador y los ajustes locales del usuario/profesor.
1.1. Ajustes Globales de Administración (settings.php)
Para empezar, los ajustes globales definen cómo se comporta tu plugin en todo el sitio. Estos valores se guardan en la tabla central mdl_config_plugins y solo pueden ser modificados por los administradores de la plataforma.
Imagina, por ejemplo, que tu bloque necesita conectarse con una API externa (ya sea para mostrar noticias del CRM o para verificar licencias, algo habitual si sueles trabajar con la API de Moodle para automatizar tareas). Evidentemente, la API Key jamás debe configurarla un profesor, ya que es un dato sensible de infraestructura.
Para habilitar este panel, debes crear el archivo settings.php en el directorio raíz de tu bloque:
<?php
defined('MOODLE_INTERNAL') || die();
// Primero, comprobamos si el administrador está en el árbol de administración
if ($ADMIN->fulltree) {
// Añadimos un encabezado visual para organizar la página
$settings->add(new admin_setting_heading(
'block_mibloque/general_settings',
get_string('generalsettings', 'block_mibloque'),
get_string('generalsettings_desc', 'block_mibloque')
));
// Luego, creamos un ajuste de tipo texto para una API Key o Token
$settings->add(new admin_setting_configtext(
'block_mibloque/api_key',
get_string('apikey', 'block_mibloque'),
get_string('apikey_desc', 'block_mibloque'),
'', // Valor por defecto
PARAM_ALPHANUMEXT, // Filtro de seguridad vital
30 // Tamaño visual en pantalla
));
// Finalmente, un interruptor para activar funcionalidades pesadas
$settings->add(new admin_setting_configcheckbox(
'block_mibloque/use_advanced_features',
get_string('useadvanced', 'block_mibloque'),
get_string('useadvanced_desc', 'block_mibloque'),
0 // Desactivado por defecto por precaución
));
}

mdl_config_plugins.Entonces, ¿cómo recuperamos este valor en el código de nuestro bloque? En la práctica, es tremendamente sencillo. Solo necesitas llamar a la función global de la siguiente manera:
$apikey = get_config('block_mibloque', 'api_key');
$is_advanced = get_config('block_mibloque', 'use_advanced_features');
if (empty($apikey)) {
// Aquí podrías manejar el error elegantemente, mostrando un aviso
}
1.2. Configuración por Instancia (edit_form.php)
Por otro lado, aquí es donde ocurre la magia de cara al usuario final. La configuración por instancia permite que cada copia del bloque sea completamente diferente, incluso si están instaladas dentro del mismo curso.

Para crear este panel visual, Moodle utiliza su biblioteca oficial MoodleForms (Form API). Solo tienes que crear el archivo edit_form.php:
<?php
class block_mibloque_edit_form extends block_edit_form {
protected function specific_definition($mform) {
// Cabecera de la sección
$mform->addElement('header', 'configheader', get_string('blocksettings', 'block_mibloque'));
// Campo de texto para que el profesor asigne un título personalizado
$mform->addElement('text', 'config_title', get_string('configtitle', 'block_mibloque'));
$mform->setType('config_title', PARAM_TEXT);
$mform->setDefault('config_title', get_string('pluginname', 'block_mibloque'));
// Un menú desplegable de opciones
$options = array(
3 => '3 elementos',
5 => '5 elementos',
10 => '10 elementos'
);
$mform->addElement('select', 'config_items_count', get_string('itemscount', 'block_mibloque'), $options);
$mform->setDefault('config_items_count', 5);
// Añadimos el clásico botón de ayuda de Moodle
$mform->addHelpButton('config_items_count', 'itemscount', 'block_mibloque');
}
}
Un detalle técnico que no debes olvidar: En los formularios de bloques, los nombres de los campos deben empezar por config_. Si no lo haces, Moodle simplemente ignorará el campo. Posteriormente, el sistema lo guardará de forma automática en la tabla mdl_block_instances.
Para utilizar estos datos, simplemente accedes a la propiedad $this->config de tu clase:
$numero_items = isset($this->config->items_count) ? $this->config->items_count : 5;2. Seguridad Avanzada: El Sistema de Contextos y Capacidades

A nivel de arquitectura, Moodle no es un sitio web plano; más bien, es una jerarquía estricta. De arriba a abajo tenemos el contexto del Sistema, la Categoría, el Curso, el Módulo y el Bloque.
Por este motivo, si tu bloque procesa información sensible, debes estar 100% seguro de que un estudiante curioso no puede alterar la URL para ver los datos de sus compañeros. Es una capa de protección tan vital como configurar la Autenticación Multifactor en Moodle para evitar accesos no deseados.
2.1. Definición de Capacidades Propias (db/access.php)
Un error gravísimo en las auditorías de código es reutilizar capacidades del núcleo (como preguntar si alguien es profesor). Nunca uses capacidades de otros. En su lugar, usa la API de Acceso de Moodle (Access API) para crear las tuyas propias. De este modo, la institución podrá asignar permisos específicos a medida.
Para implementarlo, crea el archivo db/access.php:
<?php
defined('MOODLE_INTERNAL') || die();
$capabilities = [
// Capacidad básica para ver el bloque
'block/mibloque:view' => [
'captype' => 'read',
'contextlevel' => CONTEXT_BLOCK,
'archetypes' => [
'guest' => CAP_PREVENT,
'student' => CAP_ALLOW,
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW,
],
],
// Capacidad avanzada para gestionar datos internos
'block/mibloque:manage_data' => [
'captype' => 'write',
'contextlevel' => CONTEXT_BLOCK,
'archetypes' => [
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW,
],
],
];
2.2. Aplicando la seguridad como un cerrojo
Una vez definidas, en tu archivo principal block_mibloque.php, debes instanciar el contexto para comprobarlas. Si el usuario no cumple los requisitos, la mejor práctica es devolver null para que el bloque ni siquiera se dibuje en pantalla.
public function get_content() {
if ($this->content !== null) {
return $this->content;
}
// Instanciamos el contexto exacto de esta instancia del bloque
$context = context_block::instance($this->instance->id);
// Si no tiene permiso de lectura, el bloque desaparece
if (!has_capability('block/mibloque:view', $context)) {
return null;
}
$this->content = new stdClass();
// Si además es administrador o profesor editor, le mostramos controles extra
if (has_capability('block/mibloque:manage_data', $context)) {
$this->content->text .= '<button>Administrar datos</button>';
}
return $this->content;
}
3. Rendimiento Crítico: La API de Caché (MUC)
Caso de Estudio Real: El bloque que casi tira la universidad
Para entender por qué el rendimiento es algo innegociable, déjame contarte un caso real. Hace unos años nos contactó una universidad en pleno pánico durante la semana de exámenes finales. Tenían un servidor muy potente, pero la base de datos estaba al 100% de uso y arrojaba errores 504.

El culpable, lamentablemente, era un bloque personalizado.
El departamento de innovación había encargado un «Ranking en Vivo». Básicamente, este bloque calculaba la posición del alumno ejecutando consultas SQL muy complejas en tiempo real.
El gran problema de diseño aquí es que un bloque se ejecuta en CADA recarga de página. Por ejemplo, durante un cuestionario, cada vez que un estudiante pulsaba «Siguiente», la página recargaba y el bloque volvía a hacer sus cálculos. Si tienes 2.000 alumnos activos, estamos hablando de 100.000 consultas pesadas por hora. Naturalmente, la base de datos colapsó.
¿Cuál fue la salvación? La Moodle Universal Cache (MUC).
Modificamos el bloque de urgencia para que usara la API de MUC de Moodle apoyada en Redis. Primero, en la carga inicial, calculaba el dato. Después, en las siguientes miles de cargas, simplemente lo recuperaba de la RAM en milisegundos. Como resultado, el uso del procesador bajó de un 100% a un 15% de manera instantánea.
3.1. Registrar tu caché en db/caches.php
Para implementar este salvavidas, primero debes registrar una definición de caché en db/caches.php:
<?php
defined('MOODLE_INTERNAL') || die();
$definitions = [
'datos_procesados' => [
'mode' => cache_store::MODE_APPLICATION, // Ideal para datos compartidos
'simplekeys' => true,
'simpledata' => true,
'staticacceleration' => true,
'ttl' => 900 // Tiempo de vida de 15 minutos exactos
]
];
3.2. La lógica del patrón caché en PHP
El patrón de programación a seguir es siempre el mismo: Preguntar -> Si no existe -> Calcular -> Guardar -> Devolver.
// Instanciamos la caché que acabamos de definir
$cache = \cache::make('block_mibloque', 'datos_procesados');
// Intentamos obtener los datos
$datos_curso = $cache->get($COURSE->id);
if ($datos_curso === false) {
// Si la caché está vacía, hacemos el trabajo pesado
$datos_curso = $this->obtener_datos_complejos_de_base_de_datos($COURSE->id);
// Guardamos el resultado para el próximo usuario
$cache->set($COURSE->id, $datos_curso);
}
// A partir de aquí, usas los datos con total tranquilidad

4. Arquitectura limpia al crear bloques Moodle: Separando PHP y HTML
Históricamente, el desarrollo en Moodle consistía en escribir bloques masivos de código PHP mezclado directamente con etiquetas HTML. Esto suele crear el temido «código espagueti». Sin embargo, la plataforma ha evolucionado enormemente y hoy el estándar oficial (y un pilar esencial si vas a personalizar Moodle con un tema hijo Boost) es usar plantillas Mustache.

4.1. Extracción de Lógica
Como regla de oro, nunca dejes tu lógica de base de datos dentro del archivo principal del bloque. En su lugar, apóyate en la autocarga de clases de Moodle para crear un servicio dedicado, por ejemplo, en classes/output/main_view.php.
4.2. La Plantilla (templates/main_view.mustache)
Además, Mustache es intencionadamente «logic-less» (no acepta lógica compleja de programación). Esto nos obliga, de forma muy sana, a separar responsabilidades. Crea el archivo .mustache en tu carpeta de plantillas:
<div class="block_mibloque_container p-3 bg-white rounded shadow-sm">
<h4 class="h5 mb-3">{{titulo_personalizado}}</h4>
<ul class="list-group list-group-flush">
{{#items}}
<li class="list-group-item d-flex justify-content-between align-items-center">
{{nombre_actividad}}
<span class="badge badge-primary badge-pill">{{puntuacion}}</span>
</li>
{{/items}}
</ul>
{{^items}}
<div class="alert alert-info" role="alert">
{{#str}} noitems, block_mibloque {{/str}}
</div>
{{/items}}
</div>
4.3. Renderizar desde PHP
Gracias a esta separación, ahora tu método get_content() se vuelve extremadamente elegante y fácil de leer:
public function get_content() {
global $OUTPUT, $COURSE;
if ($this->content !== null) {
return $this->content;
}
$context = context_block::instance($this->instance->id);
$this->content = new stdClass();
// Recopilamos los datos (el Modelo)
$datos_para_plantilla = [
'titulo_personalizado' => $this->config->title ?? get_string('defaulttitle', 'block_mibloque'),
'items' => [
['nombre_actividad' => 'Foro Tema 1', 'puntuacion' => '10/10']
]
];
// Finalmente, renderizamos la vista pasándole los datos
$this->content->text = $OUTPUT->render_from_template('block_mibloque/main_view', $datos_para_plantilla);
return $this->content;
}
5. El toque final: Internacionalización (Idiomas)
Para terminar de redondear nuestra obra maestra, debemos cumplir una regla inquebrantable de la comunidad Moodle: nunca escribas texto directamente en inglés o español en el código. Dado que Moodle es una plataforma global, toda cadena de texto debe pasar por tu archivo de idioma en lang/en/block_mibloque.php.
<?php
$string['pluginname'] = 'Mi Bloque Profesional';
$string['blocksettings'] = 'Ajustes de instancia del bloque';
$string['configtitle'] = 'Título personalizado';
$string['apikey'] = 'Clave de API externa';
// Etc...
Resumen y Siguientes Pasos
En resumen, a lo largo de esta entrega hemos transformado un bloque básico en un componente de grado empresarial. Básicamente, hemos dejado atrás las prácticas amateur para centrarnos en lo que importa:
- Es altamente configurable: El administrador mantiene el control global y, simultáneamente, el profesor adapta lo visual a su curso.
- Es seguro por diseño: Validamos explícitamente los permisos en cada contexto.
- Está optimizado: Implementamos la MUC para sobrevivir sin problemas a los picos de carga.
- Es limpio y mantenible: Separamos completamente la lógica del HTML usando plantillas Mustache.
En definitiva, este es el nivel de calidad técnica que se te va a exigir al desarrollar proyectos reales cuando te encarguen crear bloques Moodle.
Entonces, ¿qué nos depara la Parte 3?
Hasta ahora, nuestro bloque es robusto pero estático. En la siguiente entrega daremos el salto definitivo a la Web Moderna:
- Crearemos interactividad con JavaScript usando módulos AMD.
- Haremos peticiones asíncronas (AJAX) sin necesidad de recargar la página.
- Y usaremos las Tareas Programadas (Cron Jobs) para procesar informes pesados en segundo plano mientras el usuario descansa.
Y tú, ¿ya estás aplicando estas arquitecturas en tus desarrollos diarios? Por otro lado, ¿qué problema de rendimiento te has encontrado alguna vez gestionando Moodle? Cuéntamelo en los comentarios, ¡te leo!
¿Tienes plugins personalizados en tu Moodle?
Muchos problemas de rendimiento en periodos críticos —exámenes, matriculación, evaluaciones masivas— no se originan en la infraestructura.
Se originan en el código.
Consultas innecesarias en cada recarga, ausencia de caché, capacidades mal definidas o lógica acoplada a la presentación son errores frecuentes en desarrollos a medida que, bajo carga real, se convierten en riesgos operativos.
Si tu institución utiliza plugins personalizados o ha realizado adaptaciones sobre Moodle, es fundamental asegurarse de que cumplen estándares sólidos de seguridad, rendimiento y arquitectura.

En Entornos de Formación ayudamos a universidades y organizaciones a:
- Auditar desarrollos personalizados y detectar cuellos de botella.
- Optimizar bloques y plugins para entornos de alta concurrencia.
- Diseñar arquitecturas escalables y mantenibles.
- Automatizar procesos complejos sin comprometer la estabilidad del sistema.
- Integrar Moodle con ecosistemas corporativos (ERP, CRM, SSO, herramientas externas).
- Evolucionar plataformas Moodle 4.x y Moodle 5 con criterios técnicos rigurosos.
Desde consultoría estratégica hasta acompañamiento técnico continuo, trabajamos como partner tecnológico, para que tu LMS no solo funcione, sino que escale con garantías.
Si quieres revisar la salud técnica de tu plataforma o planificar su evolución, podemos analizar tu caso en una conversación técnica.
👉 Entornos de Formación – edTech Solutions
🌐 https://edf.global






