The Architecture of Open Source Applications (Volume 2)Moodle (2024)

Tim Hunt

If you enjoy these books, you may also enjoy Software Design by Example in Python and Software Design by Example in JavaScript.

Moodle is a web application used in educational settings. While this chapter willtry to give an overview of all aspects of how Moodle works, itfocuses on those areas where Moodle's design is particularlyinteresting:

  • The way the application is divided into plugins;
  • The permission system, which controls which users can perform which actions in different parts of the system;
  • The way output is generated, so that different themes (skins) can be used to give different appearances, and so that the interface can be localised.
  • The database abstraction layer.

Moodle provides a place onlinewhere students and teachers can come together to teach and learn. AMoodle site is divided into courses. A course has usersenrolled in it with different roles, such as Student or Teacher. Eachcourse comprises a number of resources and activities. A resourcemight be a PDF file, a page of HTML within Moodle, or a link tosomething elsewhere on the web. An activity might be a forum, a quiz or awiki. Within the course, these resources and activities will bestructured in some way. For example they may be grouped into logicaltopics, or into weeks on a calendar.

The Architecture of Open Source Applications (Volume 2)Moodle (1)

Moodle can be used as a standalone application. Should you wish toteach courses on software architecture (for example) you could download Moodle toyour web host, install it, start creating courses, and wait forstudents to come and self-register. Alternatively, if you are a largeinstitution, Moodle would be just one of the systems you run. Youwould probably also have the infrastructure shown inFigure 13.2.

The Architecture of Open Source Applications (Volume 2)Moodle (2)
  • An authentication/identity provider (for example LDAP) to control user accounts across all your systems.
  • A student information system; that is, a database of all your students, which program of study they are on, and hence which courses they need to complete; and their transcript—a high-level summary of the results of the courses they have completed. This would also deal with other administrative functions, like tracking whether they have paid their fees.
  • A document repository (for example, Alfresco); to store files, and track workflow as users collaborate to create files.
  • An ePortfolio; this is a place where students can assemble assets, either to build a CV (resume), or to provide evidence that they have met the requirements of a practice-based course.
  • A reporting or analytics tool; to generate high-level information about what is going on in your institution.

Moodle focuses on providing an online space forteaching and learning, rather than any of the other systems that aneducational organisation might need. Moodle provides a basicimplementation of the other functionalities, so that it can function either asa stand-alone system or integrated with other systems. The roleMoodle plays is normally called a virtual learning environment (VLE),or learning or course management system (LMS, CMS or even LCMS).

Moodle is open source or free software (GPL). It is written in PHP. Itwill run on most common web servers, on common platforms. It requiresa database, and will work with MySQL, PostgreSQL, Microsoft SQL Server orOracle.

The Moodle project was started by Martin Dougiamas in 1999, while hewas working at Curtin University, Australia. Version 1.0 was releasedin 2002, at which time PHP4.2 and MySQL 3.23 were the technologiesavailable. This limited the kind of architecture that was possibleinitially, but much has changed since then. The current release is theMoodle 2.2.x series.

13.1. An Overview of How Moodle Works

A Moodle installation comprises three parts:

  1. The code, typically in a folder like /var/www/moodle or ~/htdocs/moodle. This should not be writable by the web server.
  2. The database, managed by one of the supported RDMSs. In fact, Moodle adds a prefix to all the table names, so it can share a database with other applications if desired.
  3. The moodledata folder. This is a folder where Moodle stores uploaded and generated files, and so needs to be writable by the web server. For security reasons, the should be outside the web root.

These can all be on a single server. Alternatively, in a load-balancedset-up, there will be multiple copies of the code on each web server,but just one shared copy of the database and moodledata, probably onother servers.

The configuration information about these three parts is stored in afile called config.php in the root of the moodle folder whenMoodle is installed.

Request Dispatching

Moodle is a web applications, so users interact with it using theirweb browser. From Moodle's point of view that means responding to HTTPrequests. An important aspect of Moodle's design is, therefore, theURL namespace, and how URLs get dispatched to different scripts.

Moodle uses the standard PHP approach to this. To view the main pagefor a course,the URL would be .../course/view.php?id=123,where 123 is the unique id of the coursein the database. Toview a forum discussion, the URL would be something like.../mod/forum/discuss.php?id=456789. That is, theseparticular scripts, course/view.phpor mod/forum/discuss.php, would handle these requests.

This is simple for the developer. To understandhow Moodle handles a particular request, you look at the URL and startreading code there. It is ugly from the user's point of view. TheseURLs are, however, permanent. The URLs do not change if the course isrenamed, or if a moderator moves a discussion to a different forum. (Thisis a good property for URLs to have, as explained in Tim Berners-Lee's articleCool URIs don't change.)

The alternative approach one could take is to have a single entrypoint…/index.php/[extra-information-to-make-the-request-unique]. The single script index.php would then dispatchthe requests in some way. This approach adds a layer of indirection,which is something software developers always like to do. The lack ofthis layer of indirection does not seem to hurt Moodle.

Plugins

Like many successful open source projects, Moodle is built out of manyplugins, working together with the core of the system. This is a goodapproach because at allows people to change and enhance Moodle indefined ways. An important advantage of an open source system is thatyou can tailor it to your particular needs. Making extensivecustomisations to the code can, however, lead to big problems when thetime comes to upgrade, even when using a good version controlsystem. By allowing as many customisations and new features aspossible to be implemented as self-contained plugins that interactwith the Moodle core through a defined API, it is easier for people tocustomise Moodle to their needs, and to share customisations, whilestill being able to upgrade the core Moodle system.

There are various ways a system can be built as a core surrounded byplugins. Moodle has a relatively fat core, and the plugins arestrongly-typed. When I say a fat core, I mean that there is a lot offunctionality in the core. This contrasts with the kind of architecturewhere just about everything, except for a small plugin-loader stub, isa plugin.

When I say plugins are strongly typed, I mean that depending on whichtype of functionality you want to implement, you have to write adifferent type of plugin, and implement a different API. For example,a new Activity module plugin would be very different from a newAuthentication plugin or a new Question type. At the last count thereare about 35 different types of plugin. (There is a full list of Moodle plugin types.) This contrasts with the kindof architecture where all plugins use basically the same API and then,perhaps, subscribe to the subset of hooks or events they areinterested in.

Generally, the trend in Moodle has been to try to shrink the core, bymoving more functionality into plugins. This effort has only been somewhatsuccessful, however, because an increasing feature-set tends to expandthe core. The other trend has been to try to standardise the differenttypes of plugin as much as possible, so that in areas of commonfunctionality, like install and upgrade, all types of plugins work thesame way.

A plugin in Moodle takes the form of a folder containing files. Theplugin has a type and a name, which together make up the"Frankenstyle" component name of the plugin. (The word "Frankenstyle"arose out of an argument in the developers' Jabber channel, buteveryone liked it and it stuck.) The plugin type and name determinethe path to the plugin folder. The plugin type gives a prefix, and thefoldername is the plugin name. Here are some examples:

Plugin typePlugin nameFrankenstyleFolder
mod (Activity module)forummod_forummod/forum
mod (Activity module)quizmod_quizmod/quiz
block (Side-block)navigationblock_navigationblocks/navigation
qtype (Question type)shortanswerqtype_shortanswerquestion/type/shortanswer
quiz (Quiz report)statisticsquiz_statisticsmod/quiz/report/statistics

The last example shows that each activity module is allowed to declaresub-plugin types. At the moment only activity modules can do this, fortwo reasons. If all plugins could have sub-plugins that might causeperformance problems. Activity modules are the main educationalactivities in Moodle, and so are the most important type of plugin, thusthey get special privileges.

An Example Plugin

I will explain a lot of details of the Moodle architecture byconsidering a specific example plugin. As is traditional, I havechosen to implement a plugin that displays "Hello world".

This plugin does not really fit naturally into any of the standardMoodle plugin types. It is just a script, with no connection toanything else, so I will choose to implement it as a "local"plugin. This is a catch-all plugin type for miscellaneousfunctionality that does not fit anywhere better. I will name my plugingreet, to give a Frankensyle name of local_greet, and afolder path of local/greet. (The plugin code can be downloaded.)

Each plugin must contain a file called version.php whichdefines some basic metadata about the plugin. This is used by theMoodle's plugin installer system to install and upgrade the plugin.For example, local/greet/version.php contains:

<?php$plugin->component = 'local_greet';$plugin->version = 2011102900;$plugin->requires = 2011102700;$plugin->maturity = MATURITY_STABLE;

It may seem redundant to include the component name, since this can bededuced from the path, but the installer uses this to verify that theplugin has been installed in the right place. The version field is theversion of this plugin. Maturity is ALPHA, BETA, RC (releasecandidate), or STABLE. Requires is the minimum version of Moodle thatthis plugin is compatible with. If necessary, one can also documentother plugins that this one depends on.

Here is the main script for this simple plugin (stored inlocal/greet/index.php):

<?phprequire_once(dirname(__FILE__) . '/../../config.php'); // 1require_login(); // 2$context = context_system::instance(); // 3require_capability('local/greet:begreeted', $context); // 4$name = optional_param('name', '', PARAM_TEXT); // 5if (!$name) { $name = fullname($USER); // 6}add_to_log(SITEID, 'local_greet', 'begreeted', 'local/greet/index.php?name=' . urlencode($name)); // 7$PAGE->set_context($context); // 8$PAGE->set_url(new moodle_url('/local/greet/index.php'), array('name' => $name)); // 9$PAGE->set_title(get_string('welcome', 'local_greet')); // 10echo $OUTPUT->header(); // 11echo $OUTPUT->box(get_string('greet', 'local_greet', format_string($name))); // 12echo $OUTPUT->footer(); // 13

Line 1: Bootstrapping Moodle

require_once(dirname(__FILE__) . '/../../config.php'); // 1

The single line of this script that does the most work is the first. Isaid above that config.php contains the details Moodle needs toconnect to the database and find the moodledata folder. It ends,however, with the line require_once('lib/setup.php'). This:

  1. loads all the standard Moodle libraries using require_once;
  2. starts the session handling;
  3. connects to the database; and
  4. sets up a number of global variables, which we shall meet later.

Line 2: Checking the User Is Logged In

require_login(); // 2

This line causes Moodle to check that the current user is logged in,using whatever authentication plugin the administrator hasconfigured. If not, the user will be redirected to the log-in form, and thisfunction will never return.

A script that was more integrated into Moodlewould pass more arguments here, to say which course or activity thispage is part of, and then require_login would also verify thatthe user is enrolled in, or otherwise allowed to access this course,and is allowed to see this activity. If not, an appropriate errorwould be displayed.

13.2. Moodle's Roles and Permissions System

The next two lines of code show how to check that the user haspermission to do something. As you can see, from the developer's pointof view, the API is very simple. Behind the scenes, however, there isa sophisticated access system which gives the administrator greatflexibility to control who can do what.

Line 3: Getting the Context

$context = context_system::instance(); // 3

In Moodle, users can have different permissions in differentplaces. For example, a user might be a Teacher in one course, and aStudent in another, and so have different permissions in eachplace. These places are called contexts. Contexts in Moodle form ahierarchy rather like a folder hierarchy in a file-system. At the toplevel is the System context (and, since this script is not very wellintegrated into Moodle, it uses that context).

Within the System context are a number of contexts for the differentcategories that have been created to organise courses. These can benested, with one category containing other categories. Categorycontexts can also contain Course contexts. Finally, each activity in acourse will have its own Module context.

The Architecture of Open Source Applications (Volume 2)Moodle (3)

Line 4: Checking the User Has Permission to Use This Script

require_capability('local/greet:begreeted', $context); // 4

Having got the context—the relevant area of Moodle—the permission can be checked. Eachbit of functionality that a user may or may not have iscalled a capability. Checking a capability provides more fine-grainedaccess control than the basic checks performed byrequire_login. Our simple example plugin has just onecapability: local/greet:begreeted.

The check is done using the require_capability function, whichtakes the capability name and the context. Like otherrequire_… functions, it will not return if the userdoes not have the capability. It will display an error instead. Inother places the non-fatal has_capabilityfunction, which returns a Boolean would be used, for example, todetermine whether to display a link to this script from another page.

How does the administrator configure which user has which permission?Here is the calculation that has_capability performs (at leastconceptually):

  1. Start from the current Context.
  2. Get a list of the Roles that the user has in this Context.
  3. Then work out what the Permission is for each Role in this Context.
  4. Aggregate those permissions to get a final answer.

Defining Capabilities

As the example shows, a plugin can define new capabilities relatingto the particular functionality it provides. Inside each Moodleplugin there is a sub-folder of the code called db. Thiscontains all the information required to install or upgrade theplugin. One of those bits of information is a file calledaccess.php that defines the capabilities. Here is theaccess.php file for our plugin, which lives inlocal/greet/db/access.php:

<?php$capabilities = array('local/greet:begreeted' => array( 'captype' => 'read', 'contextlevel' => CONTEXT_SYSTEM, 'archetypes' => array('guest' => CAP_ALLOW, 'user' => CAP_ALLOW)));

This gives some metadata about each capability which are used whenconstructing the permissions management user interface. It also givedefault permissions for common types of role.

Roles

The next part of the Moodle permissions system is roles. A role isreally just a named set of permissions. When you are logged intoMoodle, you will have the "Authenticated user" role in the Systemcontext, and since the System context is the root of the hierarchy,that role will apply everywhere.

Within a particular course, you may be a Student, and that roleassignment will apply in the Course context and all the Modulecontexts within it. In another course, however, you may have adifferent role. For example, Mr Gradgrind may be Teacher in the"Facts, Facts, Facts" course, but a Student in the professionaldevelopment course "Facts Aren't Everything". Finally, a user mightbe given the Moderator role in one particular forum (Module context).

Permissions

A role defines a permission for each capability. For examplethe Teacher role will probably ALLOW moodle/course:manage,but the Student role will not. However, both Student and Teacher willallow mod/forum:startdiscussion.

The roles are normally defined globally, but they can be re-defined ineach context. For example, one particular wikican be made read-only to students by overridingthe permission for the mod/wiki:edit capability for the Student role inthat wiki (Module) context, to PREVENT.

There are four Permissions:

  • NOT SET/INHERIT (default)
  • ALLOW
  • PREVENT
  • PROHIBIT

In a given context, a role will have one of these four permissions foreach capability. One difference between PROHIBIT and PREVENT is that aPROHIBIT cannot be overridden in sub-contexts.

Permission Aggregation

Finally the permissions for all the roles the user has inthis context are aggregated.

  • If any role gives the permission PROHIBIT for this capability, return false.
  • Otherwise, if any role gives ALLOW for this capability, return true.
  • Otherwise return false.

A use case for PROHIBIT is this: Suppose a user has been makingabusive posts in a number of forums, and we want to stop themimmediately. We can create a Naughty user role, which setsmod/forum:post and other such capabilities to PROHIBIT. We canthen assign this role to the abusive user in the System context. Thatway, we can be sure that the user will not be able to post any more inany forum. (We would then talk to the student, and having reached asatisfactory outcome, remove that role assignment so that they mayuse the system again.)

So, Moodle's permissions system gives administrators a huge amount offlexibility. They can define whichever roles they like with differentpermissions for each capability; they can alter the role definitionsin sub-contexts; and then they can assign different roles to users indifferent contexts.

13.3. Back to Our Example Script

The next part of the script illustrates some miscellaneous points:

Line 5: Get Data From the Request

$name = optional_param('name', '', PARAM_TEXT); // 5

Something that every web application has to do is get data from arequest (GET or POST variables) without being susceptible to SQLinjection or cross-site scripting attacks. Moodle provides two ways todo this.

The simple method is the one shown here. It gets a single variablegiven the parameter name (here name) a default value, and theexpected type. The expected type is used to clean the input of allunexpected characters. There are numerous types likePARAM_INT, PARAM_ALPHANUM, PARAM_EMAIL, and soon.

There is also a similar required_param function, which likeother require_… functions stops execution and displaysan error message if the expected parameter is not found.

The other mechanism Moodle has for getting data from the request is afully fledged forms library. This is a wrapper around the HTMLQuickForm library from PEAR. (For non-PHP programmers, PEAR isPHP's equivalent of CPAN.) This seemed like a good choice when it wasselected, but is now no longer maintained. At some time in thefuture we will have to tackle moving to a new forms library, whichmany of us look forwards to, because QuickForm has several irritatingdesign issues. For now, however, it is adequate. Forms can be definedas a collection of fields of various types (e.g. text box, selectdrop-down, date-selector) with client- and server- side validation(including use of the same PARAM_… types).

Line 6: Global Variables

if (!$name) { $name = fullname($USER); // 6}

This snippet shows the first of the global variables Moodleprovides. $USER makes accessible the information about theuser accessing this script. Other globals include:

  • $CFG: holds the commonly used configuration settings.
  • $DB: the database connection.
  • $SESSION: a wrapper around the PHP session.
  • $COURSE: the course the current request relates to.

and several others, some of which we will encounter below.

You may have read the words "global variable" with horror. Note,however, that PHP processes a single request at a time. Thereforethese variables are not as global as all that. In fact, PHP globalvariables can be seen as an implementation of the thread-scopedregistry pattern (see Martin Fowler's Patterns ofEnterprise Application Architecture) and this is the way in whichMoodle uses them. It is very convenient in that it makes commonly usedobjects available throughout the code, without requiring them to bepassed to every function and method. It is only infrequently abused.

Nothing is Simple

This line also serves to make a point about the problemdomain: nothing is ever simple. To display a user'sname is more complicated than simply concatenating $USER->firstname,'~', and $USER->lastname. The school may have policiesabout showing either of those parts, and different cultures havedifferent conventions for which order to show names. Therefore, thereare several configurations settings and a function toassemble the full name according to the rules.

Dates are a similar problem. Different users may be in differenttime-zones. Moodle stores all dates as Unix time-stamps, which areintegers, and so work in all databases. There is then a userdatefunction to display the time-stamp to the user using the appropriatetimezone and locale settings.

Line 7: Logging

add_to_log(SITEID, 'local_greet', 'begreeted', 'local/greet/index.php?name=' . urlencode($name)); // 7

All significant actions in Moodle are logged. Logs are written to atable in the database. This is a trade-off. It makes sophisticatedanalysis quite easy, and indeed various reports based on the logs areincluded with Moodle. On a large and busy site, however, it is aperformance problem. The log table gets huge, which makes backing upthe database more difficult, and makes queries on the log tableslow. There can also be write contention on the log table. Theseproblems can be mitigated in various ways, for example by batchingwrites, or archiving or deleting old records to remove them from themain database.

13.4. Generating Output

Output is mainly handled via two global objects.

Line 8: The $PAGE Global

$PAGE->set_context($context); // 8

$PAGE stores the information about the page to beoutput. This information is then readily available to the code that generates theHTML. This script needs to explicitly specify the currentcontext. (In other situations, this might have been set automaticallyby require_login.) The URL for thispage must also be set explicitly. This may seem redundant, but the rationale for requiring it is thatyou might get to a particular page using any number of different URLs,but the URL passed to set_url should be the canonical URL forthe page—a good permalink, if you like. The pagetitle is also set. This will end up in the head element of the HTML.

Line 9: Moodle URL

$PAGE->set_url(new moodle_url('/local/greet/index.php'), array('name' => $name)); // 9

I just wanted to flag this nice little helper class which makes manipulating URLs much easier. As anaside, recall that the add_to_log function call above did notuse this helper class. Indeed, the log API cannot acceptmoodle_url objects. This sort of inconsistency is a typicalsign of a code-base as old as Moodle's.

Line 10: Internationalisation

$PAGE->set_title(get_string('welcome', 'local_greet')); // 10

Moodle uses its own system to allow the interface to be translatedinto any language. There may now be good PHP internationalisationlibraries, but in 2002 when it was first implemented there was not oneavailable that was adequate. The system is based around theget_string function. Strings are identified by a key and theplugin Frankenstyle name. As can be seen on line 12, it is possible tointerpolate values into the string. (Multiple values are handled usingPHP arrays or objects.)

The strings are looked up in language files that are just plain PHParrays. Here is the language filelocal/greet/lang/en/local_greet.php for our plugin:

<?php$string['greet:begreeted'] = 'Be greeted by the hello world example';$string['welcome'] = 'Welcome';$string['greet'] = 'Hello, {$a}!';$string['pluginname'] = 'Hello world example';

Note that, as well as the two string used in our script, there are alsostrings to give a name to the capability, and the name of the plugin asit appears in the user interface.

The different languages are identified by the two-letter country code(en here). Languages packs may derive from other languagepacks. For example the fr_ca (French Canadian) language packdeclares fr (French) as the parent language, and thus only hasto define those strings that differ from the French. Since Moodleoriginated in Australia, en means British English, anden_us (American English) is derived from it.

Again, the simple get_string API for plugin developers hides alot of complexity, including working out the current language (whichmay depend on the current user's preferences, or the settings for theparticular course they are currently in), and then searching throughall the language packs and parent language packs to find the string.

Producing the language pack files and co-ordinating the translationeffort is managed at http://lang.moodle.org/, which is Moodlewith a custom plugin (local_amos). It uses both Git and thedatabase as a backend to store the language files with full versionhistory.

Line 11: Starting Output

echo $OUTPUT->header(); // 11

This is another innocuous-looking line that does much more than itseems. The point is that before any output can be done, the applicabletheme (skin) must be worked out. This may depend on acombination of the page context and the user'spreferences. $PAGE->context was, however, onlyset on line 8, so the $OUTPUT global could not have been initialised at the start of the script. In order to solve this problem, some PHP magic is usedto create the proper $OUTPUT object based on the information in $PAGE the first time any output method is called.

Another thing to consider is that every page in Moodle maycontain blocks. These are extra configurable bits of contentthat are normally displayed to the left or right of the main content. (They are a type of plugin.) Again, theexact collection of blocks to display depends, in a flexible way (thatthe administrator can control) on the page context and some otheraspects of the page identity. Therefore, another part of preparing foroutput is a call to$PAGE->blocks->load_blocks().

Once all the necessary information has been worked out, the themeplugin (that controls the overall look of the page) is called togenerate the overall page layout, including whatever standard headerand footer is desired. This call is also responsible for adding theoutput from the blocks at the appropriate place in the HTML. In themiddle of the layout there will be a div where the specific contentfor this page goes. The HTML of this layout is generated, and thensplit in half after the start of the main content div. The first halfis returned, and the rest is stored to be returned by$OUTPUT->footer().

Line 12: Outputting the Body of the Page

echo $OUTPUT->box(get_string('greet', 'local_greet', format_string($name))); // 12

This line outputs the body of the page. Here it simply displays thegreeting in a box. The greeting is, again, a localised string, this timewith a value substituted into a placeholder. The core renderer$OUTPUT provides many convenience methods like box todescribe the required output in quite high-level terms. Differentthemes can control what HTML is actually output to make the box.

The content that originally came from the user ($name) isoutput though the format_string function. This is the otherpart of providing XSS protection. It also enables the user of textfilters (another plugin type). An example filter would be the LaTeXfilter, which replaces input like $$x + 1$$ with an imageof the equation. I will mention, but not explain, that there areactually three different functions (s, format_string,and format_text) depending on the particular type of contentbeing output.

Line 13: Finishing Output

echo $OUTPUT->footer(); // 13

Finally, the footer of the page is output. This example doesnot show it, but Moodle tracks all the JavaScript that is required bythe page, and outputs all the necessary script tags in thefooter. This is standard good practice. It allows users to see thepage without waiting for all the JavaScript to load. A developer wouldinclude JavaScript using API calls like$PAGE->requires->js('/local/greet/cooleffect.js').

Should This Script Mix Logic and Output?

Obviously, putting the output code directly in index.php, evenif at a high level of abstraction, limits the flexibility that themeshave to control the output. This is another sign of the age of theMoodle code-base. The $OUTPUT global was introduced in 2010 asa stepping stone on the way from the old code, where the output andcontroller code were in the same file, to a design where all the viewcode was properly separated. This also explains the rather ugly waythat the entire page layout is generated, then split in half, so thatany output from the script itself can be placed between the header andthe footer. Once the view code has been separated out of the script,into what Moodle calls a renderer, the theme can then choose tocompletely (or partially) override the view code for a given script.

A small refactoring can move all theoutput code out of our index.php and into a renderer.The end of index.php (lines 11 to 13) would change to:

$output = $PAGE->get_renderer('local_greet');echo $output->greeting_page($name);

and there would be a new file local/greet/renderer.php:

<?phpclass local_greet_renderer extends plugin_renderer_base { public function greeting_page($name) { $output = ''; $output .= $this->header(); $output .= $this->box(get_string('greet', 'local_greet', $name)); $output .= $this->footer(); return $output; }}

If the theme wished to completely change this output, it would definea subclass of this renderer that overrides the greeting_pagemethod. $PAGE->get_renderer() determines theappropriate renderer class to instantiate depending on the currenttheme. Thus, the output (view) code is fully separated from thecontroller code in index.php, and the plugin has been refactoredfrom typical legacy Moodle code to a clean MVC architecture.

13.5. Database Abstraction

The "Hello world" script was sufficiently simple that it did not need toaccess the database, although several of the Moodle library calls useddid do database queries. I will now briefly describe the Moodledatabase layer.

Moodle used to use the ADOdb library as the basis of its databaseabstraction layer, but there were issues for us, and the extra layerof library code had a noticeable impact on performance. Therefore, in Moodle2.0 we switched to our own abstraction layer, which is a thin wrapperaround the various PHP database libraries.

The moodle_database Class

The heart of the library is the moodle_database class. Thisdefines the interface provided by the $DB global variable,which gives access to the database connection. A typical usage mightbe:

$course = $DB->get_record('course', array('id' => $courseid));

That translates into the SQL:

SELECT * FROM mdl_course WHERE id = $courseid;

and returns the data as a plain PHP object with public fields, so youcould access $course->id,$course->fullname, etc.

Simple methods like this deal with basic queries, and simple updatesand inserts. Sometimes it is necessary to do more complex SQL, forexample to run reports. In that case, there are methods to executearbitrary SQL:

$courseswithactivitycounts = $DB->get_records_sql( 'SELECT c.id, ' . $DB->sql_concat('shortname', "' '", 'fullname') . ' AS coursename, COUNT(1) AS activitycount FROM {course} c JOIN {course_modules} cm ON cm.course = c.id WHERE c.category = :categoryid GROUP BY c.id, c.shortname, c.fullname ORDER BY c.shortname, c.fullname', array('categoryid' => $category));

Some things to note there:

  • The table names are wrapped in {} so that the library can find them and prepend the table name prefix.
  • The library uses placeholders to insert values into the SQL. In some cases this uses the facilities of the underlying database driver. In other cases the values have to be escaped and inserted into the SQL using string manipulation. The library supports both named placeholders (as above) and anonymous ones, using ? as the placeholder.
  • For queries to work on all our supported databases a safe subset of standard SQL must be used. For example, you can see that I have used the AS keyword for column aliases, but not for table aliases. Both of these usage rules are necessary.
  • Even so, there are some situations where no subset of standard SQL will work on all our supported databases; for example, every database has a different way to concatenate strings. In these cases there are compatibility functions to generate the correct SQL.

Defining the Database Structure

Another area where database management systems differ a lot is in theSQL syntax required to define tables. To get around this problem, eachMoodle plugin (and Moodle core) defines the required database tablesin an XML file. The Moodle install system parses the install.xml files and usesthe information they contain to create the required tables and indexes.There is a developer tool called XMLDB built into Moodle tohelp create and edit these install files.

If the database structure needs to change between two releases ofMoodle (or of a plugin) then the developer is responsible for writingcode (using an additional database object that provides DDL methods)to update the database structure, while preserving all the users'data. Thus, Moodle will always self-update from one release to thenext, simplifying maintenance for administrators.

One contentious point, stemming from the fact that Moodle started outusing MySQL 3, is that the Moodle database does not use foreignkeys. This allows some buggy behaviour to remain undetected eventhough modern databases would be capable of detecting the problem. Thedifficulty is that people have been running Moodle sites withoutforeign keys for years, so there is almost certainly inconsistent datapresent. Adding the keys now would be impossible, without avery difficult clean-up job. Even so, since the XMLDB system was addedto Moodle 1.7 (in 2006!) the install.xml files have contained thedefinitions of the foreign keys that should exist, and we are stillhoping, one day, to do all the work necessary to allow us to createthem during the install process.

13.6. What Has Not Been Covered

I hope I have given you a good overview of how Moodle works. Due tolack of space I have had to omit several interesting topics, includinghow authentication, enrolment and grade plugins allow Moodle tointeroperate with student information systems, and the interestingcontent-addressed way that Moodle stores uploaded files. Details ofthese, and other aspects of Moodle's design, can be found in thedeveloper documentation.

13.7. Lessons Learned

One interesting aspect of working on Moodle is that it came out of aresearch project. Moodle enables (but does not enforce) a socialconstructivistpedagogy. That is,we learn best by actually creating something, and we learn from eachother as a community. Martin Dougiamas's PhD question did not askwhether this was an effective model for education, but rather whether it is an effective model for running an open sourceproject. That is, can we view the Moodle project as an attempt tolearn how to build and use a VLE, and an attempt to learn that byactually building and using Moodle as a community where teachers,developers, administrators and students all teach and learn from eachother? I find this a good model for thinking about an open sourcesoftware development project. The main place where developers and users learn fromeach other is in discussions in the Moodle project forums, and in the bugdatabase.

Perhaps the most important consequence of this learning approach isthat you should not be afraid to start by implementing the simplestpossible solution first. For example, early versions of Moodle had just a fewhard-coded roles like Teacher, Student and Administrator. That was enoughfor many years, but eventually the limitations had to be addressed.When the time came to design the Roles systemfor Moodle 1.7, there was a lot of experience in the community abouthow people were using Moodle, and many little feature requeststhat showed what people needed to be able to adjust using a more flexible access controlsystem. This all helped design the Roles system to be as simple aspossible, but as complex as necessary. (In fact, the first version ofthe roles system ended up slightly too complex, and it wassubsequently simplified a little in Moodle 2.0.)

If you take the view that programming is a problem-solvingexercise, then you might think that Moodle got the design wrong thefirst time, and later had to waste time correcting it. I suggest thatis an unhelpful viewpoint when trying to solve complex real-world problems. At the time Moodle started, no-one knewenough to design the roles system we now have. If you take thelearning viewpoint, then the various stages Moodle went through to reach the current design werenecessary and inevitable.

For this perspective to work, it must be possible to change almost any aspect of asystem's architecture once you have learned more. I think Moodle shows that this is possible.For example, we found a way for code to be gradually refactored fromlegacy scripts to a cleaner MVC architecture. This requires effort, but it seems that whennecessary, the resources to implement these changes can be found in open source projects. From the user's point of view, the system graduallyevolves with each major release.

The Architecture of Open Source Applications (Volume 2)Moodle (2024)
Top Articles
XDC: The ideal coin for Green Energy and the Renewables Sector
Vacation Rental Home Staff Tipping – How much and when?
Radikale Landküche am Landgut Schönwalde
Skyward Sinton
Chambersburg star athlete JJ Kelly makes his college decision, and he’s going DI
Truist Park Section 135
Alpha Kenny Buddy - Songs, Events and Music Stats | Viberate.com
Craigslist Free Grand Rapids
Tight Tiny Teen Scouts 5
Connexus Outage Map
Dexter Gomovies
Burn Ban Map Oklahoma
Craigslist Panama City Fl
Xomissmandi
Gemita Alvarez Desnuda
Pekin Soccer Tournament
1v1.LOL - Play Free Online | Spatial
Why Is 365 Market Troy Mi On My Bank Statement
Morristown Daily Record Obituary
Hdmovie2 Sbs
ABCproxy | World-Leading Provider of Residential IP Proxies
Heart Ring Worth Aj
Chase Bank Pensacola Fl
Pocono Recird Obits
Craigslist Maryland Trucks - By Owner
Reviews over Supersaver - Opiness - Spreekt uit ervaring
Jordan Poyer Wiki
Macu Heloc Rate
Jcp Meevo Com
Catchvideo Chrome Extension
Unity Webgl Car Tag
Purdue Timeforge
Soiza Grass
Gideon Nicole Riddley Read Online Free
Chase Bank Cerca De Mí
Today's Gas Price At Buc-Ee's
Los Garroberros Menu
How much does Painttool SAI costs?
Ferguson Employee Pipeline
Lcwc 911 Live Incident List Live Status
Weekly Math Review Q2 7 Answer Key
Craigslist Central Il
فیلم گارد ساحلی زیرنویس فارسی بدون سانسور تاینی موویز
Why Are The French So Google Feud Answers
Gon Deer Forum
Rocket League Tracker: A useful tool for every player
Plasma Donation Greensburg Pa
Mikayla Campinos Alive Or Dead
Basic requirements | UC Admissions
Latest Posts
Article information

Author: Golda Nolan II

Last Updated:

Views: 5826

Rating: 4.8 / 5 (78 voted)

Reviews: 93% of readers found this page helpful

Author information

Name: Golda Nolan II

Birthday: 1998-05-14

Address: Suite 369 9754 Roberts Pines, West Benitaburgh, NM 69180-7958

Phone: +522993866487

Job: Sales Executive

Hobby: Worldbuilding, Shopping, Quilting, Cooking, Homebrewing, Leather crafting, Pet

Introduction: My name is Golda Nolan II, I am a thoughtful, clever, cute, jolly, brave, powerful, splendid person who loves writing and wants to share my knowledge and understanding with you.