Uploaded image for project: 'Moodle'
  1. Moodle
  2. MDL-53451

Integrate CBE as a core plugin




      CBE is the acronym for Competency-Based Education, and also the name of the HQ project adding support for competencies in Moodle. It has been developed as an admin tool primarily to be self-contained and potentially backportable, but also to simplify the development spread over many months as it reduces the need and complexity of weekly rebases.

      CBE comes with several new concepts (basic "models", "serialisers", ...) which could be beneficial to other developers, they have been developed to be pushed to core. However, due to time constraint and lack of broader discussions we will not send those new concepts for integration and will leave them in the admin tool. We are thinking of preparing them for integration during the 3.2 cycle which would leave more room for improvement and full considerations. (MDL-52243 summarises the elements that should be moved to core).

      In order for the plugin to be 100% self-contained we have had to make some adjustments to core (adding support for backups, navigation, etc...). Most of these adjustments have been made through separate MDL issues which have been integrated following the formal integration process. However, a few have made it to the CBE branch. We will likely extract those to separate issues when required by an integrator, at this stage it is easier for us to have them there (rebase, ...).

      New concepts

      Persistent, or Model

      The class persistent represents an object in the database. It is typically what is called a model in general. It contains the list of fields present in the database table, as well as their PARAM_* types. Basic validation of the param types is automatically performed prior to any database write operations. Additional validation can also be added by developers. In essence, the persistent knows everything about its data and when it is valid or not. Persistents are able to CRUD themselves in the database. They are also the right place to perform complex queries which join multiple persistents together.

      There is never any access control performed within the persistent! However, CBE is oriented model-based ACL, therefore some of the persistents contain helper ACL methods (boolean function can_do_the_thing();). The main benefit of adding ACL methods to the persistent is to avoid repeating the capability names all over the place, and thus reduce code complexity and human errors.


      The API class contains all the methods which are publicly available to any code taking action on CBE related objects and data. No code should ever directly call the database, or persistent methods, unless they are within an API method. All public API methods MUST perform the capability checks relevant to the actions that they are representing. You will notice that none of the external functions contain logic, they all defer it to the API class. The API methods can be seen as the controllers, they are a mandatory step to accessing the data, even within CBE itself.


      When writing external functions and templates we often needed to explicitly specify all the properties of the persistents. For instance, when returning a competency from an external function, when creating a competency from an external function, when preparing the context of a template, when getting the context for a template from an external function, etc... As persistents already define their properties we decided to create exporters (they are sometimes referred to as serialisers), and persistent-based exporters.

      Exporters are classes which convert objects to stdClasses. They may add more properties to the objects as they export them (E.g. generating a timeago property from the date property of the object). Therefore exporters are aware and define all the properties that can be read from an object, and all the properties that are saved in an object.

      They have 2 purposes.

      1. They export an object consistently wherever they are used, e.g. all properties will always be present
      2. They generate the external function parameters/returns structure automatically
        • The parameters will be the update structure, which contains the real fields
        • The returns structure will be the read structure, which contains everything.

      Exporters can define related objects. When specified those must be passed, mainly to improve performance. At this stage I find them a bit weak and difficult to work with as adding related object to an existing exporter will require all usage of this exporter to be updated. Also, when nested, the exporters don't work as nicely as I would like, we have raised an issue to try to improve their design (hence the reason to not push them to core for 3.1 - MDL-53052).

      Though, exporters have come very handy when we had to add new properties to persistent, or to export new properties from an existing object. They greatly simplify maintainability of external functions and templatable::export_for_template().

      Remaining work

      We've had an internal soft-freeze during which we agreed on the feature set required for 3.1 and thus we are not adding any new feature. If some were to be missing at the time this branch was pushed for integration they would be finished within 48h.

      Our main focus now is to improve the user experience through UI changes. An external person to HQ is looking at the whole project as we speak. Once we get their report we will actively work on the user interface. Our other main focus is to improve the overall stability.

      The epic MDL-49458 as it stands as I speak contains all the work remaining, including the work that will not make it to 3.1. More issues will be added there as we identify them. We will reorganise the epic once an approach is agreed upon by developers and integrators.


      This is a very basic introduction to how CBE is organised, viewed from my own eyes.

      Competencies exist through a framework and are unique within that framework. Next to frameworks we have templates (and plans) which are a set of competencies setting the goals a learner needs to achieve (Fire safety training, Medicine Year 1, ...). "In order to be considered as proficient in 'Fire safety' I need to acquire all these competencies".

      Competencies are rated (graded), the rating will determine the proficiency of the learner for that specific competency. Rating can happen either manually (by a teacher, a supervisor), or automatically (upon completion, when another one is rated, ...).

      Anything that may affect the proficiency of a learner should be recorded as an evidence. Evidence are plain text descriptions of what happened and how it has (or could have) affected the competency. When someone rates a competency an evidence will be added recording who rated, when, how, where and what rating was given. They could be seen as logs but cannot be logs as they are essential to competency based education.

      Learners also have the option to submit evidence of prior learning (user_evidence persistent) and request the review of the competencies affected by these evidence.

      Importantly, learning plans, records of proficiency, etc... and most of the things related to a user live in the user context. That is fairly different to how Moodle is structured in general where most things live in courses, or in the system.

      CBE as a plugin

      While CBE is self-contained and would do just fine as a plugin for a little while, it has some implications:

      1. Third parties should be aware that the namespace could change without notice (tool_lp\api to \core_cbe\api for instance).
      2. Event names will be bound to tool_lp, but once moved to core they will not any more, e.g. will be core_cbe

      We need to consider, address, and/or document these.

      Side note: Ignore the upgrade steps, they were there for us but they will be all removed as this is integrated.

      Description of the models

      • competency: A single competency
      • competency_framework: A framework of competencies
      • course_competency: A relationship table (Many-to-Many) linking courses and competencies
      • course_module_competency: A relationship table (Many-to-Many) linking course modules and competencies
      • evidence: A piece of evidence relating the proficiency of a user towards a competency
      • plan: A group of competencies for a user to achieve. (Sometimes referred to as user plans)
      • plan_competency: A relationship table (Many-to-Many) linking plans and competencies
      • related_competency: A relationship table (Many-to-Many) linking competency to other competencies (within the same framework)
      • template: A template for a plan
      • template_competency: A relationship table (Many-to-Many) linking templates and competencies
      • template_cohort: The cohorts attached to a template, from which we will create plans.
      • user_competency: The record of the proficiency and rating of a user towards a competency
      • user_competency_course: The record of the rating of a user towards a competency but limited to the scope of a course
      • user_competency_plan: The record of the proficiency and rating of a user within a plan that was archived, understand frozen.
      • user_evidence: A piece of evidence of prior learning
      • user_evidence_competency: Many-to-many relationship between evidence of prior learning and competencies

      I'm sure I could write another thousand words but this is all I can think of now.



        Issue Links



              fred Frédéric Massart
              fred Frédéric Massart
              David Monllaó David Monllaó
              Jun Pataleta Jun Pataleta
              Jean-Philippe Gaudreau, Steve Massicotte, Serge Gauthier, Issam Taboubi, Ilya Tregubov, Kevin Percy, Mathew May, Mihail Geshoski, Shamim Rezaie
              0 Vote for this issue
              14 Start watching this issue