Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Creating a web service and a web service function

From MoodleDocs

Moodle 2.0


Simple example

Have a look to the web service template. It is a simple hello_world function.

Complex example

Important: this complex example is from a core developer perspective. As third party developer, you will create your new web service into a Moodle local plugin. See the web service simple example, it is a local plugin. Basically, from this complex example, as third party developer, you will need to change the files locations and the externallib.php class name.

We will write a web service function create_groups($groups). This function create a group into a Moodle course. - actually the function already exists into Moodle. You can browse Github repository to have a look to the latest code.


Write the function specification documentation

functional

create_groups($groups) will take a list of group object as parameters and it will return the same groups with their newly created id. If ever one group creation fails, the function will throw an exception, and no creation will happen.

Note: we now tend to go away from stopping execution when we write bulk operations. When an error occurs, we recommend to send an error message string instead to throw an exception (for new web service bulk functions).

technical

  • the core functions: groups_create_group() from /group/lib.php.
  • the parameter types: a list of object. This object are groups, with id/name/courseid.
  • the returned value types: a list of group object with their id.
  • the user capabilities: moodle/course:managegroups

Write a simple test client

It is helpful to start writing a web service test client. You will often discover use cases that you didn't think about. See How to create a web service client.

Declare and map the web service function

Web service functions are declared/mapped in db/services.php files. create_groups() is a core external function, so it goes to /lib/db/services.php.

$functions = array(

   'core_group_create_groups' => array(         //web service function name
       'classname'   => 'core_group_external',  //class containing the external function
       'methodname'  => 'create_groups',          //external function name
       'classpath'   => 'group/externallib.php',  //file containing the class/external function
       'description' => 'Creates new groups.',    //human readable description of the web service function
       'type'        => 'write',                  //database rights of the web service function (read, write)
   ),

);

Web service functions should match the naming convention.

Define the web service description

Every web service function is mapped to an external function. The core_group_create_groups mapping is for example in /lib/db/services.php. Each external function is written with two other functions describing the parameters and the return values. These two description functions are located in the same file and the same class mentioned in the mapping.

Thus for the web service core_group_create_groups() we need write a class named core_group_external in the file group/externallib.php. The class will contain:

  • create_groups(...)
  • create_groups_parameters()
  • create_groups_return()

create_groups_parameters()

require_once("$CFG->libdir/externallib.php");

class moodle_group_external extends external_api {

   /**
    * Returns description of method parameters
    * @return external_function_parameters
    */
   public static function create_groups_parameters() {
       return new external_function_parameters(
           array(
               'groups' => new external_multiple_structure(
                   new external_single_structure(
                       array(
                           'courseid' => new external_value(PARAM_INT, 'id of course'),
                           'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
                           'description' => new external_value(PARAM_RAW, 'group description text'),
                           'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
                       )
                   )
               )
           )
       );
   }

A web service function without parameters will have a parameter description function like that: /**

    * Returns description of method parameters
    * @return external_function_parameters
    */
   public static function functionname_parameters() {
       return new external_function_parameters(
           array(
              //if I had any parameters, they would be described here. But I don't have any, so this array is empty.
           )
       );
   }

A parameter can be described as:

  • a list => external_multiple_structure
  • an object => external_single_structure
  • a primary type => external_value


Our create_groups() function expects one parameter named groups, so we will first write:

   /**
    * Returns description of method parameters
    * @return external_function_parameters
    */
   public static function create_groups_parameters() {
       return new external_function_parameters(
           array(
               'groups' => ...
               
           )
       );
   }

This groups parameter is a list of group. So we will write :

               'groups' => new external_multiple_structure(
                   ...
               )          

An external_multiple_structure object (list) can be contrsucted with:

  • external_single_structure (object)
  • external_value (primary type)
  • external_multiple_structure (list).


For our function it will be a external_single_structure:

                   new external_single_structure(
                       array(
                           'courseid' => ...,
                           'name' => ...,
                           'description' => ...,
                           'enrolmentkey' => ...,
                       )
                   )           

Thus we obtain :

               'groups' => new external_multiple_structure(
                   new external_single_structure(
                       array(
                           'courseid' => ...,
                           'name' => ...,
                           'description' => ...,
                           'enrolmentkey' => ...,
                       )
                   )
               )          

Each group values is a external_value (primary type):

  • courseid is an integer
  • name is a string (text only, not tag)
  • description is a string (can be anything)
  • enrolmentkey is also a string (can be anything)


We add them to the description :

               'groups' => new external_multiple_structure(
                   new external_single_structure(
                       array(
                           'courseid' => new external_value(PARAM_INT, 'id of course'), //the second argument is a human readable description text. This text is displayed in the automatically generated documentation.
                           'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
                           'description' => new external_value(PARAM_RAW, 'group description text'),
                           'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
                       )
                   )
               )          

create_groups_return()

It's similar to create_groups_parameters(), but instead to describe the parameters, it describes the return values.

public static function create_groups_returns() {

       return new external_multiple_structure(
           new external_single_structure(
               array(
                   'id' => new external_value(PARAM_INT, 'group record id'),
                   'courseid' => new external_value(PARAM_INT, 'id of course'),
                   'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
                   'description' => new external_value(PARAM_RAW, 'group description text'),
                   'enrolmentkey' => new external_value(PARAM_RAW, 'group enrol secret phrase'),
               )
           )
       );
   }

Required, Optional or Default value

A value can be VALUE_REQUIRED, VALUE_OPTIONAL, or VALUE_DEFAULT. If not mentioned, a value is VALUE_REQUIRED by default.

                           'yearofstudy' => new external_value(PARAM_INT, 'year of study',VALUE_DEFAULT, 1979),                        

  • VALUE_REQUIRED - if the value is not supplied => the server throws an error message
  • VALUE_OPTIONAL - if the value is not supplied => the value is ignored
  • VALUE_DEFAULT - if the value is not supplied => the default value is used

Implement the external function

We declared our web service function and we defined the external function parameters and return values. We will now implement the external function:

   /**
    * Create groups
    * @param array $groups array of group description arrays (with keys groupname and courseid)
    * @return array of newly created groups
    */
   public static function create_groups($groups) { //Don't forget to set it as static
       global $CFG, $DB;
       require_once("$CFG->dirroot/group/lib.php");
       $params = self::validate_parameters(self::create_groups_parameters(), array('groups'=>$groups));
       $transaction = $DB->start_delegated_transaction(); //If an exception is thrown in the below code, all DB queries in this code will be rollback.
       $groups = array();
       foreach ($params['groups'] as $group) {
           $group = (object)$group;
           if (trim($group->name) == ) {
               throw new invalid_parameter_exception('Invalid group name');
           }
           if ($DB->get_record('groups', array('courseid'=>$group->courseid, 'name'=>$group->name))) {
               throw new invalid_parameter_exception('Group with the same name already exists in the course');
           }
           // now security checks
           $context = get_context_instance(CONTEXT_COURSE, $group->courseid);
           self::validate_context($context);
           require_capability('moodle/course:managegroups', $context);
           // finally create the group
           $group->id = groups_create_group($group, false);
           $groups[] = (array)$group;
       }
       $transaction->allow_commit();
       return $groups;
   }

Parameter validation

$params = self::validate_parameters(self::create_groups_parameters(), array('groups'=>$groups));

This validate_parameters function validates the external function parameters against the description. It will return an exception if some required parameters are missing, if parameters are not well-formed, and check the parameters validity. It is essential that you do this call to avoid potential hack.

Important: the parameters of the external function and their declaration in the description must be the same order. In this example we have only one parameter named $groups, so we don't need to worry about the order.

Context and Capability checks

In a long term, in Moodle 2.0, all context/capability checks will disappear from the core functions and will be located into the external functions. Don't forget them. /// now security checks $context = get_context_instance(CONTEXT_COURSE, $group->courseid); self::validate_context($context); require_capability('moodle/course:managegroups', $context);

Exceptions

You can throw exceptions. There are automatically handle by Moodle web service servers. //Note: it is good practice to add detailled information in $debuginfo, // and only send back a generic exception message when Moodle DEBUG mode < NORMAL. // It's what we do here throwing the invalid_parameter_exception($debug) exception throw new invalid_parameter_exception('Group with the same name already exists in the course');

Correct return values

The return values will be validated by the Moodle web service servers:

  • return values contain some values not described => these values will be skipped.
  • return values miss some required values (VALUE_REQUIRED) => the server will return an error.
  • return values types don't match the description (int != PARAM_ALPHA) => the server will return an error

Note: cast all your returned objects into arrays.

Add the web service function into a service

You can pre-build a service including any functions, so the Moodle administrator doesn't need to do it. Add into /lib/db/services.php:

 $services = array(
     'groupservice' => array(                                                //the name of the web service
         'functions' => array ('moodle_add_groups', 'moodle_create_groups'), //web service functions of this service
         'requiredcapability' => 'some/capability:specified',                //if set, the web service user need this capability to access 
                                                                             //any function of this service                   
         'restrictedusers' = >1,                                             //if enabled, the Moodle administrator must link some user to this service
                                                                             //into the administration
         'enabled'=>0,                                                       //if enabled, the service can be reachable on a default installation
      )
 );

Note: it is not possible for an administrator to add/remove any function from a pre-built service.

See also

Specification: