Relationships are defined means of determining a user's relationship to other users in the system. In a nutshell, it resolves common types of relations between users, such as managers and appraisers. In the current implementation, relationships are defined on the system level, and are not modifiable.

How relationships work

A relationship can process any developer defined input. Different kinds of input can be specified, such as user IDs, job assignment IDs, course IDs etc.

The output for a resolved relationship is an array of data transfer objects (DTOs). Each DTO is intended to contain the user_id of a resolved user, the source of the user (default is an internal user record), and any extra metadata information associated with the resolved user. A DTO gives plugins implementing relationship functionality more flexibility - in the case of performance activities within Totara Perform it means we can return users outside of the system who do not have a user record.

Each relationship can potentially have many resolver as required, although their inputs must be compatible. For example, there could exist a relationship 'supervisor' which contains both 'manager' and 'appraiser' resolvers, which would both accept user ID and/or job assignment ID as inputs. It could not additionally contain a 'seminar' resolver, because that would require a seminar ID (unless the seminar resolver accepted either user ID and/or job assignment ID as input).

Each relationship is an individual record in the totara_core_relationship table. Relationship resolvers are extensible classes that can be defined on a per-plugin basis. Each relationship record can have many relationship resolvers associated with it. The link between resolvers and relationships are stored in the totara_core_relationship_resolver table.


Relationships extend entity models, and are loaded from the database like any other entity. A relationship can be created by passing in an array of at least one resolver. The compatibility of resolvers is checked before creation.

// Creation of a relationship
\totara_core\relationship\relationship::create([resolver_class_one::class, resolver_class_two::class, ...]);

To get users, simply pass in the supported input(s):

// Getting users
$team_members = \totara_core\relationship\relationship::load_by_id(1);
$team_members->get_users(['user_id' => 2]);

$all_supervisors = \totara_core\relationship\relationship::load_by_id(2);
$all_supervisors->get_users(['job_assignment_id' => 14]);

$attended_same_seminar = \totara_core\relationship\relationship::load_by_id(3);
$attended_same_seminar->get_users(['user_id' => 2, 'seminar_id' => 4]);

If you have multiple relationships that you wanted to resolve that use the same input, you can take advantage of relationship_collection_manager.

$relationship_collection = new \totara_core\relationship\helpers\relationship_collection_manager([
]); // Pass in array of relationship instances or their IDs

$user_one_reports_to = $relationship_collection->get_users(['job_assignment_id' => 1]);
$user_two_reports_to = $relationship_collection->get_users(['user_id' => 2]);
$user_three_reports_to = $relationship_collection->get_users(['job_assignment_id' => 3]);

If you want to get a list of relationships to use or display, you can use the relationship_provider class.

// Get all relationships.
$relationship_provider = new relationship_provider();

// Get all relationships that are compatible with the mod_perform plugin and support a user_id input.
$relationship_provider = new relationship_provider();
    ->filter_by_component('mod_perform', true)

Existing relationships

For the moment, relationships are defined on a system level. There are currently several usable relationship resolvers which can easily be used anywhere across the system.

  • Subject: Accepts only user_id as input, and just returns the input without processing (\totara_core\relationship\resolvers\subject) 
  • Manager: Accepts user_id and/or job_assignment_id as input, returns the manager(s) for the user/job assignment (\totara_job\relationship\resolvers\manager)
  • Manager's manager: Accepts user_id and/or job_assignment_id as input, returns the manager of the user's manager(s) for the user/job assignment (\totara_job\relationship\resolvers\managers_manager)
  • Appraiser: Accepts user_id and/or job_assignment_id as input, returns the appraiser(s) for the user/job assignment (\totara_job\relationship\resolvers\appraiser)

Each of these resolvers have a corresponding relationship record too.

Potential future functionality

The current implementation was made with the intention of allowing user defined relationships (created by admins via UI). This could allow admins to customise what users are displayed/used in certain areas, and could allow functionality such as allowing admins to create custom team member dashboards for certain users/user groups.