<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://docs.moodle.org/dev/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Fmcorz</id>
	<title>MoodleDocs - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://docs.moodle.org/dev/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Fmcorz"/>
	<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/Special:Contributions/Fmcorz"/>
	<updated>2026-06-06T11:49:11Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Persistent&amp;diff=51674</id>
		<title>Persistent</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Persistent&amp;diff=51674"/>
		<updated>2016-12-30T03:07:42Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Reading property values */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle persistents are equivalent to models (or active records). They represent an object stored in the database and provide the methods to create, read, update, and delete those objects. Additionally, the persistents validate their own data against automatic and custom validation rules. Persistents are made by extending the abstract class &#039;&#039;core\persistent&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Properties ==&lt;br /&gt;
&lt;br /&gt;
Each class must define the properties that it contains. This can be done by defining the method &#039;&#039;protected static define_properties()&#039;&#039;. It returns an array where the keys are the names of the properties (and implies the database column names), and their attributes. The &#039;&#039;type&#039;&#039; of each property must be specified using one of the &#039;&#039;PARAM_*&#039;&#039; constants. This type will be used to automatically validate the property&#039;s value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the definition of the properties of this model.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
        )&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Properties attributes ===&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must be one of the many &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
;default&lt;br /&gt;
: The default value to attach to the property when it hasn&#039;t been provided. Without a &#039;&#039;default&#039;&#039;, the property&#039;s value is required. Alternatively this can be a &#039;&#039;Closure&#039;&#039; returning the default value.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants &#039;&#039;NULL_ALLOWED&#039;&#039; or &#039;&#039;NULL_NOT_ALLOWED&#039;&#039; determining if the null value is accepted. This defaults to &#039;&#039;NULL_NOT_ALLOWED&#039;&#039;.&lt;br /&gt;
;message&lt;br /&gt;
: The default error message (as a &#039;&#039;lang_string&#039;&#039; instance) to return when the validation of this field fails.&lt;br /&gt;
;choices&lt;br /&gt;
: An array of values which the property must fall in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
    &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
    &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
),&lt;br /&gt;
&#039;location&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
    &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
    &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
    &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
        return get_config(&#039;core&#039;, &#039;default_location&#039;);&lt;br /&gt;
    },&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that you should always use a &#039;&#039;Closure&#039;&#039; for the &#039;&#039;default&#039;&#039; value when you cannot guarantee that it will not change since the start of the request. The list of properties and their attributes is cached, and so failure to use a &#039;&#039;Closure&#039;&#039; can result in using an outdated default value.&lt;br /&gt;
&lt;br /&gt;
=== Mandatory properties ===&lt;br /&gt;
&lt;br /&gt;
Four fields are always added to your persistent and should be reflected in your database table. You must not define those properties in &#039;&#039;define_properties()&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
;id (non-null integer)&lt;br /&gt;
: The primary key of the record.&lt;br /&gt;
;usermodified (non-null integer)&lt;br /&gt;
: The user who created/modified the object. It is automatically set.&lt;br /&gt;
;timecreated (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set.&lt;br /&gt;
;timemodified (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set, and defaults to 0.&lt;br /&gt;
&lt;br /&gt;
== Linking to a database table ==&lt;br /&gt;
&lt;br /&gt;
While the persistent class is helpful for database interactions, it does not automatically fetch the properties from the database, nor does it create the tables. You will need to create the table yourself, as well as pointing the persistent to it. This can be done by defining the class constant &#039;&#039;TABLE&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** Table name for the persistent. */&lt;br /&gt;
const TABLE = &#039;status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The table name must not contain the Moodle prefix. Also it is common practice to always refer to your table name using the constant.&lt;br /&gt;
&lt;br /&gt;
== Reading property values ==&lt;br /&gt;
&lt;br /&gt;
This can be done using the method &#039;&#039;get()&#039;&#039;, or the magic method &#039;&#039;get_&#039;&#039; followed by the property name. Alternatively you can also use &#039;&#039;to_record()&#039;&#039; which exports the whole object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Create a new object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using the magic getter.&lt;br /&gt;
$userid = $persistent-&amp;gt;get_userid();&lt;br /&gt;
&lt;br /&gt;
// Get all the properties in an stdClass.&lt;br /&gt;
$data = $persistent-&amp;gt;to_record();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may override the magic getters to implement your own custom logic. For instance you could convert the data in another format automatically as a convenience for developers. However, use this sparingly as it may lead to confusion: what you get is not what is stored in the database. Note that you cannot guarantee that your getter will be used, it is possible for developers to call the alternative &#039;&#039;get()&#039;&#039; which will not use your custom logic.&lt;br /&gt;
&lt;br /&gt;
It is, however, encouraged to add convenience methods such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns the user object of the author.&lt;br /&gt;
 *&lt;br /&gt;
 * @return stdClass&lt;br /&gt;
 */&lt;br /&gt;
public function get_author() {&lt;br /&gt;
    return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Assigning values to properties ==&lt;br /&gt;
&lt;br /&gt;
There are two ways to do so.&lt;br /&gt;
&lt;br /&gt;
You use an object (&#039;&#039;stdClass&#039;&#039;) to assign a bunch of properties at once. Use it with the constructor, or the method &#039;&#039;from_record()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) array(&#039;userid&#039; =&amp;gt; 2, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instantiates a new object with value for some properties.&lt;br /&gt;
$persistent = new status(0, $data);&lt;br /&gt;
&lt;br /&gt;
// Is similar to.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
$persistent-&amp;gt;from_record($data);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or, you can use the magic setters &#039;&#039;set_&#039;&#039; followed by the property name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Defining a custom setter ===&lt;br /&gt;
&lt;br /&gt;
Though you don&#039;t have to for the code to work, you can define your own &#039;&#039;setter&#039;&#039; methods which will override the magic setters. They are useful if you want to extract the data out of a more complex object prior to assigning it. Though note that those setters will then have to use the &#039;&#039;set()&#039;&#039; method to assign the values.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Convenience method to set the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
 */&lt;br /&gt;
public function set_userid($idorobject) {&lt;br /&gt;
    $userid = $idorobject;&lt;br /&gt;
    if (is_object($idorobject)) {&lt;br /&gt;
        $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
    }&lt;br /&gt;
    $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example we will accept an object or an ID, as a convenience for developers we will extract the ID value out of the object passed if any.&lt;br /&gt;
&lt;br /&gt;
Just like custom getters, you cannot guarantee that they will be used. Developers can directly call the &#039;&#039;set()&#039;&#039; method. Therefore you must not use a custom setter to reliably transform the data added to a property. For instance do not add a custom setter to remove HTML tags out of a text field, it may not always happen.&lt;br /&gt;
&lt;br /&gt;
You can obviously create your own setters which aren&#039;t based on any properties just as a convenience. For instance we could have created &#039;&#039;set_userid_from_user(object $user)&#039;&#039; which is more verbose and more predictable&lt;br /&gt;
&lt;br /&gt;
== Read, save and delete entries ==&lt;br /&gt;
&lt;br /&gt;
The methods to &#039;&#039;create&#039;&#039;, &#039;&#039;read&#039;&#039;, &#039;&#039;update&#039;&#039; and &#039;&#039;delete&#039;&#039; are eponymous. Your object will be validated before you &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; it. The &#039;&#039;update&#039;&#039;, &#039;&#039;delete&#039;&#039; and &#039;&#039;read&#039;&#039; methods require your object to contain its ID. And you also won&#039;t be allowed to &#039;&#039;create&#039;&#039; an entry which already had an ID defined.&lt;br /&gt;
&lt;br /&gt;
Here are some code examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Fetches an object from database based on its ID.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Create object in the database.&lt;br /&gt;
$data = new stdClass();&lt;br /&gt;
$data-&amp;gt;message = &#039;Hello new world&#039;;&lt;br /&gt;
$persistent = new persistent(0, $data);&lt;br /&gt;
$persistent-&amp;gt;create();&lt;br /&gt;
// $persistent-&amp;gt;get(&#039;id&#039;) will now return an id.&lt;br /&gt;
&lt;br /&gt;
// Load an object from the database, and update it.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
$persistent-&amp;gt;update();&lt;br /&gt;
&lt;br /&gt;
// Reset the instance to the values in the database.&lt;br /&gt;
$persistent-&amp;gt;read();&lt;br /&gt;
&lt;br /&gt;
// Permanently delete the object from the database.&lt;br /&gt;
$persistent-&amp;gt;delete();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fetching records ==&lt;br /&gt;
&lt;br /&gt;
Once you start using persistents you should never directly interact with the database outside of your class. The persistent class comes with a few handy methods allowing you to retrieve your objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Use the constructor to fetch one object from its ID.&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Get one record from a set of conditions.&lt;br /&gt;
$persistent = status::get_record([&#039;userid&#039; =&amp;gt; $userid, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;]);&lt;br /&gt;
&lt;br /&gt;
// Get multiple records from a set of conditions.&lt;br /&gt;
$persistents = status::get_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Count the records.&lt;br /&gt;
$count = status::count_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Check whether a record exists.&lt;br /&gt;
$exists = status::record_exists($id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure to also check their additional parameters and their variants (&#039;&#039;record_exists_select()&#039;&#039;, &#039;&#039;count_records_select&#039;&#039;, &#039;&#039;get_records_select&#039;&#039;, ...).&lt;br /&gt;
&lt;br /&gt;
=== Custom fetching ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s always a good idea to add more complex queries directly within your persistent. By convention you should always return an instance of your persistent and never an stdClass. Here we add a custom method which allows to directly fetch all records by username.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get all records from a user&#039;s username.&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $username The username.&lt;br /&gt;
 * @return status[]&lt;br /&gt;
 */&lt;br /&gt;
public static function get_records_by_username($username) {&lt;br /&gt;
    global $DB;&lt;br /&gt;
&lt;br /&gt;
    $sql = &#039;SELECT s.*&lt;br /&gt;
              FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
              JOIN {user} u&lt;br /&gt;
                ON u.id = s.userid&lt;br /&gt;
             WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
    $persistents = [];&lt;br /&gt;
&lt;br /&gt;
    $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
    foreach ($recordset as $record) {&lt;br /&gt;
        $persistents[] = new static(0, $record);&lt;br /&gt;
    }&lt;br /&gt;
    $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
    return $persistents;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to join persistents together and be able to extract their respective properties in a single database query, you should have a look at the following methods:&lt;br /&gt;
&lt;br /&gt;
;get_sql_fields(string $alias, string $prefix = null)&lt;br /&gt;
: Returns the SQL statement to include in the SELECT clause to prefix columns.&lt;br /&gt;
;extract_record(stdClass $row, string $prefix = null)&lt;br /&gt;
: Extracts all the properties from a row based on the given prefix.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Minimalist example.&lt;br /&gt;
$sqlfields = status::get_sql_fields(&#039;s&#039;, &#039;statprops&#039;);&lt;br /&gt;
$sql = &amp;quot;SELECT $sqlfields, u.username&lt;br /&gt;
          FROM {&amp;quot; . status::TABLE . &amp;quot;} s&lt;br /&gt;
          JOIN {user} ON s.userid = u.id&lt;br /&gt;
         WHERE s.id = 1&amp;quot;;&lt;br /&gt;
$row = $DB-&amp;gt;get_record($sql, []);&lt;br /&gt;
$statusdata = status::extract_record($row, &#039;statprops&#039;);&lt;br /&gt;
$persistent = new status(0, $statusdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Validating ==&lt;br /&gt;
&lt;br /&gt;
Basic validation of the properties values happens automatically based on their type (&#039;&#039;PARAM_*&#039;&#039; constant), however this is not always enough. In order to implement your own custom validation, simply define a &#039;&#039;protected&#039;&#039; method starting with &#039;&#039;validate_&#039;&#039; followed with the property name. This method will be called whenever the model needs to be validated and will receive the data to validate.&lt;br /&gt;
&lt;br /&gt;
A validation method must always return either &#039;&#039;true&#039;&#039; or an instance of &#039;&#039;lang_string&#039;&#039; which contains the error message to send to the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param int $value The value.&lt;br /&gt;
 * @return true|lang_string&lt;br /&gt;
 */&lt;br /&gt;
protected function validate_userid($value) {&lt;br /&gt;
    if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
        return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above example ensures that the &#039;&#039;userid&#039;&#039; property contains a valid user ID.&lt;br /&gt;
&lt;br /&gt;
Note that the basic validation is always performed first, and thus your custom validation method will not be called when the value did not pass the basic validation.&lt;br /&gt;
&lt;br /&gt;
=== Validation results ===&lt;br /&gt;
&lt;br /&gt;
The validation of the object automatically happens upon &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039;. If the validation did not pass, an &#039;&#039;invalid_persisten_exception&#039;&#039; will be raised. You can validate the object prior to saving the object and get the validation results if you need to.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// We can catch the invalid_persistent_exception.&lt;br /&gt;
try {&lt;br /&gt;
    $persistent = new status();&lt;br /&gt;
    $persistent-&amp;gt;create();&lt;br /&gt;
} catch (invalid_persistent_exception $e) {&lt;br /&gt;
    // Whoops, something wrong happened.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Check whether the object is valid.&lt;br /&gt;
$persistent-&amp;gt;is_valid();        // True or false.&lt;br /&gt;
&lt;br /&gt;
// Get the validation errors.&lt;br /&gt;
$persistent-&amp;gt;get_errors();      // Array where keys are properties and values are errors.&lt;br /&gt;
&lt;br /&gt;
// Validate the object.&lt;br /&gt;
$persistent-&amp;gt;validate();        // Returns true, or an array of errors.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hooks ==&lt;br /&gt;
&lt;br /&gt;
You can define the following methods to be notified prior to, or after, something happened:&lt;br /&gt;
&lt;br /&gt;
;protected before_validate()&lt;br /&gt;
: Do something before the object is validated.&lt;br /&gt;
;protected before_create()&lt;br /&gt;
: Do something before the object is inserted in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_create()&lt;br /&gt;
: Do something right after the object was added to the database.&lt;br /&gt;
;protected before_update()&lt;br /&gt;
: Do something before the object is updated in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_update(bool $result)&lt;br /&gt;
: Do something right after the object was updated in the database.&lt;br /&gt;
;protected before_delete()&lt;br /&gt;
: Do something right before the object is deleted from the database.&lt;br /&gt;
;protected after_delete(bool $result)&lt;br /&gt;
: Do something right after the object was deleted from the database.&lt;br /&gt;
&lt;br /&gt;
== Persistent and forms ==&lt;br /&gt;
&lt;br /&gt;
If your persistents are exposed to your users via forms, head to the [[Persistent form|persistent form page]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# You must create the database table yourself, using the [[XMLDB editor]] and an upgrade script.&lt;br /&gt;
# You must include the [[#Mandatory_properties|mandatory fields]] in your table schema.&lt;br /&gt;
# You must never extend another persistent as this leads to unpredictable errors whenever the parent changes (new properties missing in your table, changes in validation, custom getters and setters, etc...).&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
class status extends core\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example;&lt;br /&gt;
&lt;br /&gt;
use core\persistent;&lt;br /&gt;
use core_user;&lt;br /&gt;
use lang_string;&lt;br /&gt;
&lt;br /&gt;
class status extends persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
                &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
                &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;location&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
                &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
                &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
                &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
                    return get_config(&#039;core&#039;, &#039;default_location&#039;);&lt;br /&gt;
                },&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the user object of the author.&lt;br /&gt;
     *&lt;br /&gt;
     * @return stdClass&lt;br /&gt;
     */&lt;br /&gt;
    public function get_author() {&lt;br /&gt;
        return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Convenience method to set the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
     */&lt;br /&gt;
    public function set_userid($idorobject) {&lt;br /&gt;
        $userid = $idorobject;&lt;br /&gt;
        if (is_object($idorobject)) {&lt;br /&gt;
            $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Validate the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param int $value The value.&lt;br /&gt;
     * @return true|lang_string&lt;br /&gt;
     */&lt;br /&gt;
    protected function validate_userid($value) {&lt;br /&gt;
        if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
            return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get all records from a user&#039;s username.&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $username The username.&lt;br /&gt;
     * @return status[]&lt;br /&gt;
     */&lt;br /&gt;
    public static function get_records_by_username($username) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $sql = &#039;SELECT s.*&lt;br /&gt;
                  FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
                  JOIN {user} u&lt;br /&gt;
                    ON u.id = s.userid&lt;br /&gt;
                 WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
        $persistents = [];&lt;br /&gt;
&lt;br /&gt;
        $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
        foreach ($recordset as $record) {&lt;br /&gt;
            $persistents[] = new static(0, $record);&lt;br /&gt;
        }&lt;br /&gt;
        $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
        return $persistents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Persistent&amp;diff=51673</id>
		<title>Persistent</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Persistent&amp;diff=51673"/>
		<updated>2016-12-30T03:06:23Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Assigning values to properties */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle persistents are equivalent to models (or active records). They represent an object stored in the database and provide the methods to create, read, update, and delete those objects. Additionally, the persistents validate their own data against automatic and custom validation rules. Persistents are made by extending the abstract class &#039;&#039;core\persistent&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Properties ==&lt;br /&gt;
&lt;br /&gt;
Each class must define the properties that it contains. This can be done by defining the method &#039;&#039;protected static define_properties()&#039;&#039;. It returns an array where the keys are the names of the properties (and implies the database column names), and their attributes. The &#039;&#039;type&#039;&#039; of each property must be specified using one of the &#039;&#039;PARAM_*&#039;&#039; constants. This type will be used to automatically validate the property&#039;s value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the definition of the properties of this model.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
        )&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Properties attributes ===&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must be one of the many &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
;default&lt;br /&gt;
: The default value to attach to the property when it hasn&#039;t been provided. Without a &#039;&#039;default&#039;&#039;, the property&#039;s value is required. Alternatively this can be a &#039;&#039;Closure&#039;&#039; returning the default value.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants &#039;&#039;NULL_ALLOWED&#039;&#039; or &#039;&#039;NULL_NOT_ALLOWED&#039;&#039; determining if the null value is accepted. This defaults to &#039;&#039;NULL_NOT_ALLOWED&#039;&#039;.&lt;br /&gt;
;message&lt;br /&gt;
: The default error message (as a &#039;&#039;lang_string&#039;&#039; instance) to return when the validation of this field fails.&lt;br /&gt;
;choices&lt;br /&gt;
: An array of values which the property must fall in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
    &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
    &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
),&lt;br /&gt;
&#039;location&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
    &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
    &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
    &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
        return get_config(&#039;core&#039;, &#039;default_location&#039;);&lt;br /&gt;
    },&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that you should always use a &#039;&#039;Closure&#039;&#039; for the &#039;&#039;default&#039;&#039; value when you cannot guarantee that it will not change since the start of the request. The list of properties and their attributes is cached, and so failure to use a &#039;&#039;Closure&#039;&#039; can result in using an outdated default value.&lt;br /&gt;
&lt;br /&gt;
=== Mandatory properties ===&lt;br /&gt;
&lt;br /&gt;
Four fields are always added to your persistent and should be reflected in your database table. You must not define those properties in &#039;&#039;define_properties()&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
;id (non-null integer)&lt;br /&gt;
: The primary key of the record.&lt;br /&gt;
;usermodified (non-null integer)&lt;br /&gt;
: The user who created/modified the object. It is automatically set.&lt;br /&gt;
;timecreated (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set.&lt;br /&gt;
;timemodified (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set, and defaults to 0.&lt;br /&gt;
&lt;br /&gt;
== Linking to a database table ==&lt;br /&gt;
&lt;br /&gt;
While the persistent class is helpful for database interactions, it does not automatically fetch the properties from the database, nor does it create the tables. You will need to create the table yourself, as well as pointing the persistent to it. This can be done by defining the class constant &#039;&#039;TABLE&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** Table name for the persistent. */&lt;br /&gt;
const TABLE = &#039;status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The table name must not contain the Moodle prefix. Also it is common practice to always refer to your table name using the constant.&lt;br /&gt;
&lt;br /&gt;
== Reading property values ==&lt;br /&gt;
&lt;br /&gt;
This can be done using the method &#039;&#039;get()&#039;&#039;, or the magic method &#039;&#039;get_&#039;&#039; followed by the property name. Alternatively you can also use &#039;&#039;to_record()&#039;&#039; which exports the whole object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Create a new object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using persistent::get().&lt;br /&gt;
$userid = $persistent-&amp;gt;get(&#039;userid&#039;);&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using the magic getter.&lt;br /&gt;
$userid = $persistent-&amp;gt;get_userid();&lt;br /&gt;
&lt;br /&gt;
// Get all the properties in an stdClass.&lt;br /&gt;
$data = $persistent-&amp;gt;to_record();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may override the magic getters to implement your own custom logic. For instance you could convert the data in another format automatically as a convenience for developers. However, use this sparingly as it may lead to confusion: what you get is not what is stored in the database. Note that you cannot guarantee that your getter will be used, remember that it is possible for developers to call the alternative &#039;&#039;get()&#039;&#039; which will not use your custom logic.&lt;br /&gt;
&lt;br /&gt;
It is, however, encouraged to add convenience methods such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns the user object of the author.&lt;br /&gt;
 *&lt;br /&gt;
 * @return stdClass&lt;br /&gt;
 */&lt;br /&gt;
public function get_author() {&lt;br /&gt;
    return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Assigning values to properties ==&lt;br /&gt;
&lt;br /&gt;
There are two ways to do so.&lt;br /&gt;
&lt;br /&gt;
You use an object (&#039;&#039;stdClass&#039;&#039;) to assign a bunch of properties at once. Use it with the constructor, or the method &#039;&#039;from_record()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) array(&#039;userid&#039; =&amp;gt; 2, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instantiates a new object with value for some properties.&lt;br /&gt;
$persistent = new status(0, $data);&lt;br /&gt;
&lt;br /&gt;
// Is similar to.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
$persistent-&amp;gt;from_record($data);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or, you can use the magic setters &#039;&#039;set_&#039;&#039; followed by the property name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Defining a custom setter ===&lt;br /&gt;
&lt;br /&gt;
Though you don&#039;t have to for the code to work, you can define your own &#039;&#039;setter&#039;&#039; methods which will override the magic setters. They are useful if you want to extract the data out of a more complex object prior to assigning it. Though note that those setters will then have to use the &#039;&#039;set()&#039;&#039; method to assign the values.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Convenience method to set the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
 */&lt;br /&gt;
public function set_userid($idorobject) {&lt;br /&gt;
    $userid = $idorobject;&lt;br /&gt;
    if (is_object($idorobject)) {&lt;br /&gt;
        $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
    }&lt;br /&gt;
    $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example we will accept an object or an ID, as a convenience for developers we will extract the ID value out of the object passed if any.&lt;br /&gt;
&lt;br /&gt;
Just like custom getters, you cannot guarantee that they will be used. Developers can directly call the &#039;&#039;set()&#039;&#039; method. Therefore you must not use a custom setter to reliably transform the data added to a property. For instance do not add a custom setter to remove HTML tags out of a text field, it may not always happen.&lt;br /&gt;
&lt;br /&gt;
You can obviously create your own setters which aren&#039;t based on any properties just as a convenience. For instance we could have created &#039;&#039;set_userid_from_user(object $user)&#039;&#039; which is more verbose and more predictable&lt;br /&gt;
&lt;br /&gt;
== Read, save and delete entries ==&lt;br /&gt;
&lt;br /&gt;
The methods to &#039;&#039;create&#039;&#039;, &#039;&#039;read&#039;&#039;, &#039;&#039;update&#039;&#039; and &#039;&#039;delete&#039;&#039; are eponymous. Your object will be validated before you &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; it. The &#039;&#039;update&#039;&#039;, &#039;&#039;delete&#039;&#039; and &#039;&#039;read&#039;&#039; methods require your object to contain its ID. And you also won&#039;t be allowed to &#039;&#039;create&#039;&#039; an entry which already had an ID defined.&lt;br /&gt;
&lt;br /&gt;
Here are some code examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Fetches an object from database based on its ID.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Create object in the database.&lt;br /&gt;
$data = new stdClass();&lt;br /&gt;
$data-&amp;gt;message = &#039;Hello new world&#039;;&lt;br /&gt;
$persistent = new persistent(0, $data);&lt;br /&gt;
$persistent-&amp;gt;create();&lt;br /&gt;
// $persistent-&amp;gt;get(&#039;id&#039;) will now return an id.&lt;br /&gt;
&lt;br /&gt;
// Load an object from the database, and update it.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
$persistent-&amp;gt;update();&lt;br /&gt;
&lt;br /&gt;
// Reset the instance to the values in the database.&lt;br /&gt;
$persistent-&amp;gt;read();&lt;br /&gt;
&lt;br /&gt;
// Permanently delete the object from the database.&lt;br /&gt;
$persistent-&amp;gt;delete();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fetching records ==&lt;br /&gt;
&lt;br /&gt;
Once you start using persistents you should never directly interact with the database outside of your class. The persistent class comes with a few handy methods allowing you to retrieve your objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Use the constructor to fetch one object from its ID.&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Get one record from a set of conditions.&lt;br /&gt;
$persistent = status::get_record([&#039;userid&#039; =&amp;gt; $userid, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;]);&lt;br /&gt;
&lt;br /&gt;
// Get multiple records from a set of conditions.&lt;br /&gt;
$persistents = status::get_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Count the records.&lt;br /&gt;
$count = status::count_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Check whether a record exists.&lt;br /&gt;
$exists = status::record_exists($id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure to also check their additional parameters and their variants (&#039;&#039;record_exists_select()&#039;&#039;, &#039;&#039;count_records_select&#039;&#039;, &#039;&#039;get_records_select&#039;&#039;, ...).&lt;br /&gt;
&lt;br /&gt;
=== Custom fetching ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s always a good idea to add more complex queries directly within your persistent. By convention you should always return an instance of your persistent and never an stdClass. Here we add a custom method which allows to directly fetch all records by username.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get all records from a user&#039;s username.&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $username The username.&lt;br /&gt;
 * @return status[]&lt;br /&gt;
 */&lt;br /&gt;
public static function get_records_by_username($username) {&lt;br /&gt;
    global $DB;&lt;br /&gt;
&lt;br /&gt;
    $sql = &#039;SELECT s.*&lt;br /&gt;
              FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
              JOIN {user} u&lt;br /&gt;
                ON u.id = s.userid&lt;br /&gt;
             WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
    $persistents = [];&lt;br /&gt;
&lt;br /&gt;
    $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
    foreach ($recordset as $record) {&lt;br /&gt;
        $persistents[] = new static(0, $record);&lt;br /&gt;
    }&lt;br /&gt;
    $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
    return $persistents;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to join persistents together and be able to extract their respective properties in a single database query, you should have a look at the following methods:&lt;br /&gt;
&lt;br /&gt;
;get_sql_fields(string $alias, string $prefix = null)&lt;br /&gt;
: Returns the SQL statement to include in the SELECT clause to prefix columns.&lt;br /&gt;
;extract_record(stdClass $row, string $prefix = null)&lt;br /&gt;
: Extracts all the properties from a row based on the given prefix.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Minimalist example.&lt;br /&gt;
$sqlfields = status::get_sql_fields(&#039;s&#039;, &#039;statprops&#039;);&lt;br /&gt;
$sql = &amp;quot;SELECT $sqlfields, u.username&lt;br /&gt;
          FROM {&amp;quot; . status::TABLE . &amp;quot;} s&lt;br /&gt;
          JOIN {user} ON s.userid = u.id&lt;br /&gt;
         WHERE s.id = 1&amp;quot;;&lt;br /&gt;
$row = $DB-&amp;gt;get_record($sql, []);&lt;br /&gt;
$statusdata = status::extract_record($row, &#039;statprops&#039;);&lt;br /&gt;
$persistent = new status(0, $statusdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Validating ==&lt;br /&gt;
&lt;br /&gt;
Basic validation of the properties values happens automatically based on their type (&#039;&#039;PARAM_*&#039;&#039; constant), however this is not always enough. In order to implement your own custom validation, simply define a &#039;&#039;protected&#039;&#039; method starting with &#039;&#039;validate_&#039;&#039; followed with the property name. This method will be called whenever the model needs to be validated and will receive the data to validate.&lt;br /&gt;
&lt;br /&gt;
A validation method must always return either &#039;&#039;true&#039;&#039; or an instance of &#039;&#039;lang_string&#039;&#039; which contains the error message to send to the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param int $value The value.&lt;br /&gt;
 * @return true|lang_string&lt;br /&gt;
 */&lt;br /&gt;
protected function validate_userid($value) {&lt;br /&gt;
    if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
        return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above example ensures that the &#039;&#039;userid&#039;&#039; property contains a valid user ID.&lt;br /&gt;
&lt;br /&gt;
Note that the basic validation is always performed first, and thus your custom validation method will not be called when the value did not pass the basic validation.&lt;br /&gt;
&lt;br /&gt;
=== Validation results ===&lt;br /&gt;
&lt;br /&gt;
The validation of the object automatically happens upon &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039;. If the validation did not pass, an &#039;&#039;invalid_persisten_exception&#039;&#039; will be raised. You can validate the object prior to saving the object and get the validation results if you need to.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// We can catch the invalid_persistent_exception.&lt;br /&gt;
try {&lt;br /&gt;
    $persistent = new status();&lt;br /&gt;
    $persistent-&amp;gt;create();&lt;br /&gt;
} catch (invalid_persistent_exception $e) {&lt;br /&gt;
    // Whoops, something wrong happened.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Check whether the object is valid.&lt;br /&gt;
$persistent-&amp;gt;is_valid();        // True or false.&lt;br /&gt;
&lt;br /&gt;
// Get the validation errors.&lt;br /&gt;
$persistent-&amp;gt;get_errors();      // Array where keys are properties and values are errors.&lt;br /&gt;
&lt;br /&gt;
// Validate the object.&lt;br /&gt;
$persistent-&amp;gt;validate();        // Returns true, or an array of errors.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hooks ==&lt;br /&gt;
&lt;br /&gt;
You can define the following methods to be notified prior to, or after, something happened:&lt;br /&gt;
&lt;br /&gt;
;protected before_validate()&lt;br /&gt;
: Do something before the object is validated.&lt;br /&gt;
;protected before_create()&lt;br /&gt;
: Do something before the object is inserted in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_create()&lt;br /&gt;
: Do something right after the object was added to the database.&lt;br /&gt;
;protected before_update()&lt;br /&gt;
: Do something before the object is updated in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_update(bool $result)&lt;br /&gt;
: Do something right after the object was updated in the database.&lt;br /&gt;
;protected before_delete()&lt;br /&gt;
: Do something right before the object is deleted from the database.&lt;br /&gt;
;protected after_delete(bool $result)&lt;br /&gt;
: Do something right after the object was deleted from the database.&lt;br /&gt;
&lt;br /&gt;
== Persistent and forms ==&lt;br /&gt;
&lt;br /&gt;
If your persistents are exposed to your users via forms, head to the [[Persistent form|persistent form page]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# You must create the database table yourself, using the [[XMLDB editor]] and an upgrade script.&lt;br /&gt;
# You must include the [[#Mandatory_properties|mandatory fields]] in your table schema.&lt;br /&gt;
# You must never extend another persistent as this leads to unpredictable errors whenever the parent changes (new properties missing in your table, changes in validation, custom getters and setters, etc...).&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
class status extends core\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example;&lt;br /&gt;
&lt;br /&gt;
use core\persistent;&lt;br /&gt;
use core_user;&lt;br /&gt;
use lang_string;&lt;br /&gt;
&lt;br /&gt;
class status extends persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
                &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
                &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;location&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
                &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
                &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
                &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
                    return get_config(&#039;core&#039;, &#039;default_location&#039;);&lt;br /&gt;
                },&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the user object of the author.&lt;br /&gt;
     *&lt;br /&gt;
     * @return stdClass&lt;br /&gt;
     */&lt;br /&gt;
    public function get_author() {&lt;br /&gt;
        return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Convenience method to set the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
     */&lt;br /&gt;
    public function set_userid($idorobject) {&lt;br /&gt;
        $userid = $idorobject;&lt;br /&gt;
        if (is_object($idorobject)) {&lt;br /&gt;
            $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Validate the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param int $value The value.&lt;br /&gt;
     * @return true|lang_string&lt;br /&gt;
     */&lt;br /&gt;
    protected function validate_userid($value) {&lt;br /&gt;
        if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
            return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get all records from a user&#039;s username.&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $username The username.&lt;br /&gt;
     * @return status[]&lt;br /&gt;
     */&lt;br /&gt;
    public static function get_records_by_username($username) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $sql = &#039;SELECT s.*&lt;br /&gt;
                  FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
                  JOIN {user} u&lt;br /&gt;
                    ON u.id = s.userid&lt;br /&gt;
                 WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
        $persistents = [];&lt;br /&gt;
&lt;br /&gt;
        $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
        foreach ($recordset as $record) {&lt;br /&gt;
            $persistents[] = new static(0, $record);&lt;br /&gt;
        }&lt;br /&gt;
        $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
        return $persistents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Persistent_form&amp;diff=51654</id>
		<title>Persistent form</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Persistent_form&amp;diff=51654"/>
		<updated>2016-12-20T05:09:25Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: Adding &amp;#039;grouping fields&amp;#039;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}If you are creating [[Persistent|persistent]] entries from forms, we&#039;ve got something for you. You can use the class &#039;&#039;core\form\persistent&#039;&#039; as a base for your form instead of &#039;&#039;moodleform&#039;&#039;. Our persistent form class comes handy tools, such as automatic validation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: for more information about forms themselves, [[:Category:Formslib|head this way]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Linking to a persistent ==&lt;br /&gt;
&lt;br /&gt;
In order for the form class to know what persistent we&#039;ll be dealing with, we must declare the &#039;&#039;protected static $persistentclass&#039;&#039; variable. The latter contains the fully qualified name of the persistent class.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var string Persistent class name. */&lt;br /&gt;
protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Defining the form fields ==&lt;br /&gt;
&lt;br /&gt;
Unfortunately this is not automatically done for us, so let&#039;s add our fields to the &#039;&#039;definition()&#039;&#039; method like you would do for any form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Define the form.&lt;br /&gt;
 */&lt;br /&gt;
public function definition() {&lt;br /&gt;
    $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
    // User ID.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
    $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
    // Message.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Location.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;add_action_buttons();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of this is pretty standard, except for the &#039;&#039;userid&#039;&#039;. When creating a new &#039;status&#039;, we do not want our users to be in control of this value. Therefore we define it as a hidden value which we lock (using &#039;&#039;setConstant&#039;&#039;) to the value we created our form with. All the mandatory fields (without a default value) of the persistent need to be added to the form. If your users cannot change their values, then they must be hidden and locked with &#039;&#039;setConstant&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Did you notice that there isn&#039;t any call to &#039;&#039;setType&#039;&#039; in the above example? That is because we automatically do it for you based on the field name and your persistent&#039;s properties.&lt;br /&gt;
&lt;br /&gt;
Also note that the &#039;&#039;id&#039;&#039; property is not included. It is not required, nor recommended, to add it to your fields as it will be handled automatically.&lt;br /&gt;
&lt;br /&gt;
== Using the form ==&lt;br /&gt;
&lt;br /&gt;
When instantiating the form, there are two little things that you need to pay attention to. &lt;br /&gt;
&lt;br /&gt;
Firstly you should always pass the URL of the current page, including its query parameters. We need this to be able to display the form with its validation errors without affecting anything else.&lt;br /&gt;
&lt;br /&gt;
Secondly, the persistent instance must be provided to the form through the custom data. That persistent instance will be used to populate the form with initial data, typically when you are editing an object. When you don&#039;t have a persistent instance yet, probably because your user will be creating a new one, then simply pass null.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,  // An instance, or null.&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like any other form, we will be using &#039;&#039;get_data()&#039;&#039; to validate the form. The only difference is that to determine whether we are editing an object, or creating a new one, we will check if the &#039;&#039;id&#039;&#039; value was returned to us. The persistent form will return the ID value from the persistent we gave it. Then it&#039;s up to you to decide how to apply the data, most likely you will defer the logic to another part of your code, one that ensures that all capability checks are fulfilled. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(0, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Additional validation ==&lt;br /&gt;
&lt;br /&gt;
There are times when the built-in validation of the persistent is not enough. Usually you would use the method &#039;&#039;validation()&#039;&#039;, but as the form persistent class does some extra stuff to make it easier for you, you must use the &#039;&#039;extra_validation()&#039;&#039; method. The latter works almost just like the &#039;&#039;validation()&#039;&#039; one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Extra validation.&lt;br /&gt;
 *&lt;br /&gt;
 * @param  stdClass $data Data to validate.&lt;br /&gt;
 * @param  array $files Array of files.&lt;br /&gt;
 * @param  array $errors Currently reported errors.&lt;br /&gt;
 * @return array of additional errors, or overridden errors.&lt;br /&gt;
 */&lt;br /&gt;
protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
    $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
    if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
        $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return $newerrors;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The typical additional validation will return an array of errors, those will override any previously defined errors. Sometimes, though rarely, you will need to remove previously reported errors, hence the reference to &#039;&#039;$errors&#039;&#039; given, which you can modify directly. Do not abuse it though, this should only be used when you have no other choice.&lt;br /&gt;
&lt;br /&gt;
== Foreign fields ==&lt;br /&gt;
&lt;br /&gt;
By default, the form class tries to be smart at detecting foreign fields such as the submit button. Failure to do so will cause troubles during validation, or when getting the data. So when your form becomes more complex, if it includes more submit buttons, or when it deals with other fields, for example file managers, we must indicate it.&lt;br /&gt;
&lt;br /&gt;
=== Fields to ignore completely ===&lt;br /&gt;
&lt;br /&gt;
The fields to remove are never validated and they are not returned when calling &#039;&#039;get_data()&#039;&#039;. By default the submit button is added to this list so that when we call &#039;&#039;get_data()&#039;&#039; we only get the persistent-related fields. To remove more fields, re-declare the &#039;&#039;protected static $fieldstoremove&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove when getting the final data. */&lt;br /&gt;
protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Do not forget to add the &#039;&#039;submitbutton&#039;&#039; back in there.&lt;br /&gt;
&lt;br /&gt;
=== Fields to validate ===&lt;br /&gt;
&lt;br /&gt;
What about when we have a &#039;&#039;legit&#039;&#039; field but it does not belong to the persistent? We still want to validate it ourselves, but we don&#039;t want it to be validated by the persistent as it will cause an error. In that case we define it in the &#039;&#039;protected static $foreignfields&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the persistent will not validate this field, and we will get the &#039;&#039;updatedelay&#039;&#039; value when we call &#039;&#039;get_data()&#039;&#039;. Just don&#039;t forget to remove it before you feed the data to your persistent.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
    $updatedelay = $data-&amp;gt;updatedelay;&lt;br /&gt;
    unset($data-&amp;gt;updatedelay);&lt;br /&gt;
    $newpersistent = new status(0, $data);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is particularily useful when dealing with file managers.&lt;br /&gt;
&lt;br /&gt;
== Grouping fields ==&lt;br /&gt;
&lt;br /&gt;
There is a little bit of work to do when you want to group some properties together. Let&#039;s imagine that we are asking our users to pick amongst a few verbs and food. They can only tell us if they like or dislike something and what it is. Our form should display both the verb and the food next to each other, and to do so we need to use a group.&lt;br /&gt;
&lt;br /&gt;
First, let&#039;s update our persistent with the new two values we will store:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
        &#039;verb&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUM,&lt;br /&gt;
            &#039;default&#039; =&amp;gt; &#039;love&#039;&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;food&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUM,&lt;br /&gt;
            &#039;default&#039; =&amp;gt; &#039;pizza&#039;&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Secondly, we must add the group to the form:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function definition() {&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    // Food I like.&lt;br /&gt;
    $elements = [];&lt;br /&gt;
    $elements[] = $mform-&amp;gt;createElement(&#039;select&#039;, &#039;verb&#039;, &#039;verb&#039;, [&lt;br /&gt;
        &#039;hate&#039; =&amp;gt; &#039;I hate&#039;,&lt;br /&gt;
        &#039;dislike&#039; =&amp;gt; &#039;I dislike&#039;,&lt;br /&gt;
        &#039;like&#039; =&amp;gt; &#039;I like&#039;,&lt;br /&gt;
        &#039;love&#039; =&amp;gt; &#039;I love&#039;&lt;br /&gt;
    ]);&lt;br /&gt;
    $elements[] = $mform-&amp;gt;createElement(&#039;select&#039;, &#039;food&#039;, &#039;Food&#039;, [&lt;br /&gt;
        &#039;egg&#039; =&amp;gt; &#039;Egg&#039;,&lt;br /&gt;
        &#039;tofu&#039; =&amp;gt; &#039;Tofu&#039;,&lt;br /&gt;
        &#039;chicken&#039; =&amp;gt; &#039;Chicken&#039;,&lt;br /&gt;
        &#039;fish&#039; =&amp;gt; &#039;Fish&#039;,&lt;br /&gt;
        &#039;pizza&#039; =&amp;gt; &#039;Pizza&#039;&lt;br /&gt;
    ]);&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;group&#039;, &#039;foodilike&#039;, &#039;Food I like&#039;, $elements);&lt;br /&gt;
&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you were to try the code like this, you would get an error telling you that the persistent did not expect to receive the property &#039;&#039;foodilike&#039;&#039;. That&#039;s normal because the group is being set to the persistent which does not know what to do with it. In order to counter this we will need to convert the group to individual properties by extending the method &#039;&#039;convert_fields(stdClass $data)&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Convert fields.&lt;br /&gt;
 *&lt;br /&gt;
 * @param stdClass $data The data.&lt;br /&gt;
 * @return stdClass&lt;br /&gt;
 */&lt;br /&gt;
protected static function convert_fields(stdClass $data) {&lt;br /&gt;
    $data = parent::convert_fields($data);&lt;br /&gt;
&lt;br /&gt;
    // Convert the group to single properties.&lt;br /&gt;
    $data-&amp;gt;verb = $data-&amp;gt;foodilike[&#039;verb&#039;];&lt;br /&gt;
    $data-&amp;gt;food = $data-&amp;gt;foodilike[&#039;food&#039;];&lt;br /&gt;
    unset($data-&amp;gt;foodilike);&lt;br /&gt;
&lt;br /&gt;
    return $data;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now it works because we&#039;ve converted our group to individual properties. Note that you must call the parent method because we&#039;re automatically doing the same thing for you when you&#039;re using a text editor field.&lt;br /&gt;
&lt;br /&gt;
While this works fine, this is not enough as when we load an existing objet we must perform the same thing the other way around. We must convert the fields from our persistent to what the group field is expecting. To do so we override the method &#039;&#039;get_default_value()&#039;&#039;. We&#039;re also taking care of the text editor for you here so don&#039;t forget to call the parent.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the default data.&lt;br /&gt;
 *&lt;br /&gt;
 * @return stdClass&lt;br /&gt;
 */&lt;br /&gt;
protected function get_default_data() {&lt;br /&gt;
    $data = parent::get_default_data();&lt;br /&gt;
&lt;br /&gt;
    // Convert the single properties into a group.&lt;br /&gt;
    $data-&amp;gt;foodilike = [&lt;br /&gt;
        &#039;verb&#039; =&amp;gt; $data-&amp;gt;verb,&lt;br /&gt;
        &#039;food&#039; =&amp;gt; $data-&amp;gt;food,&lt;br /&gt;
    ];&lt;br /&gt;
    unset($data-&amp;gt;verb);&lt;br /&gt;
    unset($data-&amp;gt;food);&lt;br /&gt;
&lt;br /&gt;
    return $data;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You should now notice that the default values are set to &amp;quot;I love&amp;quot; and &amp;quot;Pizza&amp;quot;. And we&#039;re done!&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
    &lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove when getting the final data. */&lt;br /&gt;
    protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
    protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Status update delay.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;duration&#039;, &#039;updatedelay&#039;, &#039;Status update delay&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Are you happy?&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;selectyesno&#039;, &#039;areyouhappy&#039;, &#039;Are you happy?&#039;);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Extra validation.&lt;br /&gt;
     *&lt;br /&gt;
     * @param  stdClass $data Data to validate.&lt;br /&gt;
     * @param  array $files Array of files.&lt;br /&gt;
     * @param  array $errors Currently reported errors.&lt;br /&gt;
     * @return array of additional errors, or overridden errors.&lt;br /&gt;
     */&lt;br /&gt;
    protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
        $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
        if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
            $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $newerrors;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Using the form ===&lt;br /&gt;
&lt;br /&gt;
Consider the following code to be a page the users will access at &#039;/example.php&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
require &#039;config.php&#039;;&lt;br /&gt;
&lt;br /&gt;
// Check if we go an ID.&lt;br /&gt;
$id = optional_param(&#039;id&#039;, null, PARAM_INT);&lt;br /&gt;
&lt;br /&gt;
// Set the PAGE URL (and mandatory context). Note the ID being recorded, this is important.&lt;br /&gt;
$PAGE-&amp;gt;set_context(context_system::instance());&lt;br /&gt;
$PAGE-&amp;gt;set_url(new moodle_url(&#039;/example.php&#039;, [&#039;id&#039; =&amp;gt; $id]));&lt;br /&gt;
&lt;br /&gt;
// Instantiate a persistent object if we received an ID. Typically receiving an ID&lt;br /&gt;
// means that we are going to be updating an object rather than creating a new one.&lt;br /&gt;
$persistent = null;&lt;br /&gt;
if (!e mpty($id)) {&lt;br /&gt;
    $persistent = new status($id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Create the form instance. We need to use the current URL and the custom data.&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        if (empty($data-&amp;gt;id)) {&lt;br /&gt;
            // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
            // Call your API to create a new persistent from this data.&lt;br /&gt;
            // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
            $persistent = new status(0, $data);&lt;br /&gt;
            $persistent-&amp;gt;create();&lt;br /&gt;
        } else {&lt;br /&gt;
            // We had an ID, this means that we are going to update a record.&lt;br /&gt;
            // Call your API to update the persistent from the data.&lt;br /&gt;
            // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
            $persistent-&amp;gt;from_record($data);&lt;br /&gt;
            $persistent-&amp;gt;update();&lt;br /&gt;
        }&lt;br /&gt;
        \core\notification::success(get_string(&#039;changessaved&#039;));&lt;br /&gt;
    } catch (Exception $e) {&lt;br /&gt;
        \core\notification::error($e-&amp;gt;getMessage());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Display the mandatory header and footer.&lt;br /&gt;
// And display the form, and its validation errors if there are any.&lt;br /&gt;
echo $OUTPUT-&amp;gt;header();&lt;br /&gt;
$form-&amp;gt;display();&lt;br /&gt;
echo $OUTPUT-&amp;gt;footer();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Persistent_form&amp;diff=51653</id>
		<title>Persistent form</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Persistent_form&amp;diff=51653"/>
		<updated>2016-12-20T03:31:25Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Defining the form fields */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}If you are creating [[Persistent|persistent]] entries from forms, we&#039;ve got something for you. You can use the class &#039;&#039;core\form\persistent&#039;&#039; as a base for your form instead of &#039;&#039;moodleform&#039;&#039;. Our persistent form class comes handy tools, such as automatic validation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: for more information about forms themselves, [[:Category:Formslib|head this way]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Linking to a persistent ==&lt;br /&gt;
&lt;br /&gt;
In order for the form class to know what persistent we&#039;ll be dealing with, we must declare the &#039;&#039;protected static $persistentclass&#039;&#039; variable. The latter contains the fully qualified name of the persistent class.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var string Persistent class name. */&lt;br /&gt;
protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Defining the form fields ==&lt;br /&gt;
&lt;br /&gt;
Unfortunately this is not automatically done for us, so let&#039;s add our fields to the &#039;&#039;definition()&#039;&#039; method like you would do for any form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Define the form.&lt;br /&gt;
 */&lt;br /&gt;
public function definition() {&lt;br /&gt;
    $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
    // User ID.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
    $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
    // Message.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Location.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;add_action_buttons();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of this is pretty standard, except for the &#039;&#039;userid&#039;&#039;. When creating a new &#039;status&#039;, we do not want our users to be in control of this value. Therefore we define it as a hidden value which we lock (using &#039;&#039;setConstant&#039;&#039;) to the value we created our form with. All the mandatory fields (without a default value) of the persistent need to be added to the form. If your users cannot change their values, then they must be hidden and locked with &#039;&#039;setConstant&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Did you notice that there isn&#039;t any call to &#039;&#039;setType&#039;&#039; in the above example? That is because we automatically do it for you based on the field name and your persistent&#039;s properties.&lt;br /&gt;
&lt;br /&gt;
Also note that the &#039;&#039;id&#039;&#039; property is not included. It is not required, nor recommended, to add it to your fields as it will be handled automatically.&lt;br /&gt;
&lt;br /&gt;
== Using the form ==&lt;br /&gt;
&lt;br /&gt;
When instantiating the form, there are two little things that you need to pay attention to. &lt;br /&gt;
&lt;br /&gt;
Firstly you should always pass the URL of the current page, including its query parameters. We need this to be able to display the form with its validation errors without affecting anything else.&lt;br /&gt;
&lt;br /&gt;
Secondly, the persistent instance must be provided to the form through the custom data. That persistent instance will be used to populate the form with initial data, typically when you are editing an object. When you don&#039;t have a persistent instance yet, probably because your user will be creating a new one, then simply pass null.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,  // An instance, or null.&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like any other form, we will be using &#039;&#039;get_data()&#039;&#039; to validate the form. The only difference is that to determine whether we are editing an object, or creating a new one, we will check if the &#039;&#039;id&#039;&#039; value was returned to us. The persistent form will return the ID value from the persistent we gave it. Then it&#039;s up to you to decide how to apply the data, most likely you will defer the logic to another part of your code, one that ensures that all capability checks are fulfilled. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(0, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Additional validation ==&lt;br /&gt;
&lt;br /&gt;
There are times when the built-in validation of the persistent is not enough. Usually you would use the method &#039;&#039;validation()&#039;&#039;, but as the form persistent class does some extra stuff to make it easier for you, you must use the &#039;&#039;extra_validation()&#039;&#039; method. The latter works almost just like the &#039;&#039;validation()&#039;&#039; one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Extra validation.&lt;br /&gt;
 *&lt;br /&gt;
 * @param  stdClass $data Data to validate.&lt;br /&gt;
 * @param  array $files Array of files.&lt;br /&gt;
 * @param  array $errors Currently reported errors.&lt;br /&gt;
 * @return array of additional errors, or overridden errors.&lt;br /&gt;
 */&lt;br /&gt;
protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
    $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
    if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
        $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return $newerrors;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The typical additional validation will return an array of errors, those will override any previously defined errors. Sometimes, though rarely, you will need to remove previously reported errors, hence the reference to &#039;&#039;$errors&#039;&#039; given, which you can modify directly. Do not abuse it though, this should only be used when you have no other choice.&lt;br /&gt;
&lt;br /&gt;
== Foreign fields ==&lt;br /&gt;
&lt;br /&gt;
By default, the form class tries to be smart at detecting foreign fields such as the submit button. Failure to do so will cause troubles during validation, or when getting the data. So when your form becomes more complex, if it includes more submit buttons, or when it deals with other fields, for example file managers, we must indicate it.&lt;br /&gt;
&lt;br /&gt;
=== Fields to ignore completely ===&lt;br /&gt;
&lt;br /&gt;
The fields to remove are never validated and they are not returned when calling &#039;&#039;get_data()&#039;&#039;. By default the submit button is added to this list so that when we call &#039;&#039;get_data()&#039;&#039; we only get the persistent-related fields. To remove more fields, re-declare the &#039;&#039;protected static $fieldstoremove&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove when getting the final data. */&lt;br /&gt;
protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Do not forget to add the &#039;&#039;submitbutton&#039;&#039; back in there.&lt;br /&gt;
&lt;br /&gt;
=== Fields to validate ===&lt;br /&gt;
&lt;br /&gt;
What about when we have a &#039;&#039;legit&#039;&#039; field but it does not belong to the persistent? We still want to validate it ourselves, but we don&#039;t want it to be validated by the persistent as it will cause an error. In that case we define it in the &#039;&#039;protected static $foreignfields&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the persistent will not validate this field, and we will get the &#039;&#039;updatedelay&#039;&#039; value when we call &#039;&#039;get_data()&#039;&#039;. Just don&#039;t forget to remove it before you feed the data to your persistent.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
    $updatedelay = $data-&amp;gt;updatedelay;&lt;br /&gt;
    unset($data-&amp;gt;updatedelay);&lt;br /&gt;
    $newpersistent = new status(0, $data);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is particularily useful when dealing with file managers.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
    &lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove when getting the final data. */&lt;br /&gt;
    protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
    protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Status update delay.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;duration&#039;, &#039;updatedelay&#039;, &#039;Status update delay&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Are you happy?&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;selectyesno&#039;, &#039;areyouhappy&#039;, &#039;Are you happy?&#039;);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Extra validation.&lt;br /&gt;
     *&lt;br /&gt;
     * @param  stdClass $data Data to validate.&lt;br /&gt;
     * @param  array $files Array of files.&lt;br /&gt;
     * @param  array $errors Currently reported errors.&lt;br /&gt;
     * @return array of additional errors, or overridden errors.&lt;br /&gt;
     */&lt;br /&gt;
    protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
        $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
        if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
            $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $newerrors;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Using the form ===&lt;br /&gt;
&lt;br /&gt;
Consider the following code to be a page the users will access at &#039;/example.php&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
require &#039;config.php&#039;;&lt;br /&gt;
&lt;br /&gt;
// Check if we go an ID.&lt;br /&gt;
$id = optional_param(&#039;id&#039;, null, PARAM_INT);&lt;br /&gt;
&lt;br /&gt;
// Set the PAGE URL (and mandatory context). Note the ID being recorded, this is important.&lt;br /&gt;
$PAGE-&amp;gt;set_context(context_system::instance());&lt;br /&gt;
$PAGE-&amp;gt;set_url(new moodle_url(&#039;/example.php&#039;, [&#039;id&#039; =&amp;gt; $id]));&lt;br /&gt;
&lt;br /&gt;
// Instantiate a persistent object if we received an ID. Typically receiving an ID&lt;br /&gt;
// means that we are going to be updating an object rather than creating a new one.&lt;br /&gt;
$persistent = null;&lt;br /&gt;
if (!e mpty($id)) {&lt;br /&gt;
    $persistent = new status($id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Create the form instance. We need to use the current URL and the custom data.&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        if (empty($data-&amp;gt;id)) {&lt;br /&gt;
            // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
            // Call your API to create a new persistent from this data.&lt;br /&gt;
            // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
            $persistent = new status(0, $data);&lt;br /&gt;
            $persistent-&amp;gt;create();&lt;br /&gt;
        } else {&lt;br /&gt;
            // We had an ID, this means that we are going to update a record.&lt;br /&gt;
            // Call your API to update the persistent from the data.&lt;br /&gt;
            // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
            $persistent-&amp;gt;from_record($data);&lt;br /&gt;
            $persistent-&amp;gt;update();&lt;br /&gt;
        }&lt;br /&gt;
        \core\notification::success(get_string(&#039;changessaved&#039;));&lt;br /&gt;
    } catch (Exception $e) {&lt;br /&gt;
        \core\notification::error($e-&amp;gt;getMessage());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Display the mandatory header and footer.&lt;br /&gt;
// And display the form, and its validation errors if there are any.&lt;br /&gt;
echo $OUTPUT-&amp;gt;header();&lt;br /&gt;
$form-&amp;gt;display();&lt;br /&gt;
echo $OUTPUT-&amp;gt;footer();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Persistent_form&amp;diff=51652</id>
		<title>Persistent form</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Persistent_form&amp;diff=51652"/>
		<updated>2016-12-20T03:31:05Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Defining the form fields */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}If you are creating [[Persistent|persistent]] entries from forms, we&#039;ve got something for you. You can use the class &#039;&#039;core\form\persistent&#039;&#039; as a base for your form instead of &#039;&#039;moodleform&#039;&#039;. Our persistent form class comes handy tools, such as automatic validation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: for more information about forms themselves, [[:Category:Formslib|head this way]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Linking to a persistent ==&lt;br /&gt;
&lt;br /&gt;
In order for the form class to know what persistent we&#039;ll be dealing with, we must declare the &#039;&#039;protected static $persistentclass&#039;&#039; variable. The latter contains the fully qualified name of the persistent class.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var string Persistent class name. */&lt;br /&gt;
protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Defining the form fields ==&lt;br /&gt;
&lt;br /&gt;
Unfortunately this is not automatically done for us, so let&#039;s add our fields to the &#039;&#039;definition()&#039;&#039; method like you would do for any form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Define the form.&lt;br /&gt;
 */&lt;br /&gt;
public function definition() {&lt;br /&gt;
    $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
    // User ID.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
    $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
    // Message.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Location.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;add_action_buttons();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of this is pretty standard, except for the &#039;&#039;userid&#039;&#039;. When creating a new &#039;status&#039;, we do not want our users to be in control of this value. Therefore we define it as a hidden value which we lock (using &#039;&#039;setConstant&#039;&#039;) to the value we created our form with. All the mandatory fields (without a default value) of the persistent need to be added to the form. If your users cannot change their values, then they must be hidden and locked with &#039;&#039;setConstant&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Did you notice that there isn&#039;t any call the &#039;&#039;setType&#039;&#039; in the above example? That is because we automatically do it for you based on the field name and your persistent&#039;s properties.&lt;br /&gt;
&lt;br /&gt;
Also note that the &#039;&#039;id&#039;&#039; property is not included. It is not required, nor recommended, to add it to your fields as it will be handled automatically.&lt;br /&gt;
&lt;br /&gt;
== Using the form ==&lt;br /&gt;
&lt;br /&gt;
When instantiating the form, there are two little things that you need to pay attention to. &lt;br /&gt;
&lt;br /&gt;
Firstly you should always pass the URL of the current page, including its query parameters. We need this to be able to display the form with its validation errors without affecting anything else.&lt;br /&gt;
&lt;br /&gt;
Secondly, the persistent instance must be provided to the form through the custom data. That persistent instance will be used to populate the form with initial data, typically when you are editing an object. When you don&#039;t have a persistent instance yet, probably because your user will be creating a new one, then simply pass null.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,  // An instance, or null.&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like any other form, we will be using &#039;&#039;get_data()&#039;&#039; to validate the form. The only difference is that to determine whether we are editing an object, or creating a new one, we will check if the &#039;&#039;id&#039;&#039; value was returned to us. The persistent form will return the ID value from the persistent we gave it. Then it&#039;s up to you to decide how to apply the data, most likely you will defer the logic to another part of your code, one that ensures that all capability checks are fulfilled. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(0, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Additional validation ==&lt;br /&gt;
&lt;br /&gt;
There are times when the built-in validation of the persistent is not enough. Usually you would use the method &#039;&#039;validation()&#039;&#039;, but as the form persistent class does some extra stuff to make it easier for you, you must use the &#039;&#039;extra_validation()&#039;&#039; method. The latter works almost just like the &#039;&#039;validation()&#039;&#039; one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Extra validation.&lt;br /&gt;
 *&lt;br /&gt;
 * @param  stdClass $data Data to validate.&lt;br /&gt;
 * @param  array $files Array of files.&lt;br /&gt;
 * @param  array $errors Currently reported errors.&lt;br /&gt;
 * @return array of additional errors, or overridden errors.&lt;br /&gt;
 */&lt;br /&gt;
protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
    $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
    if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
        $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return $newerrors;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The typical additional validation will return an array of errors, those will override any previously defined errors. Sometimes, though rarely, you will need to remove previously reported errors, hence the reference to &#039;&#039;$errors&#039;&#039; given, which you can modify directly. Do not abuse it though, this should only be used when you have no other choice.&lt;br /&gt;
&lt;br /&gt;
== Foreign fields ==&lt;br /&gt;
&lt;br /&gt;
By default, the form class tries to be smart at detecting foreign fields such as the submit button. Failure to do so will cause troubles during validation, or when getting the data. So when your form becomes more complex, if it includes more submit buttons, or when it deals with other fields, for example file managers, we must indicate it.&lt;br /&gt;
&lt;br /&gt;
=== Fields to ignore completely ===&lt;br /&gt;
&lt;br /&gt;
The fields to remove are never validated and they are not returned when calling &#039;&#039;get_data()&#039;&#039;. By default the submit button is added to this list so that when we call &#039;&#039;get_data()&#039;&#039; we only get the persistent-related fields. To remove more fields, re-declare the &#039;&#039;protected static $fieldstoremove&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove when getting the final data. */&lt;br /&gt;
protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Do not forget to add the &#039;&#039;submitbutton&#039;&#039; back in there.&lt;br /&gt;
&lt;br /&gt;
=== Fields to validate ===&lt;br /&gt;
&lt;br /&gt;
What about when we have a &#039;&#039;legit&#039;&#039; field but it does not belong to the persistent? We still want to validate it ourselves, but we don&#039;t want it to be validated by the persistent as it will cause an error. In that case we define it in the &#039;&#039;protected static $foreignfields&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the persistent will not validate this field, and we will get the &#039;&#039;updatedelay&#039;&#039; value when we call &#039;&#039;get_data()&#039;&#039;. Just don&#039;t forget to remove it before you feed the data to your persistent.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
    $updatedelay = $data-&amp;gt;updatedelay;&lt;br /&gt;
    unset($data-&amp;gt;updatedelay);&lt;br /&gt;
    $newpersistent = new status(0, $data);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is particularily useful when dealing with file managers.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
    &lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove when getting the final data. */&lt;br /&gt;
    protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
    protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Status update delay.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;duration&#039;, &#039;updatedelay&#039;, &#039;Status update delay&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Are you happy?&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;selectyesno&#039;, &#039;areyouhappy&#039;, &#039;Are you happy?&#039;);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Extra validation.&lt;br /&gt;
     *&lt;br /&gt;
     * @param  stdClass $data Data to validate.&lt;br /&gt;
     * @param  array $files Array of files.&lt;br /&gt;
     * @param  array $errors Currently reported errors.&lt;br /&gt;
     * @return array of additional errors, or overridden errors.&lt;br /&gt;
     */&lt;br /&gt;
    protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
        $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
        if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
            $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $newerrors;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Using the form ===&lt;br /&gt;
&lt;br /&gt;
Consider the following code to be a page the users will access at &#039;/example.php&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
require &#039;config.php&#039;;&lt;br /&gt;
&lt;br /&gt;
// Check if we go an ID.&lt;br /&gt;
$id = optional_param(&#039;id&#039;, null, PARAM_INT);&lt;br /&gt;
&lt;br /&gt;
// Set the PAGE URL (and mandatory context). Note the ID being recorded, this is important.&lt;br /&gt;
$PAGE-&amp;gt;set_context(context_system::instance());&lt;br /&gt;
$PAGE-&amp;gt;set_url(new moodle_url(&#039;/example.php&#039;, [&#039;id&#039; =&amp;gt; $id]));&lt;br /&gt;
&lt;br /&gt;
// Instantiate a persistent object if we received an ID. Typically receiving an ID&lt;br /&gt;
// means that we are going to be updating an object rather than creating a new one.&lt;br /&gt;
$persistent = null;&lt;br /&gt;
if (!e mpty($id)) {&lt;br /&gt;
    $persistent = new status($id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Create the form instance. We need to use the current URL and the custom data.&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        if (empty($data-&amp;gt;id)) {&lt;br /&gt;
            // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
            // Call your API to create a new persistent from this data.&lt;br /&gt;
            // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
            $persistent = new status(0, $data);&lt;br /&gt;
            $persistent-&amp;gt;create();&lt;br /&gt;
        } else {&lt;br /&gt;
            // We had an ID, this means that we are going to update a record.&lt;br /&gt;
            // Call your API to update the persistent from the data.&lt;br /&gt;
            // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
            $persistent-&amp;gt;from_record($data);&lt;br /&gt;
            $persistent-&amp;gt;update();&lt;br /&gt;
        }&lt;br /&gt;
        \core\notification::success(get_string(&#039;changessaved&#039;));&lt;br /&gt;
    } catch (Exception $e) {&lt;br /&gt;
        \core\notification::error($e-&amp;gt;getMessage());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Display the mandatory header and footer.&lt;br /&gt;
// And display the form, and its validation errors if there are any.&lt;br /&gt;
echo $OUTPUT-&amp;gt;header();&lt;br /&gt;
$form-&amp;gt;display();&lt;br /&gt;
echo $OUTPUT-&amp;gt;footer();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Persistent_form&amp;diff=51651</id>
		<title>Persistent form</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Persistent_form&amp;diff=51651"/>
		<updated>2016-12-20T03:27:47Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: Remote setType&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}If you are creating [[Persistent|persistent]] entries from forms, we&#039;ve got something for you. You can use the class &#039;&#039;core\form\persistent&#039;&#039; as a base for your form instead of &#039;&#039;moodleform&#039;&#039;. Our persistent form class comes handy tools, such as automatic validation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: for more information about forms themselves, [[:Category:Formslib|head this way]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Linking to a persistent ==&lt;br /&gt;
&lt;br /&gt;
In order for the form class to know what persistent we&#039;ll be dealing with, we must declare the &#039;&#039;protected static $persistentclass&#039;&#039; variable. The latter contains the fully qualified name of the persistent class.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var string Persistent class name. */&lt;br /&gt;
protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Defining the form fields ==&lt;br /&gt;
&lt;br /&gt;
Unfortunately this is not automatically done for us, so let&#039;s add our fields to the &#039;&#039;definition()&#039;&#039; method like you would do for any form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Define the form.&lt;br /&gt;
 */&lt;br /&gt;
public function definition() {&lt;br /&gt;
    $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
    // User ID.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
    $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
    // Message.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Location.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;add_action_buttons();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of this is pretty standard, except for the &#039;&#039;userid&#039;&#039;. When creating a new &#039;status&#039;, we do not want our users to be in control of this value. Therefore we define it as a hidden value which we lock (using &#039;&#039;setConstant&#039;&#039;) to the value we created our form with. All the mandatory fields (without a default value) of the persistent need to be added to the form. If your users cannot change their values, then they must be hidden and locked with &#039;&#039;setConstant&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Also note that the &#039;&#039;id&#039;&#039; property is not included. It is not required, nor recommended, to add it to your fields as it will be handled automatically.&lt;br /&gt;
&lt;br /&gt;
== Using the form ==&lt;br /&gt;
&lt;br /&gt;
When instantiating the form, there are two little things that you need to pay attention to. &lt;br /&gt;
&lt;br /&gt;
Firstly you should always pass the URL of the current page, including its query parameters. We need this to be able to display the form with its validation errors without affecting anything else.&lt;br /&gt;
&lt;br /&gt;
Secondly, the persistent instance must be provided to the form through the custom data. That persistent instance will be used to populate the form with initial data, typically when you are editing an object. When you don&#039;t have a persistent instance yet, probably because your user will be creating a new one, then simply pass null.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,  // An instance, or null.&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like any other form, we will be using &#039;&#039;get_data()&#039;&#039; to validate the form. The only difference is that to determine whether we are editing an object, or creating a new one, we will check if the &#039;&#039;id&#039;&#039; value was returned to us. The persistent form will return the ID value from the persistent we gave it. Then it&#039;s up to you to decide how to apply the data, most likely you will defer the logic to another part of your code, one that ensures that all capability checks are fulfilled. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(0, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Additional validation ==&lt;br /&gt;
&lt;br /&gt;
There are times when the built-in validation of the persistent is not enough. Usually you would use the method &#039;&#039;validation()&#039;&#039;, but as the form persistent class does some extra stuff to make it easier for you, you must use the &#039;&#039;extra_validation()&#039;&#039; method. The latter works almost just like the &#039;&#039;validation()&#039;&#039; one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Extra validation.&lt;br /&gt;
 *&lt;br /&gt;
 * @param  stdClass $data Data to validate.&lt;br /&gt;
 * @param  array $files Array of files.&lt;br /&gt;
 * @param  array $errors Currently reported errors.&lt;br /&gt;
 * @return array of additional errors, or overridden errors.&lt;br /&gt;
 */&lt;br /&gt;
protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
    $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
    if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
        $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return $newerrors;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The typical additional validation will return an array of errors, those will override any previously defined errors. Sometimes, though rarely, you will need to remove previously reported errors, hence the reference to &#039;&#039;$errors&#039;&#039; given, which you can modify directly. Do not abuse it though, this should only be used when you have no other choice.&lt;br /&gt;
&lt;br /&gt;
== Foreign fields ==&lt;br /&gt;
&lt;br /&gt;
By default, the form class tries to be smart at detecting foreign fields such as the submit button. Failure to do so will cause troubles during validation, or when getting the data. So when your form becomes more complex, if it includes more submit buttons, or when it deals with other fields, for example file managers, we must indicate it.&lt;br /&gt;
&lt;br /&gt;
=== Fields to ignore completely ===&lt;br /&gt;
&lt;br /&gt;
The fields to remove are never validated and they are not returned when calling &#039;&#039;get_data()&#039;&#039;. By default the submit button is added to this list so that when we call &#039;&#039;get_data()&#039;&#039; we only get the persistent-related fields. To remove more fields, re-declare the &#039;&#039;protected static $fieldstoremove&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove when getting the final data. */&lt;br /&gt;
protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Do not forget to add the &#039;&#039;submitbutton&#039;&#039; back in there.&lt;br /&gt;
&lt;br /&gt;
=== Fields to validate ===&lt;br /&gt;
&lt;br /&gt;
What about when we have a &#039;&#039;legit&#039;&#039; field but it does not belong to the persistent? We still want to validate it ourselves, but we don&#039;t want it to be validated by the persistent as it will cause an error. In that case we define it in the &#039;&#039;protected static $foreignfields&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the persistent will not validate this field, and we will get the &#039;&#039;updatedelay&#039;&#039; value when we call &#039;&#039;get_data()&#039;&#039;. Just don&#039;t forget to remove it before you feed the data to your persistent.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
    $updatedelay = $data-&amp;gt;updatedelay;&lt;br /&gt;
    unset($data-&amp;gt;updatedelay);&lt;br /&gt;
    $newpersistent = new status(0, $data);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is particularily useful when dealing with file managers.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
    &lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove when getting the final data. */&lt;br /&gt;
    protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
    protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Status update delay.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;duration&#039;, &#039;updatedelay&#039;, &#039;Status update delay&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Are you happy?&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;selectyesno&#039;, &#039;areyouhappy&#039;, &#039;Are you happy?&#039;);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Extra validation.&lt;br /&gt;
     *&lt;br /&gt;
     * @param  stdClass $data Data to validate.&lt;br /&gt;
     * @param  array $files Array of files.&lt;br /&gt;
     * @param  array $errors Currently reported errors.&lt;br /&gt;
     * @return array of additional errors, or overridden errors.&lt;br /&gt;
     */&lt;br /&gt;
    protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
        $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
        if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
            $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $newerrors;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Using the form ===&lt;br /&gt;
&lt;br /&gt;
Consider the following code to be a page the users will access at &#039;/example.php&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
require &#039;config.php&#039;;&lt;br /&gt;
&lt;br /&gt;
// Check if we go an ID.&lt;br /&gt;
$id = optional_param(&#039;id&#039;, null, PARAM_INT);&lt;br /&gt;
&lt;br /&gt;
// Set the PAGE URL (and mandatory context). Note the ID being recorded, this is important.&lt;br /&gt;
$PAGE-&amp;gt;set_context(context_system::instance());&lt;br /&gt;
$PAGE-&amp;gt;set_url(new moodle_url(&#039;/example.php&#039;, [&#039;id&#039; =&amp;gt; $id]));&lt;br /&gt;
&lt;br /&gt;
// Instantiate a persistent object if we received an ID. Typically receiving an ID&lt;br /&gt;
// means that we are going to be updating an object rather than creating a new one.&lt;br /&gt;
$persistent = null;&lt;br /&gt;
if (!e mpty($id)) {&lt;br /&gt;
    $persistent = new status($id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Create the form instance. We need to use the current URL and the custom data.&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    try {&lt;br /&gt;
        if (empty($data-&amp;gt;id)) {&lt;br /&gt;
            // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
            // Call your API to create a new persistent from this data.&lt;br /&gt;
            // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
            $persistent = new status(0, $data);&lt;br /&gt;
            $persistent-&amp;gt;create();&lt;br /&gt;
        } else {&lt;br /&gt;
            // We had an ID, this means that we are going to update a record.&lt;br /&gt;
            // Call your API to update the persistent from the data.&lt;br /&gt;
            // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
            $persistent-&amp;gt;from_record($data);&lt;br /&gt;
            $persistent-&amp;gt;update();&lt;br /&gt;
        }&lt;br /&gt;
        \core\notification::success(get_string(&#039;changessaved&#039;));&lt;br /&gt;
    } catch (Exception $e) {&lt;br /&gt;
        \core\notification::error($e-&amp;gt;getMessage());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Display the mandatory header and footer.&lt;br /&gt;
// And display the form, and its validation errors if there are any.&lt;br /&gt;
echo $OUTPUT-&amp;gt;header();&lt;br /&gt;
$form-&amp;gt;display();&lt;br /&gt;
echo $OUTPUT-&amp;gt;footer();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Persistent_form&amp;diff=51640</id>
		<title>Persistent form</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Persistent_form&amp;diff=51640"/>
		<updated>2016-12-19T02:38:24Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Using the form */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}If you are creating [[Persistent|persistent]] entries from forms, we&#039;ve got something for you. You can use the class &#039;&#039;core\form\persistent&#039;&#039; as a base for your form instead of &#039;&#039;moodleform&#039;&#039;. Our persistent form class comes handy tools, such as automatic validation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: for more information about forms themselves, [[:Category:Formslib|head this way]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Linking to a persistent ==&lt;br /&gt;
&lt;br /&gt;
In order for the form class to know what persistent we&#039;ll be dealing with, we must declare the &#039;&#039;protected static $persistentclass&#039;&#039; variable. The latter contains the fully qualified name of the persistent class.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var string Persistent class name. */&lt;br /&gt;
protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Defining the form fields ==&lt;br /&gt;
&lt;br /&gt;
Unfortunately this is not automatically done for us, so let&#039;s add our fields to the &#039;&#039;definition()&#039;&#039; method like you would do for any form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Define the form.&lt;br /&gt;
 */&lt;br /&gt;
public function definition() {&lt;br /&gt;
    $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
    // User ID.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
    $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
    // Message.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
    // Location.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;add_action_buttons();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of this is pretty standard, except for the &#039;&#039;userid&#039;&#039;. When creating a new &#039;status&#039;, we do not want our users to be in control of this value. Therefore we define it as a hidden value which we lock (using &#039;&#039;setConstant&#039;&#039;) to the value we created our form with. All the mandatory fields (without a default value) of the persistent need to be added to the form. If your users cannot change their values, then they must be hidden and locked with &#039;&#039;setConstant&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Also note that the &#039;&#039;id&#039;&#039; property is not included. It is not required, nor recommended, to add it to your fields as it will be handled automatically.&lt;br /&gt;
&lt;br /&gt;
== Using the form ==&lt;br /&gt;
&lt;br /&gt;
When instantiating the form, there are two little things that you need to pay attention to. &lt;br /&gt;
&lt;br /&gt;
Firstly you should always pass the URL of the current page, including its query parameters. We need this to be able to display the form with its validation errors without affecting anything else.&lt;br /&gt;
&lt;br /&gt;
Secondly, the persistent instance must be provided to the form through the custom data. That persistent instance will be used to populate the form with initial data, typically when you are editing an object. When you don&#039;t have a persistent instance yet, probably because your user will be creating a new one, then simply pass null.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,  // An instance, or null.&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like any other form, we will be using &#039;&#039;get_data()&#039;&#039; to validate the form. The only difference is that to determine whether we are editing an object, or creating a new one, we will check if the &#039;&#039;id&#039;&#039; value was returned to us. The persistent form will return the ID value from the persistent we gave it. Then it&#039;s up to you to decide how to apply the data, most likely you will defer the logic to another part of your code, one that ensures that all capability checks are fulfilled. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(0, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Additional validation ==&lt;br /&gt;
&lt;br /&gt;
There are times when the built-in validation of the persistent is not enough. Usually you would use the method &#039;&#039;validation()&#039;&#039;, but as the form persistent class does some extra stuff to make it easier for you, you must use the &#039;&#039;extra_validation()&#039;&#039; method. The latter works almost just like the &#039;&#039;validation()&#039;&#039; one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Extra validation.&lt;br /&gt;
 *&lt;br /&gt;
 * @param  stdClass $data Data to validate.&lt;br /&gt;
 * @param  array $files Array of files.&lt;br /&gt;
 * @param  array $errors Currently reported errors.&lt;br /&gt;
 * @return array of additional errors, or overridden errors.&lt;br /&gt;
 */&lt;br /&gt;
protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
    $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
    if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
        $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return $newerrors;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The typical additional validation will return an array of errors, those will override any previously defined errors. Sometimes, though rarely, you will need to remove previously reported errors, hence the reference to &#039;&#039;$errors&#039;&#039; given, which you can modify directly. Do not abuse it though, this should only be used when you have no other choice.&lt;br /&gt;
&lt;br /&gt;
== Foreign fields ==&lt;br /&gt;
&lt;br /&gt;
By default, the form class tries to be smart at detecting foreign fields such as the submit button. Failure to do so will cause troubles during validation, or when getting the data. So when your form becomes more complex, if it includes more submit buttons, or when it deals with other fields, for example file managers, we must indicate it.&lt;br /&gt;
&lt;br /&gt;
=== Fields to ignore completely ===&lt;br /&gt;
&lt;br /&gt;
The fields to remove are never validated and they are not returned when calling &#039;&#039;get_data()&#039;&#039;. By default the submit button is added to this list so that when we call &#039;&#039;get_data()&#039;&#039; we only get the persistent-related fields. To remove more fields, re-declare the &#039;&#039;protected static $fieldstoremove&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove when getting the final data. */&lt;br /&gt;
protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Do not forget to add the &#039;&#039;submitbutton&#039;&#039; back in there.&lt;br /&gt;
&lt;br /&gt;
=== Fields to validate ===&lt;br /&gt;
&lt;br /&gt;
What about when we have a &#039;&#039;legit&#039;&#039; field but it does not belong to the persistent? We still want to validate it ourselves, but we don&#039;t want it to be validated by the persistent as it will cause an error. In that case we define it in the &#039;&#039;protected static $foreignfields&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the persistent will not validate this field, and we will get the &#039;&#039;updatedelay&#039;&#039; value when we call &#039;&#039;get_data()&#039;&#039;. Just don&#039;t forget to remove it before you feed the data to your persistent.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
    $updatedelay = $data-&amp;gt;updatedelay;&lt;br /&gt;
    unset($data-&amp;gt;updatedelay);&lt;br /&gt;
    $newpersistent = new status(0, $data);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is particularily useful when dealing with file managers.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
    &lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove when getting the final data. */&lt;br /&gt;
    protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
    protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
        // Status update delay.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;duration&#039;, &#039;updatedelay&#039;, &#039;Status update delay&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Are you happy?&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;selectyesno&#039;, &#039;areyouhappy&#039;, &#039;Are you happy?&#039;);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Extra validation.&lt;br /&gt;
     *&lt;br /&gt;
     * @param  stdClass $data Data to validate.&lt;br /&gt;
     * @param  array $files Array of files.&lt;br /&gt;
     * @param  array $errors Currently reported errors.&lt;br /&gt;
     * @return array of additional errors, or overridden errors.&lt;br /&gt;
     */&lt;br /&gt;
    protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
        $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
        if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
            $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $newerrors;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Using the form ===&lt;br /&gt;
&lt;br /&gt;
Consider the following code to be a page thye users will access at &#039;/example.php&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
require &#039;config.php&#039;;&lt;br /&gt;
&lt;br /&gt;
// Check if we go an ID.&lt;br /&gt;
$id = optional_param(&#039;id&#039;, null, PARAM_INT);&lt;br /&gt;
&lt;br /&gt;
// Set the PAGE URL (and mandatory context). Note the ID being recorded, this is important.&lt;br /&gt;
$PAGE-&amp;gt;set_context(context_system::instance());&lt;br /&gt;
$PAGE-&amp;gt;set_url(new moodle_url(&#039;/example.php&#039;, [&#039;id&#039; =&amp;gt; $id]));&lt;br /&gt;
&lt;br /&gt;
// Instantiate a persistent object if we received an ID. Typically receiving an ID&lt;br /&gt;
// means that we are going to be updating an object rather than creating a new one.&lt;br /&gt;
$persistent = null;&lt;br /&gt;
if (!empty($id)) {&lt;br /&gt;
    $persistent = new status($id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Create the form instance. We need to use the current URL and the custom data.&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(0, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Display the mandatory header and footer.&lt;br /&gt;
// And display the form, and its validation errors if there are any.&lt;br /&gt;
echo $OUTPUT-&amp;gt;header();&lt;br /&gt;
$form-&amp;gt;display();&lt;br /&gt;
echo $OUTPUT-&amp;gt;footer();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51639</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51639"/>
		<updated>2016-12-19T02:36:12Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Common pitfalls */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected when instantiating your exporter, and the ones it will export at the same time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These properties will allow us to generate a &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; structure for external functions, [[#In_external_functions|more on this later]]. Oh, and if you are using persistent you do not need to do this: [[#Exporters_and_persistent|check this out]].&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the field &#039;username&#039; to be passed in the key &#039;user&#039;. The &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039; structures include all of the &#039;&#039;standard&#039;&#039; properties, none of the &#039;&#039;[[#Additional_properties|other]]&#039;&#039; ones. Note that the &#039;&#039;create&#039;&#039; structure does not include the &#039;&#039;id&#039;&#039; property. Use &#039;&#039; user_exporter::get_update_structure()&#039;&#039; if to update a user and thus receive the ID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not be flexible, and may not generate a valid structure for some webservice protocols.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, [&#039;context&#039; =&amp;gt; context_user::instance(123)]);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... we&#039;ve cheating a bit above. Did you notice that we&#039;ve passed a context to the exporter? We passed a [[#Related_objects|related object]], more on them later.&lt;br /&gt;
&lt;br /&gt;
=== Formatting parameters ===&lt;br /&gt;
&lt;br /&gt;
Formatting requires a context. If you&#039;ve defined a [[#Related_objects|related object]] with the name &#039;&#039;context&#039;&#039; we will automatically use this one. But there are cases where you need multiple contexts, or you need to rewrite the pluginfile URLs. When that is the case, you will define a method called &#039;get_format_parameters_for_&#039; followed with the name of the property. The latter also accepts the various options which both &#039;&#039;format_text&#039;&#039; and &#039;&#039;format_string&#039;&#039; support.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the formatting parameters for the description.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected function get_format_parameters_for_description() {&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;component&#039; =&amp;gt; &#039;core_user&#039;,&lt;br /&gt;
        &#039;filearea&#039; =&amp;gt; &#039;description&#039;,&lt;br /&gt;
        &#039;itemid&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a complete list of options, refer to the PHP documentation of the method [https://github.com/moodle/moodle/blob/master/lib/classes/external/exporter.php core\external\exporter::get_format_parameters()].&lt;br /&gt;
&lt;br /&gt;
Note that when you return a context from the above method, it is advised that you get it from the related objects. Doing so will prevent contexts from being loaded from the database from within the exporter and cause unwanted performance issues. The &#039;&#039;system context&#039;&#039; is an exception is this.&lt;br /&gt;
&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns a list of objects that are related.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_related() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;context&#039; =&amp;gt; &#039;context&#039;,                     // Must be an instance of context.&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,  // Must be an array of status instances.&lt;br /&gt;
        &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,              // Can be a mother instance, or null.&lt;br /&gt;
        &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,         // Can be null, or an array of brother instances.&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional relateds must still be provided but as &#039;&#039;null&#039;&#039;. Related objects are accessible from within the exporter using &#039;&#039;$this-&amp;gt;related[&#039;nameOfRelated&#039;]&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Creating a brand new exporter class, and leaving the other one unchanged.&lt;br /&gt;
#* Adding the new related, and assigning it a default value in your &#039;&#039;constructor&#039;&#039; if undefined. Also print a debugging message for developers to fix their code.&lt;br /&gt;
#** Note: Adding the related as &#039;&#039;optional&#039;&#039; does not work. Optional relateds still need to be passed to the constructor, but as &#039;&#039;null&#039;&#039;.&lt;br /&gt;
#* Adding the new related, and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the formatting parameters for the description.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_format_parameters_for_description() {&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;component&#039; =&amp;gt; &#039;core_user&#039;,&lt;br /&gt;
            &#039;filearea&#039; =&amp;gt; &#039;description&#039;,&lt;br /&gt;
            &#039;itemid&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Persistent_form&amp;diff=51633</id>
		<title>Persistent form</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Persistent_form&amp;diff=51633"/>
		<updated>2016-12-16T08:01:41Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Using the form */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}If you are creating [[Persistent|persistent]] entries from forms, we&#039;ve got something for you. You can use the class &#039;&#039;core\form\persistent&#039;&#039; as a base for your form instead of &#039;&#039;moodleform&#039;&#039;. Our persistent form class comes handy tools, such as automatic validation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: for more information about forms themselves, [[:Category:Formslib|head this way]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Linking to a persistent ==&lt;br /&gt;
&lt;br /&gt;
In order for the form class to know what persistent we&#039;ll be dealing with, we must declare the &#039;&#039;protected static $persistentclass&#039;&#039; variable. The latter contains the fully qualified name of the persistent class.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var string Persistent class name. */&lt;br /&gt;
protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Defining the form fields ==&lt;br /&gt;
&lt;br /&gt;
Unfortunately this is not automatically done for us, so let&#039;s add our fields to the &#039;&#039;definition()&#039;&#039; method like you would do for any form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Define the form.&lt;br /&gt;
 */&lt;br /&gt;
public function definition() {&lt;br /&gt;
    $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
    // User ID.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
    $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
    // Message.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
    // Location.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;add_action_buttons();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of this is pretty standard, except for the &#039;&#039;userid&#039;&#039;. When creating a new &#039;status&#039;, we do not want our users to be in control of this value. Therefore we define it as a hidden value which we lock (using &#039;&#039;setConstant&#039;&#039;) to the value we created our form with. All the mandatory fields (without a default value) of the persistent need to be added to the form. If your users cannot change their values, then they must be hidden and locked with &#039;&#039;setConstant&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Also note that the &#039;&#039;id&#039;&#039; property is not included. It is not required, nor recommended, to add it to your fields as it will be handled automatically.&lt;br /&gt;
&lt;br /&gt;
== Using the form ==&lt;br /&gt;
&lt;br /&gt;
When instantiating the form, there are two little things that you need to pay attention to. &lt;br /&gt;
&lt;br /&gt;
Firstly you should always pass the URL of the current page, including its query parameters. We need this to be able to display the form with its validation errors without affecting anything else.&lt;br /&gt;
&lt;br /&gt;
Secondly, the persistent instance must be provided to the form through the custom data. That persistent instance will be used to populate the form with initial data, typically when you are editing an object. When you don&#039;t have a persistent instance yet, probably because your user will be creating a new one, then simply pass null.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,  // An instance, or null.&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like any other form, we will be using &#039;&#039;get_data()&#039;&#039; to validate the form. The only difference is that to determine whether we are editing an object, or creating a new one, we will check if the &#039;&#039;id&#039;&#039; value was returned to us. The persistent form will return the ID value from the persistent we gave it. Then it&#039;s up to you to decide how to apply the data, most likely you will defer the logic to another part of your code, one that ensures that all capability checks are fulfilled. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(0, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Additional validation ==&lt;br /&gt;
&lt;br /&gt;
There are times when the built-in validation of the persistent is not enough. Usually you would use the method &#039;&#039;validation()&#039;&#039;, but as the form persistent class does some extra stuff to make it easier for you, you must use the &#039;&#039;extra_validation()&#039;&#039; method. The latter works almost just like the &#039;&#039;validation()&#039;&#039; one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Extra validation.&lt;br /&gt;
 *&lt;br /&gt;
 * @param  stdClass $data Data to validate.&lt;br /&gt;
 * @param  array $files Array of files.&lt;br /&gt;
 * @param  array $errors Currently reported errors.&lt;br /&gt;
 * @return array of additional errors, or overridden errors.&lt;br /&gt;
 */&lt;br /&gt;
protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
    $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
    if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
        $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return $newerrors;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The typical additional validation will return an array of errors, those will override any previously defined errors. Sometimes, though rarely, you will need to remove previously reported errors, hence the reference to &#039;&#039;$errors&#039;&#039; given, which you can modify directly. Do not abuse it though, this should only be used when you have no other choice.&lt;br /&gt;
&lt;br /&gt;
== Foreign fields ==&lt;br /&gt;
&lt;br /&gt;
By default, the form class tries to be smart at detecting foreign fields such as the submit button. Failure to do so will cause troubles during validation, or when getting the data. So when your form becomes more complex, if it includes more submit buttons, or when it deals with other fields, for example file managers, we must indicate it.&lt;br /&gt;
&lt;br /&gt;
=== Fields to ignore completely ===&lt;br /&gt;
&lt;br /&gt;
The fields to remove are never validated and they are not returned when calling &#039;&#039;get_data()&#039;&#039;. By default the submit button is added to this list so that when we call &#039;&#039;get_data()&#039;&#039; we only get the persistent-related fields. To remove more fields, re-declare the &#039;&#039;protected static $fieldstoremove&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove when getting the final data. */&lt;br /&gt;
protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Do not forget to add the &#039;&#039;submitbutton&#039;&#039; back in there.&lt;br /&gt;
&lt;br /&gt;
=== Fields to validate ===&lt;br /&gt;
&lt;br /&gt;
What about when we have a &#039;&#039;legit&#039;&#039; field but it does not belong to the persistent? We still want to validate it ourselves, but we don&#039;t want it to be validated by the persistent as it will cause an error. In that case we define it in the &#039;&#039;protected static $foreignfields&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the persistent will not validate this field, and we will get the &#039;&#039;updatedelay&#039;&#039; value when we call &#039;&#039;get_data()&#039;&#039;. Just don&#039;t forget to remove it before you feed the data to your persistent.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
    $updatedelay = $data-&amp;gt;updatedelay;&lt;br /&gt;
    unset($data-&amp;gt;updatedelay);&lt;br /&gt;
    $newpersistent = new status(0, $data);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is particularily useful when dealing with file managers.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
    &lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove when getting the final data. */&lt;br /&gt;
    protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
    protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
        // Status update delay.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;duration&#039;, &#039;updatedelay&#039;, &#039;Status update delay&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Are you happy?&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;selectyesno&#039;, &#039;areyouhappy&#039;, &#039;Are you happy?&#039;);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Extra validation.&lt;br /&gt;
     *&lt;br /&gt;
     * @param  stdClass $data Data to validate.&lt;br /&gt;
     * @param  array $files Array of files.&lt;br /&gt;
     * @param  array $errors Currently reported errors.&lt;br /&gt;
     * @return array of additional errors, or overridden errors.&lt;br /&gt;
     */&lt;br /&gt;
    protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
        $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
        if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
            $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $newerrors;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Using the form ===&lt;br /&gt;
&lt;br /&gt;
Consider the following code to be a page you users will access at &#039;/example.php&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
require &#039;config.php&#039;;&lt;br /&gt;
&lt;br /&gt;
// Check if we go an ID.&lt;br /&gt;
$id = optional_param(&#039;id&#039;, null, PARAM_INT);&lt;br /&gt;
&lt;br /&gt;
// Set the PAGE URL (and mandatory context). Note the ID being recorded, this is important.&lt;br /&gt;
$PAGE-&amp;gt;set_context(context_system::instance());&lt;br /&gt;
$PAGE-&amp;gt;set_url(new moodle_url(&#039;/example.php&#039;, [&#039;id&#039; =&amp;gt; $id]));&lt;br /&gt;
&lt;br /&gt;
// Instantiate a persistent object if we received an ID. Typically receiving an ID&lt;br /&gt;
// means that we are going to be updating an object rather than creating a new one.&lt;br /&gt;
$persistent = null;&lt;br /&gt;
if (!empty($id)) {&lt;br /&gt;
    $persistent = new status($id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Create the form instance. We need to use the current URL and the custom data.&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(0, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Display the mandatory header and footer.&lt;br /&gt;
// And display the form, and its validation errors if there are any.&lt;br /&gt;
echo $OUTPUT-&amp;gt;header();&lt;br /&gt;
$form-&amp;gt;display();&lt;br /&gt;
echo $OUTPUT-&amp;gt;footer();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Persistent_form&amp;diff=51632</id>
		<title>Persistent form</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Persistent_form&amp;diff=51632"/>
		<updated>2016-12-16T08:01:25Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Using the form */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}If you are creating [[Persistent|persistent]] entries from forms, we&#039;ve got something for you. You can use the class &#039;&#039;core\form\persistent&#039;&#039; as a base for your form instead of &#039;&#039;moodleform&#039;&#039;. Our persistent form class comes handy tools, such as automatic validation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: for more information about forms themselves, [[:Category:Formslib|head this way]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Linking to a persistent ==&lt;br /&gt;
&lt;br /&gt;
In order for the form class to know what persistent we&#039;ll be dealing with, we must declare the &#039;&#039;protected static $persistentclass&#039;&#039; variable. The latter contains the fully qualified name of the persistent class.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var string Persistent class name. */&lt;br /&gt;
protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Defining the form fields ==&lt;br /&gt;
&lt;br /&gt;
Unfortunately this is not automatically done for us, so let&#039;s add our fields to the &#039;&#039;definition()&#039;&#039; method like you would do for any form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Define the form.&lt;br /&gt;
 */&lt;br /&gt;
public function definition() {&lt;br /&gt;
    $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
    // User ID.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
    $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
    // Message.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
    // Location.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;add_action_buttons();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of this is pretty standard, except for the &#039;&#039;userid&#039;&#039;. When creating a new &#039;status&#039;, we do not want our users to be in control of this value. Therefore we define it as a hidden value which we lock (using &#039;&#039;setConstant&#039;&#039;) to the value we created our form with. All the mandatory fields (without a default value) of the persistent need to be added to the form. If your users cannot change their values, then they must be hidden and locked with &#039;&#039;setConstant&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Also note that the &#039;&#039;id&#039;&#039; property is not included. It is not required, nor recommended, to add it to your fields as it will be handled automatically.&lt;br /&gt;
&lt;br /&gt;
== Using the form ==&lt;br /&gt;
&lt;br /&gt;
When instantiating the form, there are two little things that you need to pay attention to. &lt;br /&gt;
&lt;br /&gt;
Firstly you should always pass the URL of the current page, including its query parameters. We need this to be able to display the form with its validation errors without affecting anything else.&lt;br /&gt;
&lt;br /&gt;
Secondly, the persistent instance must be provided to the form through the custom data. That persistent instance will be used to populate the form with initial data, typically when you are editing an object. When you don&#039;t have a persistent instance yet, probably because your user will be creating a new one, then simply pass null.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,  // An instance, or null.&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like any other form, we will be using &#039;&#039;get_data()&#039;&#039; to validate the form. The only difference is that to determine whether we are editing an object, or creating a new one, we will check if the &#039;&#039;id&#039;&#039; value was returned to us. The persistent form will return the ID value from the persistent we gave it. Then it&#039;s up to you to decide how to apply the data, most likely you will defer the logic to another part of your code, one that ensures that all capability checks are fulfilled. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(null, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Additional validation ==&lt;br /&gt;
&lt;br /&gt;
There are times when the built-in validation of the persistent is not enough. Usually you would use the method &#039;&#039;validation()&#039;&#039;, but as the form persistent class does some extra stuff to make it easier for you, you must use the &#039;&#039;extra_validation()&#039;&#039; method. The latter works almost just like the &#039;&#039;validation()&#039;&#039; one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Extra validation.&lt;br /&gt;
 *&lt;br /&gt;
 * @param  stdClass $data Data to validate.&lt;br /&gt;
 * @param  array $files Array of files.&lt;br /&gt;
 * @param  array $errors Currently reported errors.&lt;br /&gt;
 * @return array of additional errors, or overridden errors.&lt;br /&gt;
 */&lt;br /&gt;
protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
    $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
    if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
        $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return $newerrors;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The typical additional validation will return an array of errors, those will override any previously defined errors. Sometimes, though rarely, you will need to remove previously reported errors, hence the reference to &#039;&#039;$errors&#039;&#039; given, which you can modify directly. Do not abuse it though, this should only be used when you have no other choice.&lt;br /&gt;
&lt;br /&gt;
== Foreign fields ==&lt;br /&gt;
&lt;br /&gt;
By default, the form class tries to be smart at detecting foreign fields such as the submit button. Failure to do so will cause troubles during validation, or when getting the data. So when your form becomes more complex, if it includes more submit buttons, or when it deals with other fields, for example file managers, we must indicate it.&lt;br /&gt;
&lt;br /&gt;
=== Fields to ignore completely ===&lt;br /&gt;
&lt;br /&gt;
The fields to remove are never validated and they are not returned when calling &#039;&#039;get_data()&#039;&#039;. By default the submit button is added to this list so that when we call &#039;&#039;get_data()&#039;&#039; we only get the persistent-related fields. To remove more fields, re-declare the &#039;&#039;protected static $fieldstoremove&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove when getting the final data. */&lt;br /&gt;
protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Do not forget to add the &#039;&#039;submitbutton&#039;&#039; back in there.&lt;br /&gt;
&lt;br /&gt;
=== Fields to validate ===&lt;br /&gt;
&lt;br /&gt;
What about when we have a &#039;&#039;legit&#039;&#039; field but it does not belong to the persistent? We still want to validate it ourselves, but we don&#039;t want it to be validated by the persistent as it will cause an error. In that case we define it in the &#039;&#039;protected static $foreignfields&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the persistent will not validate this field, and we will get the &#039;&#039;updatedelay&#039;&#039; value when we call &#039;&#039;get_data()&#039;&#039;. Just don&#039;t forget to remove it before you feed the data to your persistent.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
    $updatedelay = $data-&amp;gt;updatedelay;&lt;br /&gt;
    unset($data-&amp;gt;updatedelay);&lt;br /&gt;
    $newpersistent = new status(0, $data);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is particularily useful when dealing with file managers.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
    &lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove when getting the final data. */&lt;br /&gt;
    protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
    protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
        // Status update delay.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;duration&#039;, &#039;updatedelay&#039;, &#039;Status update delay&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Are you happy?&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;selectyesno&#039;, &#039;areyouhappy&#039;, &#039;Are you happy?&#039;);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Extra validation.&lt;br /&gt;
     *&lt;br /&gt;
     * @param  stdClass $data Data to validate.&lt;br /&gt;
     * @param  array $files Array of files.&lt;br /&gt;
     * @param  array $errors Currently reported errors.&lt;br /&gt;
     * @return array of additional errors, or overridden errors.&lt;br /&gt;
     */&lt;br /&gt;
    protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
        $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
        if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
            $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $newerrors;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Using the form ===&lt;br /&gt;
&lt;br /&gt;
Consider the following code to be a page you users will access at &#039;/example.php&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
require &#039;config.php&#039;;&lt;br /&gt;
&lt;br /&gt;
// Check if we go an ID.&lt;br /&gt;
$id = optional_param(&#039;id&#039;, null, PARAM_INT);&lt;br /&gt;
&lt;br /&gt;
// Set the PAGE URL (and mandatory context). Note the ID being recorded, this is important.&lt;br /&gt;
$PAGE-&amp;gt;set_context(context_system::instance());&lt;br /&gt;
$PAGE-&amp;gt;set_url(new moodle_url(&#039;/example.php&#039;, [&#039;id&#039; =&amp;gt; $id]));&lt;br /&gt;
&lt;br /&gt;
// Instantiate a persistent object if we received an ID. Typically receiving an ID&lt;br /&gt;
// means that we are going to be updating an object rather than creating a new one.&lt;br /&gt;
$persistent = null;&lt;br /&gt;
if (!empty($id)) {&lt;br /&gt;
    $persistent = new status($id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Create the form instance. We need to use the current URL and the custom data.&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(0, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Display the mandatory header and footer.&lt;br /&gt;
// And display the form, and its validation errors if there are any.&lt;br /&gt;
echo $OUTPUT-&amp;gt;header();&lt;br /&gt;
$form-&amp;gt;display();&lt;br /&gt;
echo $OUTPUT-&amp;gt;footer();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Persistent&amp;diff=51631</id>
		<title>Persistent</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Persistent&amp;diff=51631"/>
		<updated>2016-12-16T08:00:29Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Hooks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle persistents are equivalent to models (or active records). They represent an object stored in the database and provide the methods to create, read, update, and delete those objects. Additionally, the persistents validate their own data against automatic and custom validation rules. Persistents are made by extending the abstract class &#039;&#039;core\persistent&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Properties ==&lt;br /&gt;
&lt;br /&gt;
Each class must define the properties that it contains. This can be done by defining the method &#039;&#039;protected static define_properties()&#039;&#039;. It returns an array where the keys are the names of the properties (and implies the database column names), and their attributes. The &#039;&#039;type&#039;&#039; of each property must be specified using one of the &#039;&#039;PARAM_*&#039;&#039; constants. This type will be used to automatically validate the property&#039;s value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the definition of the properties of this model.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
        )&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Properties attributes ===&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must be one of the many &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
;default&lt;br /&gt;
: The default value to attach to the property when it hasn&#039;t been provided. Without a &#039;&#039;default&#039;&#039;, the property&#039;s value is required. Alternatively this can be a &#039;&#039;Closure&#039;&#039; returning the default value.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants &#039;&#039;NULL_ALLOWED&#039;&#039; or &#039;&#039;NULL_NOT_ALLOWED&#039;&#039; determining if the null value is accepted. This defaults to &#039;&#039;NULL_NOT_ALLOWED&#039;&#039;.&lt;br /&gt;
;message&lt;br /&gt;
: The default error message (as a &#039;&#039;lang_string&#039;&#039; instance) to return when the validation of this field fails.&lt;br /&gt;
;choices&lt;br /&gt;
: An array of values which the property must fall in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
    &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
    &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
),&lt;br /&gt;
&#039;location&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
    &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
    &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
    &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
        return get_config(&#039;core&#039;, &#039;default_location&#039;);&lt;br /&gt;
    },&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that you should always use a &#039;&#039;Closure&#039;&#039; for the &#039;&#039;default&#039;&#039; value when you cannot guarantee that it will not change since the start of the request. The list of properties and their attributes is cached, and so failure to use a &#039;&#039;Closure&#039;&#039; can result in using an outdated default value.&lt;br /&gt;
&lt;br /&gt;
=== Mandatory properties ===&lt;br /&gt;
&lt;br /&gt;
Four fields are always added to your persistent and should be reflected in your database table. You must not define those properties in &#039;&#039;define_properties()&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
;id (non-null integer)&lt;br /&gt;
: The primary key of the record.&lt;br /&gt;
;usermodified (non-null integer)&lt;br /&gt;
: The user who created/modified the object. It is automatically set.&lt;br /&gt;
;timecreated (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set.&lt;br /&gt;
;timemodified (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set, and defaults to 0.&lt;br /&gt;
&lt;br /&gt;
== Linking to a database table ==&lt;br /&gt;
&lt;br /&gt;
While the persistent class is helpful for database interactions, it does not automatically fetch the properties from the database, nor does it create the tables. You will need to create the table yourself, as well as pointing the persistent to it. This can be done by defining the class constant &#039;&#039;TABLE&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** Table name for the persistent. */&lt;br /&gt;
const TABLE = &#039;status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The table name must not contain the Moodle prefix. Also it is common practice to always refer to your table name using the constant.&lt;br /&gt;
&lt;br /&gt;
== Reading property values ==&lt;br /&gt;
&lt;br /&gt;
This can be done using the method &#039;&#039;get()&#039;&#039;, or the magic method &#039;&#039;get_&#039;&#039; followed by the property name. Alternatively you can also use &#039;&#039;to_record()&#039;&#039; which exports the whole object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Create a new object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using persistent::get().&lt;br /&gt;
$userid = $persistent-&amp;gt;get(&#039;userid&#039;);&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using the magic getter.&lt;br /&gt;
$userid = $persistent-&amp;gt;get_userid();&lt;br /&gt;
&lt;br /&gt;
// Get all the properties in an stdClass.&lt;br /&gt;
$data = $persistent-&amp;gt;to_record();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may override the magic getters to implement your own custom logic. For instance you could convert the data in another format automatically as a convenience for developers. However, use this sparingly as it may lead to confusion: what you get is not what is stored in the database. Note that you cannot guarantee that your getter will be used, remember that it is possible for developers to call the alternative &#039;&#039;get()&#039;&#039; which will not use your custom logic.&lt;br /&gt;
&lt;br /&gt;
It is, however, encouraged to add convenience methods such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns the user object of the author.&lt;br /&gt;
 *&lt;br /&gt;
 * @return stdClass&lt;br /&gt;
 */&lt;br /&gt;
public function get_author() {&lt;br /&gt;
    return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Assigning values to properties ==&lt;br /&gt;
&lt;br /&gt;
There are a few methods to do so.&lt;br /&gt;
&lt;br /&gt;
You use an object (&#039;&#039;stdClass&#039;&#039;) to assign a bunch of properties at once. Use it with the constructor, or the method &#039;&#039;from_record()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) array(&#039;userid&#039; =&amp;gt; 2, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instantiates a new object with value for some properties.&lt;br /&gt;
$persistent = new status(0, $data);&lt;br /&gt;
&lt;br /&gt;
// Is similar to.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
$persistent-&amp;gt;from_record($data);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or you can use the &#039;&#039;set()&#039;&#039; method on an instance.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set(&#039;message&#039;, &#039;Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally you can use the magic setters &#039;&#039;set_&#039;&#039; followed by the property name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Defining a custom setter ===&lt;br /&gt;
&lt;br /&gt;
Though you don&#039;t have to for the code to work, you can define your own &#039;&#039;setter&#039;&#039; methods which will override the magic setters. They are useful if you want to extract the data out of a more complex object prior to assigning it. Though note that those setters will then have to use the &#039;&#039;set()&#039;&#039; method to assign the values.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Convenience method to set the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
 */&lt;br /&gt;
public function set_userid($idorobject) {&lt;br /&gt;
    $userid = $idorobject;&lt;br /&gt;
    if (is_object($idorobject)) {&lt;br /&gt;
        $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
    }&lt;br /&gt;
    $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example we will accept an object or an ID, as a convenience for developers we will extract the ID value out of the object passed if any.&lt;br /&gt;
&lt;br /&gt;
Just like custom getters, you cannot guarantee that they will be used. Developers can directly call the &#039;&#039;set()&#039;&#039; method. Therefore you must not use a custom setter to reliably transform the data added to a property. For instance do not add a custom setter to remove HTML tags out of a text field, it may not always happen.&lt;br /&gt;
&lt;br /&gt;
You can obviously create your own setters which aren&#039;t based on any properties just as a convenience. For instance we could have created &#039;&#039;set_userid_from_user(object $user)&#039;&#039; which is more verbose and more predictable&lt;br /&gt;
&lt;br /&gt;
== Read, save and delete entries ==&lt;br /&gt;
&lt;br /&gt;
The methods to &#039;&#039;create&#039;&#039;, &#039;&#039;read&#039;&#039;, &#039;&#039;update&#039;&#039; and &#039;&#039;delete&#039;&#039; are eponymous. Your object will be validated before you &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; it. The &#039;&#039;update&#039;&#039;, &#039;&#039;delete&#039;&#039; and &#039;&#039;read&#039;&#039; methods require your object to contain its ID. And you also won&#039;t be allowed to &#039;&#039;create&#039;&#039; an entry which already had an ID defined.&lt;br /&gt;
&lt;br /&gt;
Here are some code examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Fetches an object from database based on its ID.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Create object in the database.&lt;br /&gt;
$data = new stdClass();&lt;br /&gt;
$data-&amp;gt;message = &#039;Hello new world&#039;;&lt;br /&gt;
$persistent = new persistent(0, $data);&lt;br /&gt;
$persistent-&amp;gt;create();&lt;br /&gt;
// $persistent-&amp;gt;get(&#039;id&#039;) will now return an id.&lt;br /&gt;
&lt;br /&gt;
// Load an object from the database, and update it.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
$persistent-&amp;gt;update();&lt;br /&gt;
&lt;br /&gt;
// Reset the instance to the values in the database.&lt;br /&gt;
$persistent-&amp;gt;read();&lt;br /&gt;
&lt;br /&gt;
// Permanently delete the object from the database.&lt;br /&gt;
$persistent-&amp;gt;delete();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fetching records ==&lt;br /&gt;
&lt;br /&gt;
Once you start using persistents you should never directly interact with the database outside of your class. The persistent class comes with a few handy methods allowing you to retrieve your objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Use the constructor to fetch one object from its ID.&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Get one record from a set of conditions.&lt;br /&gt;
$persistent = status::get_record([&#039;userid&#039; =&amp;gt; $userid, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;]);&lt;br /&gt;
&lt;br /&gt;
// Get multiple records from a set of conditions.&lt;br /&gt;
$persistents = status::get_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Count the records.&lt;br /&gt;
$count = status::count_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Check whether a record exists.&lt;br /&gt;
$exists = status::record_exists($id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure to also check their additional parameters and their variants (&#039;&#039;record_exists_select()&#039;&#039;, &#039;&#039;count_records_select&#039;&#039;, &#039;&#039;get_records_select&#039;&#039;, ...).&lt;br /&gt;
&lt;br /&gt;
=== Custom fetching ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s always a good idea to add more complex queries directly within your persistent. By convention you should always return an instance of your persistent and never an stdClass. Here we add a custom method which allows to directly fetch all records by username.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get all records from a user&#039;s username.&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $username The username.&lt;br /&gt;
 * @return status[]&lt;br /&gt;
 */&lt;br /&gt;
public static function get_records_by_username($username) {&lt;br /&gt;
    global $DB;&lt;br /&gt;
&lt;br /&gt;
    $sql = &#039;SELECT s.*&lt;br /&gt;
              FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
              JOIN {user} u&lt;br /&gt;
                ON u.id = s.userid&lt;br /&gt;
             WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
    $persistents = [];&lt;br /&gt;
&lt;br /&gt;
    $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
    foreach ($recordset as $record) {&lt;br /&gt;
        $persistents[] = new static(0, $record);&lt;br /&gt;
    }&lt;br /&gt;
    $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
    return $persistents;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to join persistents together and be able to extract their respective properties in a single database query, you should have a look at the following methods:&lt;br /&gt;
&lt;br /&gt;
;get_sql_fields(string $alias, string $prefix = null)&lt;br /&gt;
: Returns the SQL statement to include in the SELECT clause to prefix columns.&lt;br /&gt;
;extract_record(stdClass $row, string $prefix = null)&lt;br /&gt;
: Extracts all the properties from a row based on the given prefix.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Minimalist example.&lt;br /&gt;
$sqlfields = status::get_sql_fields(&#039;s&#039;, &#039;statprops&#039;);&lt;br /&gt;
$sql = &amp;quot;SELECT $sqlfields, u.username&lt;br /&gt;
          FROM {&amp;quot; . status::TABLE . &amp;quot;} s&lt;br /&gt;
          JOIN {user} ON s.userid = u.id&lt;br /&gt;
         WHERE s.id = 1&amp;quot;;&lt;br /&gt;
$row = $DB-&amp;gt;get_record($sql, []);&lt;br /&gt;
$statusdata = status::extract_record($row, &#039;statprops&#039;);&lt;br /&gt;
$persistent = new status(0, $statusdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Validating ==&lt;br /&gt;
&lt;br /&gt;
Basic validation of the properties values happens automatically based on their type (&#039;&#039;PARAM_*&#039;&#039; constant), however this is not always enough. In order to implement your own custom validation, simply define a &#039;&#039;protected&#039;&#039; method starting with &#039;&#039;validate_&#039;&#039; followed with the property name. This method will be called whenever the model needs to be validated and will receive the data to validate.&lt;br /&gt;
&lt;br /&gt;
A validation method must always return either &#039;&#039;true&#039;&#039; or an instance of &#039;&#039;lang_string&#039;&#039; which contains the error message to send to the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param int $value The value.&lt;br /&gt;
 * @return true|lang_string&lt;br /&gt;
 */&lt;br /&gt;
protected function validate_userid($value) {&lt;br /&gt;
    if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
        return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above example ensures that the &#039;&#039;userid&#039;&#039; property contains a valid user ID.&lt;br /&gt;
&lt;br /&gt;
Note that the basic validation is always performed first, and thus your custom validation method will not be called when the value did not pass the basic validation.&lt;br /&gt;
&lt;br /&gt;
=== Validation results ===&lt;br /&gt;
&lt;br /&gt;
The validation of the object automatically happens upon &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039;. If the validation did not pass, an &#039;&#039;invalid_persisten_exception&#039;&#039; will be raised. You can validate the object prior to saving the object and get the validation results if you need to.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// We can catch the invalid_persistent_exception.&lt;br /&gt;
try {&lt;br /&gt;
    $persistent = new status();&lt;br /&gt;
    $persistent-&amp;gt;create();&lt;br /&gt;
} catch (invalid_persistent_exception $e) {&lt;br /&gt;
    // Whoops, something wrong happened.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Check whether the object is valid.&lt;br /&gt;
$persistent-&amp;gt;is_valid();        // True or false.&lt;br /&gt;
&lt;br /&gt;
// Get the validation errors.&lt;br /&gt;
$persistent-&amp;gt;get_errors();      // Array where keys are properties and values are errors.&lt;br /&gt;
&lt;br /&gt;
// Validate the object.&lt;br /&gt;
$persistent-&amp;gt;validate();        // Returns true, or an array of errors.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hooks ==&lt;br /&gt;
&lt;br /&gt;
You can define the following methods to be notified prior to, or after, something happened:&lt;br /&gt;
&lt;br /&gt;
;protected before_validate()&lt;br /&gt;
: Do something before the object is validated.&lt;br /&gt;
;protected before_create()&lt;br /&gt;
: Do something before the object is inserted in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_create()&lt;br /&gt;
: Do something right after the object was added to the database.&lt;br /&gt;
;protected before_update()&lt;br /&gt;
: Do something before the object is updated in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_update(bool $result)&lt;br /&gt;
: Do something right after the object was updated in the database.&lt;br /&gt;
;protected before_delete()&lt;br /&gt;
: Do something right before the object is deleted from the database.&lt;br /&gt;
;protected after_delete(bool $result)&lt;br /&gt;
: Do something right after the object was deleted from the database.&lt;br /&gt;
&lt;br /&gt;
== Persistent and forms ==&lt;br /&gt;
&lt;br /&gt;
If your persistents are exposed to your users via forms, head to the [[Persistent form|persistent form page]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# You must create the database table yourself, using the [[XMLDB editor]] and an upgrade script.&lt;br /&gt;
# You must include the [[#Mandatory_properties|mandatory fields]] in your table schema.&lt;br /&gt;
# You must never extend another persistent as this leads to unpredictable errors whenever the parent changes (new properties missing in your table, changes in validation, custom getters and setters, etc...).&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
class status extends core\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example;&lt;br /&gt;
&lt;br /&gt;
use core\persistent;&lt;br /&gt;
use core_user;&lt;br /&gt;
use lang_string;&lt;br /&gt;
&lt;br /&gt;
class status extends persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
                &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
                &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;location&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
                &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
                &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
                &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
                    return get_config(&#039;core&#039;, &#039;default_location&#039;);&lt;br /&gt;
                },&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the user object of the author.&lt;br /&gt;
     *&lt;br /&gt;
     * @return stdClass&lt;br /&gt;
     */&lt;br /&gt;
    public function get_author() {&lt;br /&gt;
        return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Convenience method to set the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
     */&lt;br /&gt;
    public function set_userid($idorobject) {&lt;br /&gt;
        $userid = $idorobject;&lt;br /&gt;
        if (is_object($idorobject)) {&lt;br /&gt;
            $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Validate the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param int $value The value.&lt;br /&gt;
     * @return true|lang_string&lt;br /&gt;
     */&lt;br /&gt;
    protected function validate_userid($value) {&lt;br /&gt;
        if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
            return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get all records from a user&#039;s username.&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $username The username.&lt;br /&gt;
     * @return status[]&lt;br /&gt;
     */&lt;br /&gt;
    public static function get_records_by_username($username) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $sql = &#039;SELECT s.*&lt;br /&gt;
                  FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
                  JOIN {user} u&lt;br /&gt;
                    ON u.id = s.userid&lt;br /&gt;
                 WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
        $persistents = [];&lt;br /&gt;
&lt;br /&gt;
        $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
        foreach ($recordset as $record) {&lt;br /&gt;
            $persistents[] = new static(0, $record);&lt;br /&gt;
        }&lt;br /&gt;
        $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
        return $persistents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Persistent_form&amp;diff=51630</id>
		<title>Persistent form</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Persistent_form&amp;diff=51630"/>
		<updated>2016-12-16T07:59:03Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: Created page with &amp;quot;{{Moodle 3.3}}If you are creating persistent entries from forms, we&amp;#039;ve got something for you. You can use the class &amp;#039;&amp;#039;core\form\persistent&amp;#039;&amp;#039; as a base for your...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}If you are creating [[Persistent|persistent]] entries from forms, we&#039;ve got something for you. You can use the class &#039;&#039;core\form\persistent&#039;&#039; as a base for your form instead of &#039;&#039;moodleform&#039;&#039;. Our persistent form class comes handy tools, such as automatic validation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: for more information about forms themselves, [[:Category:Formslib|head this way]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Linking to a persistent ==&lt;br /&gt;
&lt;br /&gt;
In order for the form class to know what persistent we&#039;ll be dealing with, we must declare the &#039;&#039;protected static $persistentclass&#039;&#039; variable. The latter contains the fully qualified name of the persistent class.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var string Persistent class name. */&lt;br /&gt;
protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Defining the form fields ==&lt;br /&gt;
&lt;br /&gt;
Unfortunately this is not automatically done for us, so let&#039;s add our fields to the &#039;&#039;definition()&#039;&#039; method like you would do for any form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Define the form.&lt;br /&gt;
 */&lt;br /&gt;
public function definition() {&lt;br /&gt;
    $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
    // User ID.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
    $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
    // Message.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
    // Location.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;add_action_buttons();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of this is pretty standard, except for the &#039;&#039;userid&#039;&#039;. When creating a new &#039;status&#039;, we do not want our users to be in control of this value. Therefore we define it as a hidden value which we lock (using &#039;&#039;setConstant&#039;&#039;) to the value we created our form with. All the mandatory fields (without a default value) of the persistent need to be added to the form. If your users cannot change their values, then they must be hidden and locked with &#039;&#039;setConstant&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Also note that the &#039;&#039;id&#039;&#039; property is not included. It is not required, nor recommended, to add it to your fields as it will be handled automatically.&lt;br /&gt;
&lt;br /&gt;
== Using the form ==&lt;br /&gt;
&lt;br /&gt;
When instantiating the form, there are two little things that you need to pay attention to. &lt;br /&gt;
&lt;br /&gt;
Firstly you should always pass the URL of the current page, including its query parameters. We need this to be able to display the form with its validation errors without affecting anything else.&lt;br /&gt;
&lt;br /&gt;
Secondly, the persistent instance must be provided to the form through the custom data. That persistent instance will be used to populate the form with initial data, typically when you are editing an object. When you don&#039;t have a persistent instance yet, probably because your user will be creating a new one, then simply pass null.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,  // An instance, or null.&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like any other form, we will be using &#039;&#039;get_data()&#039;&#039; to validate the form. The only difference is that to determine whether we are editing an object, or creating a new one, we will check if the &#039;&#039;id&#039;&#039; value was returned to us. The persistent form will return the ID value from the persistent we gave it. Then it&#039;s up to you to decide how to apply the data, most likely you will defer the logic to another part of your code, one that ensures that all capability checks are fulfilled. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(null, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Additional validation ==&lt;br /&gt;
&lt;br /&gt;
There are times when the built-in validation of the persistent is not enough. Usually you would use the method &#039;&#039;validation()&#039;&#039;, but as the form persistent class does some extra stuff to make it easier for you, you must use the &#039;&#039;extra_validation()&#039;&#039; method. The latter works almost just like the &#039;&#039;validation()&#039;&#039; one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Extra validation.&lt;br /&gt;
 *&lt;br /&gt;
 * @param  stdClass $data Data to validate.&lt;br /&gt;
 * @param  array $files Array of files.&lt;br /&gt;
 * @param  array $errors Currently reported errors.&lt;br /&gt;
 * @return array of additional errors, or overridden errors.&lt;br /&gt;
 */&lt;br /&gt;
protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
    $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
    if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
        $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return $newerrors;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The typical additional validation will return an array of errors, those will override any previously defined errors. Sometimes, though rarely, you will need to remove previously reported errors, hence the reference to &#039;&#039;$errors&#039;&#039; given, which you can modify directly. Do not abuse it though, this should only be used when you have no other choice.&lt;br /&gt;
&lt;br /&gt;
== Foreign fields ==&lt;br /&gt;
&lt;br /&gt;
By default, the form class tries to be smart at detecting foreign fields such as the submit button. Failure to do so will cause troubles during validation, or when getting the data. So when your form becomes more complex, if it includes more submit buttons, or when it deals with other fields, for example file managers, we must indicate it.&lt;br /&gt;
&lt;br /&gt;
=== Fields to ignore completely ===&lt;br /&gt;
&lt;br /&gt;
The fields to remove are never validated and they are not returned when calling &#039;&#039;get_data()&#039;&#039;. By default the submit button is added to this list so that when we call &#039;&#039;get_data()&#039;&#039; we only get the persistent-related fields. To remove more fields, re-declare the &#039;&#039;protected static $fieldstoremove&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove when getting the final data. */&lt;br /&gt;
protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Do not forget to add the &#039;&#039;submitbutton&#039;&#039; back in there.&lt;br /&gt;
&lt;br /&gt;
=== Fields to validate ===&lt;br /&gt;
&lt;br /&gt;
What about when we have a &#039;&#039;legit&#039;&#039; field but it does not belong to the persistent? We still want to validate it ourselves, but we don&#039;t want it to be validated by the persistent as it will cause an error. In that case we define it in the &#039;&#039;protected static $foreignfields&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the persistent will not validate this field, and we will get the &#039;&#039;updatedelay&#039;&#039; value when we call &#039;&#039;get_data()&#039;&#039;. Just don&#039;t forget to remove it before you feed the data to your persistent.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
    $updatedelay = $data-&amp;gt;updatedelay;&lt;br /&gt;
    unset($data-&amp;gt;updatedelay);&lt;br /&gt;
    $newpersistent = new status(0, $data);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is particularily useful when dealing with file managers.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
    &lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove when getting the final data. */&lt;br /&gt;
    protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
    protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
        // Status update delay.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;duration&#039;, &#039;updatedelay&#039;, &#039;Status update delay&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Are you happy?&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;selectyesno&#039;, &#039;areyouhappy&#039;, &#039;Are you happy?&#039;);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Extra validation.&lt;br /&gt;
     *&lt;br /&gt;
     * @param  stdClass $data Data to validate.&lt;br /&gt;
     * @param  array $files Array of files.&lt;br /&gt;
     * @param  array $errors Currently reported errors.&lt;br /&gt;
     * @return array of additional errors, or overridden errors.&lt;br /&gt;
     */&lt;br /&gt;
    protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
        $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
        if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
            $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $newerrors;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Using the form ===&lt;br /&gt;
&lt;br /&gt;
Consider the following code to be a page you users will access at &#039;/example.php&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
require &#039;config.php&#039;;&lt;br /&gt;
&lt;br /&gt;
// Check if we go an ID.&lt;br /&gt;
$id = optional_param(&#039;id&#039;, null, PARAM_INT);&lt;br /&gt;
&lt;br /&gt;
// Set the PAGE URL (and mandatory context). Note the ID being recorded, this is important.&lt;br /&gt;
$PAGE-&amp;gt;set_context(context_system::instance());&lt;br /&gt;
$PAGE-&amp;gt;set_url(new moodle_url(&#039;/example.php&#039;, [&#039;id&#039; =&amp;gt; $id]));&lt;br /&gt;
&lt;br /&gt;
// Instantiate a persistent object if we received an ID. Typically receiving an ID&lt;br /&gt;
// means that we are going to be updating an object rather than creating a new one.&lt;br /&gt;
$persistent = null;&lt;br /&gt;
if (!empty($id)) {&lt;br /&gt;
    $persistent = new status($id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Create the form instance. We need to use the current URL and the custom data.&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(null, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Display the mandatory header and footer.&lt;br /&gt;
// And display the form, and its validation errors if there are any.&lt;br /&gt;
echo $OUTPUT-&amp;gt;header();&lt;br /&gt;
$form-&amp;gt;display();&lt;br /&gt;
echo $OUTPUT-&amp;gt;footer();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51629</id>
		<title>User:Frédéric Massart</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51629"/>
		<updated>2016-12-16T07:58:22Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}If you are creating [[Persistent|persistent]] entries from forms, we&#039;ve got something for you. You can use the class &#039;&#039;core\form\persistent&#039;&#039; as a base for your form instead of &#039;&#039;moodleform&#039;&#039;. Our persistent form class comes handy tools, such as automatic validation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: for more information about forms themselves, [[:Category:Formslib|head this way]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Linking to a persistent ==&lt;br /&gt;
&lt;br /&gt;
In order for the form class to know what persistent we&#039;ll be dealing with, we must declare the &#039;&#039;protected static $persistentclass&#039;&#039; variable. The latter contains the fully qualified name of the persistent class.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var string Persistent class name. */&lt;br /&gt;
protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Defining the form fields ==&lt;br /&gt;
&lt;br /&gt;
Unfortunately this is not automatically done for us, so let&#039;s add our fields to the &#039;&#039;definition()&#039;&#039; method like you would do for any form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Define the form.&lt;br /&gt;
 */&lt;br /&gt;
public function definition() {&lt;br /&gt;
    $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
    // User ID.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
    $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
    // Message.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
    // Location.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;add_action_buttons();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of this is pretty standard, except for the &#039;&#039;userid&#039;&#039;. When creating a new &#039;status&#039;, we do not want our users to be in control of this value. Therefore we define it as a hidden value which we lock (using &#039;&#039;setConstant&#039;&#039;) to the value we created our form with. All the mandatory fields (without a default value) of the persistent need to be added to the form. If your users cannot change their values, then they must be hidden and locked with &#039;&#039;setConstant&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Also note that the &#039;&#039;id&#039;&#039; property is not included. It is not required, nor recommended, to add it to your fields as it will be handled automatically.&lt;br /&gt;
&lt;br /&gt;
== Using the form ==&lt;br /&gt;
&lt;br /&gt;
When instantiating the form, there are two little things that you need to pay attention to. &lt;br /&gt;
&lt;br /&gt;
Firstly you should always pass the URL of the current page, including its query parameters. We need this to be able to display the form with its validation errors without affecting anything else.&lt;br /&gt;
&lt;br /&gt;
Secondly, the persistent instance must be provided to the form through the custom data. That persistent instance will be used to populate the form with initial data, typically when you are editing an object. When you don&#039;t have a persistent instance yet, probably because your user will be creating a new one, then simply pass null.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,  // An instance, or null.&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like any other form, we will be using &#039;&#039;get_data()&#039;&#039; to validate the form. The only difference is that to determine whether we are editing an object, or creating a new one, we will check if the &#039;&#039;id&#039;&#039; value was returned to us. The persistent form will return the ID value from the persistent we gave it. Then it&#039;s up to you to decide how to apply the data, most likely you will defer the logic to another part of your code, one that ensures that all capability checks are fulfilled. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(null, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Additional validation ==&lt;br /&gt;
&lt;br /&gt;
There are times when the built-in validation of the persistent is not enough. Usually you would use the method &#039;&#039;validation()&#039;&#039;, but as the form persistent class does some extra stuff to make it easier for you, you must use the &#039;&#039;extra_validation()&#039;&#039; method. The latter works almost just like the &#039;&#039;validation()&#039;&#039; one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Extra validation.&lt;br /&gt;
 *&lt;br /&gt;
 * @param  stdClass $data Data to validate.&lt;br /&gt;
 * @param  array $files Array of files.&lt;br /&gt;
 * @param  array $errors Currently reported errors.&lt;br /&gt;
 * @return array of additional errors, or overridden errors.&lt;br /&gt;
 */&lt;br /&gt;
protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
    $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
    if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
        $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return $newerrors;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The typical additional validation will return an array of errors, those will override any previously defined errors. Sometimes, though rarely, you will need to remove previously reported errors, hence the reference to &#039;&#039;$errors&#039;&#039; given, which you can modify directly. Do not abuse it though, this should only be used when you have no other choice.&lt;br /&gt;
&lt;br /&gt;
== Foreign fields ==&lt;br /&gt;
&lt;br /&gt;
By default, the form class tries to be smart at detecting foreign fields such as the submit button. Failure to do so will cause troubles during validation, or when getting the data. So when your form becomes more complex, if it includes more submit buttons, or when it deals with other fields, for example file managers, we must indicate it.&lt;br /&gt;
&lt;br /&gt;
=== Fields to ignore completely ===&lt;br /&gt;
&lt;br /&gt;
The fields to remove are never validated and they are not returned when calling &#039;&#039;get_data()&#039;&#039;. By default the submit button is added to this list so that when we call &#039;&#039;get_data()&#039;&#039; we only get the persistent-related fields. To remove more fields, re-declare the &#039;&#039;protected static $fieldstoremove&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove when getting the final data. */&lt;br /&gt;
protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Do not forget to add the &#039;&#039;submitbutton&#039;&#039; back in there.&lt;br /&gt;
&lt;br /&gt;
=== Fields to validate ===&lt;br /&gt;
&lt;br /&gt;
What about when we have a &#039;&#039;legit&#039;&#039; field but it does not belong to the persistent? We still want to validate it ourselves, but we don&#039;t want it to be validated by the persistent as it will cause an error. In that case we define it in the &#039;&#039;protected static $foreignfields&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the persistent will not validate this field, and we will get the &#039;&#039;updatedelay&#039;&#039; value when we call &#039;&#039;get_data()&#039;&#039;. Just don&#039;t forget to remove it before you feed the data to your persistent.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
    $updatedelay = $data-&amp;gt;updatedelay;&lt;br /&gt;
    unset($data-&amp;gt;updatedelay);&lt;br /&gt;
    $newpersistent = new status(0, $data);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is particularily useful when dealing with file managers.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
    &lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove when getting the final data. */&lt;br /&gt;
    protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
    protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
        // Status update delay.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;duration&#039;, &#039;updatedelay&#039;, &#039;Status update delay&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Are you happy?&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;selectyesno&#039;, &#039;areyouhappy&#039;, &#039;Are you happy?&#039;);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Extra validation.&lt;br /&gt;
     *&lt;br /&gt;
     * @param  stdClass $data Data to validate.&lt;br /&gt;
     * @param  array $files Array of files.&lt;br /&gt;
     * @param  array $errors Currently reported errors.&lt;br /&gt;
     * @return array of additional errors, or overridden errors.&lt;br /&gt;
     */&lt;br /&gt;
    protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
        $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
        if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
            $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $newerrors;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Using the form ===&lt;br /&gt;
&lt;br /&gt;
Consider the following code to be a page you users will access at &#039;/example.php&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
require &#039;config.php&#039;;&lt;br /&gt;
&lt;br /&gt;
// Check if we go an ID.&lt;br /&gt;
$id = optional_param(&#039;id&#039;, null, PARAM_INT);&lt;br /&gt;
&lt;br /&gt;
// Set the PAGE URL (and mandatory context). Note the ID being recorded, this is important.&lt;br /&gt;
$PAGE-&amp;gt;set_context(context_system::instance());&lt;br /&gt;
$PAGE-&amp;gt;set_url(new moodle_url(&#039;/example.php&#039;, [&#039;id&#039; =&amp;gt; $id]));&lt;br /&gt;
&lt;br /&gt;
// Instantiate a persistent object if we received an ID. Typically receiving an ID&lt;br /&gt;
// means that we are going to be updating an object rather than creating a new one.&lt;br /&gt;
$persistent = null;&lt;br /&gt;
if (!empty($id)) {&lt;br /&gt;
    $persistent = new status($id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Create the form instance. We need to use the current URL and the custom data.&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(null, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Display the mandatory header and footer.&lt;br /&gt;
// And display the form, and its validation errors if there are any.&lt;br /&gt;
echo $OUTPUT-&amp;gt;header();&lt;br /&gt;
$form-&amp;gt;display();&lt;br /&gt;
echo $OUTPUT-&amp;gt;footer();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51628</id>
		<title>User:Frédéric Massart</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51628"/>
		<updated>2016-12-16T07:47:27Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}If you are creating [[Persistent|persistent]] entries from forms, we&#039;ve got something for you. You can use the class &#039;&#039;core\form\persistent&#039;&#039; as a base for your form instead of &#039;&#039;moodleform&#039;&#039;. Our persistent form class comes handy tools, such as automatic validation.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: for more information about forms themselves, [[Category:Formslib|head this way]].&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Linking to a persistent ==&lt;br /&gt;
&lt;br /&gt;
In order for the form class to know what persistent we&#039;ll be dealing with, we must declare the &#039;&#039;protected static $persistentclass&#039;&#039; variable. The latter contains the fully qualified name of the persistent class.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var string Persistent class name. */&lt;br /&gt;
protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Defining the form fields ==&lt;br /&gt;
&lt;br /&gt;
Unfortunately this is not automatically done for us, so let&#039;s add our fields to the &#039;&#039;definition()&#039;&#039; method like you would do for any form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Define the form.&lt;br /&gt;
 */&lt;br /&gt;
public function definition() {&lt;br /&gt;
    $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
    // User ID.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
    $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
    // Message.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
    // Location.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;add_action_buttons();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of this is pretty standard, except for the &#039;&#039;userid&#039;&#039;. When creating a new &#039;status&#039;, we do not want our users to be in control of this value. Therefore we define it as a hidden value which we lock (using &#039;&#039;setConstant&#039;&#039;) to the value we created our form with. All mandatory fields of the persistent need to be defined, but if they are not customised by the user, then they must be hidden and combined with &#039;&#039;setConstant&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Also note that the &#039;&#039;id&#039;&#039; property is not included. It is not required, nor recommended, to add it to your fields it will be handled automatically.&lt;br /&gt;
&lt;br /&gt;
== Using the form ==&lt;br /&gt;
&lt;br /&gt;
When instantiating the form, there are two little things that you need to pay attention to. &lt;br /&gt;
&lt;br /&gt;
Firstly you should always pass the URL of the current page, including the different query parameters it included. We need this to be able to display the form with its validation errors without affecting anything else.&lt;br /&gt;
&lt;br /&gt;
Secondly, the persistent instance must be provided to the form through the custom data. That instance will be used to populate the form with initial data, typically when you are editing an object. When you don&#039;t have a persistent instance yet, probably because your user will be creating a new one, then simply pass null.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,  // An instance, or null.&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like any other form, we will be using &#039;&#039;get_data()&#039;&#039; to validate the form. The only difference is that to determine whether we are editing an object, or creating a new one, we will check if the &#039;&#039;id&#039;&#039; value was returned to us. The peristent form will return the ID value from the persistent we gave it. Then it&#039;s up to you to decide how to apply the data, most likely you will defer the logic to another part of your code, one that ensures that all capability checks are fulfilled. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(null, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Additional validation ==&lt;br /&gt;
&lt;br /&gt;
There are times where the built-in validation of the persistent is not enough. Usually you would use the method &#039;&#039;validation()&#039;&#039;, but as the form persistent class does some extra stuff to make it easier for you, you must use the &#039;&#039;extra_validation()&#039;&#039; method. The latter works almost just like the &#039;&#039;validation()&#039;&#039; one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Extra validation.&lt;br /&gt;
 *&lt;br /&gt;
 * @param  stdClass $data Data to validate.&lt;br /&gt;
 * @param  array $files Array of files.&lt;br /&gt;
 * @param  array $errors Currently reported errors.&lt;br /&gt;
 * @return array of additional errors, or overridden errors.&lt;br /&gt;
 */&lt;br /&gt;
protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
    $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
    if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
        $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return $newerrors;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The typical additional validation will return an array of errors, those will override any previous error. Rarely you will need to remove previously reported errors, hence the reference to &#039;&#039;$errors&#039;&#039; given, which you can modify directly. Do not abuse it though, this should only be used when you have no other choice.&lt;br /&gt;
&lt;br /&gt;
== Foreign fields ==&lt;br /&gt;
&lt;br /&gt;
By default, the form class tries to be smart at detecting foreign fields such as the submit button. Failure to do so will cause trouble during validation or other else. So when your form becomes more complex, if it includes more submit buttons, or when it deals with other fields for example files, we must specify it.&lt;br /&gt;
&lt;br /&gt;
=== Fields to ignore completely ===&lt;br /&gt;
&lt;br /&gt;
The fields to remove are never validated and they are not returned when calling &#039;&#039;get_data()&#039;&#039;. By default the submit button is added to this list so that when we call &#039;&#039;get_data()&#039;&#039; we only get the persistent-related fields. To remove more fields, re-declare the &#039;&#039;protected static $fieldstoremove&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove when getting the final data. */&lt;br /&gt;
protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Do not forget to add the &#039;&#039;submitbutton&#039;&#039; back in there.&lt;br /&gt;
&lt;br /&gt;
=== Fields to validate ===&lt;br /&gt;
&lt;br /&gt;
What about when we have a &#039;&#039;legit&#039;&#039; but that does not belong to the persistent? We still want to validate it ourselves, but we don&#039;t want it to be validated by the persistent as it will cause an error. In that case we define it in the &#039;&#039;protected static $foreignfields&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the persistent will not validate this field, and we will get the &#039;&#039;updatedelay&#039;&#039; value when we call &#039;&#039;get_data()&#039;&#039;. Just don&#039;t forget to remove it before you feed the data to your persistent.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
    $updatedelay = $data-&amp;gt;updatedelay;&lt;br /&gt;
    unset($data-&amp;gt;updatedelay);&lt;br /&gt;
    $newpersistent = new status(0, $data);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is particularily useful when dealing with file managers.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
    &lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove when getting the final data. */&lt;br /&gt;
    protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
    protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
        // Status update delay.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;duration&#039;, &#039;updatedelay&#039;, &#039;Status update delay&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Are you happy?&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;selectyesno&#039;, &#039;areyouhappy&#039;, &#039;Are you happy?&#039;);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Extra validation.&lt;br /&gt;
     *&lt;br /&gt;
     * @param  stdClass $data Data to validate.&lt;br /&gt;
     * @param  array $files Array of files.&lt;br /&gt;
     * @param  array $errors Currently reported errors.&lt;br /&gt;
     * @return array of additional errors, or overridden errors.&lt;br /&gt;
     */&lt;br /&gt;
    protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
        $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
        if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
            $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $newerrors;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Using the form ===&lt;br /&gt;
&lt;br /&gt;
Consider the following code to be a page you users will access at &#039;/example.php&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
require &#039;config.php&#039;;&lt;br /&gt;
&lt;br /&gt;
// Check if we go an ID.&lt;br /&gt;
$id = optional_param(&#039;id&#039;, null, PARAM_INT);&lt;br /&gt;
&lt;br /&gt;
// Set the PAGE URL (and mandatory context). Note the ID being recorded, this is important.&lt;br /&gt;
$PAGE-&amp;gt;set_context(context_system::instance());&lt;br /&gt;
$PAGE-&amp;gt;set_url(new moodle_url(&#039;/example.php&#039;, [&#039;id&#039; =&amp;gt; $id]));&lt;br /&gt;
&lt;br /&gt;
// Instantiate a persistent object if we received an ID. Typically receiving an ID&lt;br /&gt;
// means that we are going to be updating an object rather than creating a new one.&lt;br /&gt;
$persistent = null;&lt;br /&gt;
if (!empty($id)) {&lt;br /&gt;
    $persistent = new status($id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Create the form instance. We need to use the current URL and the custom data.&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(null, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Display the mandatory header and footer.&lt;br /&gt;
// And display the form, and its validation errors if there are any.&lt;br /&gt;
echo $OUTPUT-&amp;gt;header();&lt;br /&gt;
$form-&amp;gt;display();&lt;br /&gt;
echo $OUTPUT-&amp;gt;footer();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51627</id>
		<title>User:Frédéric Massart</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51627"/>
		<updated>2016-12-16T07:47:02Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}If you are creating [[Persistent|persistent]] entries from forms, we&#039;ve got something for you. You can use the class &#039;&#039;core\form\persistent&#039;&#039; as a base for your form instead of &#039;&#039;moodleform&#039;&#039;. Our persistent form class comes handy tools, such as automatic validation.&lt;br /&gt;
&lt;br /&gt;
_Note: for more information about forms themselves, [[Category:Formslib|head this way]]._&lt;br /&gt;
&lt;br /&gt;
== Defining your form ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
First of all, let&lt;br /&gt;
&lt;br /&gt;
First, here is some code to create a form for the persistent we worked in [[Persistent|the persistent documentation]]:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Linking to a persistent ==&lt;br /&gt;
&lt;br /&gt;
In order for the form class to know what persistent we&#039;ll be dealing with, we must declare the &#039;&#039;protected static $persistentclass&#039;&#039; variable. The latter contains the fully qualified name of the persistent class.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var string Persistent class name. */&lt;br /&gt;
protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Defining the form fields ==&lt;br /&gt;
&lt;br /&gt;
Unfortunately this is not automatically done for us, so let&#039;s add our fields to the &#039;&#039;definition()&#039;&#039; method like you would do for any form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Define the form.&lt;br /&gt;
 */&lt;br /&gt;
public function definition() {&lt;br /&gt;
    $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
    // User ID.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
    $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
    // Message.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
    // Location.&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
    $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;add_action_buttons();&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All of this is pretty standard, except for the &#039;&#039;userid&#039;&#039;. When creating a new &#039;status&#039;, we do not want our users to be in control of this value. Therefore we define it as a hidden value which we lock (using &#039;&#039;setConstant&#039;&#039;) to the value we created our form with. All mandatory fields of the persistent need to be defined, but if they are not customised by the user, then they must be hidden and combined with &#039;&#039;setConstant&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Also note that the &#039;&#039;id&#039;&#039; property is not included. It is not required, nor recommended, to add it to your fields it will be handled automatically.&lt;br /&gt;
&lt;br /&gt;
== Using the form ==&lt;br /&gt;
&lt;br /&gt;
When instantiating the form, there are two little things that you need to pay attention to. &lt;br /&gt;
&lt;br /&gt;
Firstly you should always pass the URL of the current page, including the different query parameters it included. We need this to be able to display the form with its validation errors without affecting anything else.&lt;br /&gt;
&lt;br /&gt;
Secondly, the persistent instance must be provided to the form through the custom data. That instance will be used to populate the form with initial data, typically when you are editing an object. When you don&#039;t have a persistent instance yet, probably because your user will be creating a new one, then simply pass null.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,  // An instance, or null.&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like any other form, we will be using &#039;&#039;get_data()&#039;&#039; to validate the form. The only difference is that to determine whether we are editing an object, or creating a new one, we will check if the &#039;&#039;id&#039;&#039; value was returned to us. The peristent form will return the ID value from the persistent we gave it. Then it&#039;s up to you to decide how to apply the data, most likely you will defer the logic to another part of your code, one that ensures that all capability checks are fulfilled. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(null, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Additional validation ==&lt;br /&gt;
&lt;br /&gt;
There are times where the built-in validation of the persistent is not enough. Usually you would use the method &#039;&#039;validation()&#039;&#039;, but as the form persistent class does some extra stuff to make it easier for you, you must use the &#039;&#039;extra_validation()&#039;&#039; method. The latter works almost just like the &#039;&#039;validation()&#039;&#039; one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Extra validation.&lt;br /&gt;
 *&lt;br /&gt;
 * @param  stdClass $data Data to validate.&lt;br /&gt;
 * @param  array $files Array of files.&lt;br /&gt;
 * @param  array $errors Currently reported errors.&lt;br /&gt;
 * @return array of additional errors, or overridden errors.&lt;br /&gt;
 */&lt;br /&gt;
protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
    $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
    if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
        $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return $newerrors;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The typical additional validation will return an array of errors, those will override any previous error. Rarely you will need to remove previously reported errors, hence the reference to &#039;&#039;$errors&#039;&#039; given, which you can modify directly. Do not abuse it though, this should only be used when you have no other choice.&lt;br /&gt;
&lt;br /&gt;
== Foreign fields ==&lt;br /&gt;
&lt;br /&gt;
By default, the form class tries to be smart at detecting foreign fields such as the submit button. Failure to do so will cause trouble during validation or other else. So when your form becomes more complex, if it includes more submit buttons, or when it deals with other fields for example files, we must specify it.&lt;br /&gt;
&lt;br /&gt;
=== Fields to ignore completely ===&lt;br /&gt;
&lt;br /&gt;
The fields to remove are never validated and they are not returned when calling &#039;&#039;get_data()&#039;&#039;. By default the submit button is added to this list so that when we call &#039;&#039;get_data()&#039;&#039; we only get the persistent-related fields. To remove more fields, re-declare the &#039;&#039;protected static $fieldstoremove&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove when getting the final data. */&lt;br /&gt;
protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Do not forget to add the &#039;&#039;submitbutton&#039;&#039; back in there.&lt;br /&gt;
&lt;br /&gt;
=== Fields to validate ===&lt;br /&gt;
&lt;br /&gt;
What about when we have a &#039;&#039;legit&#039;&#039; but that does not belong to the persistent? We still want to validate it ourselves, but we don&#039;t want it to be validated by the persistent as it will cause an error. In that case we define it in the &#039;&#039;protected static $foreignfields&#039;&#039; class variable.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the persistent will not validate this field, and we will get the &#039;&#039;updatedelay&#039;&#039; value when we call &#039;&#039;get_data()&#039;&#039;. Just don&#039;t forget to remove it before you feed the data to your persistent.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
    $updatedelay = $data-&amp;gt;updatedelay;&lt;br /&gt;
    unset($data-&amp;gt;updatedelay);&lt;br /&gt;
    $newpersistent = new status(0, $data);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method is particularily useful when dealing with file managers.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
    &lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_form extends \core\form\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** @var string Persistent class name. */&lt;br /&gt;
    protected static $persistentclass = &#039;example\\status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove when getting the final data. */&lt;br /&gt;
    protected static $fieldstoremove = array(&#039;submitbutton&#039;, &#039;areyouhappy&#039;);&lt;br /&gt;
&lt;br /&gt;
    /** @var array Fields to remove from the persistent validation. */&lt;br /&gt;
    protected static $foreignfields = array(&#039;updatedelay&#039;);&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Define the form.&lt;br /&gt;
     */&lt;br /&gt;
    public function definition() {&lt;br /&gt;
        $mform = $this-&amp;gt;_form;&lt;br /&gt;
&lt;br /&gt;
        // User ID.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;userid&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;userid&#039;, PARAM_INT);&lt;br /&gt;
        $mform-&amp;gt;setConstant(&#039;userid&#039;, $this-&amp;gt;_customdata[&#039;userid&#039;]);&lt;br /&gt;
&lt;br /&gt;
        // Message.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;editor&#039;, &#039;message&#039;, &#039;Message&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;message&#039;, PARAM_RAW);&lt;br /&gt;
&lt;br /&gt;
        // Location.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;location&#039;, &#039;Location&#039;);&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;location&#039;, PARAM_ALPHANUMEXT);&lt;br /&gt;
&lt;br /&gt;
        // Status update delay.&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;duration&#039;, &#039;updatedelay&#039;, &#039;Status update delay&#039;);&lt;br /&gt;
&lt;br /&gt;
        // Are you happy?&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;selectyesno&#039;, &#039;areyouhappy&#039;, &#039;Are you happy?&#039;);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;add_action_buttons();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Extra validation.&lt;br /&gt;
     *&lt;br /&gt;
     * @param  stdClass $data Data to validate.&lt;br /&gt;
     * @param  array $files Array of files.&lt;br /&gt;
     * @param  array $errors Currently reported errors.&lt;br /&gt;
     * @return array of additional errors, or overridden errors.&lt;br /&gt;
     */&lt;br /&gt;
    protected function extra_validation($data, $files, array &amp;amp;$errors) {&lt;br /&gt;
        $newerrors = array();&lt;br /&gt;
&lt;br /&gt;
        if ($data-&amp;gt;location === &#039;SFO&#039;) {&lt;br /&gt;
            $newerrors[&#039;location&#039;] = &#039;San-Francisco Airport is not accepted from the form.&#039;;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $newerrors;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Using the form ===&lt;br /&gt;
&lt;br /&gt;
Consider the following code to be a page you users will access at &#039;/example.php&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
require &#039;config.php&#039;;&lt;br /&gt;
&lt;br /&gt;
// Check if we go an ID.&lt;br /&gt;
$id = optional_param(&#039;id&#039;, null, PARAM_INT);&lt;br /&gt;
&lt;br /&gt;
// Set the PAGE URL (and mandatory context). Note the ID being recorded, this is important.&lt;br /&gt;
$PAGE-&amp;gt;set_context(context_system::instance());&lt;br /&gt;
$PAGE-&amp;gt;set_url(new moodle_url(&#039;/example.php&#039;, [&#039;id&#039; =&amp;gt; $id]));&lt;br /&gt;
&lt;br /&gt;
// Instantiate a persistent object if we received an ID. Typically receiving an ID&lt;br /&gt;
// means that we are going to be updating an object rather than creating a new one.&lt;br /&gt;
$persistent = null;&lt;br /&gt;
if (!empty($id)) {&lt;br /&gt;
    $persistent = new status($id);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Create the form instance. We need to use the current URL and the custom data.&lt;br /&gt;
$customdata = [&lt;br /&gt;
    &#039;persistent&#039; =&amp;gt; $persistent,&lt;br /&gt;
    &#039;userid&#039; =&amp;gt; $USER-&amp;gt;id         // For the hidden userid field.&lt;br /&gt;
];&lt;br /&gt;
$form = new status_form($PAGE-&amp;gt;url-&amp;gt;out(false), $customdata);&lt;br /&gt;
&lt;br /&gt;
// Get the data. This ensures that the form was validated.&lt;br /&gt;
if (($data = $form-&amp;gt;get_data())) {&lt;br /&gt;
&lt;br /&gt;
    if (empty($data-&amp;gt;id)) {&lt;br /&gt;
        // If we don&#039;t have an ID, we know that we must create a new record.&lt;br /&gt;
        // Call your API to create a new persistent from this data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent = new status(null, $data);&lt;br /&gt;
        $persistent-&amp;gt;create();&lt;br /&gt;
    } else {&lt;br /&gt;
        // We had an ID, this means that we are going to update a record.&lt;br /&gt;
        // Call your API to update the persistent from the data.&lt;br /&gt;
        // Or, do the following if you don&#039;t want capability checks (discouraged).&lt;br /&gt;
        $persistent-&amp;gt;from_record($data);&lt;br /&gt;
        $persistent-&amp;gt;update();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // We are done, so let&#039;s redirect somewhere.&lt;br /&gt;
    redirect(new moodle_url(&#039;/&#039;));&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Display the mandatory header and footer.&lt;br /&gt;
// And display the form, and its validation errors if there are any.&lt;br /&gt;
echo $OUTPUT-&amp;gt;header();&lt;br /&gt;
$form-&amp;gt;display();&lt;br /&gt;
echo $OUTPUT-&amp;gt;footer();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Persistent&amp;diff=51626</id>
		<title>Persistent</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Persistent&amp;diff=51626"/>
		<updated>2016-12-16T06:12:21Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle persistents are equivalent to models (or active records). They represent an object stored in the database and provide the methods to create, read, update, and delete those objects. Additionally, the persistents validate their own data against automatic and custom validation rules. Persistents are made by extending the abstract class &#039;&#039;core\persistent&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Properties ==&lt;br /&gt;
&lt;br /&gt;
Each class must define the properties that it contains. This can be done by defining the method &#039;&#039;protected static define_properties()&#039;&#039;. It returns an array where the keys are the names of the properties (and implies the database column names), and their attributes. The &#039;&#039;type&#039;&#039; of each property must be specified using one of the &#039;&#039;PARAM_*&#039;&#039; constants. This type will be used to automatically validate the property&#039;s value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the definition of the properties of this model.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
        )&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Properties attributes ===&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must be one of the many &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
;default&lt;br /&gt;
: The default value to attach to the property when it hasn&#039;t been provided. Without a &#039;&#039;default&#039;&#039;, the property&#039;s value is required. Alternatively this can be a &#039;&#039;Closure&#039;&#039; returning the default value.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants &#039;&#039;NULL_ALLOWED&#039;&#039; or &#039;&#039;NULL_NOT_ALLOWED&#039;&#039; determining if the null value is accepted. This defaults to &#039;&#039;NULL_NOT_ALLOWED&#039;&#039;.&lt;br /&gt;
;message&lt;br /&gt;
: The default error message (as a &#039;&#039;lang_string&#039;&#039; instance) to return when the validation of this field fails.&lt;br /&gt;
;choices&lt;br /&gt;
: An array of values which the property must fall in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
    &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
    &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
),&lt;br /&gt;
&#039;location&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
    &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
    &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
    &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
        return get_config(&#039;core&#039;, &#039;default_location&#039;);&lt;br /&gt;
    },&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that you should always use a &#039;&#039;Closure&#039;&#039; for the &#039;&#039;default&#039;&#039; value when you cannot guarantee that it will not change since the start of the request. The list of properties and their attributes is cached, and so failure to use a &#039;&#039;Closure&#039;&#039; can result in using an outdated default value.&lt;br /&gt;
&lt;br /&gt;
=== Mandatory properties ===&lt;br /&gt;
&lt;br /&gt;
Four fields are always added to your persistent and should be reflected in your database table. You must not define those properties in &#039;&#039;define_properties()&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
;id (non-null integer)&lt;br /&gt;
: The primary key of the record.&lt;br /&gt;
;usermodified (non-null integer)&lt;br /&gt;
: The user who created/modified the object. It is automatically set.&lt;br /&gt;
;timecreated (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set.&lt;br /&gt;
;timemodified (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set, and defaults to 0.&lt;br /&gt;
&lt;br /&gt;
== Linking to a database table ==&lt;br /&gt;
&lt;br /&gt;
While the persistent class is helpful for database interactions, it does not automatically fetch the properties from the database, nor does it create the tables. You will need to create the table yourself, as well as pointing the persistent to it. This can be done by defining the class constant &#039;&#039;TABLE&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** Table name for the persistent. */&lt;br /&gt;
const TABLE = &#039;status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The table name must not contain the Moodle prefix. Also it is common practice to always refer to your table name using the constant.&lt;br /&gt;
&lt;br /&gt;
== Reading property values ==&lt;br /&gt;
&lt;br /&gt;
This can be done using the method &#039;&#039;get()&#039;&#039;, or the magic method &#039;&#039;get_&#039;&#039; followed by the property name. Alternatively you can also use &#039;&#039;to_record()&#039;&#039; which exports the whole object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Create a new object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using persistent::get().&lt;br /&gt;
$userid = $persistent-&amp;gt;get(&#039;userid&#039;);&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using the magic getter.&lt;br /&gt;
$userid = $persistent-&amp;gt;get_userid();&lt;br /&gt;
&lt;br /&gt;
// Get all the properties in an stdClass.&lt;br /&gt;
$data = $persistent-&amp;gt;to_record();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may override the magic getters to implement your own custom logic. For instance you could convert the data in another format automatically as a convenience for developers. However, use this sparingly as it may lead to confusion: what you get is not what is stored in the database. Note that you cannot guarantee that your getter will be used, remember that it is possible for developers to call the alternative &#039;&#039;get()&#039;&#039; which will not use your custom logic.&lt;br /&gt;
&lt;br /&gt;
It is, however, encouraged to add convenience methods such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns the user object of the author.&lt;br /&gt;
 *&lt;br /&gt;
 * @return stdClass&lt;br /&gt;
 */&lt;br /&gt;
public function get_author() {&lt;br /&gt;
    return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Assigning values to properties ==&lt;br /&gt;
&lt;br /&gt;
There are a few methods to do so.&lt;br /&gt;
&lt;br /&gt;
You use an object (&#039;&#039;stdClass&#039;&#039;) to assign a bunch of properties at once. Use it with the constructor, or the method &#039;&#039;from_record()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) array(&#039;userid&#039; =&amp;gt; 2, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instantiates a new object with value for some properties.&lt;br /&gt;
$persistent = new status(0, $data);&lt;br /&gt;
&lt;br /&gt;
// Is similar to.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
$persistent-&amp;gt;from_record($data);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or you can use the &#039;&#039;set()&#039;&#039; method on an instance.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set(&#039;message&#039;, &#039;Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally you can use the magic setters &#039;&#039;set_&#039;&#039; followed by the property name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Defining a custom setter ===&lt;br /&gt;
&lt;br /&gt;
Though you don&#039;t have to for the code to work, you can define your own &#039;&#039;setter&#039;&#039; methods which will override the magic setters. They are useful if you want to extract the data out of a more complex object prior to assigning it. Though note that those setters will then have to use the &#039;&#039;set()&#039;&#039; method to assign the values.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Convenience method to set the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
 */&lt;br /&gt;
public function set_userid($idorobject) {&lt;br /&gt;
    $userid = $idorobject;&lt;br /&gt;
    if (is_object($idorobject)) {&lt;br /&gt;
        $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
    }&lt;br /&gt;
    $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example we will accept an object or an ID, as a convenience for developers we will extract the ID value out of the object passed if any.&lt;br /&gt;
&lt;br /&gt;
Just like custom getters, you cannot guarantee that they will be used. Developers can directly call the &#039;&#039;set()&#039;&#039; method. Therefore you must not use a custom setter to reliably transform the data added to a property. For instance do not add a custom setter to remove HTML tags out of a text field, it may not always happen.&lt;br /&gt;
&lt;br /&gt;
You can obviously create your own setters which aren&#039;t based on any properties just as a convenience. For instance we could have created &#039;&#039;set_userid_from_user(object $user)&#039;&#039; which is more verbose and more predictable&lt;br /&gt;
&lt;br /&gt;
== Read, save and delete entries ==&lt;br /&gt;
&lt;br /&gt;
The methods to &#039;&#039;create&#039;&#039;, &#039;&#039;read&#039;&#039;, &#039;&#039;update&#039;&#039; and &#039;&#039;delete&#039;&#039; are eponymous. Your object will be validated before you &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; it. The &#039;&#039;update&#039;&#039;, &#039;&#039;delete&#039;&#039; and &#039;&#039;read&#039;&#039; methods require your object to contain its ID. And you also won&#039;t be allowed to &#039;&#039;create&#039;&#039; an entry which already had an ID defined.&lt;br /&gt;
&lt;br /&gt;
Here are some code examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Fetches an object from database based on its ID.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Create object in the database.&lt;br /&gt;
$data = new stdClass();&lt;br /&gt;
$data-&amp;gt;message = &#039;Hello new world&#039;;&lt;br /&gt;
$persistent = new persistent(0, $data);&lt;br /&gt;
$persistent-&amp;gt;create();&lt;br /&gt;
// $persistent-&amp;gt;get(&#039;id&#039;) will now return an id.&lt;br /&gt;
&lt;br /&gt;
// Load an object from the database, and update it.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
$persistent-&amp;gt;update();&lt;br /&gt;
&lt;br /&gt;
// Reset the instance to the values in the database.&lt;br /&gt;
$persistent-&amp;gt;read();&lt;br /&gt;
&lt;br /&gt;
// Permanently delete the object from the database.&lt;br /&gt;
$persistent-&amp;gt;delete();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fetching records ==&lt;br /&gt;
&lt;br /&gt;
Once you start using persistents you should never directly interact with the database outside of your class. The persistent class comes with a few handy methods allowing you to retrieve your objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Use the constructor to fetch one object from its ID.&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Get one record from a set of conditions.&lt;br /&gt;
$persistent = status::get_record([&#039;userid&#039; =&amp;gt; $userid, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;]);&lt;br /&gt;
&lt;br /&gt;
// Get multiple records from a set of conditions.&lt;br /&gt;
$persistents = status::get_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Count the records.&lt;br /&gt;
$count = status::count_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Check whether a record exists.&lt;br /&gt;
$exists = status::record_exists($id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure to also check their additional parameters and their variants (&#039;&#039;record_exists_select()&#039;&#039;, &#039;&#039;count_records_select&#039;&#039;, &#039;&#039;get_records_select&#039;&#039;, ...).&lt;br /&gt;
&lt;br /&gt;
=== Custom fetching ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s always a good idea to add more complex queries directly within your persistent. By convention you should always return an instance of your persistent and never an stdClass. Here we add a custom method which allows to directly fetch all records by username.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get all records from a user&#039;s username.&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $username The username.&lt;br /&gt;
 * @return status[]&lt;br /&gt;
 */&lt;br /&gt;
public static function get_records_by_username($username) {&lt;br /&gt;
    global $DB;&lt;br /&gt;
&lt;br /&gt;
    $sql = &#039;SELECT s.*&lt;br /&gt;
              FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
              JOIN {user} u&lt;br /&gt;
                ON u.id = s.userid&lt;br /&gt;
             WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
    $persistents = [];&lt;br /&gt;
&lt;br /&gt;
    $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
    foreach ($recordset as $record) {&lt;br /&gt;
        $persistents[] = new static(0, $record);&lt;br /&gt;
    }&lt;br /&gt;
    $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
    return $persistents;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to join persistents together and be able to extract their respective properties in a single database query, you should have a look at the following methods:&lt;br /&gt;
&lt;br /&gt;
;get_sql_fields(string $alias, string $prefix = null)&lt;br /&gt;
: Returns the SQL statement to include in the SELECT clause to prefix columns.&lt;br /&gt;
;extract_record(stdClass $row, string $prefix = null)&lt;br /&gt;
: Extracts all the properties from a row based on the given prefix.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Minimalist example.&lt;br /&gt;
$sqlfields = status::get_sql_fields(&#039;s&#039;, &#039;statprops&#039;);&lt;br /&gt;
$sql = &amp;quot;SELECT $sqlfields, u.username&lt;br /&gt;
          FROM {&amp;quot; . status::TABLE . &amp;quot;} s&lt;br /&gt;
          JOIN {user} ON s.userid = u.id&lt;br /&gt;
         WHERE s.id = 1&amp;quot;;&lt;br /&gt;
$row = $DB-&amp;gt;get_record($sql, []);&lt;br /&gt;
$statusdata = status::extract_record($row, &#039;statprops&#039;);&lt;br /&gt;
$persistent = new status(0, $statusdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Validating ==&lt;br /&gt;
&lt;br /&gt;
Basic validation of the properties values happens automatically based on their type (&#039;&#039;PARAM_*&#039;&#039; constant), however this is not always enough. In order to implement your own custom validation, simply define a &#039;&#039;protected&#039;&#039; method starting with &#039;&#039;validate_&#039;&#039; followed with the property name. This method will be called whenever the model needs to be validated and will receive the data to validate.&lt;br /&gt;
&lt;br /&gt;
A validation method must always return either &#039;&#039;true&#039;&#039; or an instance of &#039;&#039;lang_string&#039;&#039; which contains the error message to send to the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param int $value The value.&lt;br /&gt;
 * @return true|lang_string&lt;br /&gt;
 */&lt;br /&gt;
protected function validate_userid($value) {&lt;br /&gt;
    if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
        return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above example ensures that the &#039;&#039;userid&#039;&#039; property contains a valid user ID.&lt;br /&gt;
&lt;br /&gt;
Note that the basic validation is always performed first, and thus your custom validation method will not be called when the value did not pass the basic validation.&lt;br /&gt;
&lt;br /&gt;
=== Validation results ===&lt;br /&gt;
&lt;br /&gt;
The validation of the object automatically happens upon &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039;. If the validation did not pass, an &#039;&#039;invalid_persisten_exception&#039;&#039; will be raised. You can validate the object prior to saving the object and get the validation results if you need to.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// We can catch the invalid_persistent_exception.&lt;br /&gt;
try {&lt;br /&gt;
    $persistent = new status();&lt;br /&gt;
    $persistent-&amp;gt;create();&lt;br /&gt;
} catch (invalid_persistent_exception $e) {&lt;br /&gt;
    // Whoops, something wrong happened.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Check whether the object is valid.&lt;br /&gt;
$persistent-&amp;gt;is_valid();        // True or false.&lt;br /&gt;
&lt;br /&gt;
// Get the validation errors.&lt;br /&gt;
$persistent-&amp;gt;get_errors();      // Array where keys are properties and values are errors.&lt;br /&gt;
&lt;br /&gt;
// Validate the object.&lt;br /&gt;
$persistent-&amp;gt;validate();        // Returns true, or an array of errors.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hooks ==&lt;br /&gt;
&lt;br /&gt;
You can define the following methods to be notified prior to, or after, something happened:&lt;br /&gt;
&lt;br /&gt;
;protected before_validate()&lt;br /&gt;
: Do something before the object is validated.&lt;br /&gt;
;protected before_create()&lt;br /&gt;
: Do something before the object is inserted in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_create()&lt;br /&gt;
: Do something right after the object was added to the database.&lt;br /&gt;
;protected before_update()&lt;br /&gt;
: Do something before the object is updated in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_update(bool $result)&lt;br /&gt;
: Do something right after the object was updated in the database.&lt;br /&gt;
;protected before_delete()&lt;br /&gt;
: Do something right before the object is deleted from the database.&lt;br /&gt;
;protected after_delete(bool $result)&lt;br /&gt;
: Do something right after the object was deleted from the database.&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# You must create the database table yourself, using the [[XMLDB editor]] and an upgrade script.&lt;br /&gt;
# You must include the [[#Mandatory_properties|mandatory fields]] in your table schema.&lt;br /&gt;
# You must never extend another persistent as this leads to unpredictable errors whenever the parent changes (new properties missing in your table, changes in validation, custom getters and setters, etc...).&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
class status extends core\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example;&lt;br /&gt;
&lt;br /&gt;
use core\persistent;&lt;br /&gt;
use core_user;&lt;br /&gt;
use lang_string;&lt;br /&gt;
&lt;br /&gt;
class status extends persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
                &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
                &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;location&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
                &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
                &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
                &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
                    return get_config(&#039;core&#039;, &#039;default_location&#039;);&lt;br /&gt;
                },&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the user object of the author.&lt;br /&gt;
     *&lt;br /&gt;
     * @return stdClass&lt;br /&gt;
     */&lt;br /&gt;
    public function get_author() {&lt;br /&gt;
        return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Convenience method to set the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
     */&lt;br /&gt;
    public function set_userid($idorobject) {&lt;br /&gt;
        $userid = $idorobject;&lt;br /&gt;
        if (is_object($idorobject)) {&lt;br /&gt;
            $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Validate the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param int $value The value.&lt;br /&gt;
     * @return true|lang_string&lt;br /&gt;
     */&lt;br /&gt;
    protected function validate_userid($value) {&lt;br /&gt;
        if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
            return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get all records from a user&#039;s username.&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $username The username.&lt;br /&gt;
     * @return status[]&lt;br /&gt;
     */&lt;br /&gt;
    public static function get_records_by_username($username) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $sql = &#039;SELECT s.*&lt;br /&gt;
                  FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
                  JOIN {user} u&lt;br /&gt;
                    ON u.id = s.userid&lt;br /&gt;
                 WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
        $persistents = [];&lt;br /&gt;
&lt;br /&gt;
        $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
        foreach ($recordset as $record) {&lt;br /&gt;
            $persistents[] = new static(0, $record);&lt;br /&gt;
        }&lt;br /&gt;
        $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
        return $persistents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51625</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51625"/>
		<updated>2016-12-16T04:12:22Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Formatting parameters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected when instantiating your exporter, and the ones it will export at the same time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These properties will allow us to generate a &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; structure for external functions, [[#In_external_functions|more on this later]]. Oh, and if you are using persistent you do not need to do this: [[#Exporters_and_persistent|check this out]].&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the field &#039;username&#039; to be passed in the key &#039;user&#039;. The &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039; structures include all of the &#039;&#039;standard&#039;&#039; properties, none of the &#039;&#039;[[#Additional_properties|other]]&#039;&#039; ones. Note that the &#039;&#039;create&#039;&#039; structure does not include the &#039;&#039;id&#039;&#039; property. Use &#039;&#039; user_exporter::get_update_structure()&#039;&#039; if to update a user and thus receive the ID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not be flexible, and may not generate a valid structure for some webservice protocols.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, [&#039;context&#039; =&amp;gt; context_user::instance(123)]);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... we&#039;ve cheating a bit above. Did you notice that we&#039;ve passed a context to the exporter? We passed a [[#Related_objects|related object]], more on them later.&lt;br /&gt;
&lt;br /&gt;
=== Formatting parameters ===&lt;br /&gt;
&lt;br /&gt;
Formatting requires a context. If you&#039;ve defined a [[#Related_objects|related object]] with the name &#039;&#039;context&#039;&#039; we will automatically use this one. But there are cases where you need multiple contexts, or you need to rewrite the pluginfile URLs. When that is the case, you will define a method called &#039;get_format_parameters_for_&#039; followed with the name of the property. The latter also accepts the various options which both &#039;&#039;format_text&#039;&#039; and &#039;&#039;format_string&#039;&#039; support.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the formatting parameters for the description.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected function get_format_parameters_for_description() {&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;component&#039; =&amp;gt; &#039;core_user&#039;,&lt;br /&gt;
        &#039;filearea&#039; =&amp;gt; &#039;description&#039;,&lt;br /&gt;
        &#039;itemid&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a complete list of options, refer to the PHP documentation of the method [https://github.com/moodle/moodle/blob/master/lib/classes/external/exporter.php core\external\exporter::get_format_parameters()].&lt;br /&gt;
&lt;br /&gt;
Note that when you return a context from the above method, it is advised that you get it from the related objects. Doing so will prevent contexts from being loaded from the database from within the exporter and cause unwanted performance issues. The &#039;&#039;system context&#039;&#039; is an exception is this.&lt;br /&gt;
&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns a list of objects that are related.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_related() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;context&#039; =&amp;gt; &#039;context&#039;,                     // Must be an instance of context.&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,  // Must be an array of status instances.&lt;br /&gt;
        &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,              // Can be a mother instance, or null.&lt;br /&gt;
        &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,         // Can be null, or an array of brother instances.&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional relateds must still be provided but as &#039;&#039;null&#039;&#039;. Related objects are accessible from within the exporter using &#039;&#039;$this-&amp;gt;related[&#039;nameOfRelated&#039;]&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Creating a brand new exporter class, and leaving the other one unchanged.&lt;br /&gt;
#* Adding the related as &#039;&#039;optional&#039;&#039;. This is strongly discouraged as this is not what &#039;&#039;optional&#039;&#039; is for.&lt;br /&gt;
#* Adding the new related and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the formatting parameters for the description.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_format_parameters_for_description() {&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;component&#039; =&amp;gt; &#039;core_user&#039;,&lt;br /&gt;
            &#039;filearea&#039; =&amp;gt; &#039;description&#039;,&lt;br /&gt;
            &#039;itemid&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51624</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51624"/>
		<updated>2016-12-16T04:12:06Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Formatting parameters */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected when instantiating your exporter, and the ones it will export at the same time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These properties will allow us to generate a &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; structure for external functions, [[#In_external_functions|more on this later]]. Oh, and if you are using persistent you do not need to do this: [[#Exporters_and_persistent|check this out]].&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the field &#039;username&#039; to be passed in the key &#039;user&#039;. The &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039; structures include all of the &#039;&#039;standard&#039;&#039; properties, none of the &#039;&#039;[[#Additional_properties|other]]&#039;&#039; ones. Note that the &#039;&#039;create&#039;&#039; structure does not include the &#039;&#039;id&#039;&#039; property. Use &#039;&#039; user_exporter::get_update_structure()&#039;&#039; if to update a user and thus receive the ID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not be flexible, and may not generate a valid structure for some webservice protocols.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, [&#039;context&#039; =&amp;gt; context_user::instance(123)]);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... we&#039;ve cheating a bit above. Did you notice that we&#039;ve passed a context to the exporter? We passed a [[#Related_objects|related object]], more on them later.&lt;br /&gt;
&lt;br /&gt;
=== Formatting parameters ===&lt;br /&gt;
&lt;br /&gt;
Formatting requires a context. If you&#039;ve defined a [[#Related_objects|related object]] with the name &#039;&#039;context&#039;&#039; we will automatically use this one. But there are cases where you need multiple contexts, or you need to rewrite the pluginfile URLs. When that is the case, you will define a method called &#039;get_format_parameters_for_&#039; followed with the name of the property. The latter also accepts the various options which both &#039;&#039;format_text&#039;&#039; and &#039;&#039;format_string&#039;&#039; support.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the formatting parameters for the description.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected function get_format_parameters_for_description() {&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;component&#039; =&amp;gt; &#039;core_user&#039;,&lt;br /&gt;
        &#039;filearea&#039; =&amp;gt; &#039;description&#039;,&lt;br /&gt;
        &#039;itemid&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a complete list of options, refer to the PHP documention of the method [https://github.com/moodle/moodle/blob/master/lib/classes/external/exporter.php core\external\exporter::get_format_parameters()].&lt;br /&gt;
&lt;br /&gt;
Note that when you return a context from the above method, it is advised that you get it from the related objects. Doing so will prevent contexts from being loaded from the database from within the exporter and cause unwanted performance issues. The &#039;&#039;system context&#039;&#039; is an exception is this.&lt;br /&gt;
&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns a list of objects that are related.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_related() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;context&#039; =&amp;gt; &#039;context&#039;,                     // Must be an instance of context.&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,  // Must be an array of status instances.&lt;br /&gt;
        &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,              // Can be a mother instance, or null.&lt;br /&gt;
        &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,         // Can be null, or an array of brother instances.&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional relateds must still be provided but as &#039;&#039;null&#039;&#039;. Related objects are accessible from within the exporter using &#039;&#039;$this-&amp;gt;related[&#039;nameOfRelated&#039;]&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Creating a brand new exporter class, and leaving the other one unchanged.&lt;br /&gt;
#* Adding the related as &#039;&#039;optional&#039;&#039;. This is strongly discouraged as this is not what &#039;&#039;optional&#039;&#039; is for.&lt;br /&gt;
#* Adding the new related and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the formatting parameters for the description.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_format_parameters_for_description() {&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;component&#039; =&amp;gt; &#039;core_user&#039;,&lt;br /&gt;
            &#039;filearea&#039; =&amp;gt; &#039;description&#039;,&lt;br /&gt;
            &#039;itemid&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51623</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51623"/>
		<updated>2016-12-16T04:06:43Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Abiding to text formatting rules */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected when instantiating your exporter, and the ones it will export at the same time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These properties will allow us to generate a &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; structure for external functions, [[#In_external_functions|more on this later]]. Oh, and if you are using persistent you do not need to do this: [[#Exporters_and_persistent|check this out]].&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the field &#039;username&#039; to be passed in the key &#039;user&#039;. The &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039; structures include all of the &#039;&#039;standard&#039;&#039; properties, none of the &#039;&#039;[[#Additional_properties|other]]&#039;&#039; ones. Note that the &#039;&#039;create&#039;&#039; structure does not include the &#039;&#039;id&#039;&#039; property. Use &#039;&#039; user_exporter::get_update_structure()&#039;&#039; if to update a user and thus receive the ID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not be flexible, and may not generate a valid structure for some webservice protocols.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, [&#039;context&#039; =&amp;gt; context_user::instance(123)]);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... we&#039;ve cheating a bit above. Did you notice that we&#039;ve passed a context to the exporter? We passed a [[#Related_objects|related object]], more on them later.&lt;br /&gt;
&lt;br /&gt;
=== Formatting parameters ===&lt;br /&gt;
&lt;br /&gt;
Formatting requires a context. If you&#039;ve defined a [[#Related_objects|related object]] with the name &#039;&#039;context&#039;&#039; we will automatically use this one. But there are cases where you need multiple contexts, or you need to rewrite the pluginfile URLs. When that is the case, you will define a method called &#039;get_format_parameters_for_&#039; followed with the name of the property. The latter also accepts the various options which both &#039;&#039;format_text&#039;&#039; and &#039;&#039;format_string&#039;&#039; support.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the formatting parameters for the description.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected function get_format_parameters_for_description() {&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;component&#039; =&amp;gt; &#039;core_user&#039;,&lt;br /&gt;
        &#039;filearea&#039; =&amp;gt; &#039;description&#039;,&lt;br /&gt;
        &#039;itemid&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a complete list of options, refer to the PHP documention of the method [[https://github.com/moodle/moodle/blob/master/lib/classes/external/exporter.php|core\external\exporter::get_format_parameters()]].&lt;br /&gt;
&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns a list of objects that are related.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_related() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;context&#039; =&amp;gt; &#039;context&#039;,                     // Must be an instance of context.&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,  // Must be an array of status instances.&lt;br /&gt;
        &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,              // Can be a mother instance, or null.&lt;br /&gt;
        &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,         // Can be null, or an array of brother instances.&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional relateds must still be provided but as &#039;&#039;null&#039;&#039;. Related objects are accessible from within the exporter using &#039;&#039;$this-&amp;gt;related[&#039;nameOfRelated&#039;]&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Creating a brand new exporter class, and leaving the other one unchanged.&lt;br /&gt;
#* Adding the related as &#039;&#039;optional&#039;&#039;. This is strongly discouraged as this is not what &#039;&#039;optional&#039;&#039; is for.&lt;br /&gt;
#* Adding the new related and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the formatting parameters for the description.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_format_parameters_for_description() {&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;component&#039; =&amp;gt; &#039;core_user&#039;,&lt;br /&gt;
            &#039;filearea&#039; =&amp;gt; &#039;description&#039;,&lt;br /&gt;
            &#039;itemid&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51622</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51622"/>
		<updated>2016-12-16T04:02:44Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* More advanced */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected when instantiating your exporter, and the ones it will export at the same time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These properties will allow us to generate a &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; structure for external functions, [[#In_external_functions|more on this later]]. Oh, and if you are using persistent you do not need to do this: [[#Exporters_and_persistent|check this out]].&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the field &#039;username&#039; to be passed in the key &#039;user&#039;. The &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039; structures include all of the &#039;&#039;standard&#039;&#039; properties, none of the &#039;&#039;[[#Additional_properties|other]]&#039;&#039; ones. Note that the &#039;&#039;create&#039;&#039; structure does not include the &#039;&#039;id&#039;&#039; property. Use &#039;&#039; user_exporter::get_update_structure()&#039;&#039; if to update a user and thus receive the ID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not be flexible, and may not generate a valid structure for some webservice protocols.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]], or read on.&lt;br /&gt;
&lt;br /&gt;
=== Formatting parameters ===&lt;br /&gt;
&lt;br /&gt;
Formatting requires a context. If you&#039;ve defined a [[#Related_objects|related object]] with the name &#039;&#039;context&#039;&#039; we will automatically use this one. But there are cases where you need multiple contexts, or you need to rewrite the pluginfile URLs. When that is the case, you will define a method called &#039;get_format_parameters_for_&#039; followed with the name of the property. The latter also accepts the various options which both &#039;&#039;format_text&#039;&#039; and &#039;&#039;format_string&#039;&#039; support.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the formatting parameters for the description.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected function get_format_parameters_for_description() {&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;component&#039; =&amp;gt; &#039;core_user&#039;,&lt;br /&gt;
        &#039;filearea&#039; =&amp;gt; &#039;description&#039;,&lt;br /&gt;
        &#039;itemid&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a complete list of options, refer to the PHP documention of the method [[https://github.com/moodle/moodle/blob/master/lib/classes/external/exporter.php|core\external\exporter::get_format_parameters()]].&lt;br /&gt;
&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns a list of objects that are related.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_related() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;context&#039; =&amp;gt; &#039;context&#039;,                     // Must be an instance of context.&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,  // Must be an array of status instances.&lt;br /&gt;
        &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,              // Can be a mother instance, or null.&lt;br /&gt;
        &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,         // Can be null, or an array of brother instances.&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional relateds must still be provided but as &#039;&#039;null&#039;&#039;. Related objects are accessible from within the exporter using &#039;&#039;$this-&amp;gt;related[&#039;nameOfRelated&#039;]&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Creating a brand new exporter class, and leaving the other one unchanged.&lt;br /&gt;
#* Adding the related as &#039;&#039;optional&#039;&#039;. This is strongly discouraged as this is not what &#039;&#039;optional&#039;&#039; is for.&lt;br /&gt;
#* Adding the new related and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the formatting parameters for the description.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_format_parameters_for_description() {&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;component&#039; =&amp;gt; &#039;core_user&#039;,&lt;br /&gt;
            &#039;filearea&#039; =&amp;gt; &#039;description&#039;,&lt;br /&gt;
            &#039;itemid&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51621</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51621"/>
		<updated>2016-12-16T04:01:52Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Abiding to text formatting rules */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected when instantiating your exporter, and the ones it will export at the same time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These properties will allow us to generate a &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; structure for external functions, [[#In_external_functions|more on this later]]. Oh, and if you are using persistent you do not need to do this: [[#Exporters_and_persistent|check this out]].&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the field &#039;username&#039; to be passed in the key &#039;user&#039;. The &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039; structures include all of the &#039;&#039;standard&#039;&#039; properties, none of the &#039;&#039;[[#Additional_properties|other]]&#039;&#039; ones. Note that the &#039;&#039;create&#039;&#039; structure does not include the &#039;&#039;id&#039;&#039; property. Use &#039;&#039; user_exporter::get_update_structure()&#039;&#039; if to update a user and thus receive the ID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not be flexible, and may not generate a valid structure for some webservice protocols.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]], or read on.&lt;br /&gt;
&lt;br /&gt;
=== Formatting parameters ===&lt;br /&gt;
&lt;br /&gt;
Formatting requires a context. If you&#039;ve defined a [[#Related_objects|related object]] with the name &#039;&#039;context&#039;&#039; we will automatically use this one. But there are cases where you need multiple contexts, or you need to rewrite the pluginfile URLs. When that is the case, you will define a method called &#039;get_format_parameters_for_&#039; followed with the name of the property. The latter also accepts the various options which both &#039;&#039;format_text&#039;&#039; and &#039;&#039;format_string&#039;&#039; support.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the formatting parameters for the description.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected function get_format_parameters_for_description() {&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;component&#039; =&amp;gt; &#039;core_user&#039;,&lt;br /&gt;
        &#039;filearea&#039; =&amp;gt; &#039;description&#039;,&lt;br /&gt;
        &#039;itemid&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For a complete list of options, refer to the PHP documention of the method [[https://github.com/moodle/moodle/blob/master/lib/classes/external/exporter.php|core\external\exporter::get_format_parameters()]].&lt;br /&gt;
&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns a list of objects that are related.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_related() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;context&#039; =&amp;gt; &#039;context&#039;,                     // Must be an instance of context.&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,  // Must be an array of status instances.&lt;br /&gt;
        &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,              // Can be a mother instance, or null.&lt;br /&gt;
        &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,         // Can be null, or an array of brother instances.&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional relateds must still be provided but as &#039;&#039;null&#039;&#039;. Related objects are accessible from within the exporter using &#039;&#039;$this-&amp;gt;related[&#039;nameOfRelated&#039;]&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Creating a brand new exporter class, and leaving the other one unchanged.&lt;br /&gt;
#* Adding the related as &#039;&#039;optional&#039;&#039;. This is strongly discouraged as this is not what &#039;&#039;optional&#039;&#039; is for.&lt;br /&gt;
#* Adding the new related and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51620</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51620"/>
		<updated>2016-12-15T02:58:29Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Common pitfalls */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected when instantiating your exporter, and the ones it will export at the same time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These properties will allow us to generate a &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; structure for external functions, [[#In_external_functions|more on this later]]. Oh, and if you are using persistent you do not need to do this: [[#Exporters_and_persistent|check this out]].&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the field &#039;username&#039; to be passed in the key &#039;user&#039;. The &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039; structures include all of the &#039;&#039;standard&#039;&#039; properties, none of the &#039;&#039;[[#Additional_properties|other]]&#039;&#039; ones. Note that the &#039;&#039;create&#039;&#039; structure does not include the &#039;&#039;id&#039;&#039; property. Use &#039;&#039; user_exporter::get_update_structure()&#039;&#039; if to update a user and thus receive the ID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not be flexible, and may not generate a valid structure for some webservice protocols.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]].&lt;br /&gt;
&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns a list of objects that are related.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_related() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;context&#039; =&amp;gt; &#039;context&#039;,                     // Must be an instance of context.&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,  // Must be an array of status instances.&lt;br /&gt;
        &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,              // Can be a mother instance, or null.&lt;br /&gt;
        &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,         // Can be null, or an array of brother instances.&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional relateds must still be provided but as &#039;&#039;null&#039;&#039;. Related objects are accessible from within the exporter using &#039;&#039;$this-&amp;gt;related[&#039;nameOfRelated&#039;]&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Creating a brand new exporter class, and leaving the other one unchanged.&lt;br /&gt;
#* Adding the related as &#039;&#039;optional&#039;&#039;. This is strongly discouraged as this is not what &#039;&#039;optional&#039;&#039; is for.&lt;br /&gt;
#* Adding the new related and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51619</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51619"/>
		<updated>2016-12-15T02:55:45Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Related objects */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected when instantiating your exporter, and the ones it will export at the same time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These properties will allow us to generate a &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; structure for external functions, [[#In_external_functions|more on this later]]. Oh, and if you are using persistent you do not need to do this: [[#Exporters_and_persistent|check this out]].&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the field &#039;username&#039; to be passed in the key &#039;user&#039;. The &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039; structures include all of the &#039;&#039;standard&#039;&#039; properties, none of the &#039;&#039;[[#Additional_properties|other]]&#039;&#039; ones. Note that the &#039;&#039;create&#039;&#039; structure does not include the &#039;&#039;id&#039;&#039; property. Use &#039;&#039; user_exporter::get_update_structure()&#039;&#039; if to update a user and thus receive the ID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not be flexible, and may not generate a valid structure for some webservice protocols.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]].&lt;br /&gt;
&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns a list of objects that are related.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_related() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;context&#039; =&amp;gt; &#039;context&#039;,                     // Must be an instance of context.&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,  // Must be an array of status instances.&lt;br /&gt;
        &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,              // Can be a mother instance, or null.&lt;br /&gt;
        &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,         // Can be null, or an array of brother instances.&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional relateds must still be provided but as &#039;&#039;null&#039;&#039;. Related objects are accessible from within the exporter using &#039;&#039;$this-&amp;gt;related[&#039;nameOfRelated&#039;]&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Consider creating a new exporter.&lt;br /&gt;
#* Consider adding the related as &#039;&#039;optional&#039;&#039;. This is strongly discouraged.&lt;br /&gt;
#* Adding the new related and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51617</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51617"/>
		<updated>2016-12-15T02:54:27Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Related objects */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected when instantiating your exporter, and the ones it will export at the same time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These properties will allow us to generate a &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; structure for external functions, [[#In_external_functions|more on this later]]. Oh, and if you are using persistent you do not need to do this: [[#Exporters_and_persistent|check this out]].&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the field &#039;username&#039; to be passed in the key &#039;user&#039;. The &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039; structures include all of the &#039;&#039;standard&#039;&#039; properties, none of the &#039;&#039;[[#Additional_properties|other]]&#039;&#039; ones. Note that the &#039;&#039;create&#039;&#039; structure does not include the &#039;&#039;id&#039;&#039; property. Use &#039;&#039; user_exporter::get_update_structure()&#039;&#039; if to update a user and thus receive the ID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not be flexible, and may not generate a valid structure for some webservice protocols.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]].&lt;br /&gt;
&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,                     // Must be an instance of context.&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,  // Must be an array of status objects.&lt;br /&gt;
            &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,              // Can be an instance, or null.&lt;br /&gt;
            &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,         // Can be null, or an array of objects.&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional relateds must still be provided but as &#039;&#039;null&#039;&#039;. Related objects are accessible from within the exporter using &#039;&#039;$this-&amp;gt;related[&#039;nameOfRelated&#039;]&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Consider creating a new exporter.&lt;br /&gt;
#* Consider adding the related as &#039;&#039;optional&#039;&#039;. This is strongly discouraged.&lt;br /&gt;
#* Adding the new related and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51616</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51616"/>
		<updated>2016-12-15T02:50:11Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Additional properties */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected when instantiating your exporter, and the ones it will export at the same time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These properties will allow us to generate a &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; structure for external functions, [[#In_external_functions|more on this later]]. Oh, and if you are using persistent you do not need to do this: [[#Exporters_and_persistent|check this out]].&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the field &#039;username&#039; to be passed in the key &#039;user&#039;. The &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039; structures include all of the &#039;&#039;standard&#039;&#039; properties, none of the &#039;&#039;[[#Additional_properties|other]]&#039;&#039; ones. Note that the &#039;&#039;create&#039;&#039; structure does not include the &#039;&#039;id&#039;&#039; property. Use &#039;&#039; user_exporter::get_update_structure()&#039;&#039; if to update a user and thus receive the ID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not be flexible, and may not generate a valid structure for some webservice protocols.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]].&lt;br /&gt;
&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
            &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,&lt;br /&gt;
            &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional related must still be provided but as &#039;&#039;null&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Consider creating a new exporter.&lt;br /&gt;
#* Consider adding the related as &#039;&#039;optional&#039;&#039;. This is strongly discouraged.&lt;br /&gt;
#* Adding the new related and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51615</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51615"/>
		<updated>2016-12-15T02:47:58Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Abiding to text formatting rules */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected when instantiating your exporter, and the ones it will export at the same time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These properties will allow us to generate a &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; structure for external functions, [[#In_external_functions|more on this later]]. Oh, and if you are using persistent you do not need to do this: [[#Exporters_and_persistent|check this out]].&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the field &#039;username&#039; to be passed in the key &#039;user&#039;. The &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039; structures include all of the &#039;&#039;standard&#039;&#039; properties, none of the &#039;&#039;[[#Additional_properties|other]]&#039;&#039; ones. Note that the &#039;&#039;create&#039;&#039; structure does not include the &#039;&#039;id&#039;&#039; property. Use &#039;&#039; user_exporter::get_update_structure()&#039;&#039; if to update a user and thus receive the ID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not be flexible, and may not generate a valid structure for some webservice protocols.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]].&lt;br /&gt;
&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure you the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
            &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,&lt;br /&gt;
            &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional related must still be provided but as &#039;&#039;null&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Consider creating a new exporter.&lt;br /&gt;
#* Consider adding the related as &#039;&#039;optional&#039;&#039;. This is strongly discouraged.&lt;br /&gt;
#* Adding the new related and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51614</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51614"/>
		<updated>2016-12-15T02:47:34Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Abiding to text formatting rules */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected when instantiating your exporter, and the ones it will export at the same time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These properties will allow us to generate a &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; structure for external functions, [[#In_external_functions|more on this later]]. Oh, and if you are using persistent you do not need to do this: [[#Exporters_and_persistent|check this out]].&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the field &#039;username&#039; to be passed in the key &#039;user&#039;. The &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039; structures include all of the &#039;&#039;standard&#039;&#039; properties, none of the &#039;&#039;[[#Additional_properties|other]]&#039;&#039; ones. Note that the &#039;&#039;create&#039;&#039; structure does not include the &#039;&#039;id&#039;&#039; property. Use &#039;&#039; user_exporter::get_update_structure()&#039;&#039; if to update a user and thus receive the ID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not be flexible, and may not generate a valid structure for some webservice protocols.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]].&lt;br /&gt;
a&lt;br /&gt;
&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure you the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
            &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,&lt;br /&gt;
            &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional related must still be provided but as &#039;&#039;null&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Consider creating a new exporter.&lt;br /&gt;
#* Consider adding the related as &#039;&#039;optional&#039;&#039;. This is strongly discouraged.&lt;br /&gt;
#* Adding the new related and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51613</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51613"/>
		<updated>2016-12-15T02:44:35Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* In external functions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected when instantiating your exporter, and the ones it will export at the same time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These properties will allow us to generate a &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; structure for external functions, [[#In_external_functions|more on this later]]. Oh, and if you are using persistent you do not need to do this: [[#Exporters_and_persistent|check this out]].&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the field &#039;username&#039; to be passed in the key &#039;user&#039;. The &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039; structures include all of the &#039;&#039;standard&#039;&#039; properties, none of the &#039;&#039;[[#Additional_properties|other]]&#039;&#039; ones. Note that the &#039;&#039;create&#039;&#039; structure does not include the &#039;&#039;id&#039;&#039; property. Use &#039;&#039; user_exporter::get_update_structure()&#039;&#039; if to update a user and thus receive the ID.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not be flexible, and may not generate a valid structure for some webservice protocols.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties (not [[#Additional_properties|other properties]]) added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]].&lt;br /&gt;
a&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure you the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
            &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,&lt;br /&gt;
            &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional related must still be provided but as &#039;&#039;null&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Consider creating a new exporter.&lt;br /&gt;
#* Consider adding the related as &#039;&#039;optional&#039;&#039;. This is strongly discouraged.&lt;br /&gt;
#* Adding the new related and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51612</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51612"/>
		<updated>2016-12-15T02:42:44Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* In external functions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected when instantiating your exporter, and the ones it will export at the same time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These properties will allow us to generate a &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; structure for external functions, [[#In_external_functions|more on this later]]. Oh, and if you are using persistent you do not need to do this: [[#Exporters_and_persistent|check this out]].&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the field &#039;username&#039; to be passed in the key &#039;user&#039;. The &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039; structures include all of the &#039;&#039;standard&#039;&#039; properties, none of the &#039;&#039;[[#Additional_properties|other]]&#039;&#039; ones. Note that the &#039;&#039;create&#039;&#039; structure does not include the &#039;&#039;id&#039;&#039; property.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not flexible, and may not generate a valid structure for some protocols. You should also checkout &#039;&#039;get_update_structure()&#039;&#039; which is essentially the same except that it does requires an &#039;&#039;ID&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties (not [[#Additional_properties|other properties]]) added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]].&lt;br /&gt;
a&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure you the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
            &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,&lt;br /&gt;
            &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional related must still be provided but as &#039;&#039;null&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Consider creating a new exporter.&lt;br /&gt;
#* Consider adding the related as &#039;&#039;optional&#039;&#039;. This is strongly discouraged.&lt;br /&gt;
#* Adding the new related and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51611</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51611"/>
		<updated>2016-12-15T02:42:22Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* In external functions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected when instantiating your exporter, and the ones it will export at the same time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These properties will allow us to generate a &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; structure for external functions, [[#In_external_functions|more on this later]]. Oh, and if you are using persistent you do not need to do this: [[#Exporters_and_persistent|check this out]].&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the field &#039;username&#039; to be passed in the key &#039;user&#039;. The &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039; structures include all of the &#039;&#039;standard&#039;&#039; properties, none of the &#039;&#039;[[#Additional_properties|other]]&#039;&#039; ones. Note that the &#039;&#039;create&#039;&#039; structure does not include &#039;&#039;id&#039;&#039; property.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not flexible, and may not generate a valid structure for some protocols. You should also checkout &#039;&#039;get_update_structure()&#039;&#039; which is essentially the same except that it does requires an &#039;&#039;ID&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties (not [[#Additional_properties|other properties]]) added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]].&lt;br /&gt;
a&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure you the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
            &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,&lt;br /&gt;
            &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional related must still be provided but as &#039;&#039;null&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Consider creating a new exporter.&lt;br /&gt;
#* Consider adding the related as &#039;&#039;optional&#039;&#039;. This is strongly discouraged.&lt;br /&gt;
#* Adding the new related and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51610</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51610"/>
		<updated>2016-12-15T02:41:37Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* In external functions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected when instantiating your exporter, and the ones it will export at the same time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These properties will allow us to generate a &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; structure for external functions, [[#In_external_functions|more on this later]]. Oh, and if you are using persistent you do not need to do this: [[#Exporters_and_persistent|check this out]].&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the field &#039;username&#039; to be passed in the key &#039;user&#039;. The &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039; structure include all of the &#039;&#039;standard&#039;&#039; properties, none of the &#039;&#039;other&#039;&#039; ones. Note that the &#039;&#039;create&#039;&#039; structure does not include &#039;&#039;id&#039;&#039; property.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not flexible, and may not generate a valid structure for some protocols. You should also checkout &#039;&#039;get_update_structure()&#039;&#039; which is essentially the same except that it does requires an &#039;&#039;ID&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties (not [[#Additional_properties|other properties]]) added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]].&lt;br /&gt;
a&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure you the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
            &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,&lt;br /&gt;
            &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional related must still be provided but as &#039;&#039;null&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Consider creating a new exporter.&lt;br /&gt;
#* Consider adding the related as &#039;&#039;optional&#039;&#039;. This is strongly discouraged.&lt;br /&gt;
#* Adding the new related and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51609</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51609"/>
		<updated>2016-12-15T02:36:31Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected when instantiating your exporter, and the ones it will export at the same time.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These properties will allow us to generate a &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; structure for external functions, [[#In_external_functions|more on this later]]. Oh, and if you are using persistent you do not need to do this: [[#Exporters_and_persistent|check this out]].&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the fields &#039;id&#039; and &#039;username&#039; to be passed in the key &#039;user&#039;. The &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039; structure include all of the &#039;&#039;standard&#039;&#039; properties, none of the &#039;&#039;other&#039;&#039; ones. Also the &#039;&#039;update&#039;&#039; structure requires the &#039;&#039;id&#039;&#039; property.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not flexible, and may not generate a valid structure for some protocols. You should also checkout &#039;&#039;get_update_structure()&#039;&#039; which is essentially the same except that it does requires an &#039;&#039;ID&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties (not [[#Additional_properties|other properties]]) added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]].&lt;br /&gt;
a&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure you the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
            &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,&lt;br /&gt;
            &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional related must still be provided but as &#039;&#039;null&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Consider creating a new exporter.&lt;br /&gt;
#* Consider adding the related as &#039;&#039;optional&#039;&#039;. This is strongly discouraged.&lt;br /&gt;
#* Adding the new related and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51608</id>
		<title>Exporter</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Exporter&amp;diff=51608"/>
		<updated>2016-12-15T02:26:40Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: Created page with &amp;quot;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform a...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected for the incoming data, and for the exported data. This also means that to create (or update) you will require those properties.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the fields &#039;id&#039; and &#039;username&#039; to be passed in the key &#039;user&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not flexible, and may not generate a valid structure for some protocols. You should also checkout &#039;&#039;get_update_structure()&#039;&#039; which is essentially the same except that it does requires an &#039;&#039;ID&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties (not [[#Additional_properties|other properties]]) added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]].&lt;br /&gt;
a&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure you the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties. As much as possible you will want to avoid calling APIs, or querying the database upon export. Exporters can be used in loops and thus subsequent queries could dramatically affect performance, hence the need for related objects.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
            &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,&lt;br /&gt;
            &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional related must still be provided but as &#039;&#039;null&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
# Adding required relateds to an exporter once it has been released can cause &#039;&#039;coding_exceptions&#039;&#039;. Each instance will require the new related, which it will not have in 3rd party code/plugins. Preferably an exporter&#039;s relateds should never change, but if they must, consider this:&lt;br /&gt;
#* Consider creating a new exporter.&lt;br /&gt;
#* Consider adding the related as &#039;&#039;optional&#039;&#039;. This is strongly discouraged.&lt;br /&gt;
#* Adding the new related and advertising that all of its usage must be updated.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51606</id>
		<title>User:Frédéric Massart</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51606"/>
		<updated>2016-12-14T09:41:43Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected for the incoming data, and for the exported data. This also means that to create (or update) you will require those properties.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the fields &#039;id&#039; and &#039;username&#039; to be passed in the key &#039;user&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not flexible, and may not generate a valid structure for some protocols. You should also checkout &#039;&#039;get_update_structure()&#039;&#039; which is essentially the same except that it does requires an &#039;&#039;ID&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties (not [[#Additional_properties|other properties]]) added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]].&lt;br /&gt;
a&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure you the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
            &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,&lt;br /&gt;
            &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional related must still be provided but as &#039;&#039;null&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\external\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51605</id>
		<title>User:Frédéric Massart</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51605"/>
		<updated>2016-12-14T09:40:22Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected for the incoming data, and for the exported data. This also means that to create (or update) you will require those properties.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the fields &#039;id&#039; and &#039;username&#039; to be passed in the key &#039;user&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not flexible, and may not generate a valid structure for some protocols. You should also checkout &#039;&#039;get_update_structure()&#039;&#039; which is essentially the same except that it does requires an &#039;&#039;ID&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties (not [[#Additional_properties|other properties]]) added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]].&lt;br /&gt;
a&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: &#039;&#039;additional properties&#039;&#039; cannot override &#039;&#039;standard&#039;&#039; properties, so make sure you the names do not conflict.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
            &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,&lt;br /&gt;
            &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional related must still be provided but as &#039;&#039;null&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Exporters and persistent ==&lt;br /&gt;
&lt;br /&gt;
Great news, if you have a [[Persistent|persistent]] and you want to export them, all the work is pretty much done for you. All of the persistent&#039;s properties will automatically be added to your exporter if you extend the class &#039;&#039;core\\external\\persistent_exporter&#039;&#039;, and add the method &#039;&#039;define_class()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class status_exporter extends core\external\persistent {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the specific class the persistent should be an instance of.&lt;br /&gt;
     *&lt;br /&gt;
     * @return string&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_class() {&lt;br /&gt;
        return &#039;some\\namespace\\status&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
 And if you wanted to add more to it, there is always the [[#Additional_properties|other properties]].&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51604</id>
		<title>User:Frédéric Massart</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51604"/>
		<updated>2016-12-14T09:27:02Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Property attributes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected for the incoming data, and for the exported data. This also means that to create (or update) you will require those properties.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
: Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the fields &#039;id&#039; and &#039;username&#039; to be passed in the key &#039;user&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not flexible, and may not generate a valid structure for some protocols. You should also checkout &#039;&#039;get_update_structure()&#039;&#039; which is essentially the same except that it does requires an &#039;&#039;ID&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties (not [[#Additional_properties|other properties]]) added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]].&lt;br /&gt;
&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
            &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,&lt;br /&gt;
            &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional related must still be provided but as &#039;&#039;null&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51603</id>
		<title>User:Frédéric Massart</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51603"/>
		<updated>2016-12-14T09:26:38Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherits this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected for the incoming data, and for the exported data. This also means that to create (or update) you will require those properties.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
; Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and only with &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, I agree that this is not quite impressive. But wait until you read about [[#Abiding_to_text_formatting_rules|automatically formatting text]], and [[#In_external_functions|usage in external functions]].&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow. The following indicates that the external function requires the fields &#039;id&#039; and &#039;username&#039; to be passed in the key &#039;user&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameters validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included under another key, above we used &#039;&#039;user&#039;&#039;. Else this would not flexible, and may not generate a valid structure for some protocols. You should also checkout &#039;&#039;get_update_structure()&#039;&#039; which is essentially the same except that it does requires an &#039;&#039;ID&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen previously, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties (not [[#Additional_properties|other properties]]) added, let&#039;s see what happens when the user&#039;s description is in the Markdown format and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1   // Corresponds to FORMAT_HTML.&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]].&lt;br /&gt;
&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of &#039;&#039;other&#039;&#039; properties are to be seen as &#039;&#039;additional&#039;&#039; properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Other&#039;&#039; properties are only included in the &#039;&#039;read&#039;&#039; structure of an object (&#039;&#039;get_read_structure&#039;&#039; and &#039;&#039;read_properties_definition&#039;&#039;) as they are dynamically generated. They are not required, nor needed, to &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
If you have defined &#039;&#039;other&#039;&#039; properties, then you must also add the logic to export them. This is done by adding the method &#039;&#039;get_other_values(renderer_base $output)&#039;&#039; to your exporter. Here is an example in which we ignored the &#039;&#039;statuses&#039;&#039; as they are optional.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get the additional values to inject while exporting.&lt;br /&gt;
 *&lt;br /&gt;
 * @param renderer_base $output The renderer.&lt;br /&gt;
 * @return array Keys are the property names, values are their values.&lt;br /&gt;
 */&lt;br /&gt;
protected function get_other_values(renderer_base $output) {&lt;br /&gt;
    $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
There are times we need more information inside the exporter in order to export it. That may be as simple as the &#039;&#039;context&#039;&#039;, but it can also be other objects to be used when exporting &#039;&#039;other&#039;&#039; properties.&lt;br /&gt;
&lt;br /&gt;
Related objects need to be defined within the exporter, that is to ensure that they are always provided and are of the right type. In some occasions they can be marked as optional, generally when the related object does not exist. For instance, if we had an issue exporter, an optional related object could be the peer reviewer, as we don&#039;t always have a peer reviewer.&lt;br /&gt;
&lt;br /&gt;
Use the method &#039;&#039;protected static define_related()&#039;&#039; as follows. The keys are an arbitrary name for the related object, and the values are the fully qualified name of the class. The name of the class can be followed by &#039;&#039;[]&#039;&#039; and/or &#039;&#039;?&#039;&#039; to respectively indicate a list of these objects, and an optional related.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
            &#039;mother&#039; =&amp;gt; &#039;family\\mother?&#039;,&lt;br /&gt;
            &#039;brothers&#039; =&amp;gt; &#039;family\\brother[]?&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We give the related objects to the &#039;&#039;exporter&#039;&#039; when we instantiate it, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
$relateds = [&lt;br /&gt;
    &#039;context&#039; =&amp;gt; context_system::instance(),&lt;br /&gt;
    &#039;statuses&#039; =&amp;gt; [&lt;br /&gt;
        new some\namespace\status(&#039;Hello&#039;),&lt;br /&gt;
        new some\namespace\status(&#039;World!&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
    &#039;mother&#039; =&amp;gt; null,&lt;br /&gt;
    &#039;brothers&#039; =&amp;gt; null&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data, $relateds);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that optional related must still be provided but as &#039;&#039;null&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# Exporters must not extend other exporters. They would become too unpredictable.&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsible for using the structure to validate the data.&lt;br /&gt;
# When exporters are nested, the &#039;&#039;type&#039;&#039; to use should be &#039;&#039;other_exporter::read_properties_definition()&#039;&#039; and not &#039;&#039;other_exporter::get_read_structure()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example\external;&lt;br /&gt;
&lt;br /&gt;
use core\external\exporter;&lt;br /&gt;
use renderer_base;&lt;br /&gt;
use moodle_url;&lt;br /&gt;
&lt;br /&gt;
class user_exporter extends exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;description&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_other_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
                &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
                &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns a list of objects that are related.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_related() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;context&#039; =&amp;gt; &#039;context&#039;,&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; &#039;some\\namespace\\status[]&#039;,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get the additional values to inject while exporting.&lt;br /&gt;
     *&lt;br /&gt;
     * @param renderer_base $output The renderer.&lt;br /&gt;
     * @return array Keys are the property names, values are their values.&lt;br /&gt;
     */&lt;br /&gt;
    protected function get_other_values(renderer_base $output) {&lt;br /&gt;
        $statuses = [];&lt;br /&gt;
        foreach ($this-&amp;gt;related[&#039;statuses&#039;] as $status) {&lt;br /&gt;
            $exporter = new status_exporter($status);&lt;br /&gt;
            $statuses[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $profileurl = new moodle_url(&#039;/user/profile.php&#039;, [&#039;id&#039; =&amp;gt; $this-&amp;gt;data-&amp;gt;id]);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;profileurl&#039; =&amp;gt; $profileurl-&amp;gt;out(false),&lt;br /&gt;
            &#039;statuses&#039; =&amp;gt; $statuses&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51602</id>
		<title>User:Frédéric Massart</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51602"/>
		<updated>2016-12-14T08:15:50Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle exporters are classes which receive data and serialise it to a simple pre-defined structure. They ensure that the format of the data exported is uniform and easily maintainable. They are also used to generate the signature (parameters and return values) of [[External functions API|external functions]].&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
When dealing with external functions (Ajax, web services, ...) and rendering methods we often hit a situation where the same object is used, and exported, from multiple places. When that situation arises, the code that serialises our objects gets duplicated, or becomes inconsistent.&lt;br /&gt;
&lt;br /&gt;
We made the exporters to remedy to this situation. An exporter clearly defines what data it exports, and contains the logic which transform the incoming data into the exported data. As it knows everything, it can generate the structure required by external functions automatically.&lt;br /&gt;
&lt;br /&gt;
This means that not only developers have less code to maintain, but they also have a more robust structure which can easily evolve with their needs. If a new property needs to be exported, it is simply added to the exporter class, and automatically all usage of the exporter inherit this added property.&lt;br /&gt;
&lt;br /&gt;
== Defining properties ==&lt;br /&gt;
&lt;br /&gt;
The method &#039;&#039;define_properties()&#039;&#039; returns a list of the properties expected for the incoming data, and for the exported data. This also means that to create (or update) you will require those properties.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of properties.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Although this is not a &#039;&#039;rule&#039;&#039;, it is recommended that the &#039;&#039;standard properties&#039;&#039; (by opposition to [[#Additional_properties|additional properties]]) only use the &#039;&#039;type&#039;&#039; [[#Property_attributes|attribute]], and with &#039;&#039;PARAM_*&#039;&#039; constants only.&lt;br /&gt;
&lt;br /&gt;
== Using an exporter ==&lt;br /&gt;
&lt;br /&gt;
Once we&#039;ve got a [[#Minimalist|minimalist exporter]] set-up, here is how to use it.&lt;br /&gt;
&lt;br /&gt;
=== Exporting the data ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&#039;id&#039; =&amp;gt; 123, &#039;username&#039; =&amp;gt; &#039;batman&#039;];&lt;br /&gt;
&lt;br /&gt;
// The only time we can give data to our exporter is when instantiating it.&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
&lt;br /&gt;
// To export, we must pass the reference to a renderer.&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If we print the content of &#039;&#039;$data&#039;&#039;, we will obtain this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== In external functions ===&lt;br /&gt;
&lt;br /&gt;
Let&#039;s imagine that we have an external function &#039;&#039;get_users&#039;&#039; which returns a list of users. For now we only want to export the user ID and their user name, so we&#039;ll use our exporter. Let&#039;s ask our exporter to create the external structure for us:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users_returns() {&lt;br /&gt;
    return external_multiple_structure(&lt;br /&gt;
        user_exporter::get_read_structure()&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that this is done, we must use our exporter as shown above to export our users&#039; data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function get_users() {&lt;br /&gt;
    global $DB, $PAGE;&lt;br /&gt;
    $output = $output = $PAGE-&amp;gt;get_renderer(&#039;core&#039;);&lt;br /&gt;
    $users = $DB-&amp;gt;get_records(&#039;user&#039;, null, &#039;&#039;, &#039;id, username&#039;, 0, 10); // Get 10 users.&lt;br /&gt;
    $result = [];&lt;br /&gt;
    foreach ($users as $userdata) {&lt;br /&gt;
        $exporter = new user_exporter($userdata);&lt;br /&gt;
        $result[] = $exporter-&amp;gt;export($output);&lt;br /&gt;
    }&lt;br /&gt;
    return $result;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Lastly, if you had another external function to create users, you could use the exporter to get the structure of the incoming data. That helps if you want your external functions to require more information to create your &#039;&#039;users&#039;&#039; as your needs grow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public static function create_user_parameters() {&lt;br /&gt;
    return new external_function_parameters([&lt;br /&gt;
        &#039;user&#039; =&amp;gt; user_exporter::get_create_structure()&lt;br /&gt;
    ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
public static function create_user($user) {&lt;br /&gt;
    // Mandatory parameter validation.&lt;br /&gt;
    $params = self::validate_parameters(self::create_user_parameters(), [&#039;user&#039; =&amp;gt; $user]);&lt;br /&gt;
    $user = $params[&#039;user&#039;];&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Important note: when used in the &#039;&#039;parameters&#039;&#039;, the exporter&#039;s structure must always be included in an argument, above we used &#039;&#039;user&#039;&#039;. Else this would not flexible, and may not generate a valid structure for some protocols.&lt;br /&gt;
&lt;br /&gt;
== Abiding to text formatting rules ==&lt;br /&gt;
&lt;br /&gt;
If we had to pass the &#039;&#039;$OUTPUT&#039;&#039; during export as seen above, that is because we are handling the text formatting automatically for you. Remember the functions &#039;&#039;format_text()&#039;&#039; and &#039;&#039;format_string()&#039;&#039;? They are used to apply [[Filters|filters]] on the content typically submitted by users, but also to convert it from a few given formats to HTML.&lt;br /&gt;
&lt;br /&gt;
Upon export, the exporter looks at the &#039;&#039;type&#039;&#039; of all your properties. When it finds a property of type &#039;&#039;PARAM_TEXT&#039;&#039;, it will use &#039;&#039;format_string()&#039;&#039;. However, if it finds a property using &#039;&#039;PARAM_RAW&#039;&#039; and there is another property of the same name but ending with &#039;&#039;format&#039;&#039; it will use &#039;&#039;format_text()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;description&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
),&lt;br /&gt;
&#039;descriptionformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the two above properties (not &#039;&#039;other properties&#039;&#039;) added, let&#039;s see what happens when the user wrote their description on Markdown and we export it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) [&lt;br /&gt;
    &#039;id&#039; =&amp;gt; 123,&lt;br /&gt;
    &#039;username&#039; =&amp;gt; &#039;batman&#039;,&lt;br /&gt;
    &#039;description&#039; =&amp;gt; &#039;Hello __world__!&#039;,&lt;br /&gt;
    &#039;descriptionformat&#039; =&amp;gt; FORMAT_MARKDOWN&lt;br /&gt;
];&lt;br /&gt;
$ue = new user_exporter($data);&lt;br /&gt;
$data = $ue-&amp;gt;export($OUTPUT);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unsurprisingly, this is what comes out of it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [id] =&amp;gt; 123&lt;br /&gt;
    [username] =&amp;gt; batman&lt;br /&gt;
    [description] =&amp;gt; &amp;lt;p&amp;gt;Hello &amp;lt;strong&amp;gt;world&amp;lt;/strong&amp;gt;!&amp;lt;/p&amp;gt;&lt;br /&gt;
    [descriptionformat] =&amp;gt; 1&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Psst... If you&#039;re wondering where we get the &#039;&#039;context&#039;&#039; from, look at [[#Related_objects|related objects]].&lt;br /&gt;
&lt;br /&gt;
== Additional properties ==&lt;br /&gt;
&lt;br /&gt;
The list of _other_ properties are to be seen as _additional_ properties which do not need to be given to the exporter for it to export them. They are dynamically generated from the data provided (and the [[#Related objects|related objects]], more on that later). For example, if we wanted our exporter to provide the URL to a user&#039;s profile, we wouldn&#039;t need the developer to pass it beforehand, we could generate it based on the ID which was already provided.&lt;br /&gt;
&lt;br /&gt;
_Other_ properties are only included in the _read_ structure of an object as they are dynamically generated. They are not required, nor needed, to _create_ or _update_ an object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the list of additional properties.&lt;br /&gt;
&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_other_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;profileurl&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_URL&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;statuses&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; status_exporter::read_properties_definition(),&lt;br /&gt;
            &#039;multiple&#039; =&amp;gt; true,&lt;br /&gt;
            &#039;optional&#039; =&amp;gt; true&lt;br /&gt;
        ),&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The snippet above defines that we will always export a URL under the property &#039;&#039;profileurl&#039;&#039;, and that we will either export a list of &#039;&#039;status_exporters&#039;&#039;, or not. As you can see, the &#039;&#039;type&#039;&#039; can use the &#039;&#039;read&#039;&#039; properties of another exporter which allows exporters to be nested.&lt;br /&gt;
&lt;br /&gt;
=== Property attributes ===&lt;br /&gt;
&lt;br /&gt;
Each property is configured using the following attributes:&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must either be one of the many PARAM_* constants, or an array of properties.&lt;br /&gt;
;default&lt;br /&gt;
: The default value when the value was not provided. When not specified, a value is required.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED telling if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;optional&lt;br /&gt;
: Whether the property can be omitted completely. Defaults to false.&lt;br /&gt;
;multiple&lt;br /&gt;
; Whether there will be more one or more entries under this property. Defaults to false.&lt;br /&gt;
&lt;br /&gt;
== Related objects ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Common misconception ==&lt;br /&gt;
&lt;br /&gt;
# Exporters do not validate your data. They use the properties&#039; attributes to generate the structure required by the [[External functions API]], the latter is responsive for using the structure to validate the data being exported.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class user_exporter extends core\external\exporter {&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the list of properties.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;id&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;username&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Persistent&amp;diff=51601</id>
		<title>Persistent</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Persistent&amp;diff=51601"/>
		<updated>2016-12-14T06:45:03Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: /* Properties attributes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle persistents are equivalent to models (or active records). They represent an object stored in the database and provide the methods to create, read, update, and delete those objects. Additionally, the persistents validate their own data against automatic and custom validation rules. Persistents are made by extending the abstract class &#039;&#039;core\persistent&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Properties ==&lt;br /&gt;
&lt;br /&gt;
Each class must define the properties that it contains. This can be done by defining the method &#039;&#039;protected static define_properties()&#039;&#039;. It returns an array where the keys are the names of the properties (and implies the database column names), and their attributes. The &#039;&#039;type&#039;&#039; of each property must be specified using one of the &#039;&#039;PARAM_*&#039;&#039; constants. This type will be used to automatically validate the property&#039;s value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the definition of the properties of this model.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
        )&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Properties attributes ===&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must be one of the many &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
;default&lt;br /&gt;
: The default value to attach to the property when it hasn&#039;t been provided. Without a &#039;&#039;default&#039;&#039;, the property&#039;s value is required. Alternatively this can be a &#039;&#039;Closure&#039;&#039; returning the default value.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants &#039;&#039;NULL_ALLOWED&#039;&#039; or &#039;&#039;NULL_NOT_ALLOWED&#039;&#039; determining if the null value is accepted. This defaults to &#039;&#039;NULL_NOT_ALLOWED&#039;&#039;.&lt;br /&gt;
;message&lt;br /&gt;
: The default error message (as a &#039;&#039;lang_string&#039;&#039; instance) to return when the validation of this field fails.&lt;br /&gt;
;choices&lt;br /&gt;
: An array of values which the property must fall in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
    &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
    &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
),&lt;br /&gt;
&#039;location&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
    &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
    &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
    &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
        return get_config(&#039;default_location&#039;);&lt;br /&gt;
    },&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that you should always use a &#039;&#039;Closure&#039;&#039; for the &#039;&#039;default&#039;&#039; value when you cannot guarantee that it will not change since the start of the request. The list of properties and their attributes is cached, and so failure to use a &#039;&#039;Closure&#039;&#039; can result in using an outdated default value.&lt;br /&gt;
&lt;br /&gt;
=== Mandatory properties ===&lt;br /&gt;
&lt;br /&gt;
Four fields are always added to your persistent and should be reflected in your database table. You must not define those properties in &#039;&#039;define_properties()&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
;id (non-null integer)&lt;br /&gt;
: The primary key of the record.&lt;br /&gt;
;usermodified (non-null integer)&lt;br /&gt;
: The user who created/modified the object. It is automatically set.&lt;br /&gt;
;timecreated (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set.&lt;br /&gt;
;timemodified (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set, and defaults to 0.&lt;br /&gt;
&lt;br /&gt;
== Linking to a database table ==&lt;br /&gt;
&lt;br /&gt;
While the persistent class is helpful for database interactions, it does not automatically fetch the properties from the database, nor does it create the tables. You will need to create the table yourself, as well as pointing the persistent to it. This can be done by defining the class constant &#039;&#039;TABLE&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** Table name for the persistent. */&lt;br /&gt;
const TABLE = &#039;status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The table name must not contain the Moodle prefix. Also it is common practice to always refer to your table name using the constant.&lt;br /&gt;
&lt;br /&gt;
== Reading property values ==&lt;br /&gt;
&lt;br /&gt;
This can be done using the method &#039;&#039;get()&#039;&#039;, or the magic method &#039;&#039;get_&#039;&#039; followed by the property name. Alternatively you can also use &#039;&#039;to_record()&#039;&#039; which exports the whole object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Create a new object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using persistent::get().&lt;br /&gt;
$userid = $persistent-&amp;gt;get(&#039;userid&#039;);&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using the magic getter.&lt;br /&gt;
$userid = $persistent-&amp;gt;get_userid();&lt;br /&gt;
&lt;br /&gt;
// Get all the properties in an stdClass.&lt;br /&gt;
$data = $persistent-&amp;gt;to_record();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may override the magic getters to implement your own custom logic. For instance you could convert the data in another format automatically as a convenience for developers. However, use this sparingly as it may lead to confusion: what you get is not what is stored in the database. Note that you cannot guarantee that your getter will be used, remember that it is possible for developers to call the alternative &#039;&#039;get()&#039;&#039; which will not use your custom logic.&lt;br /&gt;
&lt;br /&gt;
It is, however, encouraged to add convenience methods such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns the user object of the author.&lt;br /&gt;
 *&lt;br /&gt;
 * @return stdClass&lt;br /&gt;
 */&lt;br /&gt;
public function get_author() {&lt;br /&gt;
    return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Assigning values to properties ==&lt;br /&gt;
&lt;br /&gt;
There are a few methods to do so.&lt;br /&gt;
&lt;br /&gt;
You use an object (&#039;&#039;stdClass&#039;&#039;) to assign a bunch of properties at once. Use it with the constructor, or the method &#039;&#039;from_record()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) array(&#039;userid&#039; =&amp;gt; 2, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instantiates a new object with value for some properties.&lt;br /&gt;
$persistent = new status(0, $data);&lt;br /&gt;
&lt;br /&gt;
// Is similar to.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
$persistent-&amp;gt;from_record($data);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or you can use the &#039;&#039;set()&#039;&#039; method on an instance.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set(&#039;message&#039;, &#039;Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally you can use the magic setters &#039;&#039;set_&#039;&#039; followed by the property name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Defining a custom setter ===&lt;br /&gt;
&lt;br /&gt;
Though you don&#039;t have to for the code to work, you can define your own &#039;&#039;setter&#039;&#039; methods which will override the magic setters. They are useful if you want to extract the data out of a more complex object prior to assigning it. Though note that those setters will then have to use the &#039;&#039;set()&#039;&#039; method to assign the values.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Convenience method to set the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
 */&lt;br /&gt;
public function set_userid($idorobject) {&lt;br /&gt;
    $userid = $idorobject;&lt;br /&gt;
    if (is_object($idorobject)) {&lt;br /&gt;
        $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
    }&lt;br /&gt;
    $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example we will accept an object or an ID, as a convenience for developers we will extract the ID value out of the object passed if any.&lt;br /&gt;
&lt;br /&gt;
Just like custom getters, you cannot guarantee that they will be used. Developers can directly call the &#039;&#039;set()&#039;&#039; method. Therefore you must not use a custom setter to reliably transform the data added to a property. For instance do not add a custom setter to remove HTML tags out of a text field, it may not always happen.&lt;br /&gt;
&lt;br /&gt;
You can obviously create your own setters which aren&#039;t based on any properties just as a convenience. For instance we could have created &#039;&#039;set_userid_from_user(object $user)&#039;&#039; which is more verbose and more predictable&lt;br /&gt;
&lt;br /&gt;
== Read, save and delete entries ==&lt;br /&gt;
&lt;br /&gt;
The methods to &#039;&#039;create&#039;&#039;, &#039;&#039;read&#039;&#039;, &#039;&#039;update&#039;&#039; and &#039;&#039;delete&#039;&#039; are eponymous. Your object will be validated before you &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; it. The &#039;&#039;update&#039;&#039;, &#039;&#039;delete&#039;&#039; and &#039;&#039;read&#039;&#039; methods require your object to contain its ID. And you also won&#039;t be allowed to &#039;&#039;create&#039;&#039; an entry which already had an ID defined.&lt;br /&gt;
&lt;br /&gt;
Here are some code examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Fetches an object from database based on its ID.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Create object in the database.&lt;br /&gt;
$data = new stdClass();&lt;br /&gt;
$data-&amp;gt;message = &#039;Hello new world&#039;;&lt;br /&gt;
$persistent = new persistent(0, $data);&lt;br /&gt;
$persistent-&amp;gt;create();&lt;br /&gt;
// $persistent-&amp;gt;get(&#039;id&#039;) will now return an id.&lt;br /&gt;
&lt;br /&gt;
// Load an object from the database, and update it.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
$persistent-&amp;gt;update();&lt;br /&gt;
&lt;br /&gt;
// Reset the instance to the values in the database.&lt;br /&gt;
$persistent-&amp;gt;read();&lt;br /&gt;
&lt;br /&gt;
// Permanently delete the object from the database.&lt;br /&gt;
$persistent-&amp;gt;delete();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fetching records ==&lt;br /&gt;
&lt;br /&gt;
Once you start using persistents you should never directly interact with the database outside of your class. The persistent class comes with a few handy methods allowing you to retrieve your objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Use the constructor to fetch one object from its ID.&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Get one record from a set of conditions.&lt;br /&gt;
$persistent = status::get_record([&#039;userid&#039; =&amp;gt; $userid, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;]);&lt;br /&gt;
&lt;br /&gt;
// Get multiple records from a set of conditions.&lt;br /&gt;
$persistents = status::get_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Count the records.&lt;br /&gt;
$count = status::count_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Check whether a record exists.&lt;br /&gt;
$exists = status::record_exists($id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure to also check their additional parameters and their variants (&#039;&#039;record_exists_select()&#039;&#039;, &#039;&#039;count_records_select&#039;&#039;, &#039;&#039;get_records_select&#039;&#039;, ...).&lt;br /&gt;
&lt;br /&gt;
=== Custom fetching ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s always a good idea to add more complex queries directly within your persistent. By convention you should always return an instance of your persistent and never an stdClass. Here we add a custom method which allows to directly fetch all records by username.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get all records from a user&#039;s username.&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $username The username.&lt;br /&gt;
 * @return status[]&lt;br /&gt;
 */&lt;br /&gt;
public static function get_records_by_username($username) {&lt;br /&gt;
    global $DB;&lt;br /&gt;
&lt;br /&gt;
    $sql = &#039;SELECT s.*&lt;br /&gt;
              FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
              JOIN {user} u&lt;br /&gt;
                ON u.id = s.userid&lt;br /&gt;
             WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
    $persistents = [];&lt;br /&gt;
&lt;br /&gt;
    $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
    foreach ($recordset as $record) {&lt;br /&gt;
        $persistents[] = new static(0, $record);&lt;br /&gt;
    }&lt;br /&gt;
    $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
    return $persistents;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to join persistents together and be able to extract their respective properties in a single database query, you should have a look at the following methods:&lt;br /&gt;
&lt;br /&gt;
;get_sql_fields(string $alias, string $prefix = null)&lt;br /&gt;
: Returns the SQL statement to include in the SELECT clause to prefix columns.&lt;br /&gt;
;extract_record(stdClass $row, string $prefix = null)&lt;br /&gt;
: Extracts all the properties from a row based on the given prefix.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Minimalist example.&lt;br /&gt;
$sqlfields = status::get_sql_fields(&#039;s&#039;, &#039;statprops&#039;);&lt;br /&gt;
$sql = &amp;quot;SELECT $sqlfields, u.username&lt;br /&gt;
          FROM {&amp;quot; . status::TABLE . &amp;quot;} s&lt;br /&gt;
          JOIN {user} ON s.userid = u.id&lt;br /&gt;
         WHERE s.id = 1&amp;quot;;&lt;br /&gt;
$row = $DB-&amp;gt;get_record($sql, []);&lt;br /&gt;
$statusdata = status::extract_record($row, &#039;statprops&#039;);&lt;br /&gt;
$persistent = new status(0, $statusdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Validating ==&lt;br /&gt;
&lt;br /&gt;
Basic validation of the properties values happens automatically based on their type (&#039;&#039;PARAM_*&#039;&#039; constant), however this is not always enough. In order to implement your own custom validation, simply define a &#039;&#039;protected&#039;&#039; method starting with &#039;&#039;validate_&#039;&#039; followed with the property name. This method will be called whenever the model needs to be validated and will receive the data to validate.&lt;br /&gt;
&lt;br /&gt;
A validation method must always return either &#039;&#039;true&#039;&#039; or an instance of &#039;&#039;lang_string&#039;&#039; which contains the error message to send to the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param int $value The value.&lt;br /&gt;
 * @return true|lang_string&lt;br /&gt;
 */&lt;br /&gt;
protected function validate_userid($value) {&lt;br /&gt;
    if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
        return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above example ensures that the &#039;&#039;userid&#039;&#039; property contains a valid user ID.&lt;br /&gt;
&lt;br /&gt;
Note that the basic validation is always performed first, and thus your custom validation method will not be called when the value did not pass the basic validation.&lt;br /&gt;
&lt;br /&gt;
=== Validation results ===&lt;br /&gt;
&lt;br /&gt;
The validation of the object automatically happens upon &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039;. If the validation did not pass, an &#039;&#039;invalid_persisten_exception&#039;&#039; will be raised. You can validate the object prior to saving the object and get the validation results if you need to.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// We can catch the invalid_persistent_exception.&lt;br /&gt;
try {&lt;br /&gt;
    $persistent = new status();&lt;br /&gt;
    $persistent-&amp;gt;create();&lt;br /&gt;
} catch (invalid_persistent_exception $e) {&lt;br /&gt;
    // Whoops, something wrong happened.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Check whether the object is valid.&lt;br /&gt;
$persistent-&amp;gt;is_valid();        // True or false.&lt;br /&gt;
&lt;br /&gt;
// Get the validation errors.&lt;br /&gt;
$persistent-&amp;gt;get_errors();      // Array where keys are properties and values are errors.&lt;br /&gt;
&lt;br /&gt;
// Validate the object.&lt;br /&gt;
$persistent-&amp;gt;validate();        // Returns true, or an array of errors.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hooks ==&lt;br /&gt;
&lt;br /&gt;
You can define the following methods to be notified prior to, or after, something happened:&lt;br /&gt;
&lt;br /&gt;
;protected before_validate()&lt;br /&gt;
: Do something before the object is validated.&lt;br /&gt;
;protected before_create()&lt;br /&gt;
: Do something before the object is inserted in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_create()&lt;br /&gt;
: Do something right after the object was added to the database.&lt;br /&gt;
;protected before_update()&lt;br /&gt;
: Do something before the object is updated in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_update(bool $result)&lt;br /&gt;
: Do something right after the object was updated in the database.&lt;br /&gt;
;protected before_delete()&lt;br /&gt;
: Do something right before the object is deleted from the database.&lt;br /&gt;
;protected after_delete(bool $result)&lt;br /&gt;
: Do something right after the object was deleted from the database.&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# You must create the database table yourself, using the [[XMLDB editor]] and an upgrade script.&lt;br /&gt;
# You must include the [[#Mandatory_properties|mandatory fields]] in your table schema.&lt;br /&gt;
# You must never extend another persistent as this leads to unpredictable errors whenever the parent changes (new properties missing in your table, changes in validation, custom getters and setters, etc...).&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
class status extends core\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example;&lt;br /&gt;
&lt;br /&gt;
use core\persistent;&lt;br /&gt;
use core_user;&lt;br /&gt;
use lang_string;&lt;br /&gt;
&lt;br /&gt;
class status extends persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
                &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
                &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;location&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
                &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
                &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
                &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
                    return get_config(&#039;default_location&#039;);&lt;br /&gt;
                },&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the user object of the author.&lt;br /&gt;
     *&lt;br /&gt;
     * @return stdClass&lt;br /&gt;
     */&lt;br /&gt;
    public function get_author() {&lt;br /&gt;
        return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Convenience method to set the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
     */&lt;br /&gt;
    public function set_userid($idorobject) {&lt;br /&gt;
        $userid = $idorobject;&lt;br /&gt;
        if (is_object($idorobject)) {&lt;br /&gt;
            $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Validate the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param int $value The value.&lt;br /&gt;
     * @return true|lang_string&lt;br /&gt;
     */&lt;br /&gt;
    protected function validate_userid($value) {&lt;br /&gt;
        if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
            return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get all records from a user&#039;s username.&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $username The username.&lt;br /&gt;
     * @return status[]&lt;br /&gt;
     */&lt;br /&gt;
    public static function get_records_by_username($username) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $sql = &#039;SELECT s.*&lt;br /&gt;
                  FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
                  JOIN {user} u&lt;br /&gt;
                    ON u.id = s.userid&lt;br /&gt;
                 WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
        $persistents = [];&lt;br /&gt;
&lt;br /&gt;
        $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
        foreach ($recordset as $record) {&lt;br /&gt;
            $persistents[] = new static(0, $record);&lt;br /&gt;
        }&lt;br /&gt;
        $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
        return $persistents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Persistent&amp;diff=51596</id>
		<title>Persistent</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Persistent&amp;diff=51596"/>
		<updated>2016-12-14T04:07:34Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle persistents are equivalent to models (or active records). They represent an object stored in the database and provide the methods to create, read, update, and delete those objects. Additionally, the persistents validate their own data against automatic and custom validation rules. Persistents are made by extending the abstract class &#039;&#039;core\persistent&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Properties ==&lt;br /&gt;
&lt;br /&gt;
Each class must define the properties that it contains. This can be done by defining the method &#039;&#039;protected static define_properties()&#039;&#039;. It returns an array where the keys are the names of the properties (and implies the database column names), and their attributes. The &#039;&#039;type&#039;&#039; of each property must be specified using one of the &#039;&#039;PARAM_*&#039;&#039; constants. This type will be used to automatically validate the property&#039;s value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the definition of the properties of this model.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
        )&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Properties attributes ===&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must be one of the many &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
;default&lt;br /&gt;
: The default value to attach to the property when it hasn&#039;t been provided. Alternatively this can be a &#039;&#039;Closure&#039;&#039; returning the default value.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants &#039;&#039;NULL_ALLOWED&#039;&#039; or &#039;&#039;NULL_NOT_ALLOWED&#039;&#039; determining if the null value is accepted. This defaults to &#039;&#039;NULL_NOT_ALLOWED&#039;&#039;.&lt;br /&gt;
;message&lt;br /&gt;
: The default error message (as a &#039;&#039;lang_string&#039;&#039; instance) to return when the validation of this field fails.&lt;br /&gt;
;choices&lt;br /&gt;
: An array of values which the property must fall in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
    &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
    &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
),&lt;br /&gt;
&#039;location&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
    &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
    &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
    &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
        return get_config(&#039;default_location&#039;);&lt;br /&gt;
    },&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that you should always use a &#039;&#039;Closure&#039;&#039; for the &#039;&#039;default&#039;&#039; value when you cannot guarantee that it will not change since the start of the request. The list of properties and their attributes is cached, and so failure to use a &#039;&#039;Closure&#039;&#039; can result in using an outdated default value.&lt;br /&gt;
&lt;br /&gt;
=== Mandatory properties ===&lt;br /&gt;
&lt;br /&gt;
Four fields are always added to your persistent and should be reflected in your database table. You must not define those properties in &#039;&#039;define_properties()&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
;id (non-null integer)&lt;br /&gt;
: The primary key of the record.&lt;br /&gt;
;usermodified (non-null integer)&lt;br /&gt;
: The user who created/modified the object. It is automatically set.&lt;br /&gt;
;timecreated (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set.&lt;br /&gt;
;timemodified (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set, and defaults to 0.&lt;br /&gt;
&lt;br /&gt;
== Linking to a database table ==&lt;br /&gt;
&lt;br /&gt;
While the persistent class is helpful for database interactions, it does not automatically fetch the properties from the database, nor does it create the tables. You will need to create the table yourself, as well as pointing the persistent to it. This can be done by defining the constant &#039;&#039;TABLE&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** Table name for the persistent. */&lt;br /&gt;
const TABLE = &#039;status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The table name must not contain the Moodle prefix. Also it is common practice to always refer to your table name using the constant.&lt;br /&gt;
&lt;br /&gt;
== Reading property values ==&lt;br /&gt;
&lt;br /&gt;
This can be done using the method &#039;&#039;get()&#039;&#039;, or the magic method &#039;&#039;get_&#039;&#039; followed by the property name. Alternatively you can also use &#039;&#039;to_record()&#039;&#039; which exports the whole object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Create a new object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using persistent::get().&lt;br /&gt;
$userid = $persistent-&amp;gt;get(&#039;userid&#039;);&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using the magic getter.&lt;br /&gt;
$userid = $persistent-&amp;gt;get_userid();&lt;br /&gt;
&lt;br /&gt;
// Get all the properties in an stdClass.&lt;br /&gt;
$data = $persistent-&amp;gt;to_record();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may override the magic getters to implements your own custom logic. For instance you could convert the data in another format automatically as a convenience for developers. However, use this sparingly as it may lead to confusion: what you get is not what is stored in the database. Note that you cannot guarantee that your getter will be used, remember that it is possible for developers to call the alternative &#039;&#039;get()&#039;&#039; which will not use your custom logic.&lt;br /&gt;
&lt;br /&gt;
It is, however, encouraged to add convenience methods such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns the user object of the author.&lt;br /&gt;
 *&lt;br /&gt;
 * @return stdClass&lt;br /&gt;
 */&lt;br /&gt;
public function get_author() {&lt;br /&gt;
    return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Assigning values to properties ==&lt;br /&gt;
&lt;br /&gt;
There are a few methods to do so.&lt;br /&gt;
&lt;br /&gt;
You use an object (&#039;&#039;stdClass&#039;&#039;) to assign a bunch of properties at once. Use it with the constructor, or the method &#039;&#039;from_record()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) array(&#039;userid&#039; =&amp;gt; 2, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instantiates a new object with value for some properties.&lt;br /&gt;
$persistent = new status(0, $data);&lt;br /&gt;
&lt;br /&gt;
// Is similar to.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
$persistent-&amp;gt;from_record($data);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or you can use the &#039;&#039;set()&#039;&#039; method on an instance.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set(&#039;message&#039;, Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally you can use the magic setters &#039;&#039;set_&#039;&#039; followed by the property name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Defining a custom setter ===&lt;br /&gt;
&lt;br /&gt;
Though you don&#039;t have to for the code to work, you can define your own &#039;&#039;setter&#039;&#039; methods which will override the magic setters. They are useful if you want to extract the data out of a more complex object prior to assigning it. Though note that those setters will then have to use the &#039;&#039;set()&#039;&#039; method to assign the values.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Convenience method to set the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
 */&lt;br /&gt;
public function set_userid($idorobject) {&lt;br /&gt;
    $userid = $idorobject;&lt;br /&gt;
    if (is_object($idorobject)) {&lt;br /&gt;
        $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
    }&lt;br /&gt;
    $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example we will accept an object or an ID, as a convenience for developers we will extract the ID value out of the object passed if any.&lt;br /&gt;
&lt;br /&gt;
Just like custom getters, you cannot guarantee that they will be used. Developers can directly call the &#039;&#039;set()&#039;&#039; method. Therefore you must not use a custom setter to reliably transform the data added to a property. For instance do not add a custom setter to remove HTML tags out of a text field, it may not always happen.&lt;br /&gt;
&lt;br /&gt;
You can obviously create your own setters which aren&#039;t based on any properties just as a convenience. For instance we could have created &#039;&#039;set_userid_from_user(object $user)&#039;&#039; which is more verbose and more predictable&lt;br /&gt;
&lt;br /&gt;
== Read, save and delete entries ==&lt;br /&gt;
&lt;br /&gt;
The methods to &#039;&#039;create&#039;&#039;, &#039;&#039;read&#039;&#039;, &#039;&#039;update&#039;&#039; and &#039;&#039;delete&#039;&#039; are eponymous. Your object will be validated before you &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; it. The &#039;&#039;update&#039;&#039;, &#039;&#039;delete&#039;&#039; and &#039;&#039;read&#039;&#039; methods require your object to contain its ID. And you also won&#039;t be allowed to &#039;&#039;create&#039;&#039; an entry which already had an ID defined.&lt;br /&gt;
&lt;br /&gt;
Here are some code examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Fetches an object from database based on its ID.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Create previously instantiated object in the database.&lt;br /&gt;
$persistent-&amp;gt;create();&lt;br /&gt;
&lt;br /&gt;
// Load an object from the database, and update it.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
$persistent-&amp;gt;update();&lt;br /&gt;
&lt;br /&gt;
// Reset the instance to the values in the database.&lt;br /&gt;
$persistent-&amp;gt;read();&lt;br /&gt;
&lt;br /&gt;
// Permanently delete the object from the database.&lt;br /&gt;
$persistent-&amp;gt;delete();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fetching records ==&lt;br /&gt;
&lt;br /&gt;
Once you start using persistents you should never directly interact with the database outside of your class. The persistent class comes with a few handy methods allowing you to retrieve your objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Use the constructor to fetch one object from its ID.&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Get one record from a set of conditions.&lt;br /&gt;
$persistent = status::get_record([&#039;userid&#039; =&amp;gt; $userid, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;]);&lt;br /&gt;
&lt;br /&gt;
// Get multiple records from a set of conditions.&lt;br /&gt;
$persistents = status::get_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Count the records.&lt;br /&gt;
$count = status::count_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Check whether a record exists.&lt;br /&gt;
$exists = status::record_exists($id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure to also check their additional parameters and their variants (&#039;&#039;record_exists_select()&#039;&#039;, &#039;&#039;count_records_select&#039;&#039;, &#039;&#039;get_records_select&#039;&#039;, ...).&lt;br /&gt;
&lt;br /&gt;
=== Custom fetching ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s always a good idea to add more complex queries directly within your persistent. By convention you should always return an instance of your persistent and never an stdClass. Here we add a custom method which allows to directly fetch all records by username.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get all records from a user&#039;s username.&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $username The username.&lt;br /&gt;
 * @return status[]&lt;br /&gt;
 */&lt;br /&gt;
public static function get_records_by_username($username) {&lt;br /&gt;
    global $DB;&lt;br /&gt;
&lt;br /&gt;
    $sql = &#039;SELECT s.*&lt;br /&gt;
              FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
              JOIN {user} u&lt;br /&gt;
                ON u.id = s.userid&lt;br /&gt;
             WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
    $persistents = [];&lt;br /&gt;
&lt;br /&gt;
    $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
    foreach ($recordset as $record) {&lt;br /&gt;
        $persistents[] = new static(0, $record);&lt;br /&gt;
    }&lt;br /&gt;
    $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
    return $persistents;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to join persistents together and be able to extract their respective properties in a single database query, you should have a look at the following methods:&lt;br /&gt;
&lt;br /&gt;
;get_sql_fields(string $alias, string $prefix = null)&lt;br /&gt;
: Returns the SQL statement to include in the SELECT clause to prefix columns.&lt;br /&gt;
;extract_record(stdClass $row, string $prefix = null)&lt;br /&gt;
: Extracts all the properties from a row based on the given prefix.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Minimalist example.&lt;br /&gt;
$sqlfields = status::get_sql_fields(&#039;s&#039;, &#039;statprops&#039;);&lt;br /&gt;
$sql = &amp;quot;SELECT $sqlfields, u.username&lt;br /&gt;
          FROM {&amp;quot; . status::TABLE . &amp;quot;} s&lt;br /&gt;
          JOIN {user} ON s.userid = u.id&lt;br /&gt;
         WHERE s.id = 1&amp;quot;;&lt;br /&gt;
$row = $DB-&amp;gt;get_record($sql, []);&lt;br /&gt;
$statusdata = status::extract_record($row, &#039;statprops&#039;);&lt;br /&gt;
$persistent = new status(0, $statusdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Validating ==&lt;br /&gt;
&lt;br /&gt;
Basic validation of the properties values happens automatically based on their type (&#039;&#039;PARAM_*&#039;&#039; constant), however this is not always enough. In order to implement your own custom validation, simply define a &#039;&#039;protected&#039;&#039; method starting with &#039;&#039;validate_&#039;&#039; followed with the property name. This method will be called whenever the model needs to be validated and will receive the data to validate.&lt;br /&gt;
&lt;br /&gt;
A validation method must always return either &#039;&#039;true&#039;&#039; or an instance of &#039;&#039;lang_string&#039;&#039; which contains the error message to send to the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param int $value The value.&lt;br /&gt;
 * @return true|lang_string&lt;br /&gt;
 */&lt;br /&gt;
protected function validate_userid($value) {&lt;br /&gt;
    if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
        return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above example ensures that the &#039;&#039;userid&#039;&#039; property contains a valid user ID.&lt;br /&gt;
&lt;br /&gt;
Note that the basic validation is always performed first, and thus your custom validation method will not be called when the value did not pass the basic validation.&lt;br /&gt;
&lt;br /&gt;
=== Validation results ===&lt;br /&gt;
&lt;br /&gt;
The validation of the object automatically happens upon &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039;. If the validation did not pass, an &#039;&#039;invalid_persisten_exception&#039;&#039; will be raised. You can validate the object prior to saving the object and get the validation results if you need to.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// We can catch the invalid_persistent_exception.&lt;br /&gt;
try {&lt;br /&gt;
    $persistent = new status();&lt;br /&gt;
    $persistent-&amp;gt;create();&lt;br /&gt;
} catch (invalid_persistent_exception $e) {&lt;br /&gt;
    // Whoops, something wrong happened.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Check whether the object is valid.&lt;br /&gt;
$persistent-&amp;gt;is_valid();        // True or false.&lt;br /&gt;
&lt;br /&gt;
// Get the validation errors.&lt;br /&gt;
$persistent-&amp;gt;get_errors();      // Array where keys are properties and values are errors.&lt;br /&gt;
&lt;br /&gt;
// Validate the object.&lt;br /&gt;
$persistent-&amp;gt;validate();        // Returns true, or an array of errors.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hooks ==&lt;br /&gt;
&lt;br /&gt;
You can define the following methods to be notified prior to, or after, something happened:&lt;br /&gt;
&lt;br /&gt;
;protected before_validate()&lt;br /&gt;
: Do something before the object is validated.&lt;br /&gt;
;protected before_create()&lt;br /&gt;
: Do something before the object is inserted in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_create()&lt;br /&gt;
: Do something right after the object was added to the database.&lt;br /&gt;
;protected before_update()&lt;br /&gt;
: Do something before the object is updated in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_update(bool $result)&lt;br /&gt;
: Do something right after the object was updated in the database.&lt;br /&gt;
;protected before_delete()&lt;br /&gt;
: Do something right before the object is deleted from the database.&lt;br /&gt;
;protected after_delete(bool $result)&lt;br /&gt;
: Do something right after the object was deleted from the database.&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# You must create the database table yourself, using the [[XMLDB editor]] and an upgrade script.&lt;br /&gt;
# You must include the [[#Mandatory_properties|mandatory fields]] in your table schema.&lt;br /&gt;
# You must never extend another persistent as this leads to unpredictable errors whenever the parent changes (new properties missing in your table, changes in validation, custom getters and setters, etc...).&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
class status extends core\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example;&lt;br /&gt;
&lt;br /&gt;
use core\persistent;&lt;br /&gt;
use core_user;&lt;br /&gt;
use lang_string;&lt;br /&gt;
&lt;br /&gt;
class status extends persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
                &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
                &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;location&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
                &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
                &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
                &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
                    return get_config(&#039;default_location&#039;);&lt;br /&gt;
                },&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the user object of the author.&lt;br /&gt;
     *&lt;br /&gt;
     * @return stdClass&lt;br /&gt;
     */&lt;br /&gt;
    public function get_author() {&lt;br /&gt;
        return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Convenience method to set the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
     */&lt;br /&gt;
    public function set_userid($idorobject) {&lt;br /&gt;
        $userid = $idorobject;&lt;br /&gt;
        if (is_object($idorobject)) {&lt;br /&gt;
            $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Validate the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param int $value The value.&lt;br /&gt;
     * @return true|lang_string&lt;br /&gt;
     */&lt;br /&gt;
    protected function validate_userid($value) {&lt;br /&gt;
        if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
            return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get all records from a user&#039;s username.&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $username The username.&lt;br /&gt;
     * @return status[]&lt;br /&gt;
     */&lt;br /&gt;
    public static function get_records_by_username($username) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $sql = &#039;SELECT s.*&lt;br /&gt;
                  FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
                  JOIN {user} u&lt;br /&gt;
                    ON u.id = s.userid&lt;br /&gt;
                 WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
        $persistents = [];&lt;br /&gt;
&lt;br /&gt;
        $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
        foreach ($recordset as $record) {&lt;br /&gt;
            $persistents[] = new static(0, $record);&lt;br /&gt;
        }&lt;br /&gt;
        $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
        return $persistents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Persistent&amp;diff=51595</id>
		<title>Persistent</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Persistent&amp;diff=51595"/>
		<updated>2016-12-14T04:07:15Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: Created page with &amp;quot;{{Moodle 3.3}}Moodle persistents are equivalent to models (or active records). They represent an object stored in the database and provide the methods to create, read, update,...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 3.3}}Moodle persistents are equivalent to models (or active records). They represent an object stored in the database and provide the methods to create, read, update, and delete those objects. Additionally, the persistents validate their own data against automatic and custom validation rules. Persistents are made by extending the abstract class &#039;&#039;core\persistent&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Properties ==&lt;br /&gt;
&lt;br /&gt;
Each class must define the properties that it contains. This can be done by defining the method &#039;&#039;protected static define_properties()&#039;&#039;. It returns an array where the keys are the names of the fields (and database columns), and their details. The &#039;&#039;type&#039;&#039; of each field must be specified using one of the &#039;&#039;PARAM_*&#039;&#039; constants. This type will be used to automatically validate the property&#039;s value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the definition of the properties of this model.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
        )&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Properties attributes ===&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must be one of the many &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
;default&lt;br /&gt;
: The default value to attach to the property when it hasn&#039;t been provided. Alternatively this can be a &#039;&#039;Closure&#039;&#039; returning the default value.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants &#039;&#039;NULL_ALLOWED&#039;&#039; or &#039;&#039;NULL_NOT_ALLOWED&#039;&#039; determining if the null value is accepted. This defaults to &#039;&#039;NULL_NOT_ALLOWED&#039;&#039;.&lt;br /&gt;
;message&lt;br /&gt;
: The default error message (as a &#039;&#039;lang_string&#039;&#039; instance) to return when the validation of this field fails.&lt;br /&gt;
;choices&lt;br /&gt;
: An array of values which the property must fall in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
    &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
    &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
),&lt;br /&gt;
&#039;location&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
    &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
    &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
    &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
        return get_config(&#039;default_location&#039;);&lt;br /&gt;
    },&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that you should always use a &#039;&#039;Closure&#039;&#039; for the &#039;&#039;default&#039;&#039; value when you cannot guarantee that it will not change since the start of the request. The list of properties and their attributes is cached, and so failure to use a &#039;&#039;Closure&#039;&#039; can result in using an outdated default value.&lt;br /&gt;
&lt;br /&gt;
=== Mandatory properties ===&lt;br /&gt;
&lt;br /&gt;
Four fields are always added to your persistent and should be reflected in your database table. You must not define those properties in &#039;&#039;define_properties()&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
;id (non-null integer)&lt;br /&gt;
: The primary key of the record.&lt;br /&gt;
;usermodified (non-null integer)&lt;br /&gt;
: The user who created/modified the object. It is automatically set.&lt;br /&gt;
;timecreated (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set.&lt;br /&gt;
;timemodified (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set, and defaults to 0.&lt;br /&gt;
&lt;br /&gt;
== Linking to a database table ==&lt;br /&gt;
&lt;br /&gt;
While the persistent class is helpful for database interactions, it does not automatically fetch the properties from the database, nor does it create the tables. You will need to create the table yourself, as well as pointing the persistent to it. This can be done by defining the constant &#039;&#039;TABLE&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** Table name for the persistent. */&lt;br /&gt;
const TABLE = &#039;status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The table name must not contain the Moodle prefix. Also it is common practice to always refer to your table name using the constant.&lt;br /&gt;
&lt;br /&gt;
== Reading property values ==&lt;br /&gt;
&lt;br /&gt;
This can be done using the method &#039;&#039;get()&#039;&#039;, or the magic method &#039;&#039;get_&#039;&#039; followed by the property name. Alternatively you can also use &#039;&#039;to_record()&#039;&#039; which exports the whole object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Create a new object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using persistent::get().&lt;br /&gt;
$userid = $persistent-&amp;gt;get(&#039;userid&#039;);&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using the magic getter.&lt;br /&gt;
$userid = $persistent-&amp;gt;get_userid();&lt;br /&gt;
&lt;br /&gt;
// Get all the properties in an stdClass.&lt;br /&gt;
$data = $persistent-&amp;gt;to_record();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may override the magic getters to implements your own custom logic. For instance you could convert the data in another format automatically as a convenience for developers. However, use this sparingly as it may lead to confusion: what you get is not what is stored in the database. Note that you cannot guarantee that your getter will be used, remember that it is possible for developers to call the alternative &#039;&#039;get()&#039;&#039; which will not use your custom logic.&lt;br /&gt;
&lt;br /&gt;
It is, however, encouraged to add convenience methods such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns the user object of the author.&lt;br /&gt;
 *&lt;br /&gt;
 * @return stdClass&lt;br /&gt;
 */&lt;br /&gt;
public function get_author() {&lt;br /&gt;
    return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Assigning values to properties ==&lt;br /&gt;
&lt;br /&gt;
There are a few methods to do so.&lt;br /&gt;
&lt;br /&gt;
You use an object (&#039;&#039;stdClass&#039;&#039;) to assign a bunch of properties at once. Use it with the constructor, or the method &#039;&#039;from_record()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) array(&#039;userid&#039; =&amp;gt; 2, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instantiates a new object with value for some properties.&lt;br /&gt;
$persistent = new status(0, $data);&lt;br /&gt;
&lt;br /&gt;
// Is similar to.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
$persistent-&amp;gt;from_record($data);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or you can use the &#039;&#039;set()&#039;&#039; method on an instance.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set(&#039;message&#039;, Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally you can use the magic setters &#039;&#039;set_&#039;&#039; followed by the property name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Defining a custom setter ===&lt;br /&gt;
&lt;br /&gt;
Though you don&#039;t have to for the code to work, you can define your own &#039;&#039;setter&#039;&#039; methods which will override the magic setters. They are useful if you want to extract the data out of a more complex object prior to assigning it. Though note that those setters will then have to use the &#039;&#039;set()&#039;&#039; method to assign the values.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Convenience method to set the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
 */&lt;br /&gt;
public function set_userid($idorobject) {&lt;br /&gt;
    $userid = $idorobject;&lt;br /&gt;
    if (is_object($idorobject)) {&lt;br /&gt;
        $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
    }&lt;br /&gt;
    $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example we will accept an object or an ID, as a convenience for developers we will extract the ID value out of the object passed if any.&lt;br /&gt;
&lt;br /&gt;
Just like custom getters, you cannot guarantee that they will be used. Developers can directly call the &#039;&#039;set()&#039;&#039; method. Therefore you must not use a custom setter to reliably transform the data added to a property. For instance do not add a custom setter to remove HTML tags out of a text field, it may not always happen.&lt;br /&gt;
&lt;br /&gt;
You can obviously create your own setters which aren&#039;t based on any properties just as a convenience. For instance we could have created &#039;&#039;set_userid_from_user(object $user)&#039;&#039; which is more verbose and more predictable&lt;br /&gt;
&lt;br /&gt;
== Read, save and delete entries ==&lt;br /&gt;
&lt;br /&gt;
The methods to &#039;&#039;create&#039;&#039;, &#039;&#039;read&#039;&#039;, &#039;&#039;update&#039;&#039; and &#039;&#039;delete&#039;&#039; are eponymous. Your object will be validated before you &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; it. The &#039;&#039;update&#039;&#039;, &#039;&#039;delete&#039;&#039; and &#039;&#039;read&#039;&#039; methods require your object to contain its ID. And you also won&#039;t be allowed to &#039;&#039;create&#039;&#039; an entry which already had an ID defined.&lt;br /&gt;
&lt;br /&gt;
Here are some code examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Fetches an object from database based on its ID.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Create previously instantiated object in the database.&lt;br /&gt;
$persistent-&amp;gt;create();&lt;br /&gt;
&lt;br /&gt;
// Load an object from the database, and update it.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
$persistent-&amp;gt;update();&lt;br /&gt;
&lt;br /&gt;
// Reset the instance to the values in the database.&lt;br /&gt;
$persistent-&amp;gt;read();&lt;br /&gt;
&lt;br /&gt;
// Permanently delete the object from the database.&lt;br /&gt;
$persistent-&amp;gt;delete();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Fetching records ==&lt;br /&gt;
&lt;br /&gt;
Once you start using persistents you should never directly interact with the database outside of your class. The persistent class comes with a few handy methods allowing you to retrieve your objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Use the constructor to fetch one object from its ID.&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Get one record from a set of conditions.&lt;br /&gt;
$persistent = status::get_record([&#039;userid&#039; =&amp;gt; $userid, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;]);&lt;br /&gt;
&lt;br /&gt;
// Get multiple records from a set of conditions.&lt;br /&gt;
$persistents = status::get_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Count the records.&lt;br /&gt;
$count = status::count_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Check whether a record exists.&lt;br /&gt;
$exists = status::record_exists($id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure to also check their additional parameters and their variants (&#039;&#039;record_exists_select()&#039;&#039;, &#039;&#039;count_records_select&#039;&#039;, &#039;&#039;get_records_select&#039;&#039;, ...).&lt;br /&gt;
&lt;br /&gt;
=== Custom fetching ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s always a good idea to add more complex queries directly within your persistent. By convention you should always return an instance of your persistent and never an stdClass. Here we add a custom method which allows to directly fetch all records by username.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get all records from a user&#039;s username.&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $username The username.&lt;br /&gt;
 * @return status[]&lt;br /&gt;
 */&lt;br /&gt;
public static function get_records_by_username($username) {&lt;br /&gt;
    global $DB;&lt;br /&gt;
&lt;br /&gt;
    $sql = &#039;SELECT s.*&lt;br /&gt;
              FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
              JOIN {user} u&lt;br /&gt;
                ON u.id = s.userid&lt;br /&gt;
             WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
    $persistents = [];&lt;br /&gt;
&lt;br /&gt;
    $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
    foreach ($recordset as $record) {&lt;br /&gt;
        $persistents[] = new static(0, $record);&lt;br /&gt;
    }&lt;br /&gt;
    $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
    return $persistents;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to join persistents together and be able to extract their respective properties in a single database query, you should have a look at the following methods:&lt;br /&gt;
&lt;br /&gt;
;get_sql_fields(string $alias, string $prefix = null)&lt;br /&gt;
: Returns the SQL statement to include in the SELECT clause to prefix columns.&lt;br /&gt;
;extract_record(stdClass $row, string $prefix = null)&lt;br /&gt;
: Extracts all the properties from a row based on the given prefix.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Minimalist example.&lt;br /&gt;
$sqlfields = status::get_sql_fields(&#039;s&#039;, &#039;statprops&#039;);&lt;br /&gt;
$sql = &amp;quot;SELECT $sqlfields, u.username&lt;br /&gt;
          FROM {&amp;quot; . status::TABLE . &amp;quot;} s&lt;br /&gt;
          JOIN {user} ON s.userid = u.id&lt;br /&gt;
         WHERE s.id = 1&amp;quot;;&lt;br /&gt;
$row = $DB-&amp;gt;get_record($sql, []);&lt;br /&gt;
$statusdata = status::extract_record($row, &#039;statprops&#039;);&lt;br /&gt;
$persistent = new status(0, $statusdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Validating ==&lt;br /&gt;
&lt;br /&gt;
Basic validation of the properties values happens automatically based on their type (&#039;&#039;PARAM_*&#039;&#039; constant), however this is not always enough. In order to implement your own custom validation, simply define a &#039;&#039;protected&#039;&#039; method starting with &#039;&#039;validate_&#039;&#039; followed with the property name. This method will be called whenever the model needs to be validated and will receive the data to validate.&lt;br /&gt;
&lt;br /&gt;
A validation method must always return either &#039;&#039;true&#039;&#039; or an instance of &#039;&#039;lang_string&#039;&#039; which contains the error message to send to the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param int $value The value.&lt;br /&gt;
 * @return true|lang_string&lt;br /&gt;
 */&lt;br /&gt;
protected function validate_userid($value) {&lt;br /&gt;
    if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
        return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above example ensures that the &#039;&#039;userid&#039;&#039; property contains a valid user ID.&lt;br /&gt;
&lt;br /&gt;
Note that the basic validation is always performed first, and thus your custom validation method will not be called when the value did not pass the basic validation.&lt;br /&gt;
&lt;br /&gt;
=== Validation results ===&lt;br /&gt;
&lt;br /&gt;
The validation of the object automatically happens upon &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039;. If the validation did not pass, an &#039;&#039;invalid_persisten_exception&#039;&#039; will be raised. You can validate the object prior to saving the object and get the validation results if you need to.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// We can catch the invalid_persistent_exception.&lt;br /&gt;
try {&lt;br /&gt;
    $persistent = new status();&lt;br /&gt;
    $persistent-&amp;gt;create();&lt;br /&gt;
} catch (invalid_persistent_exception $e) {&lt;br /&gt;
    // Whoops, something wrong happened.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Check whether the object is valid.&lt;br /&gt;
$persistent-&amp;gt;is_valid();        // True or false.&lt;br /&gt;
&lt;br /&gt;
// Get the validation errors.&lt;br /&gt;
$persistent-&amp;gt;get_errors();      // Array where keys are properties and values are errors.&lt;br /&gt;
&lt;br /&gt;
// Validate the object.&lt;br /&gt;
$persistent-&amp;gt;validate();        // Returns true, or an array of errors.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Hooks ==&lt;br /&gt;
&lt;br /&gt;
You can define the following methods to be notified prior to, or after, something happened:&lt;br /&gt;
&lt;br /&gt;
;protected before_validate()&lt;br /&gt;
: Do something before the object is validated.&lt;br /&gt;
;protected before_create()&lt;br /&gt;
: Do something before the object is inserted in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_create()&lt;br /&gt;
: Do something right after the object was added to the database.&lt;br /&gt;
;protected before_update()&lt;br /&gt;
: Do something before the object is updated in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_update(bool $result)&lt;br /&gt;
: Do something right after the object was updated in the database.&lt;br /&gt;
;protected before_delete()&lt;br /&gt;
: Do something right before the object is deleted from the database.&lt;br /&gt;
;protected after_delete(bool $result)&lt;br /&gt;
: Do something right after the object was deleted from the database.&lt;br /&gt;
&lt;br /&gt;
== Common pitfalls ==&lt;br /&gt;
&lt;br /&gt;
# You must create the database table yourself, using the [[XMLDB editor]] and an upgrade script.&lt;br /&gt;
# You must include the [[#Mandatory_properties|mandatory fields]] in your table schema.&lt;br /&gt;
# You must never extend another persistent as this leads to unpredictable errors whenever the parent changes (new properties missing in your table, changes in validation, custom getters and setters, etc...).&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Minimalist ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
class status extends core\persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More advanced ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example;&lt;br /&gt;
&lt;br /&gt;
use core\persistent;&lt;br /&gt;
use core_user;&lt;br /&gt;
use lang_string;&lt;br /&gt;
&lt;br /&gt;
class status extends persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
                &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
                &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;location&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
                &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
                &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
                &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
                    return get_config(&#039;default_location&#039;);&lt;br /&gt;
                },&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the user object of the author.&lt;br /&gt;
     *&lt;br /&gt;
     * @return stdClass&lt;br /&gt;
     */&lt;br /&gt;
    public function get_author() {&lt;br /&gt;
        return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Convenience method to set the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
     */&lt;br /&gt;
    public function set_userid($idorobject) {&lt;br /&gt;
        $userid = $idorobject;&lt;br /&gt;
        if (is_object($idorobject)) {&lt;br /&gt;
            $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Validate the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param int $value The value.&lt;br /&gt;
     * @return true|lang_string&lt;br /&gt;
     */&lt;br /&gt;
    protected function validate_userid($value) {&lt;br /&gt;
        if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
            return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get all records from a user&#039;s username.&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $username The username.&lt;br /&gt;
     * @return status[]&lt;br /&gt;
     */&lt;br /&gt;
    public static function get_records_by_username($username) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $sql = &#039;SELECT s.*&lt;br /&gt;
                  FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
                  JOIN {user} u&lt;br /&gt;
                    ON u.id = s.userid&lt;br /&gt;
                 WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
        $persistents = [];&lt;br /&gt;
&lt;br /&gt;
        $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
        foreach ($recordset as $record) {&lt;br /&gt;
            $persistents[] = new static(0, $record);&lt;br /&gt;
        }&lt;br /&gt;
        $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
        return $persistents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Template:Moodle_3.3&amp;diff=51594</id>
		<title>Template:Moodle 3.3</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Template:Moodle_3.3&amp;diff=51594"/>
		<updated>2016-12-14T04:00:50Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: Created page with &amp;quot;&amp;lt;span class=&amp;quot;small-info-right&amp;quot;&amp;gt;Moodle &amp;lt;span class=&amp;quot;text-big new&amp;quot;&amp;gt;3.3&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;includeonly&amp;gt;Category:Moodle 3.3&amp;lt;/includeonly&amp;gt; &amp;lt;noinclude&amp;gt;This template will categorise...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;span class=&amp;quot;small-info-right&amp;quot;&amp;gt;Moodle &amp;lt;span class=&amp;quot;text-big new&amp;quot;&amp;gt;3.3&amp;lt;/span&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;[[Category:Moodle 3.3]]&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;This template will categorise articles that include it into [[:Category:Moodle 3.3]].&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Category:Moodle_3.3&amp;diff=51593</id>
		<title>Category:Moodle 3.3</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Category:Moodle_3.3&amp;diff=51593"/>
		<updated>2016-12-14T03:58:41Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: Created page with &amp;quot;Pages that relate to the planning and development of Moodle 3.3 features.&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Pages that relate to the planning and development of Moodle 3.3 features.&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51592</id>
		<title>User:Frédéric Massart</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51592"/>
		<updated>2016-12-14T03:41:05Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Persistents ==&lt;br /&gt;
&lt;br /&gt;
Moodle persistents are equivalent to models (or active records). They represent an object stored in the database and provide the methods to create, read, update, and delete those objects. Additionally, the persistents validate its own data against automatic and custom validation rules.&lt;br /&gt;
&lt;br /&gt;
Persistents extend the abstract class &#039;&#039;core\persistent&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Properties ===&lt;br /&gt;
&lt;br /&gt;
Defining properties can be done by defining the method &#039;&#039;protected static define_properties()&#039;&#039;. It returns an array where the keys are the names of the fields (and database columns), and their details. The &#039;&#039;type&#039;&#039; of each field must be specified using one of the &#039;&#039;PARAM_*&#039;&#039; constants. This type will be used to automatically validate the property&#039;s value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the definition of the properties of this model.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
        )&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Properties attributes ====&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must be one of the many &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
;default&lt;br /&gt;
: The default value to attach to the property when it hasn&#039;t been provided. Alternatively this can be a &#039;&#039;Closure&#039;&#039; returning the default value.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants &#039;&#039;NULL_ALLOWED&#039;&#039; or &#039;&#039;NULL_NOT_ALLOWED&#039;&#039; determining if the null value is accepted. This defaults to &#039;&#039;NULL_NOT_ALLOWED&#039;&#039;.&lt;br /&gt;
;message&lt;br /&gt;
: The default error message (as a &#039;&#039;lang_string&#039;&#039; instance) to return when the validation of this field fails.&lt;br /&gt;
;choices&lt;br /&gt;
: An array of values which the property must fall in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
    &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
    &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
),&lt;br /&gt;
&#039;location&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
    &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
    &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
    &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
        return get_config(&#039;default_location&#039;);&lt;br /&gt;
    },&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that you should always use a &#039;&#039;Closure&#039;&#039; for the &#039;&#039;default&#039;&#039; value when you cannot guarantee that it will not change since the start of the request. The list of properties and their attributes is cached, and so failure to use a &#039;&#039;Closure&#039;&#039; can result in using an outdated default value.&lt;br /&gt;
&lt;br /&gt;
==== Mandatory properties ====&lt;br /&gt;
&lt;br /&gt;
Four fields are always added to your persistent and should be reflected in your database table. You must not define those properties in &#039;&#039;define_properties()&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
;id (non-null integer)&lt;br /&gt;
: The primary key of the record.&lt;br /&gt;
;usermodified (non-null integer)&lt;br /&gt;
: The user who created/modified the object. It is automatically set.&lt;br /&gt;
;timecreated (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set.&lt;br /&gt;
;timemodified (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set, and defaults to 0.&lt;br /&gt;
&lt;br /&gt;
=== Linking to a database table ===&lt;br /&gt;
&lt;br /&gt;
While the persistent class is helpful for database interactions, it does not automatically fetch the properties from the database, nor does it create the tables. You will need to create the table yourself, as well as pointing the persistent to it. This can be done by defining the constant &#039;&#039;TABLE&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** Table name for the persistent. */&lt;br /&gt;
const TABLE = &#039;status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The table name must not contain the Moodle prefix. Also it is common practice to always refer to your table name using the constant.&lt;br /&gt;
&lt;br /&gt;
=== Reading property values ===&lt;br /&gt;
&lt;br /&gt;
This can be done using the method &#039;&#039;get()&#039;&#039;, or the magic method &#039;&#039;get_&#039;&#039; followed by the property name. Alternatively you can also use &#039;&#039;to_record()&#039;&#039; which exports the whole object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Create a new object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using persistent::get().&lt;br /&gt;
$userid = $persistent-&amp;gt;get(&#039;userid&#039;);&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using the magic getter.&lt;br /&gt;
$userid = $persistent-&amp;gt;get_userid();&lt;br /&gt;
&lt;br /&gt;
// Get all the properties in an stdClass.&lt;br /&gt;
$data = $persistent-&amp;gt;to_record();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may override the magic getters to implements your own custom logic. For instance you could convert the data in another format automatically as a convenience for developers. However, use this sparingly as it may lead to confusion: what you get is not what is stored in the database. Note that you cannot guarantee that your getter will be used, remember that it is possible for developers to call the alternative &#039;&#039;get()&#039;&#039; which will not use your custom logic.&lt;br /&gt;
&lt;br /&gt;
It is, however, encouraged to add convenience methods such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns the user object of the author.&lt;br /&gt;
 *&lt;br /&gt;
 * @return stdClass&lt;br /&gt;
 */&lt;br /&gt;
public function get_author() {&lt;br /&gt;
    return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Assigning values to properties ===&lt;br /&gt;
&lt;br /&gt;
There are a few methods to do so.&lt;br /&gt;
&lt;br /&gt;
You use an object (&#039;&#039;stdClass&#039;&#039;) to assign a bunch of properties at once. Use it with the constructor, or the method &#039;&#039;from_record()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) array(&#039;userid&#039; =&amp;gt; 2, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instantiates a new object with value for some properties.&lt;br /&gt;
$persistent = new status(0, $data);&lt;br /&gt;
&lt;br /&gt;
// Is similar to.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
$persistent-&amp;gt;from_record($data);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or you can use the &#039;&#039;set()&#039;&#039; method on an instance.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set(&#039;message&#039;, Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally you can use the magic setters &#039;&#039;set_&#039;&#039; followed by the property name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Defining a custom setter ====&lt;br /&gt;
&lt;br /&gt;
Though you don&#039;t have to for the code to work, you can define your own &#039;&#039;setter&#039;&#039; methods which will override the magic setters. They are useful if you want to extract the data out of a more complex object prior to assigning it. Though note that those setters will then have to use the &#039;&#039;set()&#039;&#039; method to assign the values.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Convenience method to set the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
 */&lt;br /&gt;
public function set_userid($idorobject) {&lt;br /&gt;
    $userid = $idorobject;&lt;br /&gt;
    if (is_object($idorobject)) {&lt;br /&gt;
        $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
    }&lt;br /&gt;
    $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example we will accept an object or an ID, as a convenience for developers we will extract the ID value out of the object passed if any.&lt;br /&gt;
&lt;br /&gt;
Just like custom getters, you cannot guarantee that they will be used. Developers can directly call the &#039;&#039;set()&#039;&#039; method. Therefore you must not use a custom setter to reliably transform the data added to a property. For instance do not add a custom setter to remove HTML tags out of a text field, it may not always happen.&lt;br /&gt;
&lt;br /&gt;
You can obviously create your own setters which aren&#039;t based on any properties just as a convenience. For instance we could have created &#039;&#039;set_userid_from_user(object $user)&#039;&#039; which is more verbose and more predictable&lt;br /&gt;
&lt;br /&gt;
=== Read, save and delete entries ===&lt;br /&gt;
&lt;br /&gt;
The methods to &#039;&#039;create&#039;&#039;, &#039;&#039;read&#039;&#039;, &#039;&#039;update&#039;&#039; and &#039;&#039;delete&#039;&#039; are eponymous. Your object will be validated before you &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; it. The &#039;&#039;update&#039;&#039;, &#039;&#039;delete&#039;&#039; and &#039;&#039;read&#039;&#039; methods require your object to contain its ID. And you also won&#039;t be allowed to &#039;&#039;create&#039;&#039; an entry which already had an ID defined.&lt;br /&gt;
&lt;br /&gt;
Here are some code examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Fetches an object from database based on its ID.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Create previously instantiated object in the database.&lt;br /&gt;
$persistent-&amp;gt;create();&lt;br /&gt;
&lt;br /&gt;
// Load an object from the database, and update it.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
$persistent-&amp;gt;update();&lt;br /&gt;
&lt;br /&gt;
// Reset the instance to the values in the database.&lt;br /&gt;
$persistent-&amp;gt;read();&lt;br /&gt;
&lt;br /&gt;
// Permanently delete the object from the database.&lt;br /&gt;
$persistent-&amp;gt;delete();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching records ===&lt;br /&gt;
&lt;br /&gt;
Once you start using persistents you should never directly interact with the database outside of your class. The persistent class comes with a few handy methods allowing you to retrieve your objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Use the constructor to fetch one object from its ID.&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Get one record from a set of conditions.&lt;br /&gt;
$persistent = status::get_record([&#039;userid&#039; =&amp;gt; $userid, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;]);&lt;br /&gt;
&lt;br /&gt;
// Get multiple records from a set of conditions.&lt;br /&gt;
$persistents = status::get_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Count the records.&lt;br /&gt;
$count = status::count_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Check whether a record exists.&lt;br /&gt;
$exists = status::record_exists($id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure to also check their additional parameters and their variants (&#039;&#039;record_exists_select()&#039;&#039;, &#039;&#039;count_records_select&#039;&#039;, &#039;&#039;get_records_select&#039;&#039;, ...).&lt;br /&gt;
&lt;br /&gt;
==== Custom fetching ====&lt;br /&gt;
&lt;br /&gt;
It&#039;s always a good idea to add more complex queries directly within your persistent. By convention you should always return an instance of your persistent and never an stdClass. Here we add a custom method which allows to directly fetch all records by username.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get all records by a user from its username&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $username The username.&lt;br /&gt;
 * @return status[]&lt;br /&gt;
 */&lt;br /&gt;
public static function get_records_by_username($username) {&lt;br /&gt;
    global $DB;&lt;br /&gt;
&lt;br /&gt;
    $sql = &#039;SELECT s.*&lt;br /&gt;
              FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
              JOIN {user} u&lt;br /&gt;
                ON u.id = s.userid&lt;br /&gt;
             WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
    $persistents = [];&lt;br /&gt;
&lt;br /&gt;
    $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
    foreach ($recordset as $record) {&lt;br /&gt;
        $persistents[] = new static(0, $record);&lt;br /&gt;
    }&lt;br /&gt;
    $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
    return $persistents;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to join persistents together and be able to extract their respective properties in a single database query, you should have a look at the following methods:&lt;br /&gt;
&lt;br /&gt;
;get_sql_fields(string $alias, string $prefix = null)&lt;br /&gt;
: Returns the SQL statement to include in the SELECT clause to prefix columns.&lt;br /&gt;
;extract_record(stdClass $row, string $prefix = null)&lt;br /&gt;
: Extracts all the properties from a row based on the given prefix.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Minimalist example.&lt;br /&gt;
$sqlfields = status::get_sql_fields(&#039;s&#039;, &#039;statprops&#039;);&lt;br /&gt;
$sql = &amp;quot;SELECT $sqlfields, u.username&lt;br /&gt;
          FROM {&amp;quot; . status::TABLE . &amp;quot;} s&lt;br /&gt;
          JOIN {user} ON s.userid = u.id&lt;br /&gt;
         WHERE s.id = 1&amp;quot;;&lt;br /&gt;
$row = $DB-&amp;gt;get_record($sql, []);&lt;br /&gt;
$statusdata = status::extract_record($row, &#039;statprops&#039;);&lt;br /&gt;
$persistent = new status(0, $statusdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Validating ===&lt;br /&gt;
&lt;br /&gt;
Basic validation of the properties values happens automatically based on their type (&#039;&#039;PARAM_*&#039;&#039; constant), however this is not always enough. In order to implement your own custom validation, simply define a &#039;&#039;protected&#039;&#039; method starting with &#039;&#039;validate_&#039;&#039; followed with the property name. This method will be called whenever the model needs to be validated and will receive the data to validate.&lt;br /&gt;
&lt;br /&gt;
A validation method must always return either &#039;&#039;true&#039;&#039; or an instance of &#039;&#039;lang_string&#039;&#039; which contains the error message to send to the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param int $value The value.&lt;br /&gt;
 * @return true|lang_string&lt;br /&gt;
 */&lt;br /&gt;
protected function validate_userid($value) {&lt;br /&gt;
    if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
        return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above example ensures that the &#039;&#039;userid&#039;&#039; property contains a valid user ID.&lt;br /&gt;
&lt;br /&gt;
Note that the basic validation is always performed first, and thus your custom validation method will not be called when the value did not pass the basic validation.&lt;br /&gt;
&lt;br /&gt;
==== Validation results ====&lt;br /&gt;
&lt;br /&gt;
The validation of the object automatically happens upon &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039;. If the validation did not pass, an &#039;&#039;invalid_persisten_exception&#039;&#039; will be raised. You can validate the object prior to saving the object and get the validation results if you need to.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// We can catch the invalid_persistent_exception.&lt;br /&gt;
try {&lt;br /&gt;
    $persistent = new status();&lt;br /&gt;
    $persistent-&amp;gt;create();&lt;br /&gt;
} catch (invalid_persistent_exception $e) {&lt;br /&gt;
    // Whoops, something wrong happened.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Check whether the object is valid.&lt;br /&gt;
$persistent-&amp;gt;is_valid();        // True or false.&lt;br /&gt;
&lt;br /&gt;
// Get the validation errors.&lt;br /&gt;
$persistent-&amp;gt;get_errors();      // Array where keys are properties and values are errors.&lt;br /&gt;
&lt;br /&gt;
// Validate the object.&lt;br /&gt;
$persistent-&amp;gt;validate();        // Returns true, or an array of errors.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
You can define the following methods to be notified prior to, or after, something happened:&lt;br /&gt;
&lt;br /&gt;
;protected before_validate()&lt;br /&gt;
: Do something before the object is validated.&lt;br /&gt;
;protected before_create()&lt;br /&gt;
: Do something before the object is inserted in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_create()&lt;br /&gt;
: Do something right after the object was added to the database.&lt;br /&gt;
;protected before_update()&lt;br /&gt;
: Do something before the object is updated in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_update(bool $result)&lt;br /&gt;
: Do something right after the object was updated in the database.&lt;br /&gt;
;protected before_delete()&lt;br /&gt;
: Do something right before the object is deleted from the database.&lt;br /&gt;
;protected after_delete(bool $result)&lt;br /&gt;
: Do something right after the object was deleted from the database.&lt;br /&gt;
&lt;br /&gt;
=== Full example ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example;&lt;br /&gt;
&lt;br /&gt;
use core\persistent;&lt;br /&gt;
use core_user;&lt;br /&gt;
use lang_string;&lt;br /&gt;
&lt;br /&gt;
class status extends persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
                &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
                &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;location&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
                &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
                &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
                &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
                    return get_config(&#039;default_location&#039;);&lt;br /&gt;
                },&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the user object of the author.&lt;br /&gt;
     *&lt;br /&gt;
     * @return stdClass&lt;br /&gt;
     */&lt;br /&gt;
    public function get_author() {&lt;br /&gt;
        return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Convenience method to set the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
     */&lt;br /&gt;
    public function set_userid($idorobject) {&lt;br /&gt;
        $userid = $idorobject;&lt;br /&gt;
        if (is_object($idorobject)) {&lt;br /&gt;
            $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Validate the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param int $value The value.&lt;br /&gt;
     * @return true|lang_string&lt;br /&gt;
     */&lt;br /&gt;
    protected function validate_userid($value) {&lt;br /&gt;
        if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
            return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get all records by a user from its username&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $username The username.&lt;br /&gt;
     * @return status[]&lt;br /&gt;
     */&lt;br /&gt;
    public static function get_records_by_username($username) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $sql = &#039;SELECT s.*&lt;br /&gt;
                  FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
                  JOIN {user} u&lt;br /&gt;
                    ON u.id = s.userid&lt;br /&gt;
                 WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
        $persistents = [];&lt;br /&gt;
&lt;br /&gt;
        $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
        foreach ($recordset as $record) {&lt;br /&gt;
            $persistents[] = new static(0, $record);&lt;br /&gt;
        }&lt;br /&gt;
        $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
        return $persistents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51587</id>
		<title>User:Frédéric Massart</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51587"/>
		<updated>2016-12-13T09:55:19Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Persistents ==&lt;br /&gt;
&lt;br /&gt;
Moodle persistents are equivalent to models (or active records). They represent an object stored in the database and provide the methods to create, read, update, and delete those objects. Additionally, the persistents validate its own data against automatic and custom validation rules.&lt;br /&gt;
&lt;br /&gt;
Persistents extend the abstract class &#039;&#039;core\persistent&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Properties ===&lt;br /&gt;
&lt;br /&gt;
Defining properties can be done by defining the method &#039;&#039;protected static define_properties()&#039;&#039;. It returns an array where the keys are the names of the fields (and database columns), and their details. The &#039;&#039;type&#039;&#039; of each field must be specified using one of the &#039;&#039;PARAM_*&#039;&#039; constants. This type will be used to automatically validate the property&#039;s value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the definition of the properties of this model.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
        )&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Properties attributes ====&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must be one of the many &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
;default&lt;br /&gt;
: The default value to attach to the property when it hasn&#039;t been provided. Alternatively this can be a &#039;&#039;Closure&#039;&#039; returning the default value.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants &#039;&#039;NULL_ALLOWED&#039;&#039; or &#039;&#039;NULL_NOT_ALLOWED&#039;&#039; determining if the null value is accepted. This defaults to &#039;&#039;NULL_NOT_ALLOWED&#039;&#039;.&lt;br /&gt;
;message&lt;br /&gt;
: The default error message (as a &#039;&#039;lang_string&#039;&#039; instance) to return when the validation of this field fails.&lt;br /&gt;
;choices&lt;br /&gt;
: An array of values which the property must fall in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
    &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
    &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
),&lt;br /&gt;
&#039;location&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
    &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
    &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
    &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
        return get_config(&#039;default_location&#039;);&lt;br /&gt;
    },&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that you should always use a &#039;&#039;Closure&#039;&#039; for the &#039;&#039;default&#039;&#039; value when you cannot guarantee that it will not change since the start of the request. The list of properties and their attributes is cached, and so failure to use a &#039;&#039;Closure&#039;&#039; can result in using an outdated default value.&lt;br /&gt;
&lt;br /&gt;
==== Mandatory properties ====&lt;br /&gt;
&lt;br /&gt;
Four fields are always added to your persistent and should be reflected in your database table. You must not define those properties in &#039;&#039;define_properties()&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
;id (non-null integer)&lt;br /&gt;
: The primary key of the record.&lt;br /&gt;
;usermodified (non-null integer)&lt;br /&gt;
: The user who created/modified the object. It is automatically set.&lt;br /&gt;
;timecreated (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set.&lt;br /&gt;
;timemodified (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set, and defaults to 0.&lt;br /&gt;
&lt;br /&gt;
=== Attaching to the database ===&lt;br /&gt;
&lt;br /&gt;
While the persistent class is helpful for database interactions, it does not automatically fetch the properties from the database, nor does it create the tables. You will need to create the table yourself, as well as pointing the persistent to it. This can be done by defining the constant &#039;&#039;TABLE&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** Table name for the persistent. */&lt;br /&gt;
const TABLE = &#039;status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The table name must not contain the Moodle prefix. Also it is common practice to always refer to your table name using the constant.&lt;br /&gt;
&lt;br /&gt;
=== Reading property values ===&lt;br /&gt;
&lt;br /&gt;
This can be done using the method &#039;&#039;get()&#039;&#039;, or the magic method &#039;&#039;get_&#039;&#039; followed by the property name. Alternatively you can also use &#039;&#039;to_record()&#039;&#039; which exports the whole object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Create a new object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using persistent::get().&lt;br /&gt;
$userid = $persistent-&amp;gt;get(&#039;userid&#039;);&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using the magic getter.&lt;br /&gt;
$userid = $persistent-&amp;gt;get_userid();&lt;br /&gt;
&lt;br /&gt;
// Get all the properties in an stdClass.&lt;br /&gt;
$data = $persistent-&amp;gt;to_record();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may override the magic getters to implements your own custom logic. For instance you could convert the data in another format automatically as a convenience for developers. However, use this sparingly as it may lead to confusion: what you get is not what is stored in the database. Note that you cannot guarantee that your getter will be used, remember that it is possible for developers to call the alternative &#039;&#039;get()&#039;&#039; which will not use your custom logic.&lt;br /&gt;
&lt;br /&gt;
It is, however, encouraged to add convenience methods such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns the user object of the author.&lt;br /&gt;
 *&lt;br /&gt;
 * @return stdClass&lt;br /&gt;
 */&lt;br /&gt;
public function get_author() {&lt;br /&gt;
    return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Assigning values to properties ===&lt;br /&gt;
&lt;br /&gt;
There are a few methods to do so.&lt;br /&gt;
&lt;br /&gt;
You use an object (&#039;&#039;stdClass&#039;&#039;) to assign a bunch of properties at once. Use it with the constructor, or the method &#039;&#039;from_record()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) array(&#039;userid&#039; =&amp;gt; 2, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instantiates a new object with value for some properties.&lt;br /&gt;
$persistent = new status(0, $data);&lt;br /&gt;
&lt;br /&gt;
// Is similar to.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
$persistent-&amp;gt;from_record($data);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or you can use the &#039;&#039;set()&#039;&#039; method on an instance.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set(&#039;message&#039;, Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally you can use the magic setters &#039;&#039;set_&#039;&#039; followed by the property name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Defining a custom setter ====&lt;br /&gt;
&lt;br /&gt;
Though you don&#039;t have to for the code to work, you can define your own &#039;&#039;setter&#039;&#039; methods which will override the magic setters. They are useful if you want to extract the data out of a more complex object prior to assigning it. Though note that those setters will then have to use the &#039;&#039;set()&#039;&#039; method to assign the values.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Convenience method to set the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
 */&lt;br /&gt;
public function set_userid($idorobject) {&lt;br /&gt;
    $userid = $idorobject;&lt;br /&gt;
    if (is_object($idorobject)) {&lt;br /&gt;
        $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
    }&lt;br /&gt;
    $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example we will accept an object or an ID, as a convenience for developers we will extract the ID value out of the object passed if any.&lt;br /&gt;
&lt;br /&gt;
Just like custom getters, you cannot guarantee that they will be used. Developers can directly call the &#039;&#039;set()&#039;&#039; method. Therefore you must not use a custom setter to reliably transform the data added to a property. For instance do not add a custom setter to remove HTML tags out of a text field, it may not always happen.&lt;br /&gt;
&lt;br /&gt;
You can obviously create your own setters which aren&#039;t based on any properties just as a convenience. For instance we could have created &#039;&#039;set_userid_from_user(object $user)&#039;&#039; which is more verbose and more predictable&lt;br /&gt;
&lt;br /&gt;
=== Read, save and delete entries ===&lt;br /&gt;
&lt;br /&gt;
The methods to &#039;&#039;create&#039;&#039;, &#039;&#039;read&#039;&#039;, &#039;&#039;update&#039;&#039; and &#039;&#039;delete&#039;&#039; are eponymous. Your object will be validated before you &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; it. The &#039;&#039;update&#039;&#039;, &#039;&#039;delete&#039;&#039; and &#039;&#039;read&#039;&#039; methods require your object to contain its ID. And you also won&#039;t be allowed to &#039;&#039;create&#039;&#039; an entry which already had an ID defined.&lt;br /&gt;
&lt;br /&gt;
Here are some code examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Fetches an object from database based on its ID.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Create previously instantiated object in the database.&lt;br /&gt;
$persistent-&amp;gt;create();&lt;br /&gt;
&lt;br /&gt;
// Load an object from the database, and update it.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
$persistent-&amp;gt;update();&lt;br /&gt;
&lt;br /&gt;
// Reset the instance to the values in the database.&lt;br /&gt;
$persistent-&amp;gt;read();&lt;br /&gt;
&lt;br /&gt;
// Permanently delete the object from the database.&lt;br /&gt;
$persistent-&amp;gt;delete();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching records ===&lt;br /&gt;
&lt;br /&gt;
Once you start using persistents you should never directly interact with the database outside of your class. The persistent class comes with a few handy methods allowing you to retrieve your objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Use the constructor to fetch one object from its ID.&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Get one record from a set of conditions.&lt;br /&gt;
$persistent = status::get_record([&#039;userid&#039; =&amp;gt; $userid, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;]);&lt;br /&gt;
&lt;br /&gt;
// Get multiple records from a set of conditions.&lt;br /&gt;
$persistents = status::get_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Count the records.&lt;br /&gt;
$count = status::count_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Check whether a record exists.&lt;br /&gt;
$exists = status::record_exists($id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure to also check their additional parameters and their variants (&#039;&#039;record_exists_select()&#039;&#039;, &#039;&#039;count_records_select&#039;&#039;, &#039;&#039;get_records_select&#039;&#039;, ...).&lt;br /&gt;
&lt;br /&gt;
==== Custom fetching ====&lt;br /&gt;
&lt;br /&gt;
It&#039;s always a good idea to add more complex queries directly within your persistent. By convention you should always return an instance of your persistent and never an stdClass. Here we add a custom method which allows to directly fetch all records by username.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get all records by a user from its username&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $username The username.&lt;br /&gt;
 * @return status[]&lt;br /&gt;
 */&lt;br /&gt;
public static function get_records_by_username($username) {&lt;br /&gt;
    global $DB;&lt;br /&gt;
&lt;br /&gt;
    $sql = &#039;SELECT s.*&lt;br /&gt;
              FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
              JOIN {user} u&lt;br /&gt;
                ON u.id = s.userid&lt;br /&gt;
             WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
    $persistents = [];&lt;br /&gt;
&lt;br /&gt;
    $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
    foreach ($recordset as $record) {&lt;br /&gt;
        $persistents[] = new static(0, $record);&lt;br /&gt;
    }&lt;br /&gt;
    $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
    return $persistents;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to join persistents together and be able to extract their respective properties in a single database query, you should have a look at the following methods:&lt;br /&gt;
&lt;br /&gt;
;get_sql_fields(string $alias, string $prefix = null)&lt;br /&gt;
: Returns the SQL statement to include in the SELECT clause to prefix columns.&lt;br /&gt;
;extract_record(stdClass $row, string $prefix = null)&lt;br /&gt;
: Extracts all the properties from a row based on the given prefix.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Minimalist example.&lt;br /&gt;
$sqlfields = status::get_sql_fields(&#039;s&#039;, &#039;statprops&#039;);&lt;br /&gt;
$sql = &amp;quot;SELECT $sqlfields, u.username&lt;br /&gt;
          FROM {&amp;quot; . status::TABLE . &amp;quot;} s&lt;br /&gt;
          JOIN {user} ON s.userid = u.id&lt;br /&gt;
         WHERE s.id = 1&amp;quot;;&lt;br /&gt;
$row = $DB-&amp;gt;get_record($sql, []);&lt;br /&gt;
$statusdata = status::extract_record($row, &#039;statprops&#039;);&lt;br /&gt;
$persistent = new status(0, $statusdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Validating ===&lt;br /&gt;
&lt;br /&gt;
Basic validation of the properties values happens automatically based on their type (&#039;&#039;PARAM_*&#039;&#039; constant), however this is not always enough. In order to implement your own custom validation, simply define a &#039;&#039;protected&#039;&#039; method starting with &#039;&#039;validate_&#039;&#039; followed with the property name. This method will be called whenever the model needs to be validated and will receive the data to validate.&lt;br /&gt;
&lt;br /&gt;
A validation method must always return either &#039;&#039;true&#039;&#039; or an instance of &#039;&#039;lang_string&#039;&#039; which contains the error message to send to the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param int $value The value.&lt;br /&gt;
 * @return true|lang_string&lt;br /&gt;
 */&lt;br /&gt;
protected function validate_userid($value) {&lt;br /&gt;
    if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
        return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above example ensures that the &#039;&#039;userid&#039;&#039; property contains a valid user ID.&lt;br /&gt;
&lt;br /&gt;
Note that the basic validation is always performed first, and thus your custom validation method will not be called when the value did not pass the basic validation.&lt;br /&gt;
&lt;br /&gt;
==== Validation results ====&lt;br /&gt;
&lt;br /&gt;
The validation of the object automatically happens upon &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039;. If the validation did not pass, an &#039;&#039;invalid_persisten_exception&#039;&#039; will be raised. You can validate the object prior to saving the object and get the validation results if you need to.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// We can catch the invalid_persistent_exception.&lt;br /&gt;
try {&lt;br /&gt;
    $persistent = new status();&lt;br /&gt;
    $persistent-&amp;gt;create();&lt;br /&gt;
} catch (invalid_persistent_exception $e) {&lt;br /&gt;
    // Whoops, something wrong happened.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Check whether the object is valid.&lt;br /&gt;
$persistent-&amp;gt;is_valid();        // True or false.&lt;br /&gt;
&lt;br /&gt;
// Get the validation errors.&lt;br /&gt;
$persistent-&amp;gt;get_errors();      // Array where keys are properties and values are errors.&lt;br /&gt;
&lt;br /&gt;
// Validate the object.&lt;br /&gt;
$persistent-&amp;gt;validate();        // Returns true, or an array of errors.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
You can define the following methods to be notified prior to, or after, something happened:&lt;br /&gt;
&lt;br /&gt;
;protected before_validate()&lt;br /&gt;
: Do something before the object is validated.&lt;br /&gt;
;protected before_create()&lt;br /&gt;
: Do something before the object is inserted in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_create()&lt;br /&gt;
: Do something right after the object was added to the database.&lt;br /&gt;
;protected before_update()&lt;br /&gt;
: Do something before the object is updated in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_update(bool $result)&lt;br /&gt;
: Do something right after the object was updated in the database.&lt;br /&gt;
;protected before_delete()&lt;br /&gt;
: Do something right before the object is deleted from the database.&lt;br /&gt;
;protected after_delete(bool $result)&lt;br /&gt;
: Do something right after the object was deleted from the database.&lt;br /&gt;
&lt;br /&gt;
=== Full example ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example;&lt;br /&gt;
&lt;br /&gt;
use core\persistent;&lt;br /&gt;
use core_user;&lt;br /&gt;
use lang_string;&lt;br /&gt;
&lt;br /&gt;
class status extends persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
                &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
                &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;location&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
                &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
                &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
                &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
                    return get_config(&#039;default_location&#039;);&lt;br /&gt;
                },&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the user object of the author.&lt;br /&gt;
     *&lt;br /&gt;
     * @return stdClass&lt;br /&gt;
     */&lt;br /&gt;
    public function get_author() {&lt;br /&gt;
        return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Convenience method to set the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
     */&lt;br /&gt;
    public function set_userid($idorobject) {&lt;br /&gt;
        $userid = $idorobject;&lt;br /&gt;
        if (is_object($idorobject)) {&lt;br /&gt;
            $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Validate the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param int $value The value.&lt;br /&gt;
     * @return true|lang_string&lt;br /&gt;
     */&lt;br /&gt;
    protected function validate_userid($value) {&lt;br /&gt;
        if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
            return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get all records by a user from its username&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $username The username.&lt;br /&gt;
     * @return status[]&lt;br /&gt;
     */&lt;br /&gt;
    public static function get_records_by_username($username) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $sql = &#039;SELECT s.*&lt;br /&gt;
                  FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
                  JOIN {user} u&lt;br /&gt;
                    ON u.id = s.userid&lt;br /&gt;
                 WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
        $persistents = [];&lt;br /&gt;
&lt;br /&gt;
        $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
        foreach ($recordset as $record) {&lt;br /&gt;
            $persistents[] = new static(0, $record);&lt;br /&gt;
        }&lt;br /&gt;
        $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
        return $persistents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51586</id>
		<title>User:Frédéric Massart</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51586"/>
		<updated>2016-12-13T09:53:56Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Persistents ==&lt;br /&gt;
&lt;br /&gt;
Moodle persistents are equivalent to models (or active records). They represent an object stored in the database and provide the methods to create, read, update, and delete those objects. Additionally, the persistents validate its own data against automatic and custom validation rules.&lt;br /&gt;
&lt;br /&gt;
Persistents extend the abstract class &#039;&#039;core\persistent&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Properties ===&lt;br /&gt;
&lt;br /&gt;
Defining properties can be done by defining the method &#039;&#039;protected static define_properties()&#039;&#039;. It returns an array where the keys are the names of the fields (and database columns), and their details. The &#039;&#039;type&#039;&#039; of each field must be specified using one of the &#039;&#039;PARAM_*&#039;&#039; constants. This type will be used to automatically validate the property&#039;s value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the definition of the properties of this model.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
        )&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Properties attributes ====&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must be one of the many &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
;default&lt;br /&gt;
: The default value to attach to the property when it hasn&#039;t been provided. Alternatively this can be a &#039;&#039;Closure&#039;&#039; returning the default value.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants &#039;&#039;NULL_ALLOWED&#039;&#039; or &#039;&#039;NULL_NOT_ALLOWED&#039;&#039; determining if the null value is accepted. This defaults to &#039;&#039;NULL_NOT_ALLOWED&#039;&#039;.&lt;br /&gt;
;message&lt;br /&gt;
: The default error message (as a &#039;&#039;lang_string&#039;&#039; instance) to return when the validation of this field fails.&lt;br /&gt;
;choices&lt;br /&gt;
: An array of values which the property must fall in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
    &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
    &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
),&lt;br /&gt;
&#039;location&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
    &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
    &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
    &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
        return get_config(&#039;default_location&#039;);&lt;br /&gt;
    },&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that you should always use a &#039;&#039;Closure&#039;&#039; for the &#039;&#039;default&#039;&#039; value when you cannot guarantee that it will not change since the start of the request. The list of properties and their attributes is cached, and so failure to use a &#039;&#039;Closure&#039;&#039; can result in using an outdated default value.&lt;br /&gt;
&lt;br /&gt;
==== Mandatory properties ====&lt;br /&gt;
&lt;br /&gt;
Four fields are always added to your persistent and should be reflected in your database table. You must not define those properties inThose are:&lt;br /&gt;
&lt;br /&gt;
;id (non-null integer)&lt;br /&gt;
: The primary key of the record.&lt;br /&gt;
;usermodified (non-null integer)&lt;br /&gt;
: The user who created/modified the object. It is automatically set.&lt;br /&gt;
;timecreated (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set.&lt;br /&gt;
;timemodified (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set, and defaults to 0.&lt;br /&gt;
&lt;br /&gt;
=== Attaching to the database ===&lt;br /&gt;
&lt;br /&gt;
While the persistent class is helpful for database interactions, it does not automatically fetch the properties from the database, nor does it create the tables. You will need to create the table yourself, as well as pointing the persistent to it. This can be done by defining the constant &#039;&#039;TABLE&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** Table name for the persistent. */&lt;br /&gt;
const TABLE = &#039;status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The table name must not contain the Moodle prefix. Also it is common practice to always refer to your table name using the constant.&lt;br /&gt;
&lt;br /&gt;
=== Reading property values ===&lt;br /&gt;
&lt;br /&gt;
This can be done using the method &#039;&#039;get()&#039;&#039;, or the magic method &#039;&#039;get_&#039;&#039; followed by the property name. Alternatively you can also use &#039;&#039;to_record()&#039;&#039; which exports the whole object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Create a new object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using persistent::get().&lt;br /&gt;
$userid = $persistent-&amp;gt;get(&#039;userid&#039;);&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using the magic getter.&lt;br /&gt;
$userid = $persistent-&amp;gt;get_userid();&lt;br /&gt;
&lt;br /&gt;
// Get all the properties in an stdClass.&lt;br /&gt;
$data = $persistent-&amp;gt;to_record();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may override the magic getters to implements your own custom logic. For instance you could convert the data in another format automatically as a convenience for developers. However, use this sparingly as it may lead to confusion: what you get is not what is stored in the database. Note that you cannot guarantee that your getter will be used, remember that it is possible for developers to call the alternative &#039;&#039;get()&#039;&#039; which will not use your custom logic.&lt;br /&gt;
&lt;br /&gt;
It is, however, encouraged to add convenience methods such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns the user object of the author.&lt;br /&gt;
 *&lt;br /&gt;
 * @return stdClass&lt;br /&gt;
 */&lt;br /&gt;
public function get_author() {&lt;br /&gt;
    return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Assigning values to properties ===&lt;br /&gt;
&lt;br /&gt;
There are a few methods to do so.&lt;br /&gt;
&lt;br /&gt;
You use an object (&#039;&#039;stdClass&#039;&#039;) to assign a bunch of properties at once. Use it with the constructor, or the method &#039;&#039;from_record()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) array(&#039;userid&#039; =&amp;gt; 2, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instantiates a new object with value for some properties.&lt;br /&gt;
$persistent = new status(0, $data);&lt;br /&gt;
&lt;br /&gt;
// Is similar to.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
$persistent-&amp;gt;from_record($data);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or you can use the &#039;&#039;set()&#039;&#039; method on an instance.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set(&#039;message&#039;, Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally you can use the magic setters &#039;&#039;set_&#039;&#039; followed by the property name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Defining a custom setter ====&lt;br /&gt;
&lt;br /&gt;
Though you don&#039;t have to for the code to work, you can define your own &#039;&#039;setter&#039;&#039; methods which will override the magic setters. They are useful if you want to extract the data out of a more complex object prior to assigning it. Though note that those setters will then have to use the &#039;&#039;set()&#039;&#039; method to assign the values.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Convenience method to set the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
 */&lt;br /&gt;
public function set_userid($idorobject) {&lt;br /&gt;
    $userid = $idorobject;&lt;br /&gt;
    if (is_object($idorobject)) {&lt;br /&gt;
        $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
    }&lt;br /&gt;
    $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example we will accept an object or an ID, as a convenience for developers we will extract the ID value out of the object passed if any.&lt;br /&gt;
&lt;br /&gt;
Just like custom getters, you cannot guarantee that they will be used. Developers can directly call the &#039;&#039;set()&#039;&#039; method. Therefore you must not use a custom setter to reliably transform the data added to a property. For instance do not add a custom setter to remove HTML tags out of a text field, it may not always happen.&lt;br /&gt;
&lt;br /&gt;
You can obviously create your own setters which aren&#039;t based on any properties just as a convenience. For instance we could have created &#039;&#039;set_userid_from_user(object $user)&#039;&#039; which is more verbose and more predictable&lt;br /&gt;
&lt;br /&gt;
=== Read, save and delete entries ===&lt;br /&gt;
&lt;br /&gt;
The methods to &#039;&#039;create&#039;&#039;, &#039;&#039;read&#039;&#039;, &#039;&#039;update&#039;&#039; and &#039;&#039;delete&#039;&#039; are eponymous. Your object will be validated before you &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; it. The &#039;&#039;update&#039;&#039;, &#039;&#039;delete&#039;&#039; and &#039;&#039;read&#039;&#039; methods require your object to contain its ID. And you also won&#039;t be allowed to &#039;&#039;create&#039;&#039; an entry which already had an ID defined.&lt;br /&gt;
&lt;br /&gt;
Here are some code examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Fetches an object from database based on its ID.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Create previously instantiated object in the database.&lt;br /&gt;
$persistent-&amp;gt;create();&lt;br /&gt;
&lt;br /&gt;
// Load an object from the database, and update it.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
$persistent-&amp;gt;update();&lt;br /&gt;
&lt;br /&gt;
// Reset the instance to the values in the database.&lt;br /&gt;
$persistent-&amp;gt;read();&lt;br /&gt;
&lt;br /&gt;
// Permanently delete the object from the database.&lt;br /&gt;
$persistent-&amp;gt;delete();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching records ===&lt;br /&gt;
&lt;br /&gt;
Once you start using persistents you should never directly interact with the database outside of your class. The persistent class comes with a few handy methods allowing you to retrieve your objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Use the constructor to fetch one object from its ID.&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Get one record from a set of conditions.&lt;br /&gt;
$persistent = status::get_record([&#039;userid&#039; =&amp;gt; $userid, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;]);&lt;br /&gt;
&lt;br /&gt;
// Get multiple records from a set of conditions.&lt;br /&gt;
$persistents = status::get_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Count the records.&lt;br /&gt;
$count = status::count_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Check whether a record exists.&lt;br /&gt;
$exists = status::record_exists($id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure to also check their additional parameters and their variants (&#039;&#039;record_exists_select()&#039;&#039;, &#039;&#039;count_records_select&#039;&#039;, &#039;&#039;get_records_select&#039;&#039;, ...).&lt;br /&gt;
&lt;br /&gt;
==== Custom fetching ====&lt;br /&gt;
&lt;br /&gt;
It&#039;s always a good idea to add more complex queries directly within your persistent. By convention you should always return an instance of your persistent and never an stdClass. Here we add a custom method which allows to directly fetch all records by username.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get all records by a user from its username&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $username The username.&lt;br /&gt;
 * @return status[]&lt;br /&gt;
 */&lt;br /&gt;
public static function get_records_by_username($username) {&lt;br /&gt;
    global $DB;&lt;br /&gt;
&lt;br /&gt;
    $sql = &#039;SELECT s.*&lt;br /&gt;
              FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
              JOIN {user} u&lt;br /&gt;
                ON u.id = s.userid&lt;br /&gt;
             WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
    $persistents = [];&lt;br /&gt;
&lt;br /&gt;
    $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
    foreach ($recordset as $record) {&lt;br /&gt;
        $persistents[] = new static(0, $record);&lt;br /&gt;
    }&lt;br /&gt;
    $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
    return $persistents;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to join persistents together and be able to extract their respective properties in a single database query, you should have a look at the following methods:&lt;br /&gt;
&lt;br /&gt;
;get_sql_fields(string $alias, string $prefix = null)&lt;br /&gt;
: Returns the SQL statement to include in the SELECT clause to prefix columns.&lt;br /&gt;
;extract_record(stdClass $row, string $prefix = null)&lt;br /&gt;
: Extracts all the properties from a row based on the given prefix.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Minimalist example.&lt;br /&gt;
$sqlfields = status::get_sql_fields(&#039;s&#039;, &#039;statprops&#039;);&lt;br /&gt;
$sql = &amp;quot;SELECT $sqlfields, u.username&lt;br /&gt;
          FROM {&amp;quot; . status::TABLE . &amp;quot;} s&lt;br /&gt;
          JOIN {user} ON s.userid = u.id&lt;br /&gt;
         WHERE s.id = 1&amp;quot;;&lt;br /&gt;
$row = $DB-&amp;gt;get_record($sql, []);&lt;br /&gt;
$statusdata = status::extract_record($row, &#039;statprops&#039;);&lt;br /&gt;
$persistent = new status(0, $statusdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Validating ===&lt;br /&gt;
&lt;br /&gt;
Basic validation of the properties values happens automatically based on their type (&#039;&#039;PARAM_*&#039;&#039; constant), however this is not always enough. In order to implement your own custom validation, simply define a &#039;&#039;protected&#039;&#039; method starting with &#039;&#039;validate_&#039;&#039; followed with the property name. This method will be called whenever the model needs to be validated and will receive the data to validate.&lt;br /&gt;
&lt;br /&gt;
A validation method must always return either &#039;&#039;true&#039;&#039; or an instance of &#039;&#039;lang_string&#039;&#039; which contains the error message to send to the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param int $value The value.&lt;br /&gt;
 * @return true|lang_string&lt;br /&gt;
 */&lt;br /&gt;
protected function validate_userid($value) {&lt;br /&gt;
    if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
        return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above example ensures that the &#039;&#039;userid&#039;&#039; property contains a valid user ID.&lt;br /&gt;
&lt;br /&gt;
Note that the basic validation is always performed first, and thus your custom validation method will not be called when the value did not pass the basic validation.&lt;br /&gt;
&lt;br /&gt;
==== Validation results ====&lt;br /&gt;
&lt;br /&gt;
The validation of the object automatically happens upon &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039;. If the validation did not pass, an &#039;&#039;invalid_persisten_exception&#039;&#039; will be raised. You can validate the object prior to saving the object and get the validation results if you need to.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// We can catch the invalid_persistent_exception.&lt;br /&gt;
try {&lt;br /&gt;
    $persistent = new status();&lt;br /&gt;
    $persistent-&amp;gt;create();&lt;br /&gt;
} catch (invalid_persistent_exception $e) {&lt;br /&gt;
    // Whoops, something wrong happened.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Check whether the object is valid.&lt;br /&gt;
$persistent-&amp;gt;is_valid();        // True or false.&lt;br /&gt;
&lt;br /&gt;
// Get the validation errors.&lt;br /&gt;
$persistent-&amp;gt;get_errors();      // Array where keys are properties and values are errors.&lt;br /&gt;
&lt;br /&gt;
// Validate the object.&lt;br /&gt;
$persistent-&amp;gt;validate();        // Returns true, or an array of errors.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
You can define the following methods to be notified prior to, or after, something happened:&lt;br /&gt;
&lt;br /&gt;
;protected before_validate()&lt;br /&gt;
: Do something before the object is validated.&lt;br /&gt;
;protected before_create()&lt;br /&gt;
: Do something before the object is inserted in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_create()&lt;br /&gt;
: Do something right after the object was added to the database.&lt;br /&gt;
;protected before_update()&lt;br /&gt;
: Do something before the object is updated in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;protected after_update(bool $result)&lt;br /&gt;
: Do something right after the object was updated in the database.&lt;br /&gt;
;protected before_delete()&lt;br /&gt;
: Do something right before the object is deleted from the database.&lt;br /&gt;
;protected after_delete(bool $result)&lt;br /&gt;
: Do something right after the object was deleted from the database.&lt;br /&gt;
&lt;br /&gt;
=== Full example ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example;&lt;br /&gt;
&lt;br /&gt;
use core\persistent;&lt;br /&gt;
use core_user;&lt;br /&gt;
use lang_string;&lt;br /&gt;
&lt;br /&gt;
class status extends persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
                &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
                &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;location&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
                &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
                &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
                &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
                    return get_config(&#039;default_location&#039;);&lt;br /&gt;
                },&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the user object of the author.&lt;br /&gt;
     *&lt;br /&gt;
     * @return stdClass&lt;br /&gt;
     */&lt;br /&gt;
    public function get_author() {&lt;br /&gt;
        return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Convenience method to set the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
     */&lt;br /&gt;
    public function set_userid($idorobject) {&lt;br /&gt;
        $userid = $idorobject;&lt;br /&gt;
        if (is_object($idorobject)) {&lt;br /&gt;
            $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Validate the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param int $value The value.&lt;br /&gt;
     * @return true|lang_string&lt;br /&gt;
     */&lt;br /&gt;
    protected function validate_userid($value) {&lt;br /&gt;
        if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
            return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get all records by a user from its username&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $username The username.&lt;br /&gt;
     * @return status[]&lt;br /&gt;
     */&lt;br /&gt;
    public static function get_records_by_username($username) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $sql = &#039;SELECT s.*&lt;br /&gt;
                  FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
                  JOIN {user} u&lt;br /&gt;
                    ON u.id = s.userid&lt;br /&gt;
                 WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
        $persistents = [];&lt;br /&gt;
&lt;br /&gt;
        $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
        foreach ($recordset as $record) {&lt;br /&gt;
            $persistents[] = new static(0, $record);&lt;br /&gt;
        }&lt;br /&gt;
        $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
        return $persistents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51585</id>
		<title>User:Frédéric Massart</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51585"/>
		<updated>2016-12-13T09:50:57Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Persistents ==&lt;br /&gt;
&lt;br /&gt;
Moodle persistents are equivalent to models (or active records). They represent an object stored in the database and provide the methods to create, read, update, and delete those objects. Additionally, the persistents validate its own data against automatic and custom validation rules.&lt;br /&gt;
&lt;br /&gt;
Persistents extend the abstract class &#039;&#039;core\persistent&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Properties ===&lt;br /&gt;
&lt;br /&gt;
Defining properties can be done by defining the method &#039;&#039;protected static define_properties()&#039;&#039;. It returns an array where the keys are the names of the fields (and database columns), and their details. The &#039;&#039;type&#039;&#039; of each field must be specified using one of the &#039;&#039;PARAM_*&#039;&#039; constants. This type will be used to automatically validate the property&#039;s value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the definition of the properties of this model.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
        )&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Properties attributes ====&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must be one of the many &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
;default&lt;br /&gt;
: The default value to attach to the property when it hasn&#039;t been provided. Alternatively this can be a &#039;&#039;Closure&#039;&#039; returning the default value.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants &#039;&#039;NULL_ALLOWED&#039;&#039; or &#039;&#039;NULL_NOT_ALLOWED&#039;&#039; determining if the null value is accepted. This defaults to &#039;&#039;NULL_NOT_ALLOWED&#039;&#039;.&lt;br /&gt;
;message&lt;br /&gt;
: The default error message (as a &#039;&#039;lang_string&#039;&#039; instance) to return when the validation of this field fails.&lt;br /&gt;
;choices&lt;br /&gt;
: An array of values which the property must fall in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
    &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
    &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
),&lt;br /&gt;
&#039;location&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
    &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
    &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
    &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
        return get_config(&#039;default_location&#039;);&lt;br /&gt;
    },&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that you should always use a &#039;&#039;Closure&#039;&#039; for the &#039;&#039;default&#039;&#039; value when you cannot guarantee that it will not change since the start of the request. The list of properties and their attributes is cached, and so failure to use a &#039;&#039;Closure&#039;&#039; can result in using an outdated default value.&lt;br /&gt;
&lt;br /&gt;
==== Mandatory properties ====&lt;br /&gt;
&lt;br /&gt;
Four fields are always added to your persistent and should be reflected in your database table. You must not define those properties inThose are:&lt;br /&gt;
&lt;br /&gt;
;id (non-null integer)&lt;br /&gt;
: The primary key of the record.&lt;br /&gt;
;usermodified (non-null integer)&lt;br /&gt;
: The user who created/modified the object. It is automatically set.&lt;br /&gt;
;timecreated (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set.&lt;br /&gt;
;timemodified (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set, and defaults to 0.&lt;br /&gt;
&lt;br /&gt;
=== Attaching to the database ===&lt;br /&gt;
&lt;br /&gt;
While the persistent class is helpful for database interactions, it does not automatically fetch the properties from the database, nor does it create the tables. You will need to create the table yourself, as well as pointing the persistent to it. This can be done by defining the constant &#039;&#039;TABLE&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** Table name for the persistent. */&lt;br /&gt;
const TABLE = &#039;status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The table name must not contain the Moodle prefix. Also it is common practice to always refer to your table name using the constant.&lt;br /&gt;
&lt;br /&gt;
=== Reading property values ===&lt;br /&gt;
&lt;br /&gt;
This can be done using the method &#039;&#039;get()&#039;&#039;, or the magic method &#039;&#039;get_&#039;&#039; followed by the property name. Alternatively you can also use &#039;&#039;to_record()&#039;&#039; which exports the whole object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Create a new object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using persistent::get().&lt;br /&gt;
$userid = $persistent-&amp;gt;get(&#039;userid&#039;);&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using the magic getter.&lt;br /&gt;
$userid = $persistent-&amp;gt;get_userid();&lt;br /&gt;
&lt;br /&gt;
// Get all the properties in an stdClass.&lt;br /&gt;
$data = $persistent-&amp;gt;to_record();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may override the magic getters to implements your own custom logic. For instance you could convert the data in another format automatically as a convenience for developers. However, use this sparingly as it may lead to confusion: what you get is not what is stored in the database. Note that you cannot guarantee that your getter will be used, remember that it is possible for developers to call the alternative &#039;&#039;get()&#039;&#039; which will not use your custom logic.&lt;br /&gt;
&lt;br /&gt;
It is, however, encouraged to add convenience methods such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns the user object of the author.&lt;br /&gt;
 *&lt;br /&gt;
 * @return stdClass&lt;br /&gt;
 */&lt;br /&gt;
public function get_author() {&lt;br /&gt;
    return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Assigning values to properties ===&lt;br /&gt;
&lt;br /&gt;
There are a few methods to do so.&lt;br /&gt;
&lt;br /&gt;
You use an object (&#039;&#039;stdClass&#039;&#039;) to assign a bunch of properties at once. Use it with the constructor, or the method &#039;&#039;from_record()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) array(&#039;userid&#039; =&amp;gt; 2, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instantiates a new object with value for some properties.&lt;br /&gt;
$persistent = new status(0, $data);&lt;br /&gt;
&lt;br /&gt;
// Is similar to.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
$persistent-&amp;gt;from_record($data);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or you can use the &#039;&#039;set()&#039;&#039; method on an instance.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set(&#039;message&#039;, Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally you can use the magic setters &#039;&#039;set_&#039;&#039; followed by the property name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Defining a custom setter ====&lt;br /&gt;
&lt;br /&gt;
Though you don&#039;t have to for the code to work, you can define your own &#039;&#039;setter&#039;&#039; methods which will override the magic setters. They are useful if you want to extract the data out of a more complex object prior to assigning it. Though note that those setters will then have to use the &#039;&#039;set()&#039;&#039; method to assign the values.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Convenience method to set the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
 */&lt;br /&gt;
public function set_userid($idorobject) {&lt;br /&gt;
    $userid = $idorobject;&lt;br /&gt;
    if (is_object($idorobject)) {&lt;br /&gt;
        $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
    }&lt;br /&gt;
    $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example we will accept an object or an ID, as a convenience for developers we will extract the ID value out of the object passed if any.&lt;br /&gt;
&lt;br /&gt;
Just like custom getters, you cannot guarantee that they will be used. Developers can directly call the &#039;&#039;set()&#039;&#039; method. Therefore you must not use a custom setter to reliably transform the data added to a property. For instance do not add a custom setter to remove HTML tags out of a text field, it may not always happen.&lt;br /&gt;
&lt;br /&gt;
You can obviously create your own setters which aren&#039;t based on any properties just as a convenience. For instance we could have created &#039;&#039;set_userid_from_user(object $user)&#039;&#039; which is more verbose and more predictable&lt;br /&gt;
&lt;br /&gt;
=== Read, save and delete entries ===&lt;br /&gt;
&lt;br /&gt;
The methods to &#039;&#039;create&#039;&#039;, &#039;&#039;read&#039;&#039;, &#039;&#039;update&#039;&#039; and &#039;&#039;delete&#039;&#039; are eponymous. Your object will be validated before you &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; it. The &#039;&#039;update&#039;&#039;, &#039;&#039;delete&#039;&#039; and &#039;&#039;read&#039;&#039; methods require your object to contain its ID. And you also won&#039;t be allowed to &#039;&#039;create&#039;&#039; an entry which already had an ID defined.&lt;br /&gt;
&lt;br /&gt;
Here are some code examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Fetches an object from database based on its ID.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Create previously instantiated object in the database.&lt;br /&gt;
$persistent-&amp;gt;create();&lt;br /&gt;
&lt;br /&gt;
// Load an object from the database, and update it.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
$persistent-&amp;gt;update();&lt;br /&gt;
&lt;br /&gt;
// Reset the instance to the values in the database.&lt;br /&gt;
$persistent-&amp;gt;read();&lt;br /&gt;
&lt;br /&gt;
// Permanently delete the object from the database.&lt;br /&gt;
$persistent-&amp;gt;delete();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching records ===&lt;br /&gt;
&lt;br /&gt;
Once you start using persistents you should never directly interact with the database outside of your class. The persistent class comes with a few handy methods allowing you to retrieve your objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Use the constructor to fetch one object from its ID.&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Get one record from a set of conditions.&lt;br /&gt;
$persistent = status::get_record([&#039;userid&#039; =&amp;gt; $userid, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;]);&lt;br /&gt;
&lt;br /&gt;
// Get multiple records from a set of conditions.&lt;br /&gt;
$persistents = status::get_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Count the records.&lt;br /&gt;
$count = status::count_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Check whether a record exists.&lt;br /&gt;
$exists = status::record_exists($id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure to also check their additional parameters and their variants (&#039;&#039;record_exists_select()&#039;&#039;, &#039;&#039;count_records_select&#039;&#039;, &#039;&#039;get_records_select&#039;&#039;, ...).&lt;br /&gt;
&lt;br /&gt;
==== Custom fetching ====&lt;br /&gt;
&lt;br /&gt;
It&#039;s always a good idea to add more complex queries directly within your persistent. By convention you should always return an instance of your persistent and never an stdClass. Here we add a custom method which allows to directly fetch all records by username.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get all records by a user from its username&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $username The username.&lt;br /&gt;
 * @return status[]&lt;br /&gt;
 */&lt;br /&gt;
public static function get_records_by_username($username) {&lt;br /&gt;
    global $DB;&lt;br /&gt;
&lt;br /&gt;
    $sql = &#039;SELECT s.*&lt;br /&gt;
              FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
              JOIN {user} u&lt;br /&gt;
                ON u.id = s.userid&lt;br /&gt;
             WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
    $persistents = [];&lt;br /&gt;
&lt;br /&gt;
    $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
    foreach ($recordset as $record) {&lt;br /&gt;
        $persistents[] = new static(0, $record);&lt;br /&gt;
    }&lt;br /&gt;
    $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
    return $persistents;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to join persistents together and be able to extract their respective properties in a single database query, you should have a look at the following methods:&lt;br /&gt;
&lt;br /&gt;
;get_sql_fields(string $alias, string $prefix = null)&lt;br /&gt;
: Returns the SQL statement to include in the SELECT clause to prefix columns.&lt;br /&gt;
;extract_record(stdClass $row, string $prefix = null)&lt;br /&gt;
: Extracts all the properties from a row based on the given prefix.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Minimalist example.&lt;br /&gt;
$sqlfields = status::get_sql_fields(&#039;s&#039;, &#039;statprops&#039;);&lt;br /&gt;
$sql = &amp;quot;SELECT $sqlfields, u.username&lt;br /&gt;
          FROM {&amp;quot; . status::TABLE . &amp;quot;} s&lt;br /&gt;
          JOIN {user} ON s.userid = u.id&lt;br /&gt;
         WHERE s.id = 1&amp;quot;;&lt;br /&gt;
$row = $DB-&amp;gt;get_record($sql, []);&lt;br /&gt;
$statusdata = status::extract_record($row, &#039;statprops&#039;);&lt;br /&gt;
$persistent = new status(0, $statusdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Validating ===&lt;br /&gt;
&lt;br /&gt;
Basic validation of the properties values happens automatically based on their type (&#039;&#039;PARAM_*&#039;&#039; constant), however this is not always enough. In order to implement your own custom validation, simply define a &#039;&#039;protected&#039;&#039; method starting with &#039;&#039;validate_&#039;&#039; followed with the property name. This method will be called whenever the model needs to be validated and will receive the data to validate.&lt;br /&gt;
&lt;br /&gt;
A validation method must always return either &#039;&#039;true&#039;&#039; or an instance of &#039;&#039;lang_string&#039;&#039; which contains the error message to send to the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param int $value The value.&lt;br /&gt;
 * @return true|lang_string&lt;br /&gt;
 */&lt;br /&gt;
protected function validate_userid($value) {&lt;br /&gt;
    if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
        return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above example ensures that the &#039;&#039;userid&#039;&#039; property contains a valid user ID.&lt;br /&gt;
&lt;br /&gt;
Note that the basic validation is always performed first, and thus your custom validation method will not be called when the value did not pass the basic validation.&lt;br /&gt;
&lt;br /&gt;
==== Validation results ====&lt;br /&gt;
&lt;br /&gt;
The validation of the object automatically happens upon &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039;. If the validation did not pass, an &#039;&#039;invalid_persisten_exception&#039;&#039; will be raised. You can validate the object prior to saving the object and get the validation results if you need to.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// We can catch the invalid_persistent_exception.&lt;br /&gt;
try {&lt;br /&gt;
    $persistent = new status();&lt;br /&gt;
    $persistent-&amp;gt;create();&lt;br /&gt;
} catch (invalid_persistent_exception $e) {&lt;br /&gt;
    // Whoops, something wrong happened.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Check whether the object is valid.&lt;br /&gt;
$persistent-&amp;gt;is_valid();        // True or false.&lt;br /&gt;
&lt;br /&gt;
// Get the validation errors.&lt;br /&gt;
$persistent-&amp;gt;get_errors();      // Array where keys are properties and values are errors.&lt;br /&gt;
&lt;br /&gt;
// Validate the object.&lt;br /&gt;
$persistent-&amp;gt;validate();        // Returns true, or an array of errors.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
You can define the following methods to be notified prior to, or after, something happened:&lt;br /&gt;
&lt;br /&gt;
;before_validate()&lt;br /&gt;
: Do something before the object is validated.&lt;br /&gt;
;before_create()&lt;br /&gt;
: Do something before the object is inserted in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;after_create()&lt;br /&gt;
: Do something right after the object was added to the database.&lt;br /&gt;
;before_update()&lt;br /&gt;
: Do something before the object is updated in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;after_update(bool $result)&lt;br /&gt;
: Do something right after the object was updated in the database.&lt;br /&gt;
;before_delete()&lt;br /&gt;
: Do something right before the object is deleted from the database.&lt;br /&gt;
;after_delete(bool $result)&lt;br /&gt;
: Do something right after the object was deleted from the database.&lt;br /&gt;
&lt;br /&gt;
=== Full example ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example;&lt;br /&gt;
&lt;br /&gt;
use core\persistent;&lt;br /&gt;
use core_user;&lt;br /&gt;
use lang_string;&lt;br /&gt;
&lt;br /&gt;
class status extends persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
                &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
                &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;location&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
                &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
                &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
                &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
                    return get_config(&#039;default_location&#039;);&lt;br /&gt;
                },&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the user object of the author.&lt;br /&gt;
     *&lt;br /&gt;
     * @return stdClass&lt;br /&gt;
     */&lt;br /&gt;
    public function get_author() {&lt;br /&gt;
        return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Convenience method to set the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
     */&lt;br /&gt;
    public function set_userid($idorobject) {&lt;br /&gt;
        $userid = $idorobject;&lt;br /&gt;
        if (is_object($idorobject)) {&lt;br /&gt;
            $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Validate the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param int $value The value.&lt;br /&gt;
     * @return true|lang_string&lt;br /&gt;
     */&lt;br /&gt;
    protected function validate_userid($value) {&lt;br /&gt;
        if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
            return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get all records by a user from its username&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $username The username.&lt;br /&gt;
     * @return status[]&lt;br /&gt;
     */&lt;br /&gt;
    public static function get_records_by_username($username) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $sql = &#039;SELECT s.*&lt;br /&gt;
                  FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
                  JOIN {user} u&lt;br /&gt;
                    ON u.id = s.userid&lt;br /&gt;
                 WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
        $persistents = [];&lt;br /&gt;
&lt;br /&gt;
        $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
        foreach ($recordset as $record) {&lt;br /&gt;
            $persistents[] = new static(0, $record);&lt;br /&gt;
        }&lt;br /&gt;
        $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
        return $persistents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51584</id>
		<title>User:Frédéric Massart</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51584"/>
		<updated>2016-12-13T09:50:00Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Persistents ==&lt;br /&gt;
&lt;br /&gt;
Moodle persistents are equivalent to models (or active records). They represent an object stored in the database and provide the methods to create, read, update, and delete those objects. Additionally, the persistents validate its own data against automatic and custom validation rules.&lt;br /&gt;
&lt;br /&gt;
Persistents extend the abstract class &#039;&#039;core\persistent&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Properties ===&lt;br /&gt;
&lt;br /&gt;
Defining properties can be done by defining the method &#039;&#039;protected static define_properties()&#039;&#039;. It returns an array where the keys are the names of the fields (and database columns), and their details. The &#039;&#039;type&#039;&#039; of each field must be specified using one of the &#039;&#039;PARAM_*&#039;&#039; constants. This type will be used to automatically validate the property&#039;s value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the definition of the properties of this model.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
        )&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Properties attributes ====&lt;br /&gt;
&lt;br /&gt;
;type&lt;br /&gt;
: The only mandatory attribute. It must be one of the many &#039;&#039;PARAM_*&#039;&#039; constants.&lt;br /&gt;
;default&lt;br /&gt;
: The default value to attach to the property when it hasn&#039;t been provided. Alternatively this can be a &#039;&#039;Closure&#039;&#039; returning the default value.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED determining if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;message&lt;br /&gt;
: The default error message (as a lang_string instance) to return when the validation of this field fails.&lt;br /&gt;
;choices&lt;br /&gt;
: An array of values which the property must fall in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
    &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
    &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
),&lt;br /&gt;
&#039;location&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
    &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
    &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
    &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
        return get_config(&#039;default_location&#039;);&lt;br /&gt;
    },&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that you should always use a &#039;&#039;Closure&#039;&#039; for the &#039;&#039;default&#039;&#039; value when you cannot guarantee that it will not change since the start of the request. The list of properties and their attributes is cached, and so failure to use a &#039;&#039;Closure&#039;&#039; can result in using an outdated default value.&lt;br /&gt;
&lt;br /&gt;
==== Mandatory properties ====&lt;br /&gt;
&lt;br /&gt;
Four fields are always added to your persistent and should be reflected in your database table. You must not define those properties inThose are:&lt;br /&gt;
&lt;br /&gt;
;id (non-null integer)&lt;br /&gt;
: The primary key of the record.&lt;br /&gt;
;usermodified (non-null integer)&lt;br /&gt;
: The user who created/modified the object. It is automatically set.&lt;br /&gt;
;timecreated (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set.&lt;br /&gt;
;timemodified (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set, and defaults to 0.&lt;br /&gt;
&lt;br /&gt;
=== Attaching to the database ===&lt;br /&gt;
&lt;br /&gt;
While the persistent class is helpful for database interactions, it does not automatically fetch the properties from the database, nor does it create the tables. You will need to create the table yourself, as well as pointing the persistent to it. This can be done by defining the constant &#039;&#039;TABLE&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** Table name for the persistent. */&lt;br /&gt;
const TABLE = &#039;status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The table name must not contain the Moodle prefix. Also it is common practice to always refer to your table name using the constant.&lt;br /&gt;
&lt;br /&gt;
=== Reading property values ===&lt;br /&gt;
&lt;br /&gt;
This can be done using the method &#039;&#039;get()&#039;&#039;, or the magic method &#039;&#039;get_&#039;&#039; followed by the property name. Alternatively you can also use &#039;&#039;to_record()&#039;&#039; which exports the whole object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Create a new object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using persistent::get().&lt;br /&gt;
$userid = $persistent-&amp;gt;get(&#039;userid&#039;);&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using the magic getter.&lt;br /&gt;
$userid = $persistent-&amp;gt;get_userid();&lt;br /&gt;
&lt;br /&gt;
// Get all the properties in an stdClass.&lt;br /&gt;
$data = $persistent-&amp;gt;to_record();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may override the magic getters to implements your own custom logic. For instance you could convert the data in another format automatically as a convenience for developers. However, use this sparingly as it may lead to confusion: what you get is not what is stored in the database. Note that you cannot guarantee that your getter will be used, remember that it is possible for developers to call the alternative &#039;&#039;get()&#039;&#039; which will not use your custom logic.&lt;br /&gt;
&lt;br /&gt;
It is, however, encouraged to add convenience methods such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns the user object of the author.&lt;br /&gt;
 *&lt;br /&gt;
 * @return stdClass&lt;br /&gt;
 */&lt;br /&gt;
public function get_author() {&lt;br /&gt;
    return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Assigning values to properties ===&lt;br /&gt;
&lt;br /&gt;
There are a few methods to do so.&lt;br /&gt;
&lt;br /&gt;
You use an object (&#039;&#039;stdClass&#039;&#039;) to assign a bunch of properties at once. Use it with the constructor, or the method &#039;&#039;from_record()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) array(&#039;userid&#039; =&amp;gt; 2, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instantiates a new object with value for some properties.&lt;br /&gt;
$persistent = new status(0, $data);&lt;br /&gt;
&lt;br /&gt;
// Is similar to.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
$persistent-&amp;gt;from_record($data);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or you can use the &#039;&#039;set()&#039;&#039; method on an instance.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set(&#039;message&#039;, Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally you can use the magic setters &#039;&#039;set_&#039;&#039; followed by the property name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Defining a custom setter ====&lt;br /&gt;
&lt;br /&gt;
Though you don&#039;t have to for the code to work, you can define your own &#039;&#039;setter&#039;&#039; methods which will override the magic setters. They are useful if you want to extract the data out of a more complex object prior to assigning it. Though note that those setters will then have to use the &#039;&#039;set()&#039;&#039; method to assign the values.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Convenience method to set the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
 */&lt;br /&gt;
public function set_userid($idorobject) {&lt;br /&gt;
    $userid = $idorobject;&lt;br /&gt;
    if (is_object($idorobject)) {&lt;br /&gt;
        $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
    }&lt;br /&gt;
    $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example we will accept an object or an ID, as a convenience for developers we will extract the ID value out of the object passed if any.&lt;br /&gt;
&lt;br /&gt;
Just like custom getters, you cannot guarantee that they will be used. Developers can directly call the &#039;&#039;set()&#039;&#039; method. Therefore you must not use a custom setter to reliably transform the data added to a property. For instance do not add a custom setter to remove HTML tags out of a text field, it may not always happen.&lt;br /&gt;
&lt;br /&gt;
You can obviously create your own setters which aren&#039;t based on any properties just as a convenience. For instance we could have created &#039;&#039;set_userid_from_user(object $user)&#039;&#039; which is more verbose and more predictable&lt;br /&gt;
&lt;br /&gt;
=== Read, save and delete entries ===&lt;br /&gt;
&lt;br /&gt;
The methods to &#039;&#039;create&#039;&#039;, &#039;&#039;read&#039;&#039;, &#039;&#039;update&#039;&#039; and &#039;&#039;delete&#039;&#039; are eponymous. Your object will be validated before you &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; it. The &#039;&#039;update&#039;&#039;, &#039;&#039;delete&#039;&#039; and &#039;&#039;read&#039;&#039; methods require your object to contain its ID. And you also won&#039;t be allowed to &#039;&#039;create&#039;&#039; an entry which already had an ID defined.&lt;br /&gt;
&lt;br /&gt;
Here are some code examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Fetches an object from database based on its ID.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Create previously instantiated object in the database.&lt;br /&gt;
$persistent-&amp;gt;create();&lt;br /&gt;
&lt;br /&gt;
// Load an object from the database, and update it.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
$persistent-&amp;gt;update();&lt;br /&gt;
&lt;br /&gt;
// Reset the instance to the values in the database.&lt;br /&gt;
$persistent-&amp;gt;read();&lt;br /&gt;
&lt;br /&gt;
// Permanently delete the object from the database.&lt;br /&gt;
$persistent-&amp;gt;delete();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching records ===&lt;br /&gt;
&lt;br /&gt;
Once you start using persistents you should never directly interact with the database outside of your class. The persistent class comes with a few handy methods allowing you to retrieve your objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Use the constructor to fetch one object from its ID.&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Get one record from a set of conditions.&lt;br /&gt;
$persistent = status::get_record([&#039;userid&#039; =&amp;gt; $userid, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;]);&lt;br /&gt;
&lt;br /&gt;
// Get multiple records from a set of conditions.&lt;br /&gt;
$persistents = status::get_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Count the records.&lt;br /&gt;
$count = status::count_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Check whether a record exists.&lt;br /&gt;
$exists = status::record_exists($id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure to also check their additional parameters and their variants (&#039;&#039;record_exists_select()&#039;&#039;, &#039;&#039;count_records_select&#039;&#039;, &#039;&#039;get_records_select&#039;&#039;, ...).&lt;br /&gt;
&lt;br /&gt;
==== Custom fetching ====&lt;br /&gt;
&lt;br /&gt;
It&#039;s always a good idea to add more complex queries directly within your persistent. By convention you should always return an instance of your persistent and never an stdClass. Here we add a custom method which allows to directly fetch all records by username.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get all records by a user from its username&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $username The username.&lt;br /&gt;
 * @return status[]&lt;br /&gt;
 */&lt;br /&gt;
public static function get_records_by_username($username) {&lt;br /&gt;
    global $DB;&lt;br /&gt;
&lt;br /&gt;
    $sql = &#039;SELECT s.*&lt;br /&gt;
              FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
              JOIN {user} u&lt;br /&gt;
                ON u.id = s.userid&lt;br /&gt;
             WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
    $persistents = [];&lt;br /&gt;
&lt;br /&gt;
    $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
    foreach ($recordset as $record) {&lt;br /&gt;
        $persistents[] = new static(0, $record);&lt;br /&gt;
    }&lt;br /&gt;
    $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
    return $persistents;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to join persistents together and be able to extract their respective properties in a single database query, you should have a look at the following methods:&lt;br /&gt;
&lt;br /&gt;
;get_sql_fields(string $alias, string $prefix = null)&lt;br /&gt;
: Returns the SQL statement to include in the SELECT clause to prefix columns.&lt;br /&gt;
;extract_record(stdClass $row, string $prefix = null)&lt;br /&gt;
: Extracts all the properties from a row based on the given prefix.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Minimalist example.&lt;br /&gt;
$sqlfields = status::get_sql_fields(&#039;s&#039;, &#039;statprops&#039;);&lt;br /&gt;
$sql = &amp;quot;SELECT $sqlfields, u.username&lt;br /&gt;
          FROM {&amp;quot; . status::TABLE . &amp;quot;} s&lt;br /&gt;
          JOIN {user} ON s.userid = u.id&lt;br /&gt;
         WHERE s.id = 1&amp;quot;;&lt;br /&gt;
$row = $DB-&amp;gt;get_record($sql, []);&lt;br /&gt;
$statusdata = status::extract_record($row, &#039;statprops&#039;);&lt;br /&gt;
$persistent = new status(0, $statusdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Validating ===&lt;br /&gt;
&lt;br /&gt;
Basic validation of the properties values happens automatically based on their type (&#039;&#039;PARAM_*&#039;&#039; constant), however this is not always enough. In order to implement your own custom validation, simply define a &#039;&#039;protected&#039;&#039; method starting with &#039;&#039;validate_&#039;&#039; followed with the property name. This method will be called whenever the model needs to be validated and will receive the data to validate.&lt;br /&gt;
&lt;br /&gt;
A validation method must always return either &#039;&#039;true&#039;&#039; or an instance of &#039;&#039;lang_string&#039;&#039; which contains the error message to send to the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param int $value The value.&lt;br /&gt;
 * @return true|lang_string&lt;br /&gt;
 */&lt;br /&gt;
protected function validate_userid($value) {&lt;br /&gt;
    if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
        return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above example ensures that the &#039;&#039;userid&#039;&#039; property contains a valid user ID.&lt;br /&gt;
&lt;br /&gt;
Note that the basic validation is always performed first, and thus your custom validation method will not be called when the value did not pass the basic validation.&lt;br /&gt;
&lt;br /&gt;
==== Validation results ====&lt;br /&gt;
&lt;br /&gt;
The validation of the object automatically happens upon &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039;. If the validation did not pass, an &#039;&#039;invalid_persisten_exception&#039;&#039; will be raised. You can validate the object prior to saving the object and get the validation results if you need to.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// We can catch the invalid_persistent_exception.&lt;br /&gt;
try {&lt;br /&gt;
    $persistent = new status();&lt;br /&gt;
    $persistent-&amp;gt;create();&lt;br /&gt;
} catch (invalid_persistent_exception $e) {&lt;br /&gt;
    // Whoops, something wrong happened.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Check whether the object is valid.&lt;br /&gt;
$persistent-&amp;gt;is_valid();        // True or false.&lt;br /&gt;
&lt;br /&gt;
// Get the validation errors.&lt;br /&gt;
$persistent-&amp;gt;get_errors();      // Array where keys are properties and values are errors.&lt;br /&gt;
&lt;br /&gt;
// Validate the object.&lt;br /&gt;
$persistent-&amp;gt;validate();        // Returns true, or an array of errors.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
You can define the following methods to be notified prior to, or after, something happened:&lt;br /&gt;
&lt;br /&gt;
;before_validate()&lt;br /&gt;
: Do something before the object is validated.&lt;br /&gt;
;before_create()&lt;br /&gt;
: Do something before the object is inserted in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;after_create()&lt;br /&gt;
: Do something right after the object was added to the database.&lt;br /&gt;
;before_update()&lt;br /&gt;
: Do something before the object is updated in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;after_update(bool $result)&lt;br /&gt;
: Do something right after the object was updated in the database.&lt;br /&gt;
;before_delete()&lt;br /&gt;
: Do something right before the object is deleted from the database.&lt;br /&gt;
;after_delete(bool $result)&lt;br /&gt;
: Do something right after the object was deleted from the database.&lt;br /&gt;
&lt;br /&gt;
=== Full example ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example;&lt;br /&gt;
&lt;br /&gt;
use core\persistent;&lt;br /&gt;
use core_user;&lt;br /&gt;
use lang_string;&lt;br /&gt;
&lt;br /&gt;
class status extends persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
                &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
                &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;location&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
                &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
                &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
                &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
                    return get_config(&#039;default_location&#039;);&lt;br /&gt;
                },&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the user object of the author.&lt;br /&gt;
     *&lt;br /&gt;
     * @return stdClass&lt;br /&gt;
     */&lt;br /&gt;
    public function get_author() {&lt;br /&gt;
        return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Convenience method to set the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
     */&lt;br /&gt;
    public function set_userid($idorobject) {&lt;br /&gt;
        $userid = $idorobject;&lt;br /&gt;
        if (is_object($idorobject)) {&lt;br /&gt;
            $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Validate the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param int $value The value.&lt;br /&gt;
     * @return true|lang_string&lt;br /&gt;
     */&lt;br /&gt;
    protected function validate_userid($value) {&lt;br /&gt;
        if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
            return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get all records by a user from its username&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $username The username.&lt;br /&gt;
     * @return status[]&lt;br /&gt;
     */&lt;br /&gt;
    public static function get_records_by_username($username) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $sql = &#039;SELECT s.*&lt;br /&gt;
                  FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
                  JOIN {user} u&lt;br /&gt;
                    ON u.id = s.userid&lt;br /&gt;
                 WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
        $persistents = [];&lt;br /&gt;
&lt;br /&gt;
        $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
        foreach ($recordset as $record) {&lt;br /&gt;
            $persistents[] = new static(0, $record);&lt;br /&gt;
        }&lt;br /&gt;
        $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
        return $persistents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51583</id>
		<title>User:Frédéric Massart</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51583"/>
		<updated>2016-12-13T09:49:06Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Persistents ==&lt;br /&gt;
&lt;br /&gt;
Moodle persistents are equivalent to models (or active records). They represent an object stored in the database and provide the methods to create, read, update, and delete those objects. Additionally, the persistents validate its own data against automatic and custom validation rules.&lt;br /&gt;
&lt;br /&gt;
Persistents extend the abstract class &#039;&#039;core\persistent&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Properties ===&lt;br /&gt;
&lt;br /&gt;
Defining properties can be done by defining the method &#039;&#039;protected static define_properties()&#039;&#039;. It returns an array where the keys are the names of the fields (and database columns), and their details. The &#039;&#039;type&#039;&#039; of each field must be specified using one of the &#039;&#039;PARAM_*&#039;&#039; constants. This type will be used to automatically validate the property&#039;s value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the definition of the properties of this model.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
        )&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Properties attributes ====&lt;br /&gt;
&lt;br /&gt;
There are a few other attributes available apart from the mandatory &#039;&#039;type&#039;&#039; one.&lt;br /&gt;
&lt;br /&gt;
;default&lt;br /&gt;
: The default value to attach to the property when it hasn&#039;t been provided. Alternatively this can be a &#039;&#039;Closure&#039;&#039; returning the default value.&lt;br /&gt;
;null&lt;br /&gt;
: Either of the constants NULL_ALLOWED or NULL_NOT_ALLOWED determining if the null value is accepted. This defaults to NULL_NOT_ALLOWED.&lt;br /&gt;
;message&lt;br /&gt;
: The default error message (as a lang_string instance) to return when the validation of this field fails.&lt;br /&gt;
;choices&lt;br /&gt;
: An array of values which the property must fall in.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
    &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
    &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
),&lt;br /&gt;
&#039;location&#039; =&amp;gt; array(&lt;br /&gt;
    &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
    &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
    &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
    &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
        return get_config(&#039;default_location&#039;);&lt;br /&gt;
    },&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that you should always use a &#039;&#039;Closure&#039;&#039; for the &#039;&#039;default&#039;&#039; value when you cannot guarantee that it will not change since the start of the request. The list of properties and their attributes is cached, and so failure to use a &#039;&#039;Closure&#039;&#039; can result in using an outdated default value.&lt;br /&gt;
&lt;br /&gt;
==== Mandatory properties ====&lt;br /&gt;
&lt;br /&gt;
Four fields are always added to your persistent and should be reflected in your database table. You must not define those properties inThose are:&lt;br /&gt;
&lt;br /&gt;
;id (non-null integer)&lt;br /&gt;
: The primary key of the record.&lt;br /&gt;
;usermodified (non-null integer)&lt;br /&gt;
: The user who created/modified the object. It is automatically set.&lt;br /&gt;
;timecreated (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set.&lt;br /&gt;
;timemodified (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set, and defaults to 0.&lt;br /&gt;
&lt;br /&gt;
=== Attaching to the database ===&lt;br /&gt;
&lt;br /&gt;
While the persistent class is helpful for database interactions, it does not automatically fetch the properties from the database, nor does it create the tables. You will need to create the table yourself, as well as pointing the persistent to it. This can be done by defining the constant &#039;&#039;TABLE&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/** Table name for the persistent. */&lt;br /&gt;
const TABLE = &#039;status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The table name must not contain the Moodle prefix. Also it is common practice to always refer to your table name using the constant.&lt;br /&gt;
&lt;br /&gt;
=== Reading property values ===&lt;br /&gt;
&lt;br /&gt;
This can be done using the method &#039;&#039;get()&#039;&#039;, or the magic method &#039;&#039;get_&#039;&#039; followed by the property name. Alternatively you can also use &#039;&#039;to_record()&#039;&#039; which exports the whole object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Create a new object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using persistent::get().&lt;br /&gt;
$userid = $persistent-&amp;gt;get(&#039;userid&#039;);&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using the magic getter.&lt;br /&gt;
$userid = $persistent-&amp;gt;get_userid();&lt;br /&gt;
&lt;br /&gt;
// Get all the properties in an stdClass.&lt;br /&gt;
$data = $persistent-&amp;gt;to_record();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may override the magic getters to implements your own custom logic. For instance you could convert the data in another format automatically as a convenience for developers. However, use this sparingly as it may lead to confusion: what you get is not what is stored in the database. Note that you cannot guarantee that your getter will be used, remember that it is possible for developers to call the alternative &#039;&#039;get()&#039;&#039; which will not use your custom logic.&lt;br /&gt;
&lt;br /&gt;
It is, however, encouraged to add convenience methods such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns the user object of the author.&lt;br /&gt;
 *&lt;br /&gt;
 * @return stdClass&lt;br /&gt;
 */&lt;br /&gt;
public function get_author() {&lt;br /&gt;
    return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Assigning values to properties ===&lt;br /&gt;
&lt;br /&gt;
There are a few methods to do so.&lt;br /&gt;
&lt;br /&gt;
You use an object (&#039;&#039;stdClass&#039;&#039;) to assign a bunch of properties at once. Use it with the constructor, or the method &#039;&#039;from_record()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$data = (object) array(&#039;userid&#039; =&amp;gt; 2, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instantiates a new object with value for some properties.&lt;br /&gt;
$persistent = new status(0, $data);&lt;br /&gt;
&lt;br /&gt;
// Is similar to.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
$persistent-&amp;gt;from_record($data);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or you can use the &#039;&#039;set()&#039;&#039; method on an instance.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set(&#039;message&#039;, Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally you can use the magic setters &#039;&#039;set_&#039;&#039; followed by the property name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Defining a custom setter ====&lt;br /&gt;
&lt;br /&gt;
Though you don&#039;t have to for the code to work, you can define your own &#039;&#039;setter&#039;&#039; methods which will override the magic setters. They are useful if you want to extract the data out of a more complex object prior to assigning it. Though note that those setters will then have to use the &#039;&#039;set()&#039;&#039; method to assign the values.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Convenience method to set the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
 */&lt;br /&gt;
public function set_userid($idorobject) {&lt;br /&gt;
    $userid = $idorobject;&lt;br /&gt;
    if (is_object($idorobject)) {&lt;br /&gt;
        $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
    }&lt;br /&gt;
    $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example we will accept an object or an ID, as a convenience for developers we will extract the ID value out of the object passed if any.&lt;br /&gt;
&lt;br /&gt;
Just like custom getters, you cannot guarantee that they will be used. Developers can directly call the &#039;&#039;set()&#039;&#039; method. Therefore you must not use a custom setter to reliably transform the data added to a property. For instance do not add a custom setter to remove HTML tags out of a text field, it may not always happen.&lt;br /&gt;
&lt;br /&gt;
You can obviously create your own setters which aren&#039;t based on any properties just as a convenience. For instance we could have created &#039;&#039;set_userid_from_user(object $user)&#039;&#039; which is more verbose and more predictable&lt;br /&gt;
&lt;br /&gt;
=== Read, save and delete entries ===&lt;br /&gt;
&lt;br /&gt;
The methods to &#039;&#039;create&#039;&#039;, &#039;&#039;read&#039;&#039;, &#039;&#039;update&#039;&#039; and &#039;&#039;delete&#039;&#039; are eponymous. Your object will be validated before you &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; it. The &#039;&#039;update&#039;&#039;, &#039;&#039;delete&#039;&#039; and &#039;&#039;read&#039;&#039; methods require your object to contain its ID. And you also won&#039;t be allowed to &#039;&#039;create&#039;&#039; an entry which already had an ID defined.&lt;br /&gt;
&lt;br /&gt;
Here are some code examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Fetches an object from database based on its ID.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Create previously instantiated object in the database.&lt;br /&gt;
$persistent-&amp;gt;create();&lt;br /&gt;
&lt;br /&gt;
// Load an object from the database, and update it.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
$persistent-&amp;gt;update();&lt;br /&gt;
&lt;br /&gt;
// Reset the instance to the values in the database.&lt;br /&gt;
$persistent-&amp;gt;read();&lt;br /&gt;
&lt;br /&gt;
// Permanently delete the object from the database.&lt;br /&gt;
$persistent-&amp;gt;delete();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching records ===&lt;br /&gt;
&lt;br /&gt;
Once you start using persistents you should never directly interact with the database outside of your class. The persistent class comes with a few handy methods allowing you to retrieve your objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Use the constructor to fetch one object from its ID.&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Get one record from a set of conditions.&lt;br /&gt;
$persistent = status::get_record([&#039;userid&#039; =&amp;gt; $userid, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;]);&lt;br /&gt;
&lt;br /&gt;
// Get multiple records from a set of conditions.&lt;br /&gt;
$persistents = status::get_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Count the records.&lt;br /&gt;
$count = status::count_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Check whether a record exists.&lt;br /&gt;
$exists = status::record_exists($id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure to also check their additional parameters and their variants (&#039;&#039;record_exists_select()&#039;&#039;, &#039;&#039;count_records_select&#039;&#039;, &#039;&#039;get_records_select&#039;&#039;, ...).&lt;br /&gt;
&lt;br /&gt;
==== Custom fetching ====&lt;br /&gt;
&lt;br /&gt;
It&#039;s always a good idea to add more complex queries directly within your persistent. By convention you should always return an instance of your persistent and never an stdClass. Here we add a custom method which allows to directly fetch all records by username.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get all records by a user from its username&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $username The username.&lt;br /&gt;
 * @return status[]&lt;br /&gt;
 */&lt;br /&gt;
public static function get_records_by_username($username) {&lt;br /&gt;
    global $DB;&lt;br /&gt;
&lt;br /&gt;
    $sql = &#039;SELECT s.*&lt;br /&gt;
              FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
              JOIN {user} u&lt;br /&gt;
                ON u.id = s.userid&lt;br /&gt;
             WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
    $persistents = [];&lt;br /&gt;
&lt;br /&gt;
    $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
    foreach ($recordset as $record) {&lt;br /&gt;
        $persistents[] = new static(0, $record);&lt;br /&gt;
    }&lt;br /&gt;
    $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
    return $persistents;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to join persistents together and be able to extract their respective properties in a single database query, you should have a look at the following methods:&lt;br /&gt;
&lt;br /&gt;
;get_sql_fields(string $alias, string $prefix = null)&lt;br /&gt;
: Returns the SQL statement to include in the SELECT clause to prefix columns.&lt;br /&gt;
;extract_record(stdClass $row, string $prefix = null)&lt;br /&gt;
: Extracts all the properties from a row based on the given prefix.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// Minimalist example.&lt;br /&gt;
$sqlfields = status::get_sql_fields(&#039;s&#039;, &#039;statprops&#039;);&lt;br /&gt;
$sql = &amp;quot;SELECT $sqlfields, u.username&lt;br /&gt;
          FROM {&amp;quot; . status::TABLE . &amp;quot;} s&lt;br /&gt;
          JOIN {user} ON s.userid = u.id&lt;br /&gt;
         WHERE s.id = 1&amp;quot;;&lt;br /&gt;
$row = $DB-&amp;gt;get_record($sql, []);&lt;br /&gt;
$statusdata = status::extract_record($row, &#039;statprops&#039;);&lt;br /&gt;
$persistent = new status(0, $statusdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Validating ===&lt;br /&gt;
&lt;br /&gt;
Basic validation of the properties values happens automatically based on their type (&#039;&#039;PARAM_*&#039;&#039; constant), however this is not always enough. In order to implement your own custom validation, simply define a &#039;&#039;protected&#039;&#039; method starting with &#039;&#039;validate_&#039;&#039; followed with the property name. This method will be called whenever the model needs to be validated and will receive the data to validate.&lt;br /&gt;
&lt;br /&gt;
A validation method must always return either &#039;&#039;true&#039;&#039; or an instance of &#039;&#039;lang_string&#039;&#039; which contains the error message to send to the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param int $value The value.&lt;br /&gt;
 * @return true|lang_string&lt;br /&gt;
 */&lt;br /&gt;
protected function validate_userid($value) {&lt;br /&gt;
    if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
        return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above example ensures that the &#039;&#039;userid&#039;&#039; property contains a valid user ID.&lt;br /&gt;
&lt;br /&gt;
Note that the basic validation is always performed first, and thus your custom validation method will not be called when the value did not pass the basic validation.&lt;br /&gt;
&lt;br /&gt;
==== Validation results ====&lt;br /&gt;
&lt;br /&gt;
The validation of the object automatically happens upon &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039;. If the validation did not pass, an &#039;&#039;invalid_persisten_exception&#039;&#039; will be raised. You can validate the object prior to saving the object and get the validation results if you need to.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
// We can catch the invalid_persistent_exception.&lt;br /&gt;
try {&lt;br /&gt;
    $persistent = new status();&lt;br /&gt;
    $persistent-&amp;gt;create();&lt;br /&gt;
} catch (invalid_persistent_exception $e) {&lt;br /&gt;
    // Whoops, something wrong happened.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Check whether the object is valid.&lt;br /&gt;
$persistent-&amp;gt;is_valid();        // True or false.&lt;br /&gt;
&lt;br /&gt;
// Get the validation errors.&lt;br /&gt;
$persistent-&amp;gt;get_errors();      // Array where keys are properties and values are errors.&lt;br /&gt;
&lt;br /&gt;
// Validate the object.&lt;br /&gt;
$persistent-&amp;gt;validate();        // Returns true, or an array of errors.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
You can define the following methods to be notified prior to, or after, something happened:&lt;br /&gt;
&lt;br /&gt;
;before_validate()&lt;br /&gt;
: Do something before the object is validated.&lt;br /&gt;
;before_create()&lt;br /&gt;
: Do something before the object is inserted in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;after_create()&lt;br /&gt;
: Do something right after the object was added to the database.&lt;br /&gt;
;before_update()&lt;br /&gt;
: Do something before the object is updated in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;after_update(bool $result)&lt;br /&gt;
: Do something right after the object was updated in the database.&lt;br /&gt;
;before_delete()&lt;br /&gt;
: Do something right before the object is deleted from the database.&lt;br /&gt;
;after_delete(bool $result)&lt;br /&gt;
: Do something right after the object was deleted from the database.&lt;br /&gt;
&lt;br /&gt;
=== Full example ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
namespace example;&lt;br /&gt;
&lt;br /&gt;
use core\persistent;&lt;br /&gt;
use core_user;&lt;br /&gt;
use lang_string;&lt;br /&gt;
&lt;br /&gt;
class status extends persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;messageformat&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
                &#039;default&#039; =&amp;gt; FORMAT_PLAIN,&lt;br /&gt;
                &#039;choices&#039; =&amp;gt; array(FORMAT_PLAIN, FORMAT_HTML, FORMAT_MOODLE, FORMAT_MARKDOWN)&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;location&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_ALPHANUMEXT,&lt;br /&gt;
                &#039;null&#039; =&amp;gt; NULL_ALLOWED,&lt;br /&gt;
                &#039;message&#039; =&amp;gt; new lang_string(&#039;invaliddata&#039;, &#039;error&#039;),&lt;br /&gt;
                &#039;default&#039; =&amp;gt; function() {&lt;br /&gt;
                    return get_config(&#039;default_location&#039;);&lt;br /&gt;
                },&lt;br /&gt;
            ),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the user object of the author.&lt;br /&gt;
     *&lt;br /&gt;
     * @return stdClass&lt;br /&gt;
     */&lt;br /&gt;
    public function get_author() {&lt;br /&gt;
        return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Convenience method to set the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
     */&lt;br /&gt;
    public function set_userid($idorobject) {&lt;br /&gt;
        $userid = $idorobject;&lt;br /&gt;
        if (is_object($idorobject)) {&lt;br /&gt;
            $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Validate the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param int $value The value.&lt;br /&gt;
     * @return true|lang_string&lt;br /&gt;
     */&lt;br /&gt;
    protected function validate_userid($value) {&lt;br /&gt;
        if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
            return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get all records by a user from its username&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $username The username.&lt;br /&gt;
     * @return status[]&lt;br /&gt;
     */&lt;br /&gt;
    public static function get_records_by_username($username) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $sql = &#039;SELECT s.*&lt;br /&gt;
                  FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
                  JOIN {user} u&lt;br /&gt;
                    ON u.id = s.userid&lt;br /&gt;
                 WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
        $persistents = [];&lt;br /&gt;
&lt;br /&gt;
        $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
        foreach ($recordset as $record) {&lt;br /&gt;
            $persistents[] = new static(0, $record);&lt;br /&gt;
        }&lt;br /&gt;
        $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
        return $persistents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51582</id>
		<title>User:Frédéric Massart</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Fr%C3%A9d%C3%A9ric_Massart&amp;diff=51582"/>
		<updated>2016-12-13T09:31:12Z</updated>

		<summary type="html">&lt;p&gt;Fmcorz: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Persistents ==&lt;br /&gt;
&lt;br /&gt;
Moodle persistents are equivalent to models (or active records). They represent an object stored in the database and provide the methods to create, read, update, and delete those objects. Additionally, the persistents validate its own data against automatic and custom validation rules.&lt;br /&gt;
&lt;br /&gt;
Persistents extend the abstract class &#039;&#039;core\persistent&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Properties ===&lt;br /&gt;
&lt;br /&gt;
Defining properties can be done by defining the method &#039;&#039;protected static define_properties()&#039;&#039;. It returns an array where the keys are the names of the fields (and database columns), and their details. The &#039;&#039;type&#039;&#039; of each field must be specified using one of the &#039;&#039;PARAM_*&#039;&#039; constants. This type will be used to automatically validate the property&#039;s value.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Return the definition of the properties of this model.&lt;br /&gt;
 *&lt;br /&gt;
 * @return array&lt;br /&gt;
 */&lt;br /&gt;
protected static function define_properties() {&lt;br /&gt;
    return array(&lt;br /&gt;
        &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
            &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
        )&lt;br /&gt;
    );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we define two mandatory fields, one being a non-null integer, and the other one a non-null free text field. For a complete list of properties&lt;br /&gt;
&lt;br /&gt;
==== Mandatory properties ====&lt;br /&gt;
&lt;br /&gt;
Four fields are always added to your persistent and should be reflected in your database table. You must not define those properties inThose are:&lt;br /&gt;
&lt;br /&gt;
;id (non-null integer)&lt;br /&gt;
: The primary key of the record.&lt;br /&gt;
;usermodified (non-null integer)&lt;br /&gt;
: The user who created/modified the object. It is automatically set.&lt;br /&gt;
;timecreated (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set.&lt;br /&gt;
;timemodified (non-null integer)&lt;br /&gt;
: The timestamp at which the record was modified. It is automatically set, and defaults to 0.&lt;br /&gt;
&lt;br /&gt;
=== Attaching to the database ===&lt;br /&gt;
&lt;br /&gt;
While the persistent class is helpful for database interactions, it does not automatically fetch the properties from the database, nor does it create the tables. You will need to create the table yourself, as well as pointing the persistent to it. This can be done by defining the constant &#039;&#039;TABLE&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
/** Table name for the persistent. */&lt;br /&gt;
const TABLE = &#039;status&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The table name must not contain the Moodle prefix. Also it is common practice to always refer to your table name using the constant.&lt;br /&gt;
&lt;br /&gt;
=== Reading property values ===&lt;br /&gt;
&lt;br /&gt;
This can be done using the method &#039;&#039;get()&#039;&#039;, or the magic method &#039;&#039;get_&#039;&#039; followed by the property name. Alternatively you can also use &#039;&#039;to_record()&#039;&#039; which exports the whole object.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
// Create a new object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using persistent::get().&lt;br /&gt;
$userid = $persistent-&amp;gt;get(&#039;userid&#039;);&lt;br /&gt;
&lt;br /&gt;
// Get the user ID using the magic getter.&lt;br /&gt;
$userid = $persistent-&amp;gt;get_userid();&lt;br /&gt;
&lt;br /&gt;
// Get all the properties in an stdClass.&lt;br /&gt;
$data = $persistent-&amp;gt;to_record();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may override the magic getters to implements your own custom logic. For instance you could convert the data in another format automatically as a convenience for developers. However, use this sparingly as it may lead to confusion: what you get is not what is stored in the database. Note that you cannot guarantee that your getter will be used, remember that it is possible for developers to call the alternative &#039;&#039;get()&#039;&#039; which will not use your custom logic.&lt;br /&gt;
&lt;br /&gt;
It is, however, encouraged to add convenience methods such as the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Returns the user object of the author.&lt;br /&gt;
 *&lt;br /&gt;
 * @return stdClass&lt;br /&gt;
 */&lt;br /&gt;
public function get_author() {&lt;br /&gt;
    return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Assigning values to properties ===&lt;br /&gt;
&lt;br /&gt;
There are a few methods to do so.&lt;br /&gt;
&lt;br /&gt;
You use an object (&#039;&#039;stdClass&#039;&#039;) to assign a bunch of properties at once. Use it with the constructor, or the method &#039;&#039;from_record()&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
$data = (object) array(&#039;userid&#039; =&amp;gt; 2, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instantiates a new object with value for some properties.&lt;br /&gt;
$persistent = new status(0, $data);&lt;br /&gt;
&lt;br /&gt;
// Is similar to.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
$persistent-&amp;gt;from_record($data);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or you can use the &#039;&#039;set()&#039;&#039; method on an instance.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set(&#039;message&#039;, Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Finally you can use the magic setters &#039;&#039;set_&#039;&#039; followed by the property name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
// Instantiates a blank object.&lt;br /&gt;
$persistent = new status();&lt;br /&gt;
&lt;br /&gt;
// Assign a new value to the &#039;message&#039; property.&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Defining a custom setter ====&lt;br /&gt;
&lt;br /&gt;
Though you don&#039;t have to for the code to work, you can define your own &#039;&#039;setter&#039;&#039; methods which will override the magic setters. They are useful if you want to extract the data out of a more complex object prior to assigning it. Though note that those setters will then have to use the &#039;&#039;set()&#039;&#039; method to assign the values.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Convenience method to set the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
 */&lt;br /&gt;
public function set_userid($idorobject) {&lt;br /&gt;
    $userid = $idorobject;&lt;br /&gt;
    if (is_object($idorobject)) {&lt;br /&gt;
        $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
    }&lt;br /&gt;
    $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the above example we will accept an object or an ID, as a convenience for developers we will extract the ID value out of the object passed if any.&lt;br /&gt;
&lt;br /&gt;
Just like custom getters, you cannot guarantee that they will be used. Developers can directly call the &#039;&#039;set()&#039;&#039; method. Therefore you must not use a custom setter to reliably transform the data added to a property. For instance do not add a custom setter to remove HTML tags out of a text field, it may not always happen.&lt;br /&gt;
&lt;br /&gt;
You can obviously create your own setters which aren&#039;t based on any properties just as a convenience. For instance we could have created &#039;&#039;set_userid_from_user(object $user)&#039;&#039; which is more verbose and more predictable&lt;br /&gt;
&lt;br /&gt;
=== Read, save and delete entries ===&lt;br /&gt;
&lt;br /&gt;
The methods to &#039;&#039;create&#039;&#039;, &#039;&#039;read&#039;&#039;, &#039;&#039;update&#039;&#039; and &#039;&#039;delete&#039;&#039; are eponymous. Your object will be validated before you &#039;&#039;create&#039;&#039; or &#039;&#039;update&#039;&#039; it. The &#039;&#039;update&#039;&#039;, &#039;&#039;delete&#039;&#039; and &#039;&#039;read&#039;&#039; methods require your object to contain its ID. And you also won&#039;t be allowed to &#039;&#039;create&#039;&#039; an entry which already had an ID defined.&lt;br /&gt;
&lt;br /&gt;
Here are some code examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
// Fetches an object from database based on its ID.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Create previously instantiated object in the database.&lt;br /&gt;
$persistent-&amp;gt;create();&lt;br /&gt;
&lt;br /&gt;
// Load an object from the database, and update it.&lt;br /&gt;
$id = 123;&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
$persistent-&amp;gt;set_message(&#039;Hello new world!&#039;);&lt;br /&gt;
$persistent-&amp;gt;update();&lt;br /&gt;
&lt;br /&gt;
// Reset the instance to the values in the database.&lt;br /&gt;
$persistent-&amp;gt;read();&lt;br /&gt;
&lt;br /&gt;
// Permanently delete the object from the database.&lt;br /&gt;
$persistent-&amp;gt;delete();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching records ===&lt;br /&gt;
&lt;br /&gt;
Once you start using persistents you should never directly interact with the database outside of your class. The persistent class comes with a few handy methods allowing you to retrieve your objects.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
// Use the constructor to fetch one object from its ID.&lt;br /&gt;
$persistent = new status($id);&lt;br /&gt;
&lt;br /&gt;
// Get one record from a set of conditions.&lt;br /&gt;
$persistent = status::get_record([&#039;userid&#039; =&amp;gt; $userid, &#039;message&#039; =&amp;gt; &#039;Hello world!&#039;]);&lt;br /&gt;
&lt;br /&gt;
// Get multiple records from a set of conditions.&lt;br /&gt;
$persistents = status::get_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Count the records.&lt;br /&gt;
$count = status::count_records([&#039;userid&#039; =&amp;gt; $userid]);&lt;br /&gt;
&lt;br /&gt;
// Check whether a record exists.&lt;br /&gt;
$exists = status::record_exists($id);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Make sure to also check their additional parameters and their variants (&#039;&#039;record_exists_select()&#039;&#039;, &#039;&#039;count_records_select&#039;&#039;, &#039;&#039;get_records_select&#039;&#039;, ...).&lt;br /&gt;
&lt;br /&gt;
==== Custom fetching ====&lt;br /&gt;
&lt;br /&gt;
It&#039;s always a good idea to add more complex queries directly within your persistent. By convention you should always return an instance of your persistent and never an stdClass. Here we add a custom method which allows to directly fetch all records by username.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Get all records by a user from its username&lt;br /&gt;
 *&lt;br /&gt;
 * @param string $username The username.&lt;br /&gt;
 * @return status[]&lt;br /&gt;
 */&lt;br /&gt;
public static function get_records_by_username($username) {&lt;br /&gt;
    global $DB;&lt;br /&gt;
&lt;br /&gt;
    $sql = &#039;SELECT s.*&lt;br /&gt;
              FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
              JOIN {user} u&lt;br /&gt;
                ON u.id = s.userid&lt;br /&gt;
             WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
    $persistents = [];&lt;br /&gt;
&lt;br /&gt;
    $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
    foreach ($recordset as $record) {&lt;br /&gt;
        $persistents[] = new static(0, $record);&lt;br /&gt;
    }&lt;br /&gt;
    $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
    return $persistents;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to join persistents together and be able to extract their respective properties in a single database query, you should have a look at the following methods:&lt;br /&gt;
&lt;br /&gt;
;get_sql_fields(string $alias, string $prefix = null)&lt;br /&gt;
: Returns the SQL statement to include in the SELECT clause to prefix columns.&lt;br /&gt;
;extract_record(stdClass $row, string $prefix = null)&lt;br /&gt;
: Extracts all the properties from a row based on the given prefix.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
// Minimalist example.&lt;br /&gt;
$sqlfields = status::get_sql_fields(&#039;s&#039;, &#039;statprops&#039;);&lt;br /&gt;
$sql = &amp;quot;SELECT $sqlfields, u.username&lt;br /&gt;
          FROM {&amp;quot; . status::TABLE . &amp;quot;} s&lt;br /&gt;
          JOIN {user} ON s.userid = u.id&lt;br /&gt;
         WHERE s.id = 1&amp;quot;;&lt;br /&gt;
$row = $DB-&amp;gt;get_record($sql, []);&lt;br /&gt;
$statusdata = status::extract_record($row, &#039;statprops&#039;);&lt;br /&gt;
$persistent = new status(0, $statusdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Validating ===&lt;br /&gt;
&lt;br /&gt;
Basic validation of the properties values happens automatically based on their type (&#039;&#039;PARAM_*&#039;&#039; constant), however this is not always enough. In order to implement your own custom validation, simply define a &#039;&#039;protected&#039;&#039; method starting with &#039;&#039;validate_&#039;&#039; followed with the property name. This method will be called whenever the model needs to be validated and will receive the data to validate.&lt;br /&gt;
&lt;br /&gt;
A validation method must always return either &#039;&#039;true&#039;&#039; or an instance of &#039;&#039;lang_string&#039;&#039; which contains the error message to send to the user.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * Validate the user ID.&lt;br /&gt;
 *&lt;br /&gt;
 * @param int $value The value.&lt;br /&gt;
 * @return true|lang_string&lt;br /&gt;
 */&lt;br /&gt;
protected function validate_userid($value) {&lt;br /&gt;
    if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
        return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above example ensures that the &#039;&#039;userid&#039;&#039; property contains a valid user ID.&lt;br /&gt;
&lt;br /&gt;
Note that the basic validation is always performed first, and thus your custom validation method will not be called when the value did not pass the basic validation.&lt;br /&gt;
&lt;br /&gt;
==== Validation results ====&lt;br /&gt;
&lt;br /&gt;
The validation of the object automatically happens upon &#039;&#039;create&#039;&#039; and &#039;&#039;update&#039;&#039;. If the validation did not pass, an &#039;&#039;invalid_persisten_exception&#039;&#039; will be raised. You can validate the object prior to saving the object and get the validation results if you need to.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
// We can catch the invalid_persistent_exception.&lt;br /&gt;
try {&lt;br /&gt;
    $persistent = new status();&lt;br /&gt;
    $persistent-&amp;gt;create();&lt;br /&gt;
} catch (invalid_persistent_exception $e) {&lt;br /&gt;
    // Whoops, something wrong happened.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Check whether the object is valid.&lt;br /&gt;
$persistent-&amp;gt;is_valid();        // True or false.&lt;br /&gt;
&lt;br /&gt;
// Get the validation errors.&lt;br /&gt;
$persistent-&amp;gt;get_errors();      // Array where keys are properties and values are errors.&lt;br /&gt;
&lt;br /&gt;
// Validate the object.&lt;br /&gt;
$persistent-&amp;gt;validate();        // Returns true, or an array of errors.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
You can define the following methods to be notified prior to, or after, something happened:&lt;br /&gt;
&lt;br /&gt;
;before_validate()&lt;br /&gt;
: Do something before the object is validated.&lt;br /&gt;
;before_create()&lt;br /&gt;
: Do something before the object is inserted in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;after_create()&lt;br /&gt;
: Do something right after the object was added to the database.&lt;br /&gt;
;before_update()&lt;br /&gt;
: Do something before the object is updated in the database. Note that values assigned to properties are not longer validated at this point.&lt;br /&gt;
;after_update(bool $result)&lt;br /&gt;
: Do something right after the object was updated in the database.&lt;br /&gt;
;before_delete()&lt;br /&gt;
: Do something right before the object is deleted from the database.&lt;br /&gt;
;after_delete(bool $result)&lt;br /&gt;
: Do something right after the object was deleted from the database.&lt;br /&gt;
&lt;br /&gt;
=== Full example ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
namespace example;&lt;br /&gt;
&lt;br /&gt;
use core\persistent;&lt;br /&gt;
use core_user;&lt;br /&gt;
use lang_string;&lt;br /&gt;
&lt;br /&gt;
class status extends persistent {&lt;br /&gt;
&lt;br /&gt;
    /** Table name for the persistent. */&lt;br /&gt;
    const TABLE = &#039;status&#039;;&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Return the definition of the properties of this model.&lt;br /&gt;
     *&lt;br /&gt;
     * @return array&lt;br /&gt;
     */&lt;br /&gt;
    protected static function define_properties() {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;userid&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_INT,&lt;br /&gt;
            ),&lt;br /&gt;
            &#039;message&#039; =&amp;gt; array(&lt;br /&gt;
                &#039;type&#039; =&amp;gt; PARAM_RAW,&lt;br /&gt;
            )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Returns the user object of the author.&lt;br /&gt;
     *&lt;br /&gt;
     * @return stdClass&lt;br /&gt;
     */&lt;br /&gt;
    public function get_author() {&lt;br /&gt;
        return core_user::get_user($this-&amp;gt;get(&#039;userid&#039;));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Convenience method to set the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param object|int $idorobject The user ID, or a user object.&lt;br /&gt;
     */&lt;br /&gt;
    public function set_userid($idorobject) {&lt;br /&gt;
        $userid = $idorobject;&lt;br /&gt;
        if (is_object($idorobject)) {&lt;br /&gt;
            $userid = $idorobject-&amp;gt;id;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;set(&#039;userid&#039;, $userid);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Validate the user ID.&lt;br /&gt;
     *&lt;br /&gt;
     * @param int $value The value.&lt;br /&gt;
     * @return true|lang_string&lt;br /&gt;
     */&lt;br /&gt;
    protected function validate_userid($value) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        if (!core_user::is_real_user($value, true)) {&lt;br /&gt;
            return new lang_string(&#039;invaliduserid&#039;, &#039;error&#039;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /**&lt;br /&gt;
     * Get all records by a user from its username&lt;br /&gt;
     *&lt;br /&gt;
     * @param string $username The username.&lt;br /&gt;
     * @return status[]&lt;br /&gt;
     */&lt;br /&gt;
    public static function get_records_by_username($username) {&lt;br /&gt;
        global $DB;&lt;br /&gt;
&lt;br /&gt;
        $sql = &#039;SELECT s.*&lt;br /&gt;
                  FROM {&#039; . static::TABLE . &#039;} s&lt;br /&gt;
                  JOIN {user} u&lt;br /&gt;
                    ON u.id = s.userid&lt;br /&gt;
                 WHERE u.username = :username&#039;;&lt;br /&gt;
&lt;br /&gt;
        $persistents = [];&lt;br /&gt;
&lt;br /&gt;
        $recordset = $DB-&amp;gt;get_recordset_sql($sql, [&#039;username&#039; =&amp;gt; $username]);&lt;br /&gt;
        foreach ($recordset as $record) {&lt;br /&gt;
            $persistents[] = new static(0, $record);&lt;br /&gt;
        }&lt;br /&gt;
        $recordset-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
        return $persistents;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fmcorz</name></author>
	</entry>
</feed>