Returning Node Tree in Web Service

Returning Node Tree in Web Service

by Jesus Ortega Miguel -
Number of replies: 0

Hi,

I'm working with moodle wservices and I've been stuck with an issue all the day. I've found a "solution" that doesn't seem good enough but anyway I would like to know if I'm missing something.

THE SCENARIO

My goal is to retrieve all the module information in a tree shape having the following node structure:

node {
    int id
    string type
    stdClass data
    array(node) children
}

The id is just an integer, type is a string and data is a stdClass object with category, course, section or module data, any of them. Children is an array of nodes.

I've managed to do it so we have a node class as root with the following data:

root > category > subcategory1 > subcategory 2 > ... > subcategoryN > course > section > module
root > category > subcategory1 > course > section > module

As we can see, subcategory1 holds another subcategory and a course at the same level. As long as I know, this is possible due to the Moodle DB structure so it is expected for this scenario.

THE PROBLEM

When we try to define the service that will return that kind of structure we can expect something like this:

    public static function get_node_description_no_children() : array {
        return
            [
                'id'        => new external_value(PARAM_INT, 'ID of the object.'),
                'type'      => new external_value(PARAM_ALPHA, 'Type of the object.'),
                'data'      => new external_single_structure(
                    [
                        'name'          => new external_value(PARAM_TEXT, 'Name of the object.', VALUE_OPTIONAL),
                        'shortname'     => new external_value(PARAM_RAW, 'Short name or code of the course.', VALUE_OPTIONAL),
                        'summary'       => new external_value(PARAM_RAW, 'Summary of the section.', VALUE_OPTIONAL),
                        'typeid'        => new external_value(PARAM_INT, 'Type ID of the module', VALUE_OPTIONAL),
                        'typename'      => new external_value(PARAM_ALPHANUM, 'Type of the module', VALUE_OPTIONAL),
                        'levelid'       => new external_value(PARAM_INT, 'Level ID of the module', VALUE_OPTIONAL),
                        'levelname'     => new external_value(PARAM_ALPHANUM, 'Level of the module', VALUE_OPTIONAL),
                        'expectedtime'  => new external_value(PARAM_INT, 'Time expected for module completion', VALUE_OPTIONAL)
                    ]
                    , 'The data of the node.', VALUE_OPTIONAL)
                'children' => new external_multiple_structure(
                    new external_single_structure(hierarchy_service::get_node_description())
            ];
    }

Of course the problem in this structure is the children variable. It makes an infinite recursive call to the get_node_definition() function and service will crash.

👉 Is there any way to solve this issue for an undefined number of nested classes ❓



My solution
I'm sure this is not the best way to solve it but it works fine for the purpose I want. I've defined a recursive function with a limit (in my case 10 due to the structure of my moodle) and will generate all the structure I want.

    public static function get_course_hierarchy_data_returns() {
        return new external_single_structure(hierarchy_service::get_node_description_with_deep(10), 'Tree data of all activities.');
    }

    public static function get_node_description_no_children() : array {
        return
            [
                'id'        => new external_value(PARAM_INT, 'ID of the object.'),
                'type'      => new external_value(PARAM_ALPHA, 'Type of the object.'),
                'data'      => new external_single_structure(
                    [
                        'name'          => new external_value(PARAM_TEXT, 'Name of the object.', VALUE_OPTIONAL),
                        'shortname'     => new external_value(PARAM_RAW, 'Short name or code of the course.', VALUE_OPTIONAL),
                        'summary'       => new external_value(PARAM_RAW, 'Summary of the section.', VALUE_OPTIONAL),
                        'typeid'        => new external_value(PARAM_INT, 'Type ID of the module', VALUE_OPTIONAL),
                        'typename'      => new external_value(PARAM_ALPHANUM, 'Type of the module', VALUE_OPTIONAL),
                        'levelid'       => new external_value(PARAM_INT, 'Level ID of the module', VALUE_OPTIONAL),
                        'levelname'     => new external_value(PARAM_ALPHANUM, 'Level of the module', VALUE_OPTIONAL),
                        'expectedtime'  => new external_value(PARAM_INT, 'Time expected for module completion', VALUE_OPTIONAL)
                    ]
                    , 'The data of the node.', VALUE_OPTIONAL)
                'children' => new external_multiple_structure(
                    new external_single_structure(hierarchy_service::get_node_description())
            ];
    }

    public static function get_node_description_with_deep(int $deep) : array {
        $structure = hierarchy_service::get_node_description_no_children();
        if($deep > 0) {
            $structure['children'] = new external_multiple_structure(
                new external_single_structure(hierarchy_service::get_node_description_with_deep($deep - 1))
            );
        }
        return $structure;
    }


* Note in my code I'm using some custom variables that doesn't belong to Moodle like the level of the module.



Average of ratings: -