<?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=Bruno</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=Bruno"/>
	<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/Special:Contributions/Bruno"/>
	<updated>2026-08-03T01:54:53Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=DML_drivers&amp;diff=11769</id>
		<title>DML drivers</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=DML_drivers&amp;diff=11769"/>
		<updated>2010-12-03T06:41:02Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* Query logging */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle_2.0}}Previous versions were using adodb abstraction partially encapsulated by old DML api. The database drivers are now fully separated from the rest of code and it is even possible to create new native drivers that do not rely on adodb abstraction anymore.&lt;br /&gt;
&lt;br /&gt;
At present there are three native drivers - mysqli, pgsql and unfinished Oracle driver. The benefits are:&lt;br /&gt;
* more optimised and probably faster&lt;br /&gt;
* consume less memory&lt;br /&gt;
* better possibility to improve logging, debugging, profiling, etc.&lt;br /&gt;
* less code, easier to fix and maintain&lt;br /&gt;
* and more&lt;br /&gt;
&lt;br /&gt;
Please note old adodb based drivers will be removed before the branching of 2.0.&lt;br /&gt;
&lt;br /&gt;
==Query logging==&lt;br /&gt;
New native DML drivers support logging of database queries to database table. Logging can be enabled in config.php&lt;br /&gt;
&lt;br /&gt;
 $CFG-&amp;gt;dboptions = array (&lt;br /&gt;
  &#039;dbpersist&#039; =&amp;gt; 0,&lt;br /&gt;
  //&#039;logall&#039;   =&amp;gt; true,&lt;br /&gt;
  &#039;logslow&#039;  =&amp;gt; 5,&lt;br /&gt;
  &#039;logerrors&#039;  =&amp;gt; true,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;logall&#039;&#039;&#039; - log all queries - suitable only for developers, causes high server loads&lt;br /&gt;
* &#039;&#039;&#039;logslow&#039;&#039;&#039; - log queries that take longer than specified number of seconds (float values are accepted)&lt;br /&gt;
* &#039;&#039;&#039;logerrors&#039;&#039;&#039; - log all error queries&lt;br /&gt;
&lt;br /&gt;
==TODO==&lt;br /&gt;
* add more info here&lt;br /&gt;
* add separate docs pages for each driver - describe all options there&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
* [[DML functions|DML functions]]: Where all the functions used to handle DB data ([[wikipedia:Data_Manipulation_Language|DML]]) are defined.&lt;br /&gt;
* [[DML exceptions|DML exceptions]]: New DML code is throwing exceptions instead of returning false if anything goes wrong&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=DML_drivers&amp;diff=11768</id>
		<title>DML drivers</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=DML_drivers&amp;diff=11768"/>
		<updated>2010-12-03T06:40:25Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* Query logging */  spelling correction&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle_2.0}}Previous versions were using adodb abstraction partially encapsulated by old DML api. The database drivers are now fully separated from the rest of code and it is even possible to create new native drivers that do not rely on adodb abstraction anymore.&lt;br /&gt;
&lt;br /&gt;
At present there are three native drivers - mysqli, pgsql and unfinished Oracle driver. The benefits are:&lt;br /&gt;
* more optimised and probably faster&lt;br /&gt;
* consume less memory&lt;br /&gt;
* better possibility to improve logging, debugging, profiling, etc.&lt;br /&gt;
* less code, easier to fix and maintain&lt;br /&gt;
* and more&lt;br /&gt;
&lt;br /&gt;
Please note old adodb based drivers will be removed before the branching of 2.0.&lt;br /&gt;
&lt;br /&gt;
==Query logging==&lt;br /&gt;
New native DML drivers support logging of database queries to database table. Logging can be enabled in config.php&lt;br /&gt;
&lt;br /&gt;
 $CFG-&amp;gt;dboptions = array (&lt;br /&gt;
  &#039;dbpersit&#039; =&amp;gt; 0,&lt;br /&gt;
  //&#039;logall&#039;   =&amp;gt; true,&lt;br /&gt;
  &#039;logslow&#039;  =&amp;gt; 5,&lt;br /&gt;
  &#039;logerrors&#039;  =&amp;gt; true,&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;logall&#039;&#039;&#039; - log all queries - suitable only for developers, causes high server loads&lt;br /&gt;
* &#039;&#039;&#039;logslow&#039;&#039;&#039; - log queries that take longer than specified number of seconds (float values are accepted)&lt;br /&gt;
* &#039;&#039;&#039;logerrors&#039;&#039;&#039; - log all error queries&lt;br /&gt;
&lt;br /&gt;
==TODO==&lt;br /&gt;
* add more info here&lt;br /&gt;
* add separate docs pages for each driver - describe all options there&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
* [[DML functions|DML functions]]: Where all the functions used to handle DB data ([[wikipedia:Data_Manipulation_Language|DML]]) are defined.&lt;br /&gt;
* [[DML exceptions|DML exceptions]]: New DML code is throwing exceptions instead of returning false if anything goes wrong&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Data_manipulation_API&amp;diff=5190</id>
		<title>Data manipulation API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Data_manipulation_API&amp;diff=5190"/>
		<updated>2010-12-03T06:29:56Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* Using Recordsets */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle_2.0}}This page describes the functions available to access data in the Moodle database. You should &#039;&#039;&#039;exclusively&#039;&#039;&#039; use these functions in order to retrieve or modify database content because these functions provide a high level of abstraction and guarantee that your database manipulation will work against different RDBMSes.&lt;br /&gt;
&lt;br /&gt;
Where possible, tricks and examples will be documented here in order to make developers&#039; lives a bit easier. Of course, feel free to clarify, complete and add more information to  this documentation. It will be welcome, absolutely!&lt;br /&gt;
&lt;br /&gt;
== Main info ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important note:&#039;&#039;&#039; All the functions shown on this page are for use in &#039;&#039;&#039;Moodle 2.0 upwards&#039;&#039;&#039;, where we changed the [[DB layer 2.0|DB layer]] to support some new features. If you need information for previous Moodle version, take a look to the [[DML functions - pre 2.0|DML functions - pre 2.0]] page. For a detailed reference of changes, see the [[DB layer 2.0 migration docs|migration docs]].&lt;br /&gt;
&lt;br /&gt;
* All the function calls on this page are public methods of the $DB global object, so you&#039;ll need to &amp;quot;import&amp;quot; it within your functions (not needed in global scripts) with one simple:&lt;br /&gt;
&amp;lt;code php&amp;gt;global $DB;&amp;lt;/code&amp;gt;&lt;br /&gt;
* All the $table parameters in the functions are meant to be the table name &#039;&#039;without&#039;&#039; prefixes.&lt;br /&gt;
&amp;lt;code php&amp;gt;$user = $DB-&amp;gt;get_record(&#039;user&#039;, array(&#039;id&#039;=&amp;gt;&#039;1&#039;);&amp;lt;/code&amp;gt;&lt;br /&gt;
* When using the xxx_sql() functions, table names must be enclosed between curly braces.&lt;br /&gt;
&amp;lt;code php&amp;gt;$user = $DB-&amp;gt;get_record_sql(&#039;SELECT * FROM {user} WHERE id = ?&#039;, array(1));&amp;lt;/code&amp;gt;&lt;br /&gt;
* All the $conditions parameters in the functions are arrays of fieldname=&amp;gt;fieldvalue elements.&lt;br /&gt;
&amp;lt;code php&amp;gt;$user = $DB-&amp;gt;get_record(&#039;user&#039;, array(&#039;firstname&#039;=&amp;gt;&#039;Martin&#039;, &#039;lastname&#039;=&amp;gt;&#039;Dougiamas&#039;));&amp;lt;/code&amp;gt;&lt;br /&gt;
* All the $params parameters in the functions are arrays of values used to fill placeholders in SQL statements. Both the question mark and named placeholders can be used. Note that named params &#039;&#039;&#039;must be unique&#039;&#039;&#039; even if the value passed is the same.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/// Question mark placeholders:&lt;br /&gt;
   $DB-&amp;gt;get_record_sql(&#039;SELECT * FROM {user} WHERE firstname = ? AND lastname = ?&#039;, &lt;br /&gt;
                       array(&#039;Martin&#039;, &#039;Dougiamas&#039;));&lt;br /&gt;
&lt;br /&gt;
/// Named placeholders:&lt;br /&gt;
   $DB-&amp;gt;get_record_sql(&#039;SELECT * FROM {user} WHERE firstname = :firstname AND lastname = :lastname&#039;,&lt;br /&gt;
                       array(&#039;firstname&#039;=&amp;gt;&#039;Martin&#039;, &#039;lastname&#039;=&amp;gt;&#039;Dougiamas&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== The functions ==&lt;br /&gt;
&lt;br /&gt;
===Seeing how many records match a given criteria===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;count_records($table, array $conditions=null) &lt;br /&gt;
  /// Count the records in a table where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;count_records_select($table, $select, array $params=null, $countitem=&amp;quot;COUNT(&#039;x&#039;)&amp;quot;) &lt;br /&gt;
  /// Count the records in a table which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;count_records_sql($sql, array $params=null) &lt;br /&gt;
  /// Get the result of a SQL SELECT COUNT(...) query.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Seeing if one record exists===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;record_exists($table, array $conditions=null) &lt;br /&gt;
  /// Test whether a record exists in a table where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;record_exists_select($table, $select, array $params=null) &lt;br /&gt;
  /// Test whether any records exists in a table which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;record_exists_sql($sql, array $params=null) &lt;br /&gt;
  /// Test whether a SQL SELECT statement returns any records.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Getting a single record===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_record($table, array $conditions, $fields=&#039;*&#039;, $ignoremultiple=false) &lt;br /&gt;
  /// Get a single database record as an object where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_record_select($table, $select, array $params=null, $fields=&#039;*&#039;, $ignoremultiple=false)&lt;br /&gt;
  /// Get a single database record as an object which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_record_sql($sql, array $params=null)&lt;br /&gt;
  /// Get a single database record as an object using a SQL statement.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Getting an array of records===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_records($table, array $conditions=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get a number of records as an array of objects where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_records_select($table, $select, array $params=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get a number of records as an array of objects which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_records_sql($sql, array $params=null, $limitfrom=0, $limitnum=0)&lt;br /&gt;
  /// Get a number of records as an array of objects using a SQL statement.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;get_records_menu($table, array $conditions=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get the first two columns from a number of records as an associative array where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_records_select_menu($table, $select, array $params=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0)&lt;br /&gt;
  /// Get the first two columns from a number of records as an associative array which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_records_sql_menu($sql, array $params=null, $limitfrom=0, $limitnum=0)&lt;br /&gt;
  /// Get the first two columns from a number of records as an associative array using a SQL statement.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;get_records_list($table, $field=&#039;&#039;, $values=&#039;&#039;, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=&#039;&#039;, $limitnum=&#039;&#039;) &lt;br /&gt;
  /// Get a number of records as an array of objects where one field match one list of values.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Getting a particular field value from one record===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_field($table, $return, array $conditions)&lt;br /&gt;
  /// Get a single field value from a table record where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_field_select($table, $return, $select, array $params=null)&lt;br /&gt;
  /// Get a single field value from a table record which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_field_sql($sql, array $params=null)&lt;br /&gt;
  /// Get a single field value (first field) using a SQL statement.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Getting a particular field value from various records===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_fieldset_select($table, $return, $select, array $params=null)&lt;br /&gt;
  /// Selects records and return values of chosen field as an array which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_fieldset_sql($sql, array $params=null)&lt;br /&gt;
  /// Selects records and return values (first field) as an array using a SQL statement.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Setting a particular field in the database===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;set_field($table, $newfield, $newvalue, array $conditions=null)&lt;br /&gt;
  /// Set a single field in every table record where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;set_field_select($table, $newfield, $newvalue, $select, array $params=null)&lt;br /&gt;
  /// Set a single field in every table record which match a particular WHERE clause.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Deleting Records===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;delete_records($table, array $conditions=null) &lt;br /&gt;
  /// Delete the records from a table where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;delete_records_select($table, $select, array $params=null)&lt;br /&gt;
  /// Delete one or more records from a table which match a particular WHERE clause.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Inserting Records===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;insert_record($table, $dataobject, $returnid=true, $bulk=false) &lt;br /&gt;
  /// Insert a record into a table and return the &amp;quot;id&amp;quot; field if required.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Updating Records===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;update_record($table, $dataobject, $bulk=false)&lt;br /&gt;
  /// Update a record in a table.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Using Recordsets===&lt;br /&gt;
&lt;br /&gt;
Where the number of records to be retrieved from DB is high, the &#039;&#039;&#039;get_records_xxx()&#039;&#039;&#039; functions above are far from optimal, because they load all the records in memory at the same time. Under those circumstances, it is highly recommended to use these &#039;&#039;&#039;get_recordset_xxx()&#039;&#039;&#039; functions instead, which use one nice mechanism to iterate over all the target records and save a lot of memory.&lt;br /&gt;
&lt;br /&gt;
Only one thing is &#039;&#039;&#039;absolutely important&#039;&#039;&#039;: Don&#039;t forget to close the recordsets after using them! (This will free up a lot of resources in the RDBMS).&lt;br /&gt;
&lt;br /&gt;
Here is the general way to iterate over records using the &#039;&#039;&#039;get_recordset_xxx()&#039;&#039;&#039; functions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($rs = $DB-&amp;gt;get_recordset(....) {&lt;br /&gt;
    foreach ($rs as $record) {&lt;br /&gt;
     /// Do whatever you want with this record&lt;br /&gt;
    }&lt;br /&gt;
    $rs-&amp;gt;close(); /// Don&#039;t forget to close the recordset!&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And this is the list of available functions (100% paired with the get_records_xxx() above):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_recordset($table, array $conditions=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get a number of records as a moodle_recordset where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_recordset_select($table, $select, array $params=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get a number of records as a moodle_recordset which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_recordset_sql($sql, array $params=null, $limitfrom=0, $limitnum=0);&lt;br /&gt;
  /// Get a number of records as a moodle_recordset using a SQL statement.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;get_recordset_list($table, $field=&#039;&#039;, $values=&#039;&#039;, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=&#039;&#039;, $limitnum=&#039;&#039;) &lt;br /&gt;
  /// Get a number of records as a moodle_recordset where one field matches one list of values.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Delegated transactions===&lt;br /&gt;
&lt;br /&gt;
* Please note some databases do not support transactions (such as the MyISAM MySQL database engine), however all server administrators are strongly encouraged to migrate to databases that support transactions (such as the InnoDB MySQL database engine).&lt;br /&gt;
* Previous versions supported only one level of transaction. Since Moodle 2.0, the DML layer emulates delegated transactions that allow nesting of transactions.&lt;br /&gt;
* Transactions should not be used much in Moodle core; they are intended for various plugins such as web services, enrol and auth plugins.&lt;br /&gt;
* Some subsystems (such as messaging) do not support transactions because is it is not possible to rollback in external systems.&lt;br /&gt;
&lt;br /&gt;
A transaction is started by:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$transaction = $DB-&amp;gt;start_delegated_transaction();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and finished by:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$transaction-&amp;gt;allow_commit();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Usually a transaction is rolled back when an exception is thrown. &amp;lt;code&amp;gt;$transaction-&amp;gt;rollback($ex);&amp;lt;/code&amp;gt; must be used very carefully because it might break compatibility with databases that do not support transactions. Transactions cannot be used as part of expected code flow; they can be used only as an emergency protection of data consistency.&lt;br /&gt;
&lt;br /&gt;
See more details in [[DB layer 2.0 delegated transactions]] or MDL-20625.&lt;br /&gt;
&lt;br /&gt;
===Helper Functions===&lt;br /&gt;
&lt;br /&gt;
In order have real cross-db compatibility, there are some helper functions used to build SQL fragments based on the DB Moodle is running. Using them we&#039;ll avoid conditional queries here and there and have those &amp;quot;incompatibilities&amp;quot; fixed once and for ever.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;sql_bitand($int1, $int2) &lt;br /&gt;
  /// Returns the SQL text to be used in order to perform one bitwise AND &lt;br /&gt;
  /// operation between 2 integers.&lt;br /&gt;
o $DB-&amp;gt;sql_bitnot($int1) &lt;br /&gt;
  /// Returns the SQL text to be used in order to perform one bitwise NOT &lt;br /&gt;
  /// operation with 1 integer.&lt;br /&gt;
o $DB-&amp;gt;sql_bitor($int1, $int2)&lt;br /&gt;
  /// Returns the SQL text to be used in order to perform one bitwise OR &lt;br /&gt;
  /// operation between 2 integers.&lt;br /&gt;
o $DB-&amp;gt;sql_bitxor($int1, $int2) &lt;br /&gt;
  /// Returns the SQL text to be used in order to perform one bitwise XOR &lt;br /&gt;
  /// operation between 2 integers.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_null_from_clause()&lt;br /&gt;
  /// Returns the FROM clause required by some DBs in all SELECT statements.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_ceil($fieldname)&lt;br /&gt;
  /// Returns the correct CEIL expression applied to fieldname.&lt;br /&gt;
o $DB-&amp;gt;sql_ilike()&lt;br /&gt;
  /// Returns the proper SQL to do LIKE in a case-insensitive way.&lt;br /&gt;
o $DB-&amp;gt;sql_length($fieldname)&lt;br /&gt;
  /// Returns the SQL text to be used to calculate the length in characters of one expression.&lt;br /&gt;
o $DB-&amp;gt;sql_modulo($int1, $int2)&lt;br /&gt;
  /// Returns the SQL text to be used in order to calculate module - remainder after division&lt;br /&gt;
o $DB-&amp;gt;sql_position($needle, $haystack)&lt;br /&gt;
  /// Returns the SQL for returning searching one string for the location of another.&lt;br /&gt;
  /// Note: If using placeholders BOTH in $needle and $haystack, they MUST be named placeholders.&lt;br /&gt;
o $DB-&amp;gt;sql_substr($expr, $start, $length=false)&lt;br /&gt;
  /// Returns the proper substr() SQL text used to extract substrings from DB.&lt;br /&gt;
  /// Note: This fuction has changed in Moodle 2.0 and now at least 2 params are mandatory.&lt;br /&gt;
  /// Note: Now it returns the whole SQL text to be used instead of only the function name.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_cast_char2int($fieldname, $text=false)&lt;br /&gt;
  /// Returns the SQL to be used in order to CAST one CHAR column to INTEGER.&lt;br /&gt;
o $DB-&amp;gt;sql_cast_char2real($fieldname, $text=false)&lt;br /&gt;
  /// Returns the SQL to be used in order to CAST one CHAR column to REAL number.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_compare_text($fieldname, $numchars=32) &lt;br /&gt;
  /// Returns the SQL text to be used to compare one TEXT (clob) column.&lt;br /&gt;
  /// with one VARCHAR column.&lt;br /&gt;
o $DB-&amp;gt;sql_order_by_text($fieldname, $numchars=32)&lt;br /&gt;
  /// Returns the SQL text to be used to order by one TEXT (clob) column.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_concat()&lt;br /&gt;
  /// Returns the proper SQL to do CONCAT between the elements passed.&lt;br /&gt;
o $DB-&amp;gt;sql_concat_join($separator=&amp;quot;&#039; &#039;&amp;quot;, $elements=array())&lt;br /&gt;
  /// Returns the proper SQL to do CONCAT between the elements passed using one separator.&lt;br /&gt;
o $DB-&amp;gt;sql_fullname($first=&#039;firstname&#039;, $last=&#039;lastname&#039;)&lt;br /&gt;
  /// Returns the proper SQL to concatenate $firstname and $lastname.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_isempty($tablename, $fieldname, $nullablefield, $textfield)&lt;br /&gt;
  /// Returns the proper SQL to know if one field is empty.&lt;br /&gt;
o $DB-&amp;gt;sql_isnotempty($tablename, $fieldname, $nullablefield, $textfield)&lt;br /&gt;
  /// Returns the proper SQL to know if one field is not empty.&lt;br /&gt;
o $DB-&amp;gt;sql_empty()&lt;br /&gt;
  /// Returns the empty string char used by every supported DB.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[DML exceptions|DML exceptions]]: New DML code is throwing exceptions instead of returning false if anything goes wrong&lt;br /&gt;
* [[DML drivers|DML drivers]]: Database drivers for new DML layer&lt;br /&gt;
* [[DML functions - pre 2.0|DML functions - pre 2.0]]: &#039;&#039;&#039;(deprecated!)&#039;&#039;&#039; For information valid before Moodle 2.0.&lt;br /&gt;
* [[DDL functions|DDL functions]]: Where all the functions used to handle DB objects ([[wikipedia:Data_Definition_Language|DDL]]) are defined.&lt;br /&gt;
* [[DB layer 2.0 examples|DB layer 2.0 examples]]: To see some code examples using various DML functions.&lt;br /&gt;
* [[DB layer 2.0 migration docs|DB layer 2.0 migration docs]]: Information about how to modify your code to work with the new Moodle 2.0 DB layer.&lt;br /&gt;
* [[DTL functions|DTL functions]]: Exporting, importing and moving of data stored in SQL databases&lt;br /&gt;
&lt;br /&gt;
[[Category:DB]]&lt;br /&gt;
[[Category:XMLDB]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Data_manipulation_API&amp;diff=5189</id>
		<title>Data manipulation API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Data_manipulation_API&amp;diff=5189"/>
		<updated>2010-12-03T06:28:37Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* Using Recordsets */  clearer english&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle_2.0}}This page describes the functions available to access data in the Moodle database. You should &#039;&#039;&#039;exclusively&#039;&#039;&#039; use these functions in order to retrieve or modify database content because these functions provide a high level of abstraction and guarantee that your database manipulation will work against different RDBMSes.&lt;br /&gt;
&lt;br /&gt;
Where possible, tricks and examples will be documented here in order to make developers&#039; lives a bit easier. Of course, feel free to clarify, complete and add more information to  this documentation. It will be welcome, absolutely!&lt;br /&gt;
&lt;br /&gt;
== Main info ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important note:&#039;&#039;&#039; All the functions shown on this page are for use in &#039;&#039;&#039;Moodle 2.0 upwards&#039;&#039;&#039;, where we changed the [[DB layer 2.0|DB layer]] to support some new features. If you need information for previous Moodle version, take a look to the [[DML functions - pre 2.0|DML functions - pre 2.0]] page. For a detailed reference of changes, see the [[DB layer 2.0 migration docs|migration docs]].&lt;br /&gt;
&lt;br /&gt;
* All the function calls on this page are public methods of the $DB global object, so you&#039;ll need to &amp;quot;import&amp;quot; it within your functions (not needed in global scripts) with one simple:&lt;br /&gt;
&amp;lt;code php&amp;gt;global $DB;&amp;lt;/code&amp;gt;&lt;br /&gt;
* All the $table parameters in the functions are meant to be the table name &#039;&#039;without&#039;&#039; prefixes.&lt;br /&gt;
&amp;lt;code php&amp;gt;$user = $DB-&amp;gt;get_record(&#039;user&#039;, array(&#039;id&#039;=&amp;gt;&#039;1&#039;);&amp;lt;/code&amp;gt;&lt;br /&gt;
* When using the xxx_sql() functions, table names must be enclosed between curly braces.&lt;br /&gt;
&amp;lt;code php&amp;gt;$user = $DB-&amp;gt;get_record_sql(&#039;SELECT * FROM {user} WHERE id = ?&#039;, array(1));&amp;lt;/code&amp;gt;&lt;br /&gt;
* All the $conditions parameters in the functions are arrays of fieldname=&amp;gt;fieldvalue elements.&lt;br /&gt;
&amp;lt;code php&amp;gt;$user = $DB-&amp;gt;get_record(&#039;user&#039;, array(&#039;firstname&#039;=&amp;gt;&#039;Martin&#039;, &#039;lastname&#039;=&amp;gt;&#039;Dougiamas&#039;));&amp;lt;/code&amp;gt;&lt;br /&gt;
* All the $params parameters in the functions are arrays of values used to fill placeholders in SQL statements. Both the question mark and named placeholders can be used. Note that named params &#039;&#039;&#039;must be unique&#039;&#039;&#039; even if the value passed is the same.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/// Question mark placeholders:&lt;br /&gt;
   $DB-&amp;gt;get_record_sql(&#039;SELECT * FROM {user} WHERE firstname = ? AND lastname = ?&#039;, &lt;br /&gt;
                       array(&#039;Martin&#039;, &#039;Dougiamas&#039;));&lt;br /&gt;
&lt;br /&gt;
/// Named placeholders:&lt;br /&gt;
   $DB-&amp;gt;get_record_sql(&#039;SELECT * FROM {user} WHERE firstname = :firstname AND lastname = :lastname&#039;,&lt;br /&gt;
                       array(&#039;firstname&#039;=&amp;gt;&#039;Martin&#039;, &#039;lastname&#039;=&amp;gt;&#039;Dougiamas&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== The functions ==&lt;br /&gt;
&lt;br /&gt;
===Seeing how many records match a given criteria===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;count_records($table, array $conditions=null) &lt;br /&gt;
  /// Count the records in a table where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;count_records_select($table, $select, array $params=null, $countitem=&amp;quot;COUNT(&#039;x&#039;)&amp;quot;) &lt;br /&gt;
  /// Count the records in a table which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;count_records_sql($sql, array $params=null) &lt;br /&gt;
  /// Get the result of a SQL SELECT COUNT(...) query.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Seeing if one record exists===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;record_exists($table, array $conditions=null) &lt;br /&gt;
  /// Test whether a record exists in a table where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;record_exists_select($table, $select, array $params=null) &lt;br /&gt;
  /// Test whether any records exists in a table which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;record_exists_sql($sql, array $params=null) &lt;br /&gt;
  /// Test whether a SQL SELECT statement returns any records.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Getting a single record===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_record($table, array $conditions, $fields=&#039;*&#039;, $ignoremultiple=false) &lt;br /&gt;
  /// Get a single database record as an object where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_record_select($table, $select, array $params=null, $fields=&#039;*&#039;, $ignoremultiple=false)&lt;br /&gt;
  /// Get a single database record as an object which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_record_sql($sql, array $params=null)&lt;br /&gt;
  /// Get a single database record as an object using a SQL statement.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Getting an array of records===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_records($table, array $conditions=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get a number of records as an array of objects where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_records_select($table, $select, array $params=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get a number of records as an array of objects which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_records_sql($sql, array $params=null, $limitfrom=0, $limitnum=0)&lt;br /&gt;
  /// Get a number of records as an array of objects using a SQL statement.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;get_records_menu($table, array $conditions=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get the first two columns from a number of records as an associative array where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_records_select_menu($table, $select, array $params=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0)&lt;br /&gt;
  /// Get the first two columns from a number of records as an associative array which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_records_sql_menu($sql, array $params=null, $limitfrom=0, $limitnum=0)&lt;br /&gt;
  /// Get the first two columns from a number of records as an associative array using a SQL statement.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;get_records_list($table, $field=&#039;&#039;, $values=&#039;&#039;, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=&#039;&#039;, $limitnum=&#039;&#039;) &lt;br /&gt;
  /// Get a number of records as an array of objects where one field match one list of values.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Getting a particular field value from one record===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_field($table, $return, array $conditions)&lt;br /&gt;
  /// Get a single field value from a table record where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_field_select($table, $return, $select, array $params=null)&lt;br /&gt;
  /// Get a single field value from a table record which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_field_sql($sql, array $params=null)&lt;br /&gt;
  /// Get a single field value (first field) using a SQL statement.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Getting a particular field value from various records===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_fieldset_select($table, $return, $select, array $params=null)&lt;br /&gt;
  /// Selects records and return values of chosen field as an array which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_fieldset_sql($sql, array $params=null)&lt;br /&gt;
  /// Selects records and return values (first field) as an array using a SQL statement.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Setting a particular field in the database===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;set_field($table, $newfield, $newvalue, array $conditions=null)&lt;br /&gt;
  /// Set a single field in every table record where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;set_field_select($table, $newfield, $newvalue, $select, array $params=null)&lt;br /&gt;
  /// Set a single field in every table record which match a particular WHERE clause.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Deleting Records===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;delete_records($table, array $conditions=null) &lt;br /&gt;
  /// Delete the records from a table where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;delete_records_select($table, $select, array $params=null)&lt;br /&gt;
  /// Delete one or more records from a table which match a particular WHERE clause.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Inserting Records===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;insert_record($table, $dataobject, $returnid=true, $bulk=false) &lt;br /&gt;
  /// Insert a record into a table and return the &amp;quot;id&amp;quot; field if required.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Updating Records===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;update_record($table, $dataobject, $bulk=false)&lt;br /&gt;
  /// Update a record in a table.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Using Recordsets===&lt;br /&gt;
&lt;br /&gt;
Where the number of records to be retrieved from DB is high, the &#039;&#039;&#039;get_records_xxx()&#039;&#039;&#039; functions above are far from optimal, because they use to load all the records in memory at the same time. Under those circumstances, it&#039;s highly recommended to use this &#039;&#039;&#039;get_recordset_xxx()&#039;&#039;&#039; functions instead, which uses one nice mechanism to iterate over all the target records and save a lot of memory.&lt;br /&gt;
&lt;br /&gt;
Only one thing is &#039;&#039;&#039;absolutely important&#039;&#039;&#039;: Don&#039;t forget to close the recordsets after using them! (This will free up a lot of resources in the RDBMS).&lt;br /&gt;
&lt;br /&gt;
Here is the general way to iterate over records using the &#039;&#039;&#039;get_recordset_xxx()&#039;&#039;&#039; functions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($rs = $DB-&amp;gt;get_recordset(....) {&lt;br /&gt;
    foreach ($rs as $record) {&lt;br /&gt;
     /// Do whatever you want with this record&lt;br /&gt;
    }&lt;br /&gt;
    $rs-&amp;gt;close(); /// Don&#039;t forget to close the recordset!&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And this is the list of available functions (100% paired with the get_records_xxx() above):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_recordset($table, array $conditions=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get a number of records as a moodle_recordset where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_recordset_select($table, $select, array $params=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get a number of records as a moodle_recordset which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_recordset_sql($sql, array $params=null, $limitfrom=0, $limitnum=0);&lt;br /&gt;
  /// Get a number of records as a moodle_recordset using a SQL statement.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;get_recordset_list($table, $field=&#039;&#039;, $values=&#039;&#039;, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=&#039;&#039;, $limitnum=&#039;&#039;) &lt;br /&gt;
  /// Get a number of records as a moodle_recordset where one field matches one list of values.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Delegated transactions===&lt;br /&gt;
&lt;br /&gt;
* Please note some databases do not support transactions (such as the MyISAM MySQL database engine), however all server administrators are strongly encouraged to migrate to databases that support transactions (such as the InnoDB MySQL database engine).&lt;br /&gt;
* Previous versions supported only one level of transaction. Since Moodle 2.0, the DML layer emulates delegated transactions that allow nesting of transactions.&lt;br /&gt;
* Transactions should not be used much in Moodle core; they are intended for various plugins such as web services, enrol and auth plugins.&lt;br /&gt;
* Some subsystems (such as messaging) do not support transactions because is it is not possible to rollback in external systems.&lt;br /&gt;
&lt;br /&gt;
A transaction is started by:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$transaction = $DB-&amp;gt;start_delegated_transaction();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and finished by:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$transaction-&amp;gt;allow_commit();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Usually a transaction is rolled back when an exception is thrown. &amp;lt;code&amp;gt;$transaction-&amp;gt;rollback($ex);&amp;lt;/code&amp;gt; must be used very carefully because it might break compatibility with databases that do not support transactions. Transactions cannot be used as part of expected code flow; they can be used only as an emergency protection of data consistency.&lt;br /&gt;
&lt;br /&gt;
See more details in [[DB layer 2.0 delegated transactions]] or MDL-20625.&lt;br /&gt;
&lt;br /&gt;
===Helper Functions===&lt;br /&gt;
&lt;br /&gt;
In order have real cross-db compatibility, there are some helper functions used to build SQL fragments based on the DB Moodle is running. Using them we&#039;ll avoid conditional queries here and there and have those &amp;quot;incompatibilities&amp;quot; fixed once and for ever.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;sql_bitand($int1, $int2) &lt;br /&gt;
  /// Returns the SQL text to be used in order to perform one bitwise AND &lt;br /&gt;
  /// operation between 2 integers.&lt;br /&gt;
o $DB-&amp;gt;sql_bitnot($int1) &lt;br /&gt;
  /// Returns the SQL text to be used in order to perform one bitwise NOT &lt;br /&gt;
  /// operation with 1 integer.&lt;br /&gt;
o $DB-&amp;gt;sql_bitor($int1, $int2)&lt;br /&gt;
  /// Returns the SQL text to be used in order to perform one bitwise OR &lt;br /&gt;
  /// operation between 2 integers.&lt;br /&gt;
o $DB-&amp;gt;sql_bitxor($int1, $int2) &lt;br /&gt;
  /// Returns the SQL text to be used in order to perform one bitwise XOR &lt;br /&gt;
  /// operation between 2 integers.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_null_from_clause()&lt;br /&gt;
  /// Returns the FROM clause required by some DBs in all SELECT statements.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_ceil($fieldname)&lt;br /&gt;
  /// Returns the correct CEIL expression applied to fieldname.&lt;br /&gt;
o $DB-&amp;gt;sql_ilike()&lt;br /&gt;
  /// Returns the proper SQL to do LIKE in a case-insensitive way.&lt;br /&gt;
o $DB-&amp;gt;sql_length($fieldname)&lt;br /&gt;
  /// Returns the SQL text to be used to calculate the length in characters of one expression.&lt;br /&gt;
o $DB-&amp;gt;sql_modulo($int1, $int2)&lt;br /&gt;
  /// Returns the SQL text to be used in order to calculate module - remainder after division&lt;br /&gt;
o $DB-&amp;gt;sql_position($needle, $haystack)&lt;br /&gt;
  /// Returns the SQL for returning searching one string for the location of another.&lt;br /&gt;
  /// Note: If using placeholders BOTH in $needle and $haystack, they MUST be named placeholders.&lt;br /&gt;
o $DB-&amp;gt;sql_substr($expr, $start, $length=false)&lt;br /&gt;
  /// Returns the proper substr() SQL text used to extract substrings from DB.&lt;br /&gt;
  /// Note: This fuction has changed in Moodle 2.0 and now at least 2 params are mandatory.&lt;br /&gt;
  /// Note: Now it returns the whole SQL text to be used instead of only the function name.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_cast_char2int($fieldname, $text=false)&lt;br /&gt;
  /// Returns the SQL to be used in order to CAST one CHAR column to INTEGER.&lt;br /&gt;
o $DB-&amp;gt;sql_cast_char2real($fieldname, $text=false)&lt;br /&gt;
  /// Returns the SQL to be used in order to CAST one CHAR column to REAL number.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_compare_text($fieldname, $numchars=32) &lt;br /&gt;
  /// Returns the SQL text to be used to compare one TEXT (clob) column.&lt;br /&gt;
  /// with one VARCHAR column.&lt;br /&gt;
o $DB-&amp;gt;sql_order_by_text($fieldname, $numchars=32)&lt;br /&gt;
  /// Returns the SQL text to be used to order by one TEXT (clob) column.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_concat()&lt;br /&gt;
  /// Returns the proper SQL to do CONCAT between the elements passed.&lt;br /&gt;
o $DB-&amp;gt;sql_concat_join($separator=&amp;quot;&#039; &#039;&amp;quot;, $elements=array())&lt;br /&gt;
  /// Returns the proper SQL to do CONCAT between the elements passed using one separator.&lt;br /&gt;
o $DB-&amp;gt;sql_fullname($first=&#039;firstname&#039;, $last=&#039;lastname&#039;)&lt;br /&gt;
  /// Returns the proper SQL to concatenate $firstname and $lastname.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_isempty($tablename, $fieldname, $nullablefield, $textfield)&lt;br /&gt;
  /// Returns the proper SQL to know if one field is empty.&lt;br /&gt;
o $DB-&amp;gt;sql_isnotempty($tablename, $fieldname, $nullablefield, $textfield)&lt;br /&gt;
  /// Returns the proper SQL to know if one field is not empty.&lt;br /&gt;
o $DB-&amp;gt;sql_empty()&lt;br /&gt;
  /// Returns the empty string char used by every supported DB.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[DML exceptions|DML exceptions]]: New DML code is throwing exceptions instead of returning false if anything goes wrong&lt;br /&gt;
* [[DML drivers|DML drivers]]: Database drivers for new DML layer&lt;br /&gt;
* [[DML functions - pre 2.0|DML functions - pre 2.0]]: &#039;&#039;&#039;(deprecated!)&#039;&#039;&#039; For information valid before Moodle 2.0.&lt;br /&gt;
* [[DDL functions|DDL functions]]: Where all the functions used to handle DB objects ([[wikipedia:Data_Definition_Language|DDL]]) are defined.&lt;br /&gt;
* [[DB layer 2.0 examples|DB layer 2.0 examples]]: To see some code examples using various DML functions.&lt;br /&gt;
* [[DB layer 2.0 migration docs|DB layer 2.0 migration docs]]: Information about how to modify your code to work with the new Moodle 2.0 DB layer.&lt;br /&gt;
* [[DTL functions|DTL functions]]: Exporting, importing and moving of data stored in SQL databases&lt;br /&gt;
&lt;br /&gt;
[[Category:DB]]&lt;br /&gt;
[[Category:XMLDB]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Data_manipulation_API&amp;diff=5188</id>
		<title>Data manipulation API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Data_manipulation_API&amp;diff=5188"/>
		<updated>2010-12-03T06:24:10Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* Getting a single record */  get_record_sql syntax was incorrect&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle_2.0}}This page describes the functions available to access data in the Moodle database. You should &#039;&#039;&#039;exclusively&#039;&#039;&#039; use these functions in order to retrieve or modify database content because these functions provide a high level of abstraction and guarantee that your database manipulation will work against different RDBMSes.&lt;br /&gt;
&lt;br /&gt;
Where possible, tricks and examples will be documented here in order to make developers&#039; lives a bit easier. Of course, feel free to clarify, complete and add more information to  this documentation. It will be welcome, absolutely!&lt;br /&gt;
&lt;br /&gt;
== Main info ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important note:&#039;&#039;&#039; All the functions shown on this page are for use in &#039;&#039;&#039;Moodle 2.0 upwards&#039;&#039;&#039;, where we changed the [[DB layer 2.0|DB layer]] to support some new features. If you need information for previous Moodle version, take a look to the [[DML functions - pre 2.0|DML functions - pre 2.0]] page. For a detailed reference of changes, see the [[DB layer 2.0 migration docs|migration docs]].&lt;br /&gt;
&lt;br /&gt;
* All the function calls on this page are public methods of the $DB global object, so you&#039;ll need to &amp;quot;import&amp;quot; it within your functions (not needed in global scripts) with one simple:&lt;br /&gt;
&amp;lt;code php&amp;gt;global $DB;&amp;lt;/code&amp;gt;&lt;br /&gt;
* All the $table parameters in the functions are meant to be the table name &#039;&#039;without&#039;&#039; prefixes.&lt;br /&gt;
&amp;lt;code php&amp;gt;$user = $DB-&amp;gt;get_record(&#039;user&#039;, array(&#039;id&#039;=&amp;gt;&#039;1&#039;);&amp;lt;/code&amp;gt;&lt;br /&gt;
* When using the xxx_sql() functions, table names must be enclosed between curly braces.&lt;br /&gt;
&amp;lt;code php&amp;gt;$user = $DB-&amp;gt;get_record_sql(&#039;SELECT * FROM {user} WHERE id = ?&#039;, array(1));&amp;lt;/code&amp;gt;&lt;br /&gt;
* All the $conditions parameters in the functions are arrays of fieldname=&amp;gt;fieldvalue elements.&lt;br /&gt;
&amp;lt;code php&amp;gt;$user = $DB-&amp;gt;get_record(&#039;user&#039;, array(&#039;firstname&#039;=&amp;gt;&#039;Martin&#039;, &#039;lastname&#039;=&amp;gt;&#039;Dougiamas&#039;));&amp;lt;/code&amp;gt;&lt;br /&gt;
* All the $params parameters in the functions are arrays of values used to fill placeholders in SQL statements. Both the question mark and named placeholders can be used. Note that named params &#039;&#039;&#039;must be unique&#039;&#039;&#039; even if the value passed is the same.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/// Question mark placeholders:&lt;br /&gt;
   $DB-&amp;gt;get_record_sql(&#039;SELECT * FROM {user} WHERE firstname = ? AND lastname = ?&#039;, &lt;br /&gt;
                       array(&#039;Martin&#039;, &#039;Dougiamas&#039;));&lt;br /&gt;
&lt;br /&gt;
/// Named placeholders:&lt;br /&gt;
   $DB-&amp;gt;get_record_sql(&#039;SELECT * FROM {user} WHERE firstname = :firstname AND lastname = :lastname&#039;,&lt;br /&gt;
                       array(&#039;firstname&#039;=&amp;gt;&#039;Martin&#039;, &#039;lastname&#039;=&amp;gt;&#039;Dougiamas&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== The functions ==&lt;br /&gt;
&lt;br /&gt;
===Seeing how many records match a given criteria===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;count_records($table, array $conditions=null) &lt;br /&gt;
  /// Count the records in a table where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;count_records_select($table, $select, array $params=null, $countitem=&amp;quot;COUNT(&#039;x&#039;)&amp;quot;) &lt;br /&gt;
  /// Count the records in a table which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;count_records_sql($sql, array $params=null) &lt;br /&gt;
  /// Get the result of a SQL SELECT COUNT(...) query.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Seeing if one record exists===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;record_exists($table, array $conditions=null) &lt;br /&gt;
  /// Test whether a record exists in a table where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;record_exists_select($table, $select, array $params=null) &lt;br /&gt;
  /// Test whether any records exists in a table which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;record_exists_sql($sql, array $params=null) &lt;br /&gt;
  /// Test whether a SQL SELECT statement returns any records.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Getting a single record===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_record($table, array $conditions, $fields=&#039;*&#039;, $ignoremultiple=false) &lt;br /&gt;
  /// Get a single database record as an object where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_record_select($table, $select, array $params=null, $fields=&#039;*&#039;, $ignoremultiple=false)&lt;br /&gt;
  /// Get a single database record as an object which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_record_sql($sql, array $params=null)&lt;br /&gt;
  /// Get a single database record as an object using a SQL statement.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Getting an array of records===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_records($table, array $conditions=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get a number of records as an array of objects where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_records_select($table, $select, array $params=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get a number of records as an array of objects which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_records_sql($sql, array $params=null, $limitfrom=0, $limitnum=0)&lt;br /&gt;
  /// Get a number of records as an array of objects using a SQL statement.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;get_records_menu($table, array $conditions=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get the first two columns from a number of records as an associative array where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_records_select_menu($table, $select, array $params=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0)&lt;br /&gt;
  /// Get the first two columns from a number of records as an associative array which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_records_sql_menu($sql, array $params=null, $limitfrom=0, $limitnum=0)&lt;br /&gt;
  /// Get the first two columns from a number of records as an associative array using a SQL statement.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;get_records_list($table, $field=&#039;&#039;, $values=&#039;&#039;, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=&#039;&#039;, $limitnum=&#039;&#039;) &lt;br /&gt;
  /// Get a number of records as an array of objects where one field match one list of values.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Getting a particular field value from one record===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_field($table, $return, array $conditions)&lt;br /&gt;
  /// Get a single field value from a table record where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_field_select($table, $return, $select, array $params=null)&lt;br /&gt;
  /// Get a single field value from a table record which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_field_sql($sql, array $params=null)&lt;br /&gt;
  /// Get a single field value (first field) using a SQL statement.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Getting a particular field value from various records===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_fieldset_select($table, $return, $select, array $params=null)&lt;br /&gt;
  /// Selects records and return values of chosen field as an array which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_fieldset_sql($sql, array $params=null)&lt;br /&gt;
  /// Selects records and return values (first field) as an array using a SQL statement.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Setting a particular field in the database===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;set_field($table, $newfield, $newvalue, array $conditions=null)&lt;br /&gt;
  /// Set a single field in every table record where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;set_field_select($table, $newfield, $newvalue, $select, array $params=null)&lt;br /&gt;
  /// Set a single field in every table record which match a particular WHERE clause.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Deleting Records===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;delete_records($table, array $conditions=null) &lt;br /&gt;
  /// Delete the records from a table where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;delete_records_select($table, $select, array $params=null)&lt;br /&gt;
  /// Delete one or more records from a table which match a particular WHERE clause.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Inserting Records===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;insert_record($table, $dataobject, $returnid=true, $bulk=false) &lt;br /&gt;
  /// Insert a record into a table and return the &amp;quot;id&amp;quot; field if required.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Updating Records===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;update_record($table, $dataobject, $bulk=false)&lt;br /&gt;
  /// Update a record in a table.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Using Recordsets===&lt;br /&gt;
&lt;br /&gt;
While the number of records to be retrieved from DB is high, the &#039;&#039;&#039;get_records_xxx()&#039;&#039;&#039; functions above are far from optimal, because they use to load all the records in memory at the same time. Under those circumstances, it&#039;s highly recommended to use this &#039;&#039;&#039;get_recordset_xxx()&#039;&#039;&#039; functions instead, which uses one nice mechanism to iterate over all the target records and save a lot of memory.&lt;br /&gt;
&lt;br /&gt;
Only one thing is &#039;&#039;&#039;absolutely important&#039;&#039;&#039;: Don&#039;t forget to close the recordsets after using them! (This will free up a lot of resources in the RDBMS).&lt;br /&gt;
&lt;br /&gt;
Here is the general way to iterate over records using the &#039;&#039;&#039;get_recordset_xxx()&#039;&#039;&#039; functions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($rs = $DB-&amp;gt;get_recordset(....) {&lt;br /&gt;
    foreach ($rs as $record) {&lt;br /&gt;
     /// Do whatever you want with this record&lt;br /&gt;
    }&lt;br /&gt;
    $rs-&amp;gt;close(); /// Don&#039;t forget to close the recordset!&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And this is the list of available functions (100% paired with the get_records_xxx() above):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_recordset($table, array $conditions=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get a number of records as a moodle_recordset where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_recordset_select($table, $select, array $params=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get a number of records as a moodle_recordset which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_recordset_sql($sql, array $params=null, $limitfrom=0, $limitnum=0);&lt;br /&gt;
  /// Get a number of records as a moodle_recordset using a SQL statement.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;get_recordset_list($table, $field=&#039;&#039;, $values=&#039;&#039;, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=&#039;&#039;, $limitnum=&#039;&#039;) &lt;br /&gt;
  /// Get a number of records as a moodle_recordset where one field matches one list of values.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Delegated transactions===&lt;br /&gt;
&lt;br /&gt;
* Please note some databases do not support transactions (such as the MyISAM MySQL database engine), however all server administrators are strongly encouraged to migrate to databases that support transactions (such as the InnoDB MySQL database engine).&lt;br /&gt;
* Previous versions supported only one level of transaction. Since Moodle 2.0, the DML layer emulates delegated transactions that allow nesting of transactions.&lt;br /&gt;
* Transactions should not be used much in Moodle core; they are intended for various plugins such as web services, enrol and auth plugins.&lt;br /&gt;
* Some subsystems (such as messaging) do not support transactions because is it is not possible to rollback in external systems.&lt;br /&gt;
&lt;br /&gt;
A transaction is started by:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$transaction = $DB-&amp;gt;start_delegated_transaction();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and finished by:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$transaction-&amp;gt;allow_commit();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Usually a transaction is rolled back when an exception is thrown. &amp;lt;code&amp;gt;$transaction-&amp;gt;rollback($ex);&amp;lt;/code&amp;gt; must be used very carefully because it might break compatibility with databases that do not support transactions. Transactions cannot be used as part of expected code flow; they can be used only as an emergency protection of data consistency.&lt;br /&gt;
&lt;br /&gt;
See more details in [[DB layer 2.0 delegated transactions]] or MDL-20625.&lt;br /&gt;
&lt;br /&gt;
===Helper Functions===&lt;br /&gt;
&lt;br /&gt;
In order have real cross-db compatibility, there are some helper functions used to build SQL fragments based on the DB Moodle is running. Using them we&#039;ll avoid conditional queries here and there and have those &amp;quot;incompatibilities&amp;quot; fixed once and for ever.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;sql_bitand($int1, $int2) &lt;br /&gt;
  /// Returns the SQL text to be used in order to perform one bitwise AND &lt;br /&gt;
  /// operation between 2 integers.&lt;br /&gt;
o $DB-&amp;gt;sql_bitnot($int1) &lt;br /&gt;
  /// Returns the SQL text to be used in order to perform one bitwise NOT &lt;br /&gt;
  /// operation with 1 integer.&lt;br /&gt;
o $DB-&amp;gt;sql_bitor($int1, $int2)&lt;br /&gt;
  /// Returns the SQL text to be used in order to perform one bitwise OR &lt;br /&gt;
  /// operation between 2 integers.&lt;br /&gt;
o $DB-&amp;gt;sql_bitxor($int1, $int2) &lt;br /&gt;
  /// Returns the SQL text to be used in order to perform one bitwise XOR &lt;br /&gt;
  /// operation between 2 integers.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_null_from_clause()&lt;br /&gt;
  /// Returns the FROM clause required by some DBs in all SELECT statements.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_ceil($fieldname)&lt;br /&gt;
  /// Returns the correct CEIL expression applied to fieldname.&lt;br /&gt;
o $DB-&amp;gt;sql_ilike()&lt;br /&gt;
  /// Returns the proper SQL to do LIKE in a case-insensitive way.&lt;br /&gt;
o $DB-&amp;gt;sql_length($fieldname)&lt;br /&gt;
  /// Returns the SQL text to be used to calculate the length in characters of one expression.&lt;br /&gt;
o $DB-&amp;gt;sql_modulo($int1, $int2)&lt;br /&gt;
  /// Returns the SQL text to be used in order to calculate module - remainder after division&lt;br /&gt;
o $DB-&amp;gt;sql_position($needle, $haystack)&lt;br /&gt;
  /// Returns the SQL for returning searching one string for the location of another.&lt;br /&gt;
  /// Note: If using placeholders BOTH in $needle and $haystack, they MUST be named placeholders.&lt;br /&gt;
o $DB-&amp;gt;sql_substr($expr, $start, $length=false)&lt;br /&gt;
  /// Returns the proper substr() SQL text used to extract substrings from DB.&lt;br /&gt;
  /// Note: This fuction has changed in Moodle 2.0 and now at least 2 params are mandatory.&lt;br /&gt;
  /// Note: Now it returns the whole SQL text to be used instead of only the function name.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_cast_char2int($fieldname, $text=false)&lt;br /&gt;
  /// Returns the SQL to be used in order to CAST one CHAR column to INTEGER.&lt;br /&gt;
o $DB-&amp;gt;sql_cast_char2real($fieldname, $text=false)&lt;br /&gt;
  /// Returns the SQL to be used in order to CAST one CHAR column to REAL number.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_compare_text($fieldname, $numchars=32) &lt;br /&gt;
  /// Returns the SQL text to be used to compare one TEXT (clob) column.&lt;br /&gt;
  /// with one VARCHAR column.&lt;br /&gt;
o $DB-&amp;gt;sql_order_by_text($fieldname, $numchars=32)&lt;br /&gt;
  /// Returns the SQL text to be used to order by one TEXT (clob) column.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_concat()&lt;br /&gt;
  /// Returns the proper SQL to do CONCAT between the elements passed.&lt;br /&gt;
o $DB-&amp;gt;sql_concat_join($separator=&amp;quot;&#039; &#039;&amp;quot;, $elements=array())&lt;br /&gt;
  /// Returns the proper SQL to do CONCAT between the elements passed using one separator.&lt;br /&gt;
o $DB-&amp;gt;sql_fullname($first=&#039;firstname&#039;, $last=&#039;lastname&#039;)&lt;br /&gt;
  /// Returns the proper SQL to concatenate $firstname and $lastname.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_isempty($tablename, $fieldname, $nullablefield, $textfield)&lt;br /&gt;
  /// Returns the proper SQL to know if one field is empty.&lt;br /&gt;
o $DB-&amp;gt;sql_isnotempty($tablename, $fieldname, $nullablefield, $textfield)&lt;br /&gt;
  /// Returns the proper SQL to know if one field is not empty.&lt;br /&gt;
o $DB-&amp;gt;sql_empty()&lt;br /&gt;
  /// Returns the empty string char used by every supported DB.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[DML exceptions|DML exceptions]]: New DML code is throwing exceptions instead of returning false if anything goes wrong&lt;br /&gt;
* [[DML drivers|DML drivers]]: Database drivers for new DML layer&lt;br /&gt;
* [[DML functions - pre 2.0|DML functions - pre 2.0]]: &#039;&#039;&#039;(deprecated!)&#039;&#039;&#039; For information valid before Moodle 2.0.&lt;br /&gt;
* [[DDL functions|DDL functions]]: Where all the functions used to handle DB objects ([[wikipedia:Data_Definition_Language|DDL]]) are defined.&lt;br /&gt;
* [[DB layer 2.0 examples|DB layer 2.0 examples]]: To see some code examples using various DML functions.&lt;br /&gt;
* [[DB layer 2.0 migration docs|DB layer 2.0 migration docs]]: Information about how to modify your code to work with the new Moodle 2.0 DB layer.&lt;br /&gt;
* [[DTL functions|DTL functions]]: Exporting, importing and moving of data stored in SQL databases&lt;br /&gt;
&lt;br /&gt;
[[Category:DB]]&lt;br /&gt;
[[Category:XMLDB]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Data_manipulation_API&amp;diff=5187</id>
		<title>Data manipulation API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Data_manipulation_API&amp;diff=5187"/>
		<updated>2010-12-03T06:22:39Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* Getting a single record */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle_2.0}}This page describes the functions available to access data in the Moodle database. You should &#039;&#039;&#039;exclusively&#039;&#039;&#039; use these functions in order to retrieve or modify database content because these functions provide a high level of abstraction and guarantee that your database manipulation will work against different RDBMSes.&lt;br /&gt;
&lt;br /&gt;
Where possible, tricks and examples will be documented here in order to make developers&#039; lives a bit easier. Of course, feel free to clarify, complete and add more information to  this documentation. It will be welcome, absolutely!&lt;br /&gt;
&lt;br /&gt;
== Main info ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important note:&#039;&#039;&#039; All the functions shown on this page are for use in &#039;&#039;&#039;Moodle 2.0 upwards&#039;&#039;&#039;, where we changed the [[DB layer 2.0|DB layer]] to support some new features. If you need information for previous Moodle version, take a look to the [[DML functions - pre 2.0|DML functions - pre 2.0]] page. For a detailed reference of changes, see the [[DB layer 2.0 migration docs|migration docs]].&lt;br /&gt;
&lt;br /&gt;
* All the function calls on this page are public methods of the $DB global object, so you&#039;ll need to &amp;quot;import&amp;quot; it within your functions (not needed in global scripts) with one simple:&lt;br /&gt;
&amp;lt;code php&amp;gt;global $DB;&amp;lt;/code&amp;gt;&lt;br /&gt;
* All the $table parameters in the functions are meant to be the table name &#039;&#039;without&#039;&#039; prefixes.&lt;br /&gt;
&amp;lt;code php&amp;gt;$user = $DB-&amp;gt;get_record(&#039;user&#039;, array(&#039;id&#039;=&amp;gt;&#039;1&#039;);&amp;lt;/code&amp;gt;&lt;br /&gt;
* When using the xxx_sql() functions, table names must be enclosed between curly braces.&lt;br /&gt;
&amp;lt;code php&amp;gt;$user = $DB-&amp;gt;get_record_sql(&#039;SELECT * FROM {user} WHERE id = ?&#039;, array(1));&amp;lt;/code&amp;gt;&lt;br /&gt;
* All the $conditions parameters in the functions are arrays of fieldname=&amp;gt;fieldvalue elements.&lt;br /&gt;
&amp;lt;code php&amp;gt;$user = $DB-&amp;gt;get_record(&#039;user&#039;, array(&#039;firstname&#039;=&amp;gt;&#039;Martin&#039;, &#039;lastname&#039;=&amp;gt;&#039;Dougiamas&#039;));&amp;lt;/code&amp;gt;&lt;br /&gt;
* All the $params parameters in the functions are arrays of values used to fill placeholders in SQL statements. Both the question mark and named placeholders can be used. Note that named params &#039;&#039;&#039;must be unique&#039;&#039;&#039; even if the value passed is the same.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
/// Question mark placeholders:&lt;br /&gt;
   $DB-&amp;gt;get_record_sql(&#039;SELECT * FROM {user} WHERE firstname = ? AND lastname = ?&#039;, &lt;br /&gt;
                       array(&#039;Martin&#039;, &#039;Dougiamas&#039;));&lt;br /&gt;
&lt;br /&gt;
/// Named placeholders:&lt;br /&gt;
   $DB-&amp;gt;get_record_sql(&#039;SELECT * FROM {user} WHERE firstname = :firstname AND lastname = :lastname&#039;,&lt;br /&gt;
                       array(&#039;firstname&#039;=&amp;gt;&#039;Martin&#039;, &#039;lastname&#039;=&amp;gt;&#039;Dougiamas&#039;));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== The functions ==&lt;br /&gt;
&lt;br /&gt;
===Seeing how many records match a given criteria===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;count_records($table, array $conditions=null) &lt;br /&gt;
  /// Count the records in a table where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;count_records_select($table, $select, array $params=null, $countitem=&amp;quot;COUNT(&#039;x&#039;)&amp;quot;) &lt;br /&gt;
  /// Count the records in a table which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;count_records_sql($sql, array $params=null) &lt;br /&gt;
  /// Get the result of a SQL SELECT COUNT(...) query.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Seeing if one record exists===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;record_exists($table, array $conditions=null) &lt;br /&gt;
  /// Test whether a record exists in a table where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;record_exists_select($table, $select, array $params=null) &lt;br /&gt;
  /// Test whether any records exists in a table which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;record_exists_sql($sql, array $params=null) &lt;br /&gt;
  /// Test whether a SQL SELECT statement returns any records.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Getting a single record===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_record($table, array $conditions, $fields=&#039;*&#039;, $ignoremultiple=false) &lt;br /&gt;
  /// Get a single database record as an object where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_record_select($table, $select, array $params=null, $fields=&#039;*&#039;, $ignoremultiple=false)&lt;br /&gt;
  /// Get a single database record as an object which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_record_sql($table, $sql, array $params=null)&lt;br /&gt;
  /// Get a single database record as an object using a SQL statement.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Getting an array of records===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_records($table, array $conditions=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get a number of records as an array of objects where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_records_select($table, $select, array $params=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get a number of records as an array of objects which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_records_sql($sql, array $params=null, $limitfrom=0, $limitnum=0)&lt;br /&gt;
  /// Get a number of records as an array of objects using a SQL statement.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;get_records_menu($table, array $conditions=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get the first two columns from a number of records as an associative array where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_records_select_menu($table, $select, array $params=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0)&lt;br /&gt;
  /// Get the first two columns from a number of records as an associative array which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_records_sql_menu($sql, array $params=null, $limitfrom=0, $limitnum=0)&lt;br /&gt;
  /// Get the first two columns from a number of records as an associative array using a SQL statement.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;get_records_list($table, $field=&#039;&#039;, $values=&#039;&#039;, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=&#039;&#039;, $limitnum=&#039;&#039;) &lt;br /&gt;
  /// Get a number of records as an array of objects where one field match one list of values.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Getting a particular field value from one record===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_field($table, $return, array $conditions)&lt;br /&gt;
  /// Get a single field value from a table record where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_field_select($table, $return, $select, array $params=null)&lt;br /&gt;
  /// Get a single field value from a table record which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_field_sql($sql, array $params=null)&lt;br /&gt;
  /// Get a single field value (first field) using a SQL statement.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Getting a particular field value from various records===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_fieldset_select($table, $return, $select, array $params=null)&lt;br /&gt;
  /// Selects records and return values of chosen field as an array which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_fieldset_sql($sql, array $params=null)&lt;br /&gt;
  /// Selects records and return values (first field) as an array using a SQL statement.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Setting a particular field in the database===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;set_field($table, $newfield, $newvalue, array $conditions=null)&lt;br /&gt;
  /// Set a single field in every table record where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;set_field_select($table, $newfield, $newvalue, $select, array $params=null)&lt;br /&gt;
  /// Set a single field in every table record which match a particular WHERE clause.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Deleting Records===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;delete_records($table, array $conditions=null) &lt;br /&gt;
  /// Delete the records from a table where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;delete_records_select($table, $select, array $params=null)&lt;br /&gt;
  /// Delete one or more records from a table which match a particular WHERE clause.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Inserting Records===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;insert_record($table, $dataobject, $returnid=true, $bulk=false) &lt;br /&gt;
  /// Insert a record into a table and return the &amp;quot;id&amp;quot; field if required.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Updating Records===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;update_record($table, $dataobject, $bulk=false)&lt;br /&gt;
  /// Update a record in a table.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Using Recordsets===&lt;br /&gt;
&lt;br /&gt;
While the number of records to be retrieved from DB is high, the &#039;&#039;&#039;get_records_xxx()&#039;&#039;&#039; functions above are far from optimal, because they use to load all the records in memory at the same time. Under those circumstances, it&#039;s highly recommended to use this &#039;&#039;&#039;get_recordset_xxx()&#039;&#039;&#039; functions instead, which uses one nice mechanism to iterate over all the target records and save a lot of memory.&lt;br /&gt;
&lt;br /&gt;
Only one thing is &#039;&#039;&#039;absolutely important&#039;&#039;&#039;: Don&#039;t forget to close the recordsets after using them! (This will free up a lot of resources in the RDBMS).&lt;br /&gt;
&lt;br /&gt;
Here is the general way to iterate over records using the &#039;&#039;&#039;get_recordset_xxx()&#039;&#039;&#039; functions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if ($rs = $DB-&amp;gt;get_recordset(....) {&lt;br /&gt;
    foreach ($rs as $record) {&lt;br /&gt;
     /// Do whatever you want with this record&lt;br /&gt;
    }&lt;br /&gt;
    $rs-&amp;gt;close(); /// Don&#039;t forget to close the recordset!&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And this is the list of available functions (100% paired with the get_records_xxx() above):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;get_recordset($table, array $conditions=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get a number of records as a moodle_recordset where all the given conditions met.&lt;br /&gt;
o $DB-&amp;gt;get_recordset_select($table, $select, array $params=null, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=0, $limitnum=0) &lt;br /&gt;
  /// Get a number of records as a moodle_recordset which match a particular WHERE clause.&lt;br /&gt;
o $DB-&amp;gt;get_recordset_sql($sql, array $params=null, $limitfrom=0, $limitnum=0);&lt;br /&gt;
  /// Get a number of records as a moodle_recordset using a SQL statement.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;get_recordset_list($table, $field=&#039;&#039;, $values=&#039;&#039;, $sort=&#039;&#039;, $fields=&#039;*&#039;, $limitfrom=&#039;&#039;, $limitnum=&#039;&#039;) &lt;br /&gt;
  /// Get a number of records as a moodle_recordset where one field matches one list of values.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Delegated transactions===&lt;br /&gt;
&lt;br /&gt;
* Please note some databases do not support transactions (such as the MyISAM MySQL database engine), however all server administrators are strongly encouraged to migrate to databases that support transactions (such as the InnoDB MySQL database engine).&lt;br /&gt;
* Previous versions supported only one level of transaction. Since Moodle 2.0, the DML layer emulates delegated transactions that allow nesting of transactions.&lt;br /&gt;
* Transactions should not be used much in Moodle core; they are intended for various plugins such as web services, enrol and auth plugins.&lt;br /&gt;
* Some subsystems (such as messaging) do not support transactions because is it is not possible to rollback in external systems.&lt;br /&gt;
&lt;br /&gt;
A transaction is started by:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$transaction = $DB-&amp;gt;start_delegated_transaction();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and finished by:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$transaction-&amp;gt;allow_commit();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Usually a transaction is rolled back when an exception is thrown. &amp;lt;code&amp;gt;$transaction-&amp;gt;rollback($ex);&amp;lt;/code&amp;gt; must be used very carefully because it might break compatibility with databases that do not support transactions. Transactions cannot be used as part of expected code flow; they can be used only as an emergency protection of data consistency.&lt;br /&gt;
&lt;br /&gt;
See more details in [[DB layer 2.0 delegated transactions]] or MDL-20625.&lt;br /&gt;
&lt;br /&gt;
===Helper Functions===&lt;br /&gt;
&lt;br /&gt;
In order have real cross-db compatibility, there are some helper functions used to build SQL fragments based on the DB Moodle is running. Using them we&#039;ll avoid conditional queries here and there and have those &amp;quot;incompatibilities&amp;quot; fixed once and for ever.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
o $DB-&amp;gt;sql_bitand($int1, $int2) &lt;br /&gt;
  /// Returns the SQL text to be used in order to perform one bitwise AND &lt;br /&gt;
  /// operation between 2 integers.&lt;br /&gt;
o $DB-&amp;gt;sql_bitnot($int1) &lt;br /&gt;
  /// Returns the SQL text to be used in order to perform one bitwise NOT &lt;br /&gt;
  /// operation with 1 integer.&lt;br /&gt;
o $DB-&amp;gt;sql_bitor($int1, $int2)&lt;br /&gt;
  /// Returns the SQL text to be used in order to perform one bitwise OR &lt;br /&gt;
  /// operation between 2 integers.&lt;br /&gt;
o $DB-&amp;gt;sql_bitxor($int1, $int2) &lt;br /&gt;
  /// Returns the SQL text to be used in order to perform one bitwise XOR &lt;br /&gt;
  /// operation between 2 integers.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_null_from_clause()&lt;br /&gt;
  /// Returns the FROM clause required by some DBs in all SELECT statements.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_ceil($fieldname)&lt;br /&gt;
  /// Returns the correct CEIL expression applied to fieldname.&lt;br /&gt;
o $DB-&amp;gt;sql_ilike()&lt;br /&gt;
  /// Returns the proper SQL to do LIKE in a case-insensitive way.&lt;br /&gt;
o $DB-&amp;gt;sql_length($fieldname)&lt;br /&gt;
  /// Returns the SQL text to be used to calculate the length in characters of one expression.&lt;br /&gt;
o $DB-&amp;gt;sql_modulo($int1, $int2)&lt;br /&gt;
  /// Returns the SQL text to be used in order to calculate module - remainder after division&lt;br /&gt;
o $DB-&amp;gt;sql_position($needle, $haystack)&lt;br /&gt;
  /// Returns the SQL for returning searching one string for the location of another.&lt;br /&gt;
  /// Note: If using placeholders BOTH in $needle and $haystack, they MUST be named placeholders.&lt;br /&gt;
o $DB-&amp;gt;sql_substr($expr, $start, $length=false)&lt;br /&gt;
  /// Returns the proper substr() SQL text used to extract substrings from DB.&lt;br /&gt;
  /// Note: This fuction has changed in Moodle 2.0 and now at least 2 params are mandatory.&lt;br /&gt;
  /// Note: Now it returns the whole SQL text to be used instead of only the function name.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_cast_char2int($fieldname, $text=false)&lt;br /&gt;
  /// Returns the SQL to be used in order to CAST one CHAR column to INTEGER.&lt;br /&gt;
o $DB-&amp;gt;sql_cast_char2real($fieldname, $text=false)&lt;br /&gt;
  /// Returns the SQL to be used in order to CAST one CHAR column to REAL number.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_compare_text($fieldname, $numchars=32) &lt;br /&gt;
  /// Returns the SQL text to be used to compare one TEXT (clob) column.&lt;br /&gt;
  /// with one VARCHAR column.&lt;br /&gt;
o $DB-&amp;gt;sql_order_by_text($fieldname, $numchars=32)&lt;br /&gt;
  /// Returns the SQL text to be used to order by one TEXT (clob) column.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_concat()&lt;br /&gt;
  /// Returns the proper SQL to do CONCAT between the elements passed.&lt;br /&gt;
o $DB-&amp;gt;sql_concat_join($separator=&amp;quot;&#039; &#039;&amp;quot;, $elements=array())&lt;br /&gt;
  /// Returns the proper SQL to do CONCAT between the elements passed using one separator.&lt;br /&gt;
o $DB-&amp;gt;sql_fullname($first=&#039;firstname&#039;, $last=&#039;lastname&#039;)&lt;br /&gt;
  /// Returns the proper SQL to concatenate $firstname and $lastname.&lt;br /&gt;
 &lt;br /&gt;
o $DB-&amp;gt;sql_isempty($tablename, $fieldname, $nullablefield, $textfield)&lt;br /&gt;
  /// Returns the proper SQL to know if one field is empty.&lt;br /&gt;
o $DB-&amp;gt;sql_isnotempty($tablename, $fieldname, $nullablefield, $textfield)&lt;br /&gt;
  /// Returns the proper SQL to know if one field is not empty.&lt;br /&gt;
o $DB-&amp;gt;sql_empty()&lt;br /&gt;
  /// Returns the empty string char used by every supported DB.&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[DML exceptions|DML exceptions]]: New DML code is throwing exceptions instead of returning false if anything goes wrong&lt;br /&gt;
* [[DML drivers|DML drivers]]: Database drivers for new DML layer&lt;br /&gt;
* [[DML functions - pre 2.0|DML functions - pre 2.0]]: &#039;&#039;&#039;(deprecated!)&#039;&#039;&#039; For information valid before Moodle 2.0.&lt;br /&gt;
* [[DDL functions|DDL functions]]: Where all the functions used to handle DB objects ([[wikipedia:Data_Definition_Language|DDL]]) are defined.&lt;br /&gt;
* [[DB layer 2.0 examples|DB layer 2.0 examples]]: To see some code examples using various DML functions.&lt;br /&gt;
* [[DB layer 2.0 migration docs|DB layer 2.0 migration docs]]: Information about how to modify your code to work with the new Moodle 2.0 DB layer.&lt;br /&gt;
* [[DTL functions|DTL functions]]: Exporting, importing and moving of data stored in SQL databases&lt;br /&gt;
&lt;br /&gt;
[[Category:DB]]&lt;br /&gt;
[[Category:XMLDB]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Bruno_Vernier&amp;diff=22666</id>
		<title>User:Bruno Vernier</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Bruno_Vernier&amp;diff=22666"/>
		<updated>2010-11-30T18:03:45Z</updated>

		<summary type="html">&lt;p&gt;Bruno: New page: teacher in Vancouver, British Columbia , Canada&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;teacher in Vancouver, British Columbia , Canada&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Calculated_question_improvements&amp;diff=4891</id>
		<title>Calculated question improvements</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Calculated_question_improvements&amp;diff=4891"/>
		<updated>2009-03-06T06:35:36Z</updated>

		<summary type="html">&lt;p&gt;Bruno: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Calculated question dev docs}}&lt;br /&gt;
I will use this page to describe the various improvements I am developping related to the calculated questions.&lt;br /&gt;
The code of 1.8 with the QuickForm HTML structure offer a considerable improvement in the interface user over the preceeding versions.&lt;br /&gt;
However at Université du Québec à Montréal, we moved from WebCT 4 to Moodle 1.6.3 in spring 2007 and will maintian this version for the next academic year. So exceptionnally, I will port down to 1.6 most of the improvements being made to the Moodle HEAD version.&lt;br /&gt;
&lt;br /&gt;
The following content of this page will be updated soon. &lt;br /&gt;
&lt;br /&gt;
==Working progress==&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!&#039;&#039;&#039;Improvements&#039;&#039;&#039;&lt;br /&gt;
!HEAD&lt;br /&gt;
!1.8&lt;br /&gt;
!1.7&lt;br /&gt;
!1.6&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
Moodle XML Import , Export&lt;br /&gt;
|Done&lt;br /&gt;
|Done&lt;br /&gt;
|Done&lt;br /&gt;
|Done&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
WebCT Import&lt;br /&gt;
|Done&lt;br /&gt;
|Done&lt;br /&gt;
|Done&lt;br /&gt;
|Done&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
Saving question at each step&lt;br /&gt;
*remove use of $SESSION to store parameters&lt;br /&gt;
|Done&lt;br /&gt;
|Done&lt;br /&gt;
|Done&lt;br /&gt;
|Done&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
Allow save as new question&lt;br /&gt;
*saving also the datasets and data items&lt;br /&gt;
|Done&lt;br /&gt;
|Done&lt;br /&gt;
|TO DO&lt;br /&gt;
|TO DO&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
Allow saving of multiple answers&lt;br /&gt;
|Done&lt;br /&gt;
|Done&lt;br /&gt;
|Done&lt;br /&gt;
|Done&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
Allow edition of multiple answers&lt;br /&gt;
|Done&lt;br /&gt;
|Done&lt;br /&gt;
|READY&lt;br /&gt;
|READY&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
Validation of user data before saving&lt;br /&gt;
*For QuickForm redisplay with red text&lt;br /&gt;
*Older version use notice when saving question-&amp;gt;options so there is a return to editquestion &lt;br /&gt;
|Done&lt;br /&gt;
|Done&lt;br /&gt;
|TESTING&lt;br /&gt;
|TESTING&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
Allow addition and removal of dataitems by steps &lt;br /&gt;
of 5,10,15, etc&lt;br /&gt;
|TESTING&lt;br /&gt;
|TESTING&lt;br /&gt;
|TESTING&lt;br /&gt;
|TESTING&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
larger field for formula &lt;br /&gt;
&lt;br /&gt;
|TO DO&lt;br /&gt;
|TO DO&lt;br /&gt;
|TO DO&lt;br /&gt;
|TO DO&lt;br /&gt;
|-&lt;br /&gt;
|&lt;br /&gt;
subquestions &lt;br /&gt;
&lt;br /&gt;
|TO DO&lt;br /&gt;
|TO DO&lt;br /&gt;
|TO DO&lt;br /&gt;
|TO DO&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
Possible steps are :&lt;br /&gt;
* TO DO&lt;br /&gt;
* CODED&lt;br /&gt;
* TESTING&lt;br /&gt;
* READY&lt;br /&gt;
* Done i.e CVS and merged&lt;br /&gt;
07/05/23&lt;br /&gt;
&lt;br /&gt;
==Foolproof and clearer creation process==&lt;br /&gt;
Actually the creation or edition of calculated question is a three steps process ( 3 different pages) where the user can make errors that he cannot readily recuperate.&lt;br /&gt;
* Identify the steps and renaming the save changes or continue buttons.&lt;br /&gt;
* Using javascript to correct for errors or non valid values in each steps before going to the next step.[[Calculated question js validating forms]]&lt;br /&gt;
* Creating buttons to access the previous steps for corrections.&lt;br /&gt;
* Creating a table showing the variables used by the other calculated questions in the same category.&lt;br /&gt;
* Allowing to create or delete data items by 1, 10, 20 etc in one step.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Moving calculated questions to another category==&lt;br /&gt;
Actually the code (Moodle 1.7) for moving questions to another category means simply to change the category field in the database question record.&lt;br /&gt;
Although this is correct for most questions types, this is not the right way to do it with calculated questions because the same category field is also used in the datasets records.&lt;br /&gt;
A dataset can be shared by questions of the same category. &lt;br /&gt;
To search if there is a already sharable dataset in a category, the parameters used are the dataset-&amp;gt;name and the dataset-&amp;gt;category.&lt;br /&gt;
The possibilities are&lt;br /&gt;
#All the dataset used by the question to be moved are reserved for this question (dataset-&amp;gt;category=0). The calculated question can be moved to another category &#039;&#039;&#039;without restriction&#039;&#039;&#039;.This is the actual implementation.&lt;br /&gt;
#At least one of the dataset used by the question to be moved is sharable (dataset-&amp;gt;category = actual question category)&lt;br /&gt;
## If &#039;&#039;&#039;all&#039;&#039;&#039; the sharable dataset are &#039;&#039;&#039;only&#039;&#039;&#039; used by the question to be moved, the calculated question can be selected to move to the other category .&lt;br /&gt;
## If &#039;&#039;&#039;all&#039;&#039;&#039; the questions that are selected to be moved, and &#039;&#039;&#039;all&#039;&#039;&#039; the sharable datasets are only used by these questions to be moved, this set of questions can be selected to move to the other category .&lt;br /&gt;
## In the case that there are questions not selected that could used a sharable dataset, this dataset cannot be moved in the another category.&lt;br /&gt;
###Notify the user that another questions in the initial category share a parameter with this category.&lt;br /&gt;
####The user can choose to cancel the move and select all the other questions (Case 2.2).&lt;br /&gt;
####The user can choose to keep the question in the selected questions to be moved. In this case, the dataset should be duplicated when moving the another category.&lt;br /&gt;
===Duplicate datasets?===&lt;br /&gt;
If a similar sharable dataset already exists in the moveto category ( identical dataset-&amp;gt;name and dataset-&amp;gt;category = movetocategory).&lt;br /&gt;
Notify the user and giving information on the&lt;br /&gt;
#sharable dataset in selected questions to be moved &lt;br /&gt;
#already existing sharable datasets in the move_to_category.&lt;br /&gt;
The user should be able to select which dataset will be used in move_to_category.&lt;br /&gt;
&lt;br /&gt;
To implement these choices we need to add new functions in default questiontype class.&lt;br /&gt;
===function movable_to_category(&amp;amp;$questions,$categoryid)===&lt;br /&gt;
 default return null &lt;br /&gt;
 $questions contain all the question records data for all the selected questions &lt;br /&gt;
 so that qtype implementation could test &amp;lt;br&amp;gt;if the selected questions satisfy option 2.2&lt;br /&gt;
 a $questions-&amp;gt;notify can be added to be used by showbank.php and move_to_category.&lt;br /&gt;
===function move_to_category($question,$tocategory)=== &lt;br /&gt;
the default implementation should something like the actual code&lt;br /&gt;
  {&lt;br /&gt;
           if (!set_field(&#039;question&#039;, &#039;category&#039;, $tocategory-&amp;gt;id, &#039;id&#039;, $question-&amp;gt;id)) {&lt;br /&gt;
                            error(&#039;Could not update category field&#039;);&lt;br /&gt;
                        }&lt;br /&gt;
        return null;&lt;br /&gt;
    }&lt;br /&gt;
the calculated question implementation should be able to duplicate the datasets in the new category&lt;br /&gt;
&lt;br /&gt;
==Improvements in general question editing interface==&lt;br /&gt;
*Showing the text of the questions&lt;br /&gt;
&lt;br /&gt;
==Creation of a short (simple) calculated question==&lt;br /&gt;
The actual calculated question allows for calculated params that can be shared by different problems.&lt;br /&gt;
This could lead to a somewhat cloze used of calculated questions.However the questions cannot migrate individually to another category.&lt;br /&gt;
A short calculated question that just allow for calculated parameters reserved for this question will allow use of this question in drag and drop cloze lesson.&lt;br /&gt;
==Manipulating data items==&lt;br /&gt;
Actual functions and propôsal for new ones for manipulating data items are discussed in [[Data_Item_Functions]]&lt;br /&gt;
==Side effects to other modules==&lt;br /&gt;
* Modification of the question type to allow moving of calculated question to another category.&lt;br /&gt;
* Creation of general functions to standardize the import or export process.&lt;br /&gt;
==Creation and modification of calculated questions==&lt;br /&gt;
See the [[Calculated question bugs and new features proposal]]&lt;br /&gt;
&lt;br /&gt;
See the [[Calculated multiquestions proposal]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
See the [[Calculated question actual 1.7 interface summary]]&lt;br /&gt;
&lt;br /&gt;
See the [[Calculated question 1.7 bugs solving proposal]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  [[User:Pierre Pichet|Pierre Pichet]]  August 2007 (WST)&lt;br /&gt;
[[Developer_notes]]&lt;br /&gt;
[[Calculated question creation developper docs]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Calculated_question_type_creation&amp;diff=3652</id>
		<title>Calculated question type creation</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Calculated_question_type_creation&amp;diff=3652"/>
		<updated>2009-03-06T06:19:26Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* A more FULL PROOF code proposal */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Calculated question dev docs}}&lt;br /&gt;
I will use this page and the followings to describe the PHP code principal processes of the calculated question creation and see how it could be improved.&lt;br /&gt;
==Calculated question parameters== &lt;br /&gt;
Calculated question is an extended numerical question where parameters of a mathematical problems are generated and used to calculate the answer using a mathematical formula.&lt;br /&gt;
&lt;br /&gt;
So the answer parameter of a numerical question is replaced by the mathematical formula using the same answer database structure that the numerical question.&lt;br /&gt;
 &lt;br /&gt;
The generated parameters are identified by a special format that is not used in a mathematical PHP equation {param} where the param name begins by a letter and can include alphanumerics symbol a-z A-Z _ 0-9  &lt;br /&gt;
 ex. {ad} {a_2} {a4_r} are valid parameter names &lt;br /&gt;
 {1a} {a+1} {a(0)} are invalid parameter names&lt;br /&gt;
  &lt;br /&gt;
These parameters are transform in datasets defined by a min,max,decimal and generation mode.&lt;br /&gt;
Using these datasets definitions a number of different values are generated (the dataset items) and used to generate different question instances where the parameters have different values although the question structure and answer formula are constant.&lt;br /&gt;
&lt;br /&gt;
==Creating a calculated question==&lt;br /&gt;
Actually the creation of a calculated question is a three step process&lt;br /&gt;
* &#039;&#039;&#039;Defining the common question parameters&#039;&#039;&#039;&lt;br /&gt;
** Question name&lt;br /&gt;
** Question text&lt;br /&gt;
** answer formula, limit, units&lt;br /&gt;
* &#039;&#039;&#039;Defining the dataset definitions range&#039;&#039;&#039; for each of them. The dataset definition ({params}) can be used in this new question only or could be used or add been already created by other questions in the question category in which the new question is created.&lt;br /&gt;
  question category is a set of questions that can contain various question types&lt;br /&gt;
* &#039;&#039;&#039;Defining the dataset definitions parameters&#039;&#039;&#039; (min,max, decimal and generation mode and &#039;&#039;&#039;adding at least one data item&#039;&#039;&#039; for each dataset definitions&lt;br /&gt;
 Actually a set of all the data items is created when you click the add button&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Saving&#039;&#039;&#039; the question parameters,dataset definitions parameters and data items.&lt;br /&gt;
The real saving procedure is done when you &#039;&#039;&#039;add&#039;&#039;&#039;  the &#039;&#039;&#039;first&#039;&#039;&#039; set of data items.&lt;br /&gt;
 Because in the actual code a calculated question needs that&lt;br /&gt;
 &#039;&#039;&#039;all&#039;&#039;&#039; these components be defined to be a &#039;&#039;&#039;valid&#039;&#039;&#039; calculated question,&lt;br /&gt;
 the saving is &#039;&#039;&#039;postponed&#039;&#039;&#039; until you click the &#039;&#039;&#039;add button&#039;&#039;&#039; to add the &#039;&#039;&#039;first&#039;&#039;&#039; set of data items.&lt;br /&gt;
 The parameters already entered are stored in the $Session object&lt;br /&gt;
 even if you click &#039;&#039;&#039;Saving changes&#039;&#039;&#039; at least&lt;br /&gt;
 &#039;&#039;&#039;twice&#039;&#039;&#039; &lt;br /&gt;
   1.&#039;&#039;&#039;Defining the common question parameters&#039;&#039;&#039; &lt;br /&gt;
   2.&#039;&#039;&#039;Defining the dataset definitions range&#039;&#039;&#039; steps.&lt;br /&gt;
 before adding your first set of data items.&lt;br /&gt;
&lt;br /&gt;
==Editing of a question==&lt;br /&gt;
When you modify the an actual question, if you click &#039;&#039;&#039;Saving changes&#039;&#039;&#039;. the changes are saved in the database. &lt;br /&gt;
==Creating a copy of a question==&lt;br /&gt;
When you create a new question as a copy of an actual question , the ordinary parameters are copied but you havo to reconstruct the database sets and the database.&lt;br /&gt;
==Moving a question to another category==&lt;br /&gt;
When you move a question to another category, the ordinary parameters are copied but you havo to reconstruct the database sets and the database.If you come back to the initial category, the initial datasets are retrivied. There is a bug in the transfer related to the complex structure of the calculated questions.&lt;br /&gt;
Correcting this bug is one of my priorities([[User:Pierre Pichet|Pierre Pichet]] 11:59, 21 August 2006 (CDT)).&lt;br /&gt;
==A summary of the actual functions==&lt;br /&gt;
See the [[Calculated question actual 1.7 interface summary]]&lt;br /&gt;
&lt;br /&gt;
==NOTHING is SAVED UNTIL you CLICK the ADD BUTTON==&lt;br /&gt;
In the preceding Moodle version (&amp;lt;1.6), the user could click the BACK TO QUIZ EDITING button before adding a first data set item&lt;br /&gt;
 If the user at the third step click BACK TO QUIZ EDITING when NO dataset items added, &lt;br /&gt;
 he LOOSE all his work because the question general parameters where not solved.&lt;br /&gt;
In the newer Moodle versions (1.65 beta,1.7, June 2006), the BACK TO QUIZ EDITING button does not appear unless there is at least one data set item.&lt;br /&gt;
see [[Calculated question back to quiz button]]&lt;br /&gt;
&lt;br /&gt;
Because of this three steps procedure, the creation of a calculated question is a process which can abort easily if the user do not follow exactly all the three steps in the order the Moodle code expect them.&lt;br /&gt;
Improvements to obtain a &#039;&#039;&#039;FOOLPROOF PROCESS&#039;&#039;&#039; are proposed in the next section.&lt;br /&gt;
&lt;br /&gt;
However, the teacher should always verify that for each added data item set, a valid answer, minimum and maximum value are obtained.&lt;br /&gt;
Fortunately, at this step the newly created calculated question is already saved in the database and the user could go back and modify the question if necessary.&lt;br /&gt;
&lt;br /&gt;
==A more FOOLPROOF code proposal == &lt;br /&gt;
See also [[Calculated question development| a proposed calculated question development]] in Developer notes  &lt;br /&gt;
&lt;br /&gt;
[[Category:Calculated question type creation]]&lt;br /&gt;
[[Category:Quiz]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Calculated_question_type_creation&amp;diff=3651</id>
		<title>Calculated question type creation</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Calculated_question_type_creation&amp;diff=3651"/>
		<updated>2009-03-06T06:19:07Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* NOTHING is SAVED UNTIL you CLICK the ADD BUTTON */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Calculated question dev docs}}&lt;br /&gt;
I will use this page and the followings to describe the PHP code principal processes of the calculated question creation and see how it could be improved.&lt;br /&gt;
==Calculated question parameters== &lt;br /&gt;
Calculated question is an extended numerical question where parameters of a mathematical problems are generated and used to calculate the answer using a mathematical formula.&lt;br /&gt;
&lt;br /&gt;
So the answer parameter of a numerical question is replaced by the mathematical formula using the same answer database structure that the numerical question.&lt;br /&gt;
 &lt;br /&gt;
The generated parameters are identified by a special format that is not used in a mathematical PHP equation {param} where the param name begins by a letter and can include alphanumerics symbol a-z A-Z _ 0-9  &lt;br /&gt;
 ex. {ad} {a_2} {a4_r} are valid parameter names &lt;br /&gt;
 {1a} {a+1} {a(0)} are invalid parameter names&lt;br /&gt;
  &lt;br /&gt;
These parameters are transform in datasets defined by a min,max,decimal and generation mode.&lt;br /&gt;
Using these datasets definitions a number of different values are generated (the dataset items) and used to generate different question instances where the parameters have different values although the question structure and answer formula are constant.&lt;br /&gt;
&lt;br /&gt;
==Creating a calculated question==&lt;br /&gt;
Actually the creation of a calculated question is a three step process&lt;br /&gt;
* &#039;&#039;&#039;Defining the common question parameters&#039;&#039;&#039;&lt;br /&gt;
** Question name&lt;br /&gt;
** Question text&lt;br /&gt;
** answer formula, limit, units&lt;br /&gt;
* &#039;&#039;&#039;Defining the dataset definitions range&#039;&#039;&#039; for each of them. The dataset definition ({params}) can be used in this new question only or could be used or add been already created by other questions in the question category in which the new question is created.&lt;br /&gt;
  question category is a set of questions that can contain various question types&lt;br /&gt;
* &#039;&#039;&#039;Defining the dataset definitions parameters&#039;&#039;&#039; (min,max, decimal and generation mode and &#039;&#039;&#039;adding at least one data item&#039;&#039;&#039; for each dataset definitions&lt;br /&gt;
 Actually a set of all the data items is created when you click the add button&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Saving&#039;&#039;&#039; the question parameters,dataset definitions parameters and data items.&lt;br /&gt;
The real saving procedure is done when you &#039;&#039;&#039;add&#039;&#039;&#039;  the &#039;&#039;&#039;first&#039;&#039;&#039; set of data items.&lt;br /&gt;
 Because in the actual code a calculated question needs that&lt;br /&gt;
 &#039;&#039;&#039;all&#039;&#039;&#039; these components be defined to be a &#039;&#039;&#039;valid&#039;&#039;&#039; calculated question,&lt;br /&gt;
 the saving is &#039;&#039;&#039;postponed&#039;&#039;&#039; until you click the &#039;&#039;&#039;add button&#039;&#039;&#039; to add the &#039;&#039;&#039;first&#039;&#039;&#039; set of data items.&lt;br /&gt;
 The parameters already entered are stored in the $Session object&lt;br /&gt;
 even if you click &#039;&#039;&#039;Saving changes&#039;&#039;&#039; at least&lt;br /&gt;
 &#039;&#039;&#039;twice&#039;&#039;&#039; &lt;br /&gt;
   1.&#039;&#039;&#039;Defining the common question parameters&#039;&#039;&#039; &lt;br /&gt;
   2.&#039;&#039;&#039;Defining the dataset definitions range&#039;&#039;&#039; steps.&lt;br /&gt;
 before adding your first set of data items.&lt;br /&gt;
&lt;br /&gt;
==Editing of a question==&lt;br /&gt;
When you modify the an actual question, if you click &#039;&#039;&#039;Saving changes&#039;&#039;&#039;. the changes are saved in the database. &lt;br /&gt;
==Creating a copy of a question==&lt;br /&gt;
When you create a new question as a copy of an actual question , the ordinary parameters are copied but you havo to reconstruct the database sets and the database.&lt;br /&gt;
==Moving a question to another category==&lt;br /&gt;
When you move a question to another category, the ordinary parameters are copied but you havo to reconstruct the database sets and the database.If you come back to the initial category, the initial datasets are retrivied. There is a bug in the transfer related to the complex structure of the calculated questions.&lt;br /&gt;
Correcting this bug is one of my priorities([[User:Pierre Pichet|Pierre Pichet]] 11:59, 21 August 2006 (CDT)).&lt;br /&gt;
==A summary of the actual functions==&lt;br /&gt;
See the [[Calculated question actual 1.7 interface summary]]&lt;br /&gt;
&lt;br /&gt;
==NOTHING is SAVED UNTIL you CLICK the ADD BUTTON==&lt;br /&gt;
In the preceding Moodle version (&amp;lt;1.6), the user could click the BACK TO QUIZ EDITING button before adding a first data set item&lt;br /&gt;
 If the user at the third step click BACK TO QUIZ EDITING when NO dataset items added, &lt;br /&gt;
 he LOOSE all his work because the question general parameters where not solved.&lt;br /&gt;
In the newer Moodle versions (1.65 beta,1.7, June 2006), the BACK TO QUIZ EDITING button does not appear unless there is at least one data set item.&lt;br /&gt;
see [[Calculated question back to quiz button]]&lt;br /&gt;
&lt;br /&gt;
Because of this three steps procedure, the creation of a calculated question is a process which can abort easily if the user do not follow exactly all the three steps in the order the Moodle code expect them.&lt;br /&gt;
Improvements to obtain a &#039;&#039;&#039;FOOLPROOF PROCESS&#039;&#039;&#039; are proposed in the next section.&lt;br /&gt;
&lt;br /&gt;
However, the teacher should always verify that for each added data item set, a valid answer, minimum and maximum value are obtained.&lt;br /&gt;
Fortunately, at this step the newly created calculated question is already saved in the database and the user could go back and modify the question if necessary.&lt;br /&gt;
&lt;br /&gt;
==A more FULL PROOF code proposal == &lt;br /&gt;
See also [[Calculated question development| a proposed calculated question development]] in Developer notes  &lt;br /&gt;
&lt;br /&gt;
[[Category:Calculated question type creation]]&lt;br /&gt;
[[Category:Quiz]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3505</id>
		<title>Mnet Web services API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3505"/>
		<updated>2007-07-19T06:30:03Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* The server script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Web Services API provides Moodle with a web service interface to allow exchange of data and information with other systems.&lt;br /&gt;
&lt;br /&gt;
For example,&lt;br /&gt;
# Manage user data - send and retrieve the information,&lt;br /&gt;
# Manage course enrolments - add/remove teachers and students,&lt;br /&gt;
# Course management - create new courses based on templates,&lt;br /&gt;
# Gradebook info - extract grades information from Moodle.&lt;br /&gt;
&lt;br /&gt;
==XML-RPC background==&lt;br /&gt;
The XML-RPC service allows other servers to contact your Moodle server and request that it call a function. The Moodle server might do something, like create a user, or it might fetch some data and serve it back to your host.&lt;br /&gt;
&lt;br /&gt;
To communicate like this with another Moodle host, you&#039;d normally use the Moodle Network [https://docs.moodle.org/en/Moodle_Network] features, but it&#039;s also possible for other kinds of program to contact your Moodle, using plain-old-XML-RPC, with Moodle Network&#039;s encryption or signed-message features.&lt;br /&gt;
&lt;br /&gt;
==The server script==&lt;br /&gt;
* allow trusted sites to access web services by configuring &#039;&#039;&#039;admin/mnet/trustedhosts.php&#039;&#039;&#039;&lt;br /&gt;
* start by pointing your browser to &#039;&#039;&#039;mnet/xmlrpc/server.php&#039;&#039;&#039; - this should show an XML error message&lt;br /&gt;
* use POST data in this format:&lt;br /&gt;
 // $method is something like: &amp;quot;mod/forum/lib/forum_add_instance&amp;quot;&lt;br /&gt;
 // $params is an array of parameters. A parameter might itself be an array.&lt;br /&gt;
 // use only Whitelist characters that are permitted in a method name&lt;br /&gt;
 // The method name must not begin with a / - avoid absolute paths&lt;br /&gt;
 // A dot character . is only allowed in the filename, i.e. something.php&lt;br /&gt;
* use &#039;&#039;&#039;mnet/xmlrpc/client.php&#039;&#039;&#039; to make remote xmlrpc calls&lt;br /&gt;
&lt;br /&gt;
==Things you can do==&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/view.php?f=965 Web Services forum]  &lt;br /&gt;
&lt;br /&gt;
[[Category:Web services API]]&lt;br /&gt;
[[Category:Administrator]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3504</id>
		<title>Mnet Web services API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3504"/>
		<updated>2007-07-19T06:06:56Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* The server script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Web Services API provides Moodle with a web service interface to allow exchange of data and information with other systems.&lt;br /&gt;
&lt;br /&gt;
For example,&lt;br /&gt;
# Manage user data - send and retrieve the information,&lt;br /&gt;
# Manage course enrolments - add/remove teachers and students,&lt;br /&gt;
# Course management - create new courses based on templates,&lt;br /&gt;
# Gradebook info - extract grades information from Moodle.&lt;br /&gt;
&lt;br /&gt;
==XML-RPC background==&lt;br /&gt;
The XML-RPC service allows other servers to contact your Moodle server and request that it call a function. The Moodle server might do something, like create a user, or it might fetch some data and serve it back to your host.&lt;br /&gt;
&lt;br /&gt;
To communicate like this with another Moodle host, you&#039;d normally use the Moodle Network [https://docs.moodle.org/en/Moodle_Network] features, but it&#039;s also possible for other kinds of program to contact your Moodle, using plain-old-XML-RPC, with Moodle Network&#039;s encryption or signed-message features.&lt;br /&gt;
&lt;br /&gt;
==The server script==&lt;br /&gt;
* allow trusted sites to access web services by configuring &#039;&#039;&#039;/admin/mnet/trustedhosts.php&#039;&#039;&#039;&lt;br /&gt;
* start by pointing your browser to &#039;&#039;&#039;/mnet/xmlrpc/server.php&#039;&#039;&#039; - this should show an XML error message&lt;br /&gt;
* use POST data in this format:&lt;br /&gt;
 // $method is something like: &amp;quot;mod/forum/lib/forum_add_instance&amp;quot;&lt;br /&gt;
 // $params is an array of parameters. A parameter might itself be an array.&lt;br /&gt;
 // use only Whitelist characters that are permitted in a method name&lt;br /&gt;
 // The method name must not begin with a / - avoid absolute paths&lt;br /&gt;
 // A dot character . is only allowed in the filename, i.e. something.php&lt;br /&gt;
&lt;br /&gt;
==Things you can do==&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/view.php?f=965 Web Services forum]  &lt;br /&gt;
&lt;br /&gt;
[[Category:Web services API]]&lt;br /&gt;
[[Category:Administrator]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3503</id>
		<title>Mnet Web services API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3503"/>
		<updated>2007-07-19T05:56:24Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* The server script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Web Services API provides Moodle with a web service interface to allow exchange of data and information with other systems.&lt;br /&gt;
&lt;br /&gt;
For example,&lt;br /&gt;
# Manage user data - send and retrieve the information,&lt;br /&gt;
# Manage course enrolments - add/remove teachers and students,&lt;br /&gt;
# Course management - create new courses based on templates,&lt;br /&gt;
# Gradebook info - extract grades information from Moodle.&lt;br /&gt;
&lt;br /&gt;
==XML-RPC background==&lt;br /&gt;
The XML-RPC service allows other servers to contact your Moodle server and request that it call a function. The Moodle server might do something, like create a user, or it might fetch some data and serve it back to your host.&lt;br /&gt;
&lt;br /&gt;
To communicate like this with another Moodle host, you&#039;d normally use the Moodle Network [https://docs.moodle.org/en/Moodle_Network] features, but it&#039;s also possible for other kinds of program to contact your Moodle, using plain-old-XML-RPC, with Moodle Network&#039;s encryption or signed-message features.&lt;br /&gt;
&lt;br /&gt;
==The server script==&lt;br /&gt;
* allow trusted sites to access web services by configuring &#039;&#039;&#039;/admin/mnet/trustedhosts.php&#039;&#039;&#039;&lt;br /&gt;
* start by pointing your browser to &#039;&#039;&#039;/mnet/xmlrpc/server.php&#039;&#039;&#039; - this should show an XML error message&lt;br /&gt;
&lt;br /&gt;
 // $method is something like: &amp;quot;mod/forum/lib/forum_add_instance&amp;quot;&lt;br /&gt;
 // $params is an array of parameters. A parameter might itself be an array.&lt;br /&gt;
 // use only Whitelist characters that are permitted in a method name&lt;br /&gt;
 // The method name must not begin with a / - avoid absolute paths&lt;br /&gt;
 // A dot character . is only allowed in the filename, i.e. something.php&lt;br /&gt;
&lt;br /&gt;
==Things you can do==&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/view.php?f=965 Web Services forum]  &lt;br /&gt;
&lt;br /&gt;
[[Category:Web services API]]&lt;br /&gt;
[[Category:Administrator]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3502</id>
		<title>Mnet Web services API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3502"/>
		<updated>2007-07-19T05:54:40Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* The server script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Web Services API provides Moodle with a web service interface to allow exchange of data and information with other systems.&lt;br /&gt;
&lt;br /&gt;
For example,&lt;br /&gt;
# Manage user data - send and retrieve the information,&lt;br /&gt;
# Manage course enrolments - add/remove teachers and students,&lt;br /&gt;
# Course management - create new courses based on templates,&lt;br /&gt;
# Gradebook info - extract grades information from Moodle.&lt;br /&gt;
&lt;br /&gt;
==XML-RPC background==&lt;br /&gt;
The XML-RPC service allows other servers to contact your Moodle server and request that it call a function. The Moodle server might do something, like create a user, or it might fetch some data and serve it back to your host.&lt;br /&gt;
&lt;br /&gt;
To communicate like this with another Moodle host, you&#039;d normally use the Moodle Network [https://docs.moodle.org/en/Moodle_Network] features, but it&#039;s also possible for other kinds of program to contact your Moodle, using plain-old-XML-RPC, with Moodle Network&#039;s encryption or signed-message features.&lt;br /&gt;
&lt;br /&gt;
==The server script==&lt;br /&gt;
* allow trusted sites to access web services by configuring &#039;&#039;&#039;/admin/mnet/trustedhosts.php&#039;&#039;&#039;&lt;br /&gt;
* start by pointing your browser to &#039;&#039;&#039;/mnet/xmlrpc/server.php&#039;&#039;&#039; - this should show an XML error message&lt;br /&gt;
&lt;br /&gt;
 // $method is something like: &amp;quot;mod/forum/lib/forum_add_instance&amp;quot;&lt;br /&gt;
 // $params is an array of parameters. A parameter might itself be an array.&lt;br /&gt;
&lt;br /&gt;
==Things you can do==&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/view.php?f=965 Web Services forum]  &lt;br /&gt;
&lt;br /&gt;
[[Category:Web services API]]&lt;br /&gt;
[[Category:Administrator]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3501</id>
		<title>Mnet Web services API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3501"/>
		<updated>2007-07-19T05:33:02Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* The server script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Web Services API provides Moodle with a web service interface to allow exchange of data and information with other systems.&lt;br /&gt;
&lt;br /&gt;
For example,&lt;br /&gt;
# Manage user data - send and retrieve the information,&lt;br /&gt;
# Manage course enrolments - add/remove teachers and students,&lt;br /&gt;
# Course management - create new courses based on templates,&lt;br /&gt;
# Gradebook info - extract grades information from Moodle.&lt;br /&gt;
&lt;br /&gt;
==XML-RPC background==&lt;br /&gt;
The XML-RPC service allows other servers to contact your Moodle server and request that it call a function. The Moodle server might do something, like create a user, or it might fetch some data and serve it back to your host.&lt;br /&gt;
&lt;br /&gt;
To communicate like this with another Moodle host, you&#039;d normally use the Moodle Network [https://docs.moodle.org/en/Moodle_Network] features, but it&#039;s also possible for other kinds of program to contact your Moodle, using plain-old-XML-RPC, with Moodle Network&#039;s encryption or signed-message features.&lt;br /&gt;
&lt;br /&gt;
==The server script==&lt;br /&gt;
* allow trusted sites to access web services by configuring &#039;&#039;&#039;/admin/mnet/trustedhosts.php&#039;&#039;&#039;&lt;br /&gt;
* start by pointing your browser to &#039;&#039;&#039;/mnet/xmlrpc/server.php&#039;&#039;&#039; - this should show an XML error message&lt;br /&gt;
&lt;br /&gt;
==Things you can do==&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/view.php?f=965 Web Services forum]  &lt;br /&gt;
&lt;br /&gt;
[[Category:Web services API]]&lt;br /&gt;
[[Category:Administrator]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3500</id>
		<title>Mnet Web services API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3500"/>
		<updated>2007-07-19T05:29:41Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* The server script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Web Services API provides Moodle with a web service interface to allow exchange of data and information with other systems.&lt;br /&gt;
&lt;br /&gt;
For example,&lt;br /&gt;
# Manage user data - send and retrieve the information,&lt;br /&gt;
# Manage course enrolments - add/remove teachers and students,&lt;br /&gt;
# Course management - create new courses based on templates,&lt;br /&gt;
# Gradebook info - extract grades information from Moodle.&lt;br /&gt;
&lt;br /&gt;
==XML-RPC background==&lt;br /&gt;
The XML-RPC service allows other servers to contact your Moodle server and request that it call a function. The Moodle server might do something, like create a user, or it might fetch some data and serve it back to your host.&lt;br /&gt;
&lt;br /&gt;
To communicate like this with another Moodle host, you&#039;d normally use the Moodle Network [https://docs.moodle.org/en/Moodle_Network] features, but it&#039;s also possible for other kinds of program to contact your Moodle, using plain-old-XML-RPC, with Moodle Network&#039;s encryption or signed-message features.&lt;br /&gt;
&lt;br /&gt;
==The server script==&lt;br /&gt;
* start by pointing your browser to &#039;&#039;_http://your_domain/path_to_moodle&#039;&#039;&#039;&#039;&#039;/mnet/xmlrpc/server.php&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==Things you can do==&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/view.php?f=965 Web Services forum]  &lt;br /&gt;
&lt;br /&gt;
[[Category:Web services API]]&lt;br /&gt;
[[Category:Administrator]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3499</id>
		<title>Mnet Web services API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3499"/>
		<updated>2007-07-19T05:29:25Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* The server script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Web Services API provides Moodle with a web service interface to allow exchange of data and information with other systems.&lt;br /&gt;
&lt;br /&gt;
For example,&lt;br /&gt;
# Manage user data - send and retrieve the information,&lt;br /&gt;
# Manage course enrolments - add/remove teachers and students,&lt;br /&gt;
# Course management - create new courses based on templates,&lt;br /&gt;
# Gradebook info - extract grades information from Moodle.&lt;br /&gt;
&lt;br /&gt;
==XML-RPC background==&lt;br /&gt;
The XML-RPC service allows other servers to contact your Moodle server and request that it call a function. The Moodle server might do something, like create a user, or it might fetch some data and serve it back to your host.&lt;br /&gt;
&lt;br /&gt;
To communicate like this with another Moodle host, you&#039;d normally use the Moodle Network [https://docs.moodle.org/en/Moodle_Network] features, but it&#039;s also possible for other kinds of program to contact your Moodle, using plain-old-XML-RPC, with Moodle Network&#039;s encryption or signed-message features.&lt;br /&gt;
&lt;br /&gt;
==The server script==&lt;br /&gt;
* start by pointing your browser to _&#039;&#039;http://your_domain/path_to_moodle&#039;&#039;&#039;&#039;&#039;/mnet/xmlrpc/server.php&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==Things you can do==&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/view.php?f=965 Web Services forum]  &lt;br /&gt;
&lt;br /&gt;
[[Category:Web services API]]&lt;br /&gt;
[[Category:Administrator]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3498</id>
		<title>Mnet Web services API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3498"/>
		<updated>2007-07-19T05:28:51Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* The server script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Web Services API provides Moodle with a web service interface to allow exchange of data and information with other systems.&lt;br /&gt;
&lt;br /&gt;
For example,&lt;br /&gt;
# Manage user data - send and retrieve the information,&lt;br /&gt;
# Manage course enrolments - add/remove teachers and students,&lt;br /&gt;
# Course management - create new courses based on templates,&lt;br /&gt;
# Gradebook info - extract grades information from Moodle.&lt;br /&gt;
&lt;br /&gt;
==XML-RPC background==&lt;br /&gt;
The XML-RPC service allows other servers to contact your Moodle server and request that it call a function. The Moodle server might do something, like create a user, or it might fetch some data and serve it back to your host.&lt;br /&gt;
&lt;br /&gt;
To communicate like this with another Moodle host, you&#039;d normally use the Moodle Network [https://docs.moodle.org/en/Moodle_Network] features, but it&#039;s also possible for other kinds of program to contact your Moodle, using plain-old-XML-RPC, with Moodle Network&#039;s encryption or signed-message features.&lt;br /&gt;
&lt;br /&gt;
==The server script==&lt;br /&gt;
* start by pointing your browser to hhttp://your_domain/path_to_moodle&#039;&#039;&#039;/mnet/xmlrpc/server.php&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==Things you can do==&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/view.php?f=965 Web Services forum]  &lt;br /&gt;
&lt;br /&gt;
[[Category:Web services API]]&lt;br /&gt;
[[Category:Administrator]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3497</id>
		<title>Mnet Web services API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3497"/>
		<updated>2007-07-19T05:27:48Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* The server script */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The Web Services API provides Moodle with a web service interface to allow exchange of data and information with other systems.&lt;br /&gt;
&lt;br /&gt;
For example,&lt;br /&gt;
# Manage user data - send and retrieve the information,&lt;br /&gt;
# Manage course enrolments - add/remove teachers and students,&lt;br /&gt;
# Course management - create new courses based on templates,&lt;br /&gt;
# Gradebook info - extract grades information from Moodle.&lt;br /&gt;
&lt;br /&gt;
==XML-RPC background==&lt;br /&gt;
The XML-RPC service allows other servers to contact your Moodle server and request that it call a function. The Moodle server might do something, like create a user, or it might fetch some data and serve it back to your host.&lt;br /&gt;
&lt;br /&gt;
To communicate like this with another Moodle host, you&#039;d normally use the Moodle Network [https://docs.moodle.org/en/Moodle_Network] features, but it&#039;s also possible for other kinds of program to contact your Moodle, using plain-old-XML-RPC, with Moodle Network&#039;s encryption or signed-message features.&lt;br /&gt;
&lt;br /&gt;
==The server script==&lt;br /&gt;
* start by pointing your browser to http://your_domain/path_to_moodle&#039;&#039;&#039;/mnet/xmlrpc/server.php&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==Things you can do==&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/view.php?f=965 Web Services forum]  &lt;br /&gt;
&lt;br /&gt;
[[Category:Web services API]]&lt;br /&gt;
[[Category:Administrator]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Adding_question_types_to_lesson&amp;diff=4331</id>
		<title>Adding question types to lesson</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Adding_question_types_to_lesson&amp;diff=4331"/>
		<updated>2006-09-10T07:10:18Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* lesson_question_instances */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Please note that this page deals with a future version of [[Moodle 2.0]].&lt;br /&gt;
&lt;br /&gt;
A regularly discussed topic is that Lesson should use the same [[#Questions_and_jumps|question classes]] as [[Quiz]].  This page is geared toward this goal by explaining the different aspects required to complete this project.--Mark Nielsen&lt;br /&gt;
&lt;br /&gt;
I think this would be a really good idea, and am willing to help--[[User:Tim Hunt|Tim Hunt]], Quiz module/question bank maintainer.&lt;br /&gt;
&lt;br /&gt;
==Project goals==&lt;br /&gt;
*(Primary) Implement the [[Question_types|question type classes]] in [[Lesson]].&lt;br /&gt;
*(Primary) Reduce Moodle&#039;s code base.  Instead of Lesson having its own question code, it can now use [[Question_engine|existing code]].&lt;br /&gt;
*Simplify Lesson&#039;s code.  There is a high mix of presentation and logic and some rather confusing algorithms due to the nature of Lesson and how it is organized.&lt;br /&gt;
&lt;br /&gt;
==Primary coding tasks==&lt;br /&gt;
&lt;br /&gt;
===Adding support for question class===&lt;br /&gt;
Implement the question class code and the necessary interfaces for adding and editing questions within Lesson.  The needed functionality is as follows:&lt;br /&gt;
*Printing questions for attempts. &#039;&#039;Should be nothing to do if we are happy with the same presentation for both quiz and lesson&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
*[[Question_engine#Grades|Grading]] the questions. &#039;&#039;Again, should be fine, as long as the question_(attempts|sessions|states) model is flexible enough for the quiz&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
*Provide a method of adding new questions to a Lesson. &#039;&#039;Hopefully question/showbank.php will work for you, perhaps with a few changs.&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
**When adding a question, display questions in course question bank to select from.&lt;br /&gt;
***Provide links/tabs for creating new questions.&lt;br /&gt;
**After adding a question, provide an interface to define possible jumps (This step may not be needed.  Depends on the solution to [[#Questions_and_jumps|the questions and jumps]] problem). &#039;&#039;This will be new work&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
&lt;br /&gt;
===Changing the attempts logic===&lt;br /&gt;
Among other Lesson table changes, one that is the most significant is changing the &#039;&#039;lesson_attempts&#039;&#039; table. Instead of storing one attempt record for every user answer, &#039;&#039;lesson_attempts&#039;&#039; table should store one record per Lesson attempt. The &#039;&#039;question_states&#039;&#039; table will replace the functionality of the original &#039;&#039;lesson_attempts&#039;&#039; table.  Since this is changing such a fundamental part of Lesson, almost all of Lesson&#039;s code will have to be adapted to the new logic of attempts.&lt;br /&gt;
&lt;br /&gt;
===Fixing code breaks===&lt;br /&gt;
By switching to the question class, nearly all of the current Lesson code will be broken and must be replaced or fixed. Here is a quick breakdown of foreseeable code breaks:&lt;br /&gt;
*High scores&lt;br /&gt;
*[[Lesson_reports|Lesson reports]]&lt;br /&gt;
**[[Quiz_reports|Quiz reports]] might provide a jump start(?)&lt;br /&gt;
*[[Lesson_module#Pages.2C_questions.2C_answers_and_responses|Page authoring]]&lt;br /&gt;
*[[Jumps#Special_jumps|Jump type interpretation algorithms]]&lt;br /&gt;
*On-going score&lt;br /&gt;
*Progress bar&lt;br /&gt;
*Backup/restore&lt;br /&gt;
**Rewrite for new table structures&lt;br /&gt;
**Add support for restoring Lessons prior question class support&lt;br /&gt;
**Add support in the primary backup/restore routines&lt;br /&gt;
&lt;br /&gt;
===Database migration===&lt;br /&gt;
Lesson tables need to be removed or changed and their old data needs to be migrated to new tables. Some of this code will be used in the restore process as well. Here is a basic overview of the database migration process:&lt;br /&gt;
*migrate &#039;&#039;lesson_pages&#039;&#039; and &#039;&#039;lesson_answer&#039;&#039; content to question tables.  Note: this will only be the content, not the logic for ordering Lesson pages or any other Lesson specific data.&lt;br /&gt;
*migrate &#039;&#039;lesson_attempts&#039;&#039; to &#039;&#039;question_states&#039;&#039;.&lt;br /&gt;
*Re-organization of the lesson tables (see [[#New_database_schema|new database schema]]).&lt;br /&gt;
&lt;br /&gt;
==Unsolved problems==&lt;br /&gt;
Here are some tricky issues that do not have a solid or obvious solution.  Please advise.&lt;br /&gt;
&lt;br /&gt;
===Questions and jumps===&lt;br /&gt;
Page [[Jumps|jumps]] determine the flow from one page to another and are a unique feature to Lesson.  So, how does one figure out all the necessary jump definitions needed for a question?  In Lesson, every answer has a jump, but this solution is not ideal.  A multiple choice question for example would have two cases: &lt;br /&gt;
*Single answer: a jump must be defined for each answer.&lt;br /&gt;
*[[Lesson_module#Multiple_choice.2C_multiple_answer|Multiple answer]]: a jump would be defined for the correct answer and a jump for the wrong answer.&lt;br /&gt;
&lt;br /&gt;
The follow two sections discuss possible solutions, but please feel free to suggest others.  When thinking about this problem and perhaps a new solution, remember the primary goals of this project: reduce code size and simplify the Lesson code to make it easier to maintain.&lt;br /&gt;
&lt;br /&gt;
====Invasive solution====&lt;br /&gt;
An invasive solution would be to modify the question class code.  This is invasive because the introduced code may only be used by Lesson unless it was implemented in such a way that it would be viable for other uses.  Some of the foreseeable changes would include the following:&lt;br /&gt;
*Add methods to the &#039;&#039;default_questiontype&#039;&#039; class that would handle the default behavior for defining jumps.  Default behavior would be two jump definitions: one for correct answers and one for incorrect answers.&lt;br /&gt;
*Provide a place to store the defined jumps.&lt;br /&gt;
*In each question type, override the default methods in the &#039;&#039;default_questiontype&#039;&#039; class to suite the behavior of the question (optional for each question type).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pros for this implementation:&#039;&#039;&#039;&lt;br /&gt;
*All question type code is in one place.&lt;br /&gt;
*Clean implementation&lt;br /&gt;
*Better work-flow for question authoring&lt;br /&gt;
&#039;&#039;&#039;Cons:&#039;&#039;&#039;&lt;br /&gt;
*Potentially introduce code that is unusable by other modules, etc.&lt;br /&gt;
&lt;br /&gt;
====Noninvasive solution====&lt;br /&gt;
Lesson would extend all question types that to handle jumps.  Lesson would have its own &#039;&#039;type&#039;&#039; folder (mod/lesson/type).  This &#039;&#039;type&#039;&#039; folder would be organized the same as &#039;&#039;question/type&#039;&#039; directory.  Each question type would have its own folder and in that folder a &#039;&#039;questiontype.php&#039;&#039;.  So, &#039;&#039;mod/lesson/type/{questiontype}/questiontype.php&#039;&#039; where {questiontype} is replaced with each question type name. The &#039;&#039;questiontype.php&#039;&#039; would extend the original question type class and add the necessary methods for handling jumps.  The following methods would be added:&lt;br /&gt;
*&#039;&#039;&#039;print_jump_form:&#039;&#039;&#039; accepts the possible jump values from Lesson and then print (or return) the contents of a form.  This form would then be presented to the user to define the possible jumps.&lt;br /&gt;
*&#039;&#039;&#039;process_jump_form:&#039;&#039;&#039; accepts the form data from &#039;&#039;&#039;print_jump_form&#039;&#039;&#039; and organize it.  It then returns the organized data so that Lesson can store it.&lt;br /&gt;
*&#039;&#039;&#039;interpret_jump:&#039;&#039;&#039; accepts the user&#039;s answer to the question and the returned value from &#039;&#039;&#039;process_jump_form&#039;&#039;&#039;.  This method would determine which jump Lesson should use.&lt;br /&gt;
*&#039;&#039;&#039;restore_jumps:&#039;&#039;&#039; restore the returned value from &#039;&#039;&#039;process_jump_form&#039;&#039;&#039; during Lesson&#039;s restore routine.  Returns the restored value so that Lesson can store it.&lt;br /&gt;
&lt;br /&gt;
Example use:&lt;br /&gt;
&lt;br /&gt;
Two jump definitions: correct or incorrect:&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;print_jump_form&#039;&#039;&#039; would return the following form:&lt;br /&gt;
  Correct answer jump: [drop-down-menu-with-lesson-jumps]&lt;br /&gt;
  Wrong answer jump: [drop-down-menu-with-lesson-jumps]&lt;br /&gt;
*&#039;&#039;&#039;process_jump_form&#039;&#039;&#039; would accept the POST data from the above form and organize it in an array like this: array(&#039;correct&#039; =&amp;gt; [lesson-jump-code], &#039;wrong&#039; =&amp;gt; [lesson-jump-code]).  Where [lesson-jump-code] is a Lesson page id or jump code from the [drop-down-menu-with-lesson-jumps].  This array would be returned so that Lesson could store it.&lt;br /&gt;
*&#039;&#039;&#039;interpret_jump&#039;&#039;&#039; would accept the students answer and the above array.  It would return the [lesson-jump-code] either associated with &#039;&#039;correct&#039;&#039; or &#039;&#039;wrong&#039;&#039; based on the student&#039;s answer.&lt;br /&gt;
*&#039;&#039;&#039;restore_jumps&#039;&#039;&#039; would be called during the Lesson restore process and it would return the restored array so that Lesson could store it.  Example: if each answer had a jump then the array would be defined like this: array(answerid =&amp;gt; [lesson-jump-code] ... ).  The &#039;&#039;answerid&#039;&#039; would have to be mapped to the new answerid during the restore process.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pros for this implementation:&#039;&#039;&#039;&lt;br /&gt;
*Potential Lesson specific code remains in Lesson.&lt;br /&gt;
&#039;&#039;&#039;Cons:&#039;&#039;&#039;&lt;br /&gt;
*Work-flow would have an extra step (create question then define jumps).&lt;br /&gt;
*Creating new question types is more difficult.&lt;br /&gt;
&lt;br /&gt;
====A middle way====&lt;br /&gt;
&lt;br /&gt;
I (Tim) don&#039;t like either of the above two solutions. I don&#039;t like excessively lesson specific code in the question types, but it would be really bad if the code for a particular question type was not all in one place.&lt;br /&gt;
&lt;br /&gt;
I think that the solution is to add a new concept: question outcomes. It will take me a couple of paragraphs to explain what I mean with this.&lt;br /&gt;
&lt;br /&gt;
Currently, after a student attempts a question, you get stuff that falls into two categories:&lt;br /&gt;
# generic stuff, like the grade (both before and after applying the penalty factor), the penalty factor itself, the classification of the before grade as Correct/Partially Correct/Incorrect, a possible manual comment added by the teacher, the state of the question (saved, graded, manual graded, closed)&lt;br /&gt;
# question-type specific stuff, like the data in question_states-&amp;gt;answer column, or the feedback that is displayed on-screen.&lt;br /&gt;
&lt;br /&gt;
I think that in the generic stuff category, we need to add the new concept of &#039;outcome&#039;. This would be what the lesson module uses to select branches, and would probably also be useful to the quiz reports. It would categorise the student&#039;s answer into one of a number of categories, and the question type class would need to implement two new methods:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;array of strings&#039;&#039; all_outcomes($question)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;string&#039;&#039; outcome($question, $state)&lt;br /&gt;
&lt;br /&gt;
For multiple-choice (single response) this would return the answer selected. For shortanswer, numerical and calculated, this would return the answer that was matched. The generic implementation in the base class would probably have to just return &#039;incorrect&#039;/&#039;partiallycorrect&#039;/&#039;correct&#039; (that is the ids of the strings that are looked up in the language file. &lt;br /&gt;
&lt;br /&gt;
The lesson would then need to do branching be specifying where to go for each outcome. It would requre a multi-stage UI (create the question using the standard questionbank code, add the question to the lesson, link up the outcomes to jump targets). However, done well, this could be a very natural UI.&lt;br /&gt;
&lt;br /&gt;
The quiz UI is not perfect, but I think the way it is now: create a question, then add the question to the quiz, then adjust the grade for that question within the quiz, is very natural to use.&lt;br /&gt;
&lt;br /&gt;
====Migration of jumps====&lt;br /&gt;
Regardless of implementation, one problem remains: if each question potentially does not have a jump defined for each answer, then how are the currently defined jumps supposed to be migrated to the new question implementation (remember, Lesson questions have one jump defined for each answer regardless if it makes sense at all)?  The only solution that comes to mind is to have strict definitions for migration for the following [[Lesson#Types_of_questions_available_within_a_lesson|question types that exist in Lesson]]:&lt;br /&gt;
*Multichoice (single and multianswer)&lt;br /&gt;
*Matching&lt;br /&gt;
*Numerical&lt;br /&gt;
*Short Answer&lt;br /&gt;
*True/False&lt;br /&gt;
*Essay&lt;br /&gt;
&lt;br /&gt;
Note: the use of migration here refers to upgrading old Lessons &#039;&#039;&#039;and&#039;&#039;&#039; restoring older Lessons.&lt;br /&gt;
&lt;br /&gt;
===Branch tables===&lt;br /&gt;
Once a solution for the above problem has been found, I would like to discuss the possibility of replacing the functionality of [[Lesson#Branches_and_branch_tables|branch tables]] with the [[Question_types#Description|description question type]].  Reason for discussing this after a solution has been found is because the solution may influence the outcome to this problem.&lt;br /&gt;
&lt;br /&gt;
==New database schema==&lt;br /&gt;
&lt;br /&gt;
  ------------------      ------------------&lt;br /&gt;
  |                |      |                |&lt;br /&gt;
  | &#039;&#039;&#039;course_modules&#039;&#039;&#039; |      | [[#lesson_default|lesson_default]] |&lt;br /&gt;
  |                |      |                |&lt;br /&gt;
  ------------------      ------------------&lt;br /&gt;
          |                        |&lt;br /&gt;
          |                        |&lt;br /&gt;
   --------------           --------------&lt;br /&gt;
   |            |           |            |&lt;br /&gt;
   |   [[#lesson|lesson]]   |-----------|   &#039;&#039;&#039;course&#039;&#039;&#039;   |&lt;br /&gt;
   |            |           |            |&lt;br /&gt;
   --------------           --------------&lt;br /&gt;
          |&lt;br /&gt;
          |      ----------------------      ----------&lt;br /&gt;
          |      |                    |      |        |&lt;br /&gt;
          |------| [[#lesson_high_scores|lesson_high_scores]] |------|  &#039;&#039;&#039;user&#039;&#039;&#039;  |&lt;br /&gt;
          |      |                    |      |        |&lt;br /&gt;
          |      ----------------------      ----------&lt;br /&gt;
          |                                       |&lt;br /&gt;
          |      -----------------                |&lt;br /&gt;
          |      |               |                |&lt;br /&gt;
          |------| [[#lesson_grades|lesson_grades]] |----------------|&lt;br /&gt;
          |      |               |                |&lt;br /&gt;
          |      -----------------                |&lt;br /&gt;
          |                                       |&lt;br /&gt;
          |      -------------------              |&lt;br /&gt;
          |      |                 |--------------|    ---------------------&lt;br /&gt;
          |------| [[#lesson_attempts|lesson_attempts]] |                   |                   |&lt;br /&gt;
          |      |                 |-------------------| &#039;&#039;&#039;question_attempts&#039;&#039;&#039; |&lt;br /&gt;
          |      -------------------                   |                   |&lt;br /&gt;
          |                                            ---------------------&lt;br /&gt;
          |      ----------------&lt;br /&gt;
          |      |              |&lt;br /&gt;
          |------| [[#lesson_pages|lesson_pages]] |&lt;br /&gt;
                 |              |&lt;br /&gt;
                 ----------------&lt;br /&gt;
                        |      -----------------------------      ------------&lt;br /&gt;
                        |      |                           |      |          |&lt;br /&gt;
                        |------| [[#lesson_question_instances|lesson_question_instances]] |------| &#039;&#039;&#039;[[Quiz_database_structure#question|question]]&#039;&#039;&#039; |&lt;br /&gt;
                        |      |                           |      |          |&lt;br /&gt;
                        |      -----------------------------      ------------&lt;br /&gt;
                        |      &lt;br /&gt;
                        |      -------------------&lt;br /&gt;
                        |      |                 |&lt;br /&gt;
                        |------| [[#lesson_branches|lesson_branches]] |&lt;br /&gt;
                               |                 |&lt;br /&gt;
                               -------------------&lt;br /&gt;
*&#039;&#039;&#039;Note&#039;&#039;&#039;: tables in &#039;&#039;&#039;bold&#039;&#039;&#039; are standard in Moodle and are there only to show relations.&lt;br /&gt;
&lt;br /&gt;
==Table Descriptions==&lt;br /&gt;
&lt;br /&gt;
Key:&lt;br /&gt;
*PK = primary key&lt;br /&gt;
*FK = foreign key&lt;br /&gt;
&lt;br /&gt;
===lesson===&lt;br /&gt;
Primary module table&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*course: FK to course&lt;br /&gt;
*name: Name of the lesson&lt;br /&gt;
*practice: Flag for practice lessons&lt;br /&gt;
*modattempts&lt;br /&gt;
*password: Stores password for password protected lessons&lt;br /&gt;
*dependency: FK to [[#lesson|lesson]] table.  The lesson that this lesson is dependent upon&lt;br /&gt;
*conditions: conditions for the dependency&lt;br /&gt;
*grade: Max grade for the lesson&lt;br /&gt;
*cusom: custom scoring flag&lt;br /&gt;
*ongoing: display on going score flag&lt;br /&gt;
*usemaxgrade: use max grade or mean flag&lt;br /&gt;
*maxanswers: maximum answers for a question (would only be used by branch tables, remove?)&lt;br /&gt;
*maxattempts: number of attempts on a question&lt;br /&gt;
*review&lt;br /&gt;
*nextpagedefault: default flow control&lt;br /&gt;
*minquestions: minimum questions to answer&lt;br /&gt;
*maxpages&lt;br /&gt;
*time: allowed time in the lesson per attempt&lt;br /&gt;
*retake: allow student to retake&lt;br /&gt;
*activitylink: FK to course_modules table&lt;br /&gt;
*mediafile&lt;br /&gt;
*mediaheight: height of pop-up&lt;br /&gt;
*mediawidth: width of pop-up&lt;br /&gt;
*mediaclose: display close button for media&lt;br /&gt;
*slideshow: display branches in slide show mode flag&lt;br /&gt;
*width: width of slide show&lt;br /&gt;
*height: height of slide show&lt;br /&gt;
*bgcolor: background color of slide show&lt;br /&gt;
*displayleft: display left menu&lt;br /&gt;
*displayleftif: display left menu if student has already completed lesson flag&lt;br /&gt;
*progressbar: display progress bar flag&lt;br /&gt;
*highscores: high scores&lt;br /&gt;
*available: when lesson is available to student&lt;br /&gt;
*deadline: when lesson closes to student&lt;br /&gt;
*timemodified: last updated&lt;br /&gt;
&lt;br /&gt;
===lesson_default===&lt;br /&gt;
Lesson default settings for a course.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*same as the lesson table minus the following fields: name, dependency, activity, mediafile, available, deadline, and modified&lt;br /&gt;
&lt;br /&gt;
===lesson_high_scores===&lt;br /&gt;
Keep track of the lesson top scores.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*userid: FK to user table&lt;br /&gt;
*gradeid: FK to [[#lesson_grades|lesson_grades]] table&lt;br /&gt;
*nickname: user&#039;s nickname&lt;br /&gt;
&lt;br /&gt;
===lesson_grades===&lt;br /&gt;
User attempt grades.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*userid: FK to user table&lt;br /&gt;
*grade: attempt grade&lt;br /&gt;
*completed: time of completion&lt;br /&gt;
&lt;br /&gt;
===lesson_attempts===&lt;br /&gt;
Lesson attempts by users.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*uniqueid: FK to question_sessions table&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*userid: FK to user table&lt;br /&gt;
*path: users path through the lesson (comma separated list of page ids from the [[#lesson_pages|lesson_pages]] table)&lt;br /&gt;
*attempt: user&#039;s attempt count&lt;br /&gt;
*sumgrade: current total for grades&lt;br /&gt;
*timestart: starting time of attempt&lt;br /&gt;
*timefinish: ending time of attempt&lt;br /&gt;
*timemodified: last time updated&lt;br /&gt;
&lt;br /&gt;
===lesson_pages===&lt;br /&gt;
All content pages in lesson.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*prevpageid: FK to [[#lesson_pages|lesson_pages]] table&lt;br /&gt;
*nextpageid: FK to [[#lesson_pages|lesson_pages]] table&lt;br /&gt;
*type: values include&lt;br /&gt;
**branch&lt;br /&gt;
**endofbranch&lt;br /&gt;
**cluster&lt;br /&gt;
**endofcluster&lt;br /&gt;
**question&lt;br /&gt;
*display: display in left menu flag&lt;br /&gt;
*jumps: jumps related to this page&lt;br /&gt;
*timemodified: time last updated&lt;br /&gt;
&lt;br /&gt;
===lesson_question_instances===&lt;br /&gt;
Store relation between lesson pages that are questions and the question table. (does not exist in moodle 1.6.1 standard)&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*pageid: FK to [[#lesson_pages|lesson_pages]] table&lt;br /&gt;
*questionid: FK to [[Quiz_database_structure#question|question]] table&lt;br /&gt;
*grade: point value of the question&lt;br /&gt;
&lt;br /&gt;
===lesson_branches===&lt;br /&gt;
Lesson pages that are branch tables. (does not exist in moodle 1.6.1 but there is different set of fields in lesson_branch)&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*pageid: FK to [[#lesson_pages|lesson_pages]] table&lt;br /&gt;
*boilerplates: text of branch buttons&lt;br /&gt;
*layout: layout of branch buttons&lt;br /&gt;
&lt;br /&gt;
==Discussion==&lt;br /&gt;
If you would like to discuss topics on this page, please make a post in the [http://moodle.org/mod/forum/view.php?f=333 lesson forum].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Sorry, I did not see this until I had added my comments inline. I hope that is not too much of a problem.&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[http://moodle.org/mod/forum/discuss.php?d=42563 Original lesson forum discussion]&lt;br /&gt;
*[[Question_engine|Question engine]]&lt;br /&gt;
*[[Lesson|Lesson module]]&lt;br /&gt;
*[[Quiz database structure#Overview]] - now, at last, with a diagram of the structure in 1.7.&lt;br /&gt;
&lt;br /&gt;
[[Category:Future]]&lt;br /&gt;
[[Category:Moodle 2.0]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Modules]]&lt;br /&gt;
[[Category:Lesson]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Adding_question_types_to_lesson&amp;diff=4330</id>
		<title>Adding question types to lesson</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Adding_question_types_to_lesson&amp;diff=4330"/>
		<updated>2006-09-10T07:09:40Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* lesson_branches */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Please note that this page deals with a future version of [[Moodle 2.0]].&lt;br /&gt;
&lt;br /&gt;
A regularly discussed topic is that Lesson should use the same [[#Questions_and_jumps|question classes]] as [[Quiz]].  This page is geared toward this goal by explaining the different aspects required to complete this project.--Mark Nielsen&lt;br /&gt;
&lt;br /&gt;
I think this would be a really good idea, and am willing to help--[[User:Tim Hunt|Tim Hunt]], Quiz module/question bank maintainer.&lt;br /&gt;
&lt;br /&gt;
==Project goals==&lt;br /&gt;
*(Primary) Implement the [[Question_types|question type classes]] in [[Lesson]].&lt;br /&gt;
*(Primary) Reduce Moodle&#039;s code base.  Instead of Lesson having its own question code, it can now use [[Question_engine|existing code]].&lt;br /&gt;
*Simplify Lesson&#039;s code.  There is a high mix of presentation and logic and some rather confusing algorithms due to the nature of Lesson and how it is organized.&lt;br /&gt;
&lt;br /&gt;
==Primary coding tasks==&lt;br /&gt;
&lt;br /&gt;
===Adding support for question class===&lt;br /&gt;
Implement the question class code and the necessary interfaces for adding and editing questions within Lesson.  The needed functionality is as follows:&lt;br /&gt;
*Printing questions for attempts. &#039;&#039;Should be nothing to do if we are happy with the same presentation for both quiz and lesson&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
*[[Question_engine#Grades|Grading]] the questions. &#039;&#039;Again, should be fine, as long as the question_(attempts|sessions|states) model is flexible enough for the quiz&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
*Provide a method of adding new questions to a Lesson. &#039;&#039;Hopefully question/showbank.php will work for you, perhaps with a few changs.&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
**When adding a question, display questions in course question bank to select from.&lt;br /&gt;
***Provide links/tabs for creating new questions.&lt;br /&gt;
**After adding a question, provide an interface to define possible jumps (This step may not be needed.  Depends on the solution to [[#Questions_and_jumps|the questions and jumps]] problem). &#039;&#039;This will be new work&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
&lt;br /&gt;
===Changing the attempts logic===&lt;br /&gt;
Among other Lesson table changes, one that is the most significant is changing the &#039;&#039;lesson_attempts&#039;&#039; table. Instead of storing one attempt record for every user answer, &#039;&#039;lesson_attempts&#039;&#039; table should store one record per Lesson attempt. The &#039;&#039;question_states&#039;&#039; table will replace the functionality of the original &#039;&#039;lesson_attempts&#039;&#039; table.  Since this is changing such a fundamental part of Lesson, almost all of Lesson&#039;s code will have to be adapted to the new logic of attempts.&lt;br /&gt;
&lt;br /&gt;
===Fixing code breaks===&lt;br /&gt;
By switching to the question class, nearly all of the current Lesson code will be broken and must be replaced or fixed. Here is a quick breakdown of foreseeable code breaks:&lt;br /&gt;
*High scores&lt;br /&gt;
*[[Lesson_reports|Lesson reports]]&lt;br /&gt;
**[[Quiz_reports|Quiz reports]] might provide a jump start(?)&lt;br /&gt;
*[[Lesson_module#Pages.2C_questions.2C_answers_and_responses|Page authoring]]&lt;br /&gt;
*[[Jumps#Special_jumps|Jump type interpretation algorithms]]&lt;br /&gt;
*On-going score&lt;br /&gt;
*Progress bar&lt;br /&gt;
*Backup/restore&lt;br /&gt;
**Rewrite for new table structures&lt;br /&gt;
**Add support for restoring Lessons prior question class support&lt;br /&gt;
**Add support in the primary backup/restore routines&lt;br /&gt;
&lt;br /&gt;
===Database migration===&lt;br /&gt;
Lesson tables need to be removed or changed and their old data needs to be migrated to new tables. Some of this code will be used in the restore process as well. Here is a basic overview of the database migration process:&lt;br /&gt;
*migrate &#039;&#039;lesson_pages&#039;&#039; and &#039;&#039;lesson_answer&#039;&#039; content to question tables.  Note: this will only be the content, not the logic for ordering Lesson pages or any other Lesson specific data.&lt;br /&gt;
*migrate &#039;&#039;lesson_attempts&#039;&#039; to &#039;&#039;question_states&#039;&#039;.&lt;br /&gt;
*Re-organization of the lesson tables (see [[#New_database_schema|new database schema]]).&lt;br /&gt;
&lt;br /&gt;
==Unsolved problems==&lt;br /&gt;
Here are some tricky issues that do not have a solid or obvious solution.  Please advise.&lt;br /&gt;
&lt;br /&gt;
===Questions and jumps===&lt;br /&gt;
Page [[Jumps|jumps]] determine the flow from one page to another and are a unique feature to Lesson.  So, how does one figure out all the necessary jump definitions needed for a question?  In Lesson, every answer has a jump, but this solution is not ideal.  A multiple choice question for example would have two cases: &lt;br /&gt;
*Single answer: a jump must be defined for each answer.&lt;br /&gt;
*[[Lesson_module#Multiple_choice.2C_multiple_answer|Multiple answer]]: a jump would be defined for the correct answer and a jump for the wrong answer.&lt;br /&gt;
&lt;br /&gt;
The follow two sections discuss possible solutions, but please feel free to suggest others.  When thinking about this problem and perhaps a new solution, remember the primary goals of this project: reduce code size and simplify the Lesson code to make it easier to maintain.&lt;br /&gt;
&lt;br /&gt;
====Invasive solution====&lt;br /&gt;
An invasive solution would be to modify the question class code.  This is invasive because the introduced code may only be used by Lesson unless it was implemented in such a way that it would be viable for other uses.  Some of the foreseeable changes would include the following:&lt;br /&gt;
*Add methods to the &#039;&#039;default_questiontype&#039;&#039; class that would handle the default behavior for defining jumps.  Default behavior would be two jump definitions: one for correct answers and one for incorrect answers.&lt;br /&gt;
*Provide a place to store the defined jumps.&lt;br /&gt;
*In each question type, override the default methods in the &#039;&#039;default_questiontype&#039;&#039; class to suite the behavior of the question (optional for each question type).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pros for this implementation:&#039;&#039;&#039;&lt;br /&gt;
*All question type code is in one place.&lt;br /&gt;
*Clean implementation&lt;br /&gt;
*Better work-flow for question authoring&lt;br /&gt;
&#039;&#039;&#039;Cons:&#039;&#039;&#039;&lt;br /&gt;
*Potentially introduce code that is unusable by other modules, etc.&lt;br /&gt;
&lt;br /&gt;
====Noninvasive solution====&lt;br /&gt;
Lesson would extend all question types that to handle jumps.  Lesson would have its own &#039;&#039;type&#039;&#039; folder (mod/lesson/type).  This &#039;&#039;type&#039;&#039; folder would be organized the same as &#039;&#039;question/type&#039;&#039; directory.  Each question type would have its own folder and in that folder a &#039;&#039;questiontype.php&#039;&#039;.  So, &#039;&#039;mod/lesson/type/{questiontype}/questiontype.php&#039;&#039; where {questiontype} is replaced with each question type name. The &#039;&#039;questiontype.php&#039;&#039; would extend the original question type class and add the necessary methods for handling jumps.  The following methods would be added:&lt;br /&gt;
*&#039;&#039;&#039;print_jump_form:&#039;&#039;&#039; accepts the possible jump values from Lesson and then print (or return) the contents of a form.  This form would then be presented to the user to define the possible jumps.&lt;br /&gt;
*&#039;&#039;&#039;process_jump_form:&#039;&#039;&#039; accepts the form data from &#039;&#039;&#039;print_jump_form&#039;&#039;&#039; and organize it.  It then returns the organized data so that Lesson can store it.&lt;br /&gt;
*&#039;&#039;&#039;interpret_jump:&#039;&#039;&#039; accepts the user&#039;s answer to the question and the returned value from &#039;&#039;&#039;process_jump_form&#039;&#039;&#039;.  This method would determine which jump Lesson should use.&lt;br /&gt;
*&#039;&#039;&#039;restore_jumps:&#039;&#039;&#039; restore the returned value from &#039;&#039;&#039;process_jump_form&#039;&#039;&#039; during Lesson&#039;s restore routine.  Returns the restored value so that Lesson can store it.&lt;br /&gt;
&lt;br /&gt;
Example use:&lt;br /&gt;
&lt;br /&gt;
Two jump definitions: correct or incorrect:&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;print_jump_form&#039;&#039;&#039; would return the following form:&lt;br /&gt;
  Correct answer jump: [drop-down-menu-with-lesson-jumps]&lt;br /&gt;
  Wrong answer jump: [drop-down-menu-with-lesson-jumps]&lt;br /&gt;
*&#039;&#039;&#039;process_jump_form&#039;&#039;&#039; would accept the POST data from the above form and organize it in an array like this: array(&#039;correct&#039; =&amp;gt; [lesson-jump-code], &#039;wrong&#039; =&amp;gt; [lesson-jump-code]).  Where [lesson-jump-code] is a Lesson page id or jump code from the [drop-down-menu-with-lesson-jumps].  This array would be returned so that Lesson could store it.&lt;br /&gt;
*&#039;&#039;&#039;interpret_jump&#039;&#039;&#039; would accept the students answer and the above array.  It would return the [lesson-jump-code] either associated with &#039;&#039;correct&#039;&#039; or &#039;&#039;wrong&#039;&#039; based on the student&#039;s answer.&lt;br /&gt;
*&#039;&#039;&#039;restore_jumps&#039;&#039;&#039; would be called during the Lesson restore process and it would return the restored array so that Lesson could store it.  Example: if each answer had a jump then the array would be defined like this: array(answerid =&amp;gt; [lesson-jump-code] ... ).  The &#039;&#039;answerid&#039;&#039; would have to be mapped to the new answerid during the restore process.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pros for this implementation:&#039;&#039;&#039;&lt;br /&gt;
*Potential Lesson specific code remains in Lesson.&lt;br /&gt;
&#039;&#039;&#039;Cons:&#039;&#039;&#039;&lt;br /&gt;
*Work-flow would have an extra step (create question then define jumps).&lt;br /&gt;
*Creating new question types is more difficult.&lt;br /&gt;
&lt;br /&gt;
====A middle way====&lt;br /&gt;
&lt;br /&gt;
I (Tim) don&#039;t like either of the above two solutions. I don&#039;t like excessively lesson specific code in the question types, but it would be really bad if the code for a particular question type was not all in one place.&lt;br /&gt;
&lt;br /&gt;
I think that the solution is to add a new concept: question outcomes. It will take me a couple of paragraphs to explain what I mean with this.&lt;br /&gt;
&lt;br /&gt;
Currently, after a student attempts a question, you get stuff that falls into two categories:&lt;br /&gt;
# generic stuff, like the grade (both before and after applying the penalty factor), the penalty factor itself, the classification of the before grade as Correct/Partially Correct/Incorrect, a possible manual comment added by the teacher, the state of the question (saved, graded, manual graded, closed)&lt;br /&gt;
# question-type specific stuff, like the data in question_states-&amp;gt;answer column, or the feedback that is displayed on-screen.&lt;br /&gt;
&lt;br /&gt;
I think that in the generic stuff category, we need to add the new concept of &#039;outcome&#039;. This would be what the lesson module uses to select branches, and would probably also be useful to the quiz reports. It would categorise the student&#039;s answer into one of a number of categories, and the question type class would need to implement two new methods:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;array of strings&#039;&#039; all_outcomes($question)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;string&#039;&#039; outcome($question, $state)&lt;br /&gt;
&lt;br /&gt;
For multiple-choice (single response) this would return the answer selected. For shortanswer, numerical and calculated, this would return the answer that was matched. The generic implementation in the base class would probably have to just return &#039;incorrect&#039;/&#039;partiallycorrect&#039;/&#039;correct&#039; (that is the ids of the strings that are looked up in the language file. &lt;br /&gt;
&lt;br /&gt;
The lesson would then need to do branching be specifying where to go for each outcome. It would requre a multi-stage UI (create the question using the standard questionbank code, add the question to the lesson, link up the outcomes to jump targets). However, done well, this could be a very natural UI.&lt;br /&gt;
&lt;br /&gt;
The quiz UI is not perfect, but I think the way it is now: create a question, then add the question to the quiz, then adjust the grade for that question within the quiz, is very natural to use.&lt;br /&gt;
&lt;br /&gt;
====Migration of jumps====&lt;br /&gt;
Regardless of implementation, one problem remains: if each question potentially does not have a jump defined for each answer, then how are the currently defined jumps supposed to be migrated to the new question implementation (remember, Lesson questions have one jump defined for each answer regardless if it makes sense at all)?  The only solution that comes to mind is to have strict definitions for migration for the following [[Lesson#Types_of_questions_available_within_a_lesson|question types that exist in Lesson]]:&lt;br /&gt;
*Multichoice (single and multianswer)&lt;br /&gt;
*Matching&lt;br /&gt;
*Numerical&lt;br /&gt;
*Short Answer&lt;br /&gt;
*True/False&lt;br /&gt;
*Essay&lt;br /&gt;
&lt;br /&gt;
Note: the use of migration here refers to upgrading old Lessons &#039;&#039;&#039;and&#039;&#039;&#039; restoring older Lessons.&lt;br /&gt;
&lt;br /&gt;
===Branch tables===&lt;br /&gt;
Once a solution for the above problem has been found, I would like to discuss the possibility of replacing the functionality of [[Lesson#Branches_and_branch_tables|branch tables]] with the [[Question_types#Description|description question type]].  Reason for discussing this after a solution has been found is because the solution may influence the outcome to this problem.&lt;br /&gt;
&lt;br /&gt;
==New database schema==&lt;br /&gt;
&lt;br /&gt;
  ------------------      ------------------&lt;br /&gt;
  |                |      |                |&lt;br /&gt;
  | &#039;&#039;&#039;course_modules&#039;&#039;&#039; |      | [[#lesson_default|lesson_default]] |&lt;br /&gt;
  |                |      |                |&lt;br /&gt;
  ------------------      ------------------&lt;br /&gt;
          |                        |&lt;br /&gt;
          |                        |&lt;br /&gt;
   --------------           --------------&lt;br /&gt;
   |            |           |            |&lt;br /&gt;
   |   [[#lesson|lesson]]   |-----------|   &#039;&#039;&#039;course&#039;&#039;&#039;   |&lt;br /&gt;
   |            |           |            |&lt;br /&gt;
   --------------           --------------&lt;br /&gt;
          |&lt;br /&gt;
          |      ----------------------      ----------&lt;br /&gt;
          |      |                    |      |        |&lt;br /&gt;
          |------| [[#lesson_high_scores|lesson_high_scores]] |------|  &#039;&#039;&#039;user&#039;&#039;&#039;  |&lt;br /&gt;
          |      |                    |      |        |&lt;br /&gt;
          |      ----------------------      ----------&lt;br /&gt;
          |                                       |&lt;br /&gt;
          |      -----------------                |&lt;br /&gt;
          |      |               |                |&lt;br /&gt;
          |------| [[#lesson_grades|lesson_grades]] |----------------|&lt;br /&gt;
          |      |               |                |&lt;br /&gt;
          |      -----------------                |&lt;br /&gt;
          |                                       |&lt;br /&gt;
          |      -------------------              |&lt;br /&gt;
          |      |                 |--------------|    ---------------------&lt;br /&gt;
          |------| [[#lesson_attempts|lesson_attempts]] |                   |                   |&lt;br /&gt;
          |      |                 |-------------------| &#039;&#039;&#039;question_attempts&#039;&#039;&#039; |&lt;br /&gt;
          |      -------------------                   |                   |&lt;br /&gt;
          |                                            ---------------------&lt;br /&gt;
          |      ----------------&lt;br /&gt;
          |      |              |&lt;br /&gt;
          |------| [[#lesson_pages|lesson_pages]] |&lt;br /&gt;
                 |              |&lt;br /&gt;
                 ----------------&lt;br /&gt;
                        |      -----------------------------      ------------&lt;br /&gt;
                        |      |                           |      |          |&lt;br /&gt;
                        |------| [[#lesson_question_instances|lesson_question_instances]] |------| &#039;&#039;&#039;[[Quiz_database_structure#question|question]]&#039;&#039;&#039; |&lt;br /&gt;
                        |      |                           |      |          |&lt;br /&gt;
                        |      -----------------------------      ------------&lt;br /&gt;
                        |      &lt;br /&gt;
                        |      -------------------&lt;br /&gt;
                        |      |                 |&lt;br /&gt;
                        |------| [[#lesson_branches|lesson_branches]] |&lt;br /&gt;
                               |                 |&lt;br /&gt;
                               -------------------&lt;br /&gt;
*&#039;&#039;&#039;Note&#039;&#039;&#039;: tables in &#039;&#039;&#039;bold&#039;&#039;&#039; are standard in Moodle and are there only to show relations.&lt;br /&gt;
&lt;br /&gt;
==Table Descriptions==&lt;br /&gt;
&lt;br /&gt;
Key:&lt;br /&gt;
*PK = primary key&lt;br /&gt;
*FK = foreign key&lt;br /&gt;
&lt;br /&gt;
===lesson===&lt;br /&gt;
Primary module table&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*course: FK to course&lt;br /&gt;
*name: Name of the lesson&lt;br /&gt;
*practice: Flag for practice lessons&lt;br /&gt;
*modattempts&lt;br /&gt;
*password: Stores password for password protected lessons&lt;br /&gt;
*dependency: FK to [[#lesson|lesson]] table.  The lesson that this lesson is dependent upon&lt;br /&gt;
*conditions: conditions for the dependency&lt;br /&gt;
*grade: Max grade for the lesson&lt;br /&gt;
*cusom: custom scoring flag&lt;br /&gt;
*ongoing: display on going score flag&lt;br /&gt;
*usemaxgrade: use max grade or mean flag&lt;br /&gt;
*maxanswers: maximum answers for a question (would only be used by branch tables, remove?)&lt;br /&gt;
*maxattempts: number of attempts on a question&lt;br /&gt;
*review&lt;br /&gt;
*nextpagedefault: default flow control&lt;br /&gt;
*minquestions: minimum questions to answer&lt;br /&gt;
*maxpages&lt;br /&gt;
*time: allowed time in the lesson per attempt&lt;br /&gt;
*retake: allow student to retake&lt;br /&gt;
*activitylink: FK to course_modules table&lt;br /&gt;
*mediafile&lt;br /&gt;
*mediaheight: height of pop-up&lt;br /&gt;
*mediawidth: width of pop-up&lt;br /&gt;
*mediaclose: display close button for media&lt;br /&gt;
*slideshow: display branches in slide show mode flag&lt;br /&gt;
*width: width of slide show&lt;br /&gt;
*height: height of slide show&lt;br /&gt;
*bgcolor: background color of slide show&lt;br /&gt;
*displayleft: display left menu&lt;br /&gt;
*displayleftif: display left menu if student has already completed lesson flag&lt;br /&gt;
*progressbar: display progress bar flag&lt;br /&gt;
*highscores: high scores&lt;br /&gt;
*available: when lesson is available to student&lt;br /&gt;
*deadline: when lesson closes to student&lt;br /&gt;
*timemodified: last updated&lt;br /&gt;
&lt;br /&gt;
===lesson_default===&lt;br /&gt;
Lesson default settings for a course.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*same as the lesson table minus the following fields: name, dependency, activity, mediafile, available, deadline, and modified&lt;br /&gt;
&lt;br /&gt;
===lesson_high_scores===&lt;br /&gt;
Keep track of the lesson top scores.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*userid: FK to user table&lt;br /&gt;
*gradeid: FK to [[#lesson_grades|lesson_grades]] table&lt;br /&gt;
*nickname: user&#039;s nickname&lt;br /&gt;
&lt;br /&gt;
===lesson_grades===&lt;br /&gt;
User attempt grades.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*userid: FK to user table&lt;br /&gt;
*grade: attempt grade&lt;br /&gt;
*completed: time of completion&lt;br /&gt;
&lt;br /&gt;
===lesson_attempts===&lt;br /&gt;
Lesson attempts by users.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*uniqueid: FK to question_sessions table&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*userid: FK to user table&lt;br /&gt;
*path: users path through the lesson (comma separated list of page ids from the [[#lesson_pages|lesson_pages]] table)&lt;br /&gt;
*attempt: user&#039;s attempt count&lt;br /&gt;
*sumgrade: current total for grades&lt;br /&gt;
*timestart: starting time of attempt&lt;br /&gt;
*timefinish: ending time of attempt&lt;br /&gt;
*timemodified: last time updated&lt;br /&gt;
&lt;br /&gt;
===lesson_pages===&lt;br /&gt;
All content pages in lesson.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*prevpageid: FK to [[#lesson_pages|lesson_pages]] table&lt;br /&gt;
*nextpageid: FK to [[#lesson_pages|lesson_pages]] table&lt;br /&gt;
*type: values include&lt;br /&gt;
**branch&lt;br /&gt;
**endofbranch&lt;br /&gt;
**cluster&lt;br /&gt;
**endofcluster&lt;br /&gt;
**question&lt;br /&gt;
*display: display in left menu flag&lt;br /&gt;
*jumps: jumps related to this page&lt;br /&gt;
*timemodified: time last updated&lt;br /&gt;
&lt;br /&gt;
===lesson_question_instances===&lt;br /&gt;
Store relation between lesson pages that are questions and the question table.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*pageid: FK to [[#lesson_pages|lesson_pages]] table&lt;br /&gt;
*questionid: FK to [[Quiz_database_structure#question|question]] table&lt;br /&gt;
*grade: point value of the question&lt;br /&gt;
&lt;br /&gt;
===lesson_branches===&lt;br /&gt;
Lesson pages that are branch tables. (does not exist in moodle 1.6.1 but there is different set of fields in lesson_branch)&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*pageid: FK to [[#lesson_pages|lesson_pages]] table&lt;br /&gt;
*boilerplates: text of branch buttons&lt;br /&gt;
*layout: layout of branch buttons&lt;br /&gt;
&lt;br /&gt;
==Discussion==&lt;br /&gt;
If you would like to discuss topics on this page, please make a post in the [http://moodle.org/mod/forum/view.php?f=333 lesson forum].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Sorry, I did not see this until I had added my comments inline. I hope that is not too much of a problem.&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[http://moodle.org/mod/forum/discuss.php?d=42563 Original lesson forum discussion]&lt;br /&gt;
*[[Question_engine|Question engine]]&lt;br /&gt;
*[[Lesson|Lesson module]]&lt;br /&gt;
*[[Quiz database structure#Overview]] - now, at last, with a diagram of the structure in 1.7.&lt;br /&gt;
&lt;br /&gt;
[[Category:Future]]&lt;br /&gt;
[[Category:Moodle 2.0]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Modules]]&lt;br /&gt;
[[Category:Lesson]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Adding_question_types_to_lesson&amp;diff=4329</id>
		<title>Adding question types to lesson</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Adding_question_types_to_lesson&amp;diff=4329"/>
		<updated>2006-09-10T07:08:53Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* lesson_branches */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Please note that this page deals with a future version of [[Moodle 2.0]].&lt;br /&gt;
&lt;br /&gt;
A regularly discussed topic is that Lesson should use the same [[#Questions_and_jumps|question classes]] as [[Quiz]].  This page is geared toward this goal by explaining the different aspects required to complete this project.--Mark Nielsen&lt;br /&gt;
&lt;br /&gt;
I think this would be a really good idea, and am willing to help--[[User:Tim Hunt|Tim Hunt]], Quiz module/question bank maintainer.&lt;br /&gt;
&lt;br /&gt;
==Project goals==&lt;br /&gt;
*(Primary) Implement the [[Question_types|question type classes]] in [[Lesson]].&lt;br /&gt;
*(Primary) Reduce Moodle&#039;s code base.  Instead of Lesson having its own question code, it can now use [[Question_engine|existing code]].&lt;br /&gt;
*Simplify Lesson&#039;s code.  There is a high mix of presentation and logic and some rather confusing algorithms due to the nature of Lesson and how it is organized.&lt;br /&gt;
&lt;br /&gt;
==Primary coding tasks==&lt;br /&gt;
&lt;br /&gt;
===Adding support for question class===&lt;br /&gt;
Implement the question class code and the necessary interfaces for adding and editing questions within Lesson.  The needed functionality is as follows:&lt;br /&gt;
*Printing questions for attempts. &#039;&#039;Should be nothing to do if we are happy with the same presentation for both quiz and lesson&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
*[[Question_engine#Grades|Grading]] the questions. &#039;&#039;Again, should be fine, as long as the question_(attempts|sessions|states) model is flexible enough for the quiz&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
*Provide a method of adding new questions to a Lesson. &#039;&#039;Hopefully question/showbank.php will work for you, perhaps with a few changs.&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
**When adding a question, display questions in course question bank to select from.&lt;br /&gt;
***Provide links/tabs for creating new questions.&lt;br /&gt;
**After adding a question, provide an interface to define possible jumps (This step may not be needed.  Depends on the solution to [[#Questions_and_jumps|the questions and jumps]] problem). &#039;&#039;This will be new work&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
&lt;br /&gt;
===Changing the attempts logic===&lt;br /&gt;
Among other Lesson table changes, one that is the most significant is changing the &#039;&#039;lesson_attempts&#039;&#039; table. Instead of storing one attempt record for every user answer, &#039;&#039;lesson_attempts&#039;&#039; table should store one record per Lesson attempt. The &#039;&#039;question_states&#039;&#039; table will replace the functionality of the original &#039;&#039;lesson_attempts&#039;&#039; table.  Since this is changing such a fundamental part of Lesson, almost all of Lesson&#039;s code will have to be adapted to the new logic of attempts.&lt;br /&gt;
&lt;br /&gt;
===Fixing code breaks===&lt;br /&gt;
By switching to the question class, nearly all of the current Lesson code will be broken and must be replaced or fixed. Here is a quick breakdown of foreseeable code breaks:&lt;br /&gt;
*High scores&lt;br /&gt;
*[[Lesson_reports|Lesson reports]]&lt;br /&gt;
**[[Quiz_reports|Quiz reports]] might provide a jump start(?)&lt;br /&gt;
*[[Lesson_module#Pages.2C_questions.2C_answers_and_responses|Page authoring]]&lt;br /&gt;
*[[Jumps#Special_jumps|Jump type interpretation algorithms]]&lt;br /&gt;
*On-going score&lt;br /&gt;
*Progress bar&lt;br /&gt;
*Backup/restore&lt;br /&gt;
**Rewrite for new table structures&lt;br /&gt;
**Add support for restoring Lessons prior question class support&lt;br /&gt;
**Add support in the primary backup/restore routines&lt;br /&gt;
&lt;br /&gt;
===Database migration===&lt;br /&gt;
Lesson tables need to be removed or changed and their old data needs to be migrated to new tables. Some of this code will be used in the restore process as well. Here is a basic overview of the database migration process:&lt;br /&gt;
*migrate &#039;&#039;lesson_pages&#039;&#039; and &#039;&#039;lesson_answer&#039;&#039; content to question tables.  Note: this will only be the content, not the logic for ordering Lesson pages or any other Lesson specific data.&lt;br /&gt;
*migrate &#039;&#039;lesson_attempts&#039;&#039; to &#039;&#039;question_states&#039;&#039;.&lt;br /&gt;
*Re-organization of the lesson tables (see [[#New_database_schema|new database schema]]).&lt;br /&gt;
&lt;br /&gt;
==Unsolved problems==&lt;br /&gt;
Here are some tricky issues that do not have a solid or obvious solution.  Please advise.&lt;br /&gt;
&lt;br /&gt;
===Questions and jumps===&lt;br /&gt;
Page [[Jumps|jumps]] determine the flow from one page to another and are a unique feature to Lesson.  So, how does one figure out all the necessary jump definitions needed for a question?  In Lesson, every answer has a jump, but this solution is not ideal.  A multiple choice question for example would have two cases: &lt;br /&gt;
*Single answer: a jump must be defined for each answer.&lt;br /&gt;
*[[Lesson_module#Multiple_choice.2C_multiple_answer|Multiple answer]]: a jump would be defined for the correct answer and a jump for the wrong answer.&lt;br /&gt;
&lt;br /&gt;
The follow two sections discuss possible solutions, but please feel free to suggest others.  When thinking about this problem and perhaps a new solution, remember the primary goals of this project: reduce code size and simplify the Lesson code to make it easier to maintain.&lt;br /&gt;
&lt;br /&gt;
====Invasive solution====&lt;br /&gt;
An invasive solution would be to modify the question class code.  This is invasive because the introduced code may only be used by Lesson unless it was implemented in such a way that it would be viable for other uses.  Some of the foreseeable changes would include the following:&lt;br /&gt;
*Add methods to the &#039;&#039;default_questiontype&#039;&#039; class that would handle the default behavior for defining jumps.  Default behavior would be two jump definitions: one for correct answers and one for incorrect answers.&lt;br /&gt;
*Provide a place to store the defined jumps.&lt;br /&gt;
*In each question type, override the default methods in the &#039;&#039;default_questiontype&#039;&#039; class to suite the behavior of the question (optional for each question type).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pros for this implementation:&#039;&#039;&#039;&lt;br /&gt;
*All question type code is in one place.&lt;br /&gt;
*Clean implementation&lt;br /&gt;
*Better work-flow for question authoring&lt;br /&gt;
&#039;&#039;&#039;Cons:&#039;&#039;&#039;&lt;br /&gt;
*Potentially introduce code that is unusable by other modules, etc.&lt;br /&gt;
&lt;br /&gt;
====Noninvasive solution====&lt;br /&gt;
Lesson would extend all question types that to handle jumps.  Lesson would have its own &#039;&#039;type&#039;&#039; folder (mod/lesson/type).  This &#039;&#039;type&#039;&#039; folder would be organized the same as &#039;&#039;question/type&#039;&#039; directory.  Each question type would have its own folder and in that folder a &#039;&#039;questiontype.php&#039;&#039;.  So, &#039;&#039;mod/lesson/type/{questiontype}/questiontype.php&#039;&#039; where {questiontype} is replaced with each question type name. The &#039;&#039;questiontype.php&#039;&#039; would extend the original question type class and add the necessary methods for handling jumps.  The following methods would be added:&lt;br /&gt;
*&#039;&#039;&#039;print_jump_form:&#039;&#039;&#039; accepts the possible jump values from Lesson and then print (or return) the contents of a form.  This form would then be presented to the user to define the possible jumps.&lt;br /&gt;
*&#039;&#039;&#039;process_jump_form:&#039;&#039;&#039; accepts the form data from &#039;&#039;&#039;print_jump_form&#039;&#039;&#039; and organize it.  It then returns the organized data so that Lesson can store it.&lt;br /&gt;
*&#039;&#039;&#039;interpret_jump:&#039;&#039;&#039; accepts the user&#039;s answer to the question and the returned value from &#039;&#039;&#039;process_jump_form&#039;&#039;&#039;.  This method would determine which jump Lesson should use.&lt;br /&gt;
*&#039;&#039;&#039;restore_jumps:&#039;&#039;&#039; restore the returned value from &#039;&#039;&#039;process_jump_form&#039;&#039;&#039; during Lesson&#039;s restore routine.  Returns the restored value so that Lesson can store it.&lt;br /&gt;
&lt;br /&gt;
Example use:&lt;br /&gt;
&lt;br /&gt;
Two jump definitions: correct or incorrect:&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;print_jump_form&#039;&#039;&#039; would return the following form:&lt;br /&gt;
  Correct answer jump: [drop-down-menu-with-lesson-jumps]&lt;br /&gt;
  Wrong answer jump: [drop-down-menu-with-lesson-jumps]&lt;br /&gt;
*&#039;&#039;&#039;process_jump_form&#039;&#039;&#039; would accept the POST data from the above form and organize it in an array like this: array(&#039;correct&#039; =&amp;gt; [lesson-jump-code], &#039;wrong&#039; =&amp;gt; [lesson-jump-code]).  Where [lesson-jump-code] is a Lesson page id or jump code from the [drop-down-menu-with-lesson-jumps].  This array would be returned so that Lesson could store it.&lt;br /&gt;
*&#039;&#039;&#039;interpret_jump&#039;&#039;&#039; would accept the students answer and the above array.  It would return the [lesson-jump-code] either associated with &#039;&#039;correct&#039;&#039; or &#039;&#039;wrong&#039;&#039; based on the student&#039;s answer.&lt;br /&gt;
*&#039;&#039;&#039;restore_jumps&#039;&#039;&#039; would be called during the Lesson restore process and it would return the restored array so that Lesson could store it.  Example: if each answer had a jump then the array would be defined like this: array(answerid =&amp;gt; [lesson-jump-code] ... ).  The &#039;&#039;answerid&#039;&#039; would have to be mapped to the new answerid during the restore process.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pros for this implementation:&#039;&#039;&#039;&lt;br /&gt;
*Potential Lesson specific code remains in Lesson.&lt;br /&gt;
&#039;&#039;&#039;Cons:&#039;&#039;&#039;&lt;br /&gt;
*Work-flow would have an extra step (create question then define jumps).&lt;br /&gt;
*Creating new question types is more difficult.&lt;br /&gt;
&lt;br /&gt;
====A middle way====&lt;br /&gt;
&lt;br /&gt;
I (Tim) don&#039;t like either of the above two solutions. I don&#039;t like excessively lesson specific code in the question types, but it would be really bad if the code for a particular question type was not all in one place.&lt;br /&gt;
&lt;br /&gt;
I think that the solution is to add a new concept: question outcomes. It will take me a couple of paragraphs to explain what I mean with this.&lt;br /&gt;
&lt;br /&gt;
Currently, after a student attempts a question, you get stuff that falls into two categories:&lt;br /&gt;
# generic stuff, like the grade (both before and after applying the penalty factor), the penalty factor itself, the classification of the before grade as Correct/Partially Correct/Incorrect, a possible manual comment added by the teacher, the state of the question (saved, graded, manual graded, closed)&lt;br /&gt;
# question-type specific stuff, like the data in question_states-&amp;gt;answer column, or the feedback that is displayed on-screen.&lt;br /&gt;
&lt;br /&gt;
I think that in the generic stuff category, we need to add the new concept of &#039;outcome&#039;. This would be what the lesson module uses to select branches, and would probably also be useful to the quiz reports. It would categorise the student&#039;s answer into one of a number of categories, and the question type class would need to implement two new methods:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;array of strings&#039;&#039; all_outcomes($question)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;string&#039;&#039; outcome($question, $state)&lt;br /&gt;
&lt;br /&gt;
For multiple-choice (single response) this would return the answer selected. For shortanswer, numerical and calculated, this would return the answer that was matched. The generic implementation in the base class would probably have to just return &#039;incorrect&#039;/&#039;partiallycorrect&#039;/&#039;correct&#039; (that is the ids of the strings that are looked up in the language file. &lt;br /&gt;
&lt;br /&gt;
The lesson would then need to do branching be specifying where to go for each outcome. It would requre a multi-stage UI (create the question using the standard questionbank code, add the question to the lesson, link up the outcomes to jump targets). However, done well, this could be a very natural UI.&lt;br /&gt;
&lt;br /&gt;
The quiz UI is not perfect, but I think the way it is now: create a question, then add the question to the quiz, then adjust the grade for that question within the quiz, is very natural to use.&lt;br /&gt;
&lt;br /&gt;
====Migration of jumps====&lt;br /&gt;
Regardless of implementation, one problem remains: if each question potentially does not have a jump defined for each answer, then how are the currently defined jumps supposed to be migrated to the new question implementation (remember, Lesson questions have one jump defined for each answer regardless if it makes sense at all)?  The only solution that comes to mind is to have strict definitions for migration for the following [[Lesson#Types_of_questions_available_within_a_lesson|question types that exist in Lesson]]:&lt;br /&gt;
*Multichoice (single and multianswer)&lt;br /&gt;
*Matching&lt;br /&gt;
*Numerical&lt;br /&gt;
*Short Answer&lt;br /&gt;
*True/False&lt;br /&gt;
*Essay&lt;br /&gt;
&lt;br /&gt;
Note: the use of migration here refers to upgrading old Lessons &#039;&#039;&#039;and&#039;&#039;&#039; restoring older Lessons.&lt;br /&gt;
&lt;br /&gt;
===Branch tables===&lt;br /&gt;
Once a solution for the above problem has been found, I would like to discuss the possibility of replacing the functionality of [[Lesson#Branches_and_branch_tables|branch tables]] with the [[Question_types#Description|description question type]].  Reason for discussing this after a solution has been found is because the solution may influence the outcome to this problem.&lt;br /&gt;
&lt;br /&gt;
==New database schema==&lt;br /&gt;
&lt;br /&gt;
  ------------------      ------------------&lt;br /&gt;
  |                |      |                |&lt;br /&gt;
  | &#039;&#039;&#039;course_modules&#039;&#039;&#039; |      | [[#lesson_default|lesson_default]] |&lt;br /&gt;
  |                |      |                |&lt;br /&gt;
  ------------------      ------------------&lt;br /&gt;
          |                        |&lt;br /&gt;
          |                        |&lt;br /&gt;
   --------------           --------------&lt;br /&gt;
   |            |           |            |&lt;br /&gt;
   |   [[#lesson|lesson]]   |-----------|   &#039;&#039;&#039;course&#039;&#039;&#039;   |&lt;br /&gt;
   |            |           |            |&lt;br /&gt;
   --------------           --------------&lt;br /&gt;
          |&lt;br /&gt;
          |      ----------------------      ----------&lt;br /&gt;
          |      |                    |      |        |&lt;br /&gt;
          |------| [[#lesson_high_scores|lesson_high_scores]] |------|  &#039;&#039;&#039;user&#039;&#039;&#039;  |&lt;br /&gt;
          |      |                    |      |        |&lt;br /&gt;
          |      ----------------------      ----------&lt;br /&gt;
          |                                       |&lt;br /&gt;
          |      -----------------                |&lt;br /&gt;
          |      |               |                |&lt;br /&gt;
          |------| [[#lesson_grades|lesson_grades]] |----------------|&lt;br /&gt;
          |      |               |                |&lt;br /&gt;
          |      -----------------                |&lt;br /&gt;
          |                                       |&lt;br /&gt;
          |      -------------------              |&lt;br /&gt;
          |      |                 |--------------|    ---------------------&lt;br /&gt;
          |------| [[#lesson_attempts|lesson_attempts]] |                   |                   |&lt;br /&gt;
          |      |                 |-------------------| &#039;&#039;&#039;question_attempts&#039;&#039;&#039; |&lt;br /&gt;
          |      -------------------                   |                   |&lt;br /&gt;
          |                                            ---------------------&lt;br /&gt;
          |      ----------------&lt;br /&gt;
          |      |              |&lt;br /&gt;
          |------| [[#lesson_pages|lesson_pages]] |&lt;br /&gt;
                 |              |&lt;br /&gt;
                 ----------------&lt;br /&gt;
                        |      -----------------------------      ------------&lt;br /&gt;
                        |      |                           |      |          |&lt;br /&gt;
                        |------| [[#lesson_question_instances|lesson_question_instances]] |------| &#039;&#039;&#039;[[Quiz_database_structure#question|question]]&#039;&#039;&#039; |&lt;br /&gt;
                        |      |                           |      |          |&lt;br /&gt;
                        |      -----------------------------      ------------&lt;br /&gt;
                        |      &lt;br /&gt;
                        |      -------------------&lt;br /&gt;
                        |      |                 |&lt;br /&gt;
                        |------| [[#lesson_branches|lesson_branches]] |&lt;br /&gt;
                               |                 |&lt;br /&gt;
                               -------------------&lt;br /&gt;
*&#039;&#039;&#039;Note&#039;&#039;&#039;: tables in &#039;&#039;&#039;bold&#039;&#039;&#039; are standard in Moodle and are there only to show relations.&lt;br /&gt;
&lt;br /&gt;
==Table Descriptions==&lt;br /&gt;
&lt;br /&gt;
Key:&lt;br /&gt;
*PK = primary key&lt;br /&gt;
*FK = foreign key&lt;br /&gt;
&lt;br /&gt;
===lesson===&lt;br /&gt;
Primary module table&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*course: FK to course&lt;br /&gt;
*name: Name of the lesson&lt;br /&gt;
*practice: Flag for practice lessons&lt;br /&gt;
*modattempts&lt;br /&gt;
*password: Stores password for password protected lessons&lt;br /&gt;
*dependency: FK to [[#lesson|lesson]] table.  The lesson that this lesson is dependent upon&lt;br /&gt;
*conditions: conditions for the dependency&lt;br /&gt;
*grade: Max grade for the lesson&lt;br /&gt;
*cusom: custom scoring flag&lt;br /&gt;
*ongoing: display on going score flag&lt;br /&gt;
*usemaxgrade: use max grade or mean flag&lt;br /&gt;
*maxanswers: maximum answers for a question (would only be used by branch tables, remove?)&lt;br /&gt;
*maxattempts: number of attempts on a question&lt;br /&gt;
*review&lt;br /&gt;
*nextpagedefault: default flow control&lt;br /&gt;
*minquestions: minimum questions to answer&lt;br /&gt;
*maxpages&lt;br /&gt;
*time: allowed time in the lesson per attempt&lt;br /&gt;
*retake: allow student to retake&lt;br /&gt;
*activitylink: FK to course_modules table&lt;br /&gt;
*mediafile&lt;br /&gt;
*mediaheight: height of pop-up&lt;br /&gt;
*mediawidth: width of pop-up&lt;br /&gt;
*mediaclose: display close button for media&lt;br /&gt;
*slideshow: display branches in slide show mode flag&lt;br /&gt;
*width: width of slide show&lt;br /&gt;
*height: height of slide show&lt;br /&gt;
*bgcolor: background color of slide show&lt;br /&gt;
*displayleft: display left menu&lt;br /&gt;
*displayleftif: display left menu if student has already completed lesson flag&lt;br /&gt;
*progressbar: display progress bar flag&lt;br /&gt;
*highscores: high scores&lt;br /&gt;
*available: when lesson is available to student&lt;br /&gt;
*deadline: when lesson closes to student&lt;br /&gt;
*timemodified: last updated&lt;br /&gt;
&lt;br /&gt;
===lesson_default===&lt;br /&gt;
Lesson default settings for a course.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*same as the lesson table minus the following fields: name, dependency, activity, mediafile, available, deadline, and modified&lt;br /&gt;
&lt;br /&gt;
===lesson_high_scores===&lt;br /&gt;
Keep track of the lesson top scores.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*userid: FK to user table&lt;br /&gt;
*gradeid: FK to [[#lesson_grades|lesson_grades]] table&lt;br /&gt;
*nickname: user&#039;s nickname&lt;br /&gt;
&lt;br /&gt;
===lesson_grades===&lt;br /&gt;
User attempt grades.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*userid: FK to user table&lt;br /&gt;
*grade: attempt grade&lt;br /&gt;
*completed: time of completion&lt;br /&gt;
&lt;br /&gt;
===lesson_attempts===&lt;br /&gt;
Lesson attempts by users.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*uniqueid: FK to question_sessions table&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*userid: FK to user table&lt;br /&gt;
*path: users path through the lesson (comma separated list of page ids from the [[#lesson_pages|lesson_pages]] table)&lt;br /&gt;
*attempt: user&#039;s attempt count&lt;br /&gt;
*sumgrade: current total for grades&lt;br /&gt;
*timestart: starting time of attempt&lt;br /&gt;
*timefinish: ending time of attempt&lt;br /&gt;
*timemodified: last time updated&lt;br /&gt;
&lt;br /&gt;
===lesson_pages===&lt;br /&gt;
All content pages in lesson.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*prevpageid: FK to [[#lesson_pages|lesson_pages]] table&lt;br /&gt;
*nextpageid: FK to [[#lesson_pages|lesson_pages]] table&lt;br /&gt;
*type: values include&lt;br /&gt;
**branch&lt;br /&gt;
**endofbranch&lt;br /&gt;
**cluster&lt;br /&gt;
**endofcluster&lt;br /&gt;
**question&lt;br /&gt;
*display: display in left menu flag&lt;br /&gt;
*jumps: jumps related to this page&lt;br /&gt;
*timemodified: time last updated&lt;br /&gt;
&lt;br /&gt;
===lesson_question_instances===&lt;br /&gt;
Store relation between lesson pages that are questions and the question table.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*pageid: FK to [[#lesson_pages|lesson_pages]] table&lt;br /&gt;
*questionid: FK to [[Quiz_database_structure#question|question]] table&lt;br /&gt;
*grade: point value of the question&lt;br /&gt;
&lt;br /&gt;
===lesson_branches===&lt;br /&gt;
Lesson pages that are branch tables. (does not exist in moodle 1.6.1)&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*pageid: FK to [[#lesson_pages|lesson_pages]] table&lt;br /&gt;
*boilerplates: text of branch buttons&lt;br /&gt;
*layout: layout of branch buttons&lt;br /&gt;
&lt;br /&gt;
==Discussion==&lt;br /&gt;
If you would like to discuss topics on this page, please make a post in the [http://moodle.org/mod/forum/view.php?f=333 lesson forum].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Sorry, I did not see this until I had added my comments inline. I hope that is not too much of a problem.&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[http://moodle.org/mod/forum/discuss.php?d=42563 Original lesson forum discussion]&lt;br /&gt;
*[[Question_engine|Question engine]]&lt;br /&gt;
*[[Lesson|Lesson module]]&lt;br /&gt;
*[[Quiz database structure#Overview]] - now, at last, with a diagram of the structure in 1.7.&lt;br /&gt;
&lt;br /&gt;
[[Category:Future]]&lt;br /&gt;
[[Category:Moodle 2.0]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Modules]]&lt;br /&gt;
[[Category:Lesson]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Adding_question_types_to_lesson&amp;diff=4328</id>
		<title>Adding question types to lesson</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Adding_question_types_to_lesson&amp;diff=4328"/>
		<updated>2006-09-10T06:51:04Z</updated>

		<summary type="html">&lt;p&gt;Bruno: /* lesson_default */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Please note that this page deals with a future version of [[Moodle 2.0]].&lt;br /&gt;
&lt;br /&gt;
A regularly discussed topic is that Lesson should use the same [[#Questions_and_jumps|question classes]] as [[Quiz]].  This page is geared toward this goal by explaining the different aspects required to complete this project.--Mark Nielsen&lt;br /&gt;
&lt;br /&gt;
I think this would be a really good idea, and am willing to help--[[User:Tim Hunt|Tim Hunt]], Quiz module/question bank maintainer.&lt;br /&gt;
&lt;br /&gt;
==Project goals==&lt;br /&gt;
*(Primary) Implement the [[Question_types|question type classes]] in [[Lesson]].&lt;br /&gt;
*(Primary) Reduce Moodle&#039;s code base.  Instead of Lesson having its own question code, it can now use [[Question_engine|existing code]].&lt;br /&gt;
*Simplify Lesson&#039;s code.  There is a high mix of presentation and logic and some rather confusing algorithms due to the nature of Lesson and how it is organized.&lt;br /&gt;
&lt;br /&gt;
==Primary coding tasks==&lt;br /&gt;
&lt;br /&gt;
===Adding support for question class===&lt;br /&gt;
Implement the question class code and the necessary interfaces for adding and editing questions within Lesson.  The needed functionality is as follows:&lt;br /&gt;
*Printing questions for attempts. &#039;&#039;Should be nothing to do if we are happy with the same presentation for both quiz and lesson&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
*[[Question_engine#Grades|Grading]] the questions. &#039;&#039;Again, should be fine, as long as the question_(attempts|sessions|states) model is flexible enough for the quiz&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
*Provide a method of adding new questions to a Lesson. &#039;&#039;Hopefully question/showbank.php will work for you, perhaps with a few changs.&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
**When adding a question, display questions in course question bank to select from.&lt;br /&gt;
***Provide links/tabs for creating new questions.&lt;br /&gt;
**After adding a question, provide an interface to define possible jumps (This step may not be needed.  Depends on the solution to [[#Questions_and_jumps|the questions and jumps]] problem). &#039;&#039;This will be new work&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
&lt;br /&gt;
===Changing the attempts logic===&lt;br /&gt;
Among other Lesson table changes, one that is the most significant is changing the &#039;&#039;lesson_attempts&#039;&#039; table. Instead of storing one attempt record for every user answer, &#039;&#039;lesson_attempts&#039;&#039; table should store one record per Lesson attempt. The &#039;&#039;question_states&#039;&#039; table will replace the functionality of the original &#039;&#039;lesson_attempts&#039;&#039; table.  Since this is changing such a fundamental part of Lesson, almost all of Lesson&#039;s code will have to be adapted to the new logic of attempts.&lt;br /&gt;
&lt;br /&gt;
===Fixing code breaks===&lt;br /&gt;
By switching to the question class, nearly all of the current Lesson code will be broken and must be replaced or fixed. Here is a quick breakdown of foreseeable code breaks:&lt;br /&gt;
*High scores&lt;br /&gt;
*[[Lesson_reports|Lesson reports]]&lt;br /&gt;
**[[Quiz_reports|Quiz reports]] might provide a jump start(?)&lt;br /&gt;
*[[Lesson_module#Pages.2C_questions.2C_answers_and_responses|Page authoring]]&lt;br /&gt;
*[[Jumps#Special_jumps|Jump type interpretation algorithms]]&lt;br /&gt;
*On-going score&lt;br /&gt;
*Progress bar&lt;br /&gt;
*Backup/restore&lt;br /&gt;
**Rewrite for new table structures&lt;br /&gt;
**Add support for restoring Lessons prior question class support&lt;br /&gt;
**Add support in the primary backup/restore routines&lt;br /&gt;
&lt;br /&gt;
===Database migration===&lt;br /&gt;
Lesson tables need to be removed or changed and their old data needs to be migrated to new tables. Some of this code will be used in the restore process as well. Here is a basic overview of the database migration process:&lt;br /&gt;
*migrate &#039;&#039;lesson_pages&#039;&#039; and &#039;&#039;lesson_answer&#039;&#039; content to question tables.  Note: this will only be the content, not the logic for ordering Lesson pages or any other Lesson specific data.&lt;br /&gt;
*migrate &#039;&#039;lesson_attempts&#039;&#039; to &#039;&#039;question_states&#039;&#039;.&lt;br /&gt;
*Re-organization of the lesson tables (see [[#New_database_schema|new database schema]]).&lt;br /&gt;
&lt;br /&gt;
==Unsolved problems==&lt;br /&gt;
Here are some tricky issues that do not have a solid or obvious solution.  Please advise.&lt;br /&gt;
&lt;br /&gt;
===Questions and jumps===&lt;br /&gt;
Page [[Jumps|jumps]] determine the flow from one page to another and are a unique feature to Lesson.  So, how does one figure out all the necessary jump definitions needed for a question?  In Lesson, every answer has a jump, but this solution is not ideal.  A multiple choice question for example would have two cases: &lt;br /&gt;
*Single answer: a jump must be defined for each answer.&lt;br /&gt;
*[[Lesson_module#Multiple_choice.2C_multiple_answer|Multiple answer]]: a jump would be defined for the correct answer and a jump for the wrong answer.&lt;br /&gt;
&lt;br /&gt;
The follow two sections discuss possible solutions, but please feel free to suggest others.  When thinking about this problem and perhaps a new solution, remember the primary goals of this project: reduce code size and simplify the Lesson code to make it easier to maintain.&lt;br /&gt;
&lt;br /&gt;
====Invasive solution====&lt;br /&gt;
An invasive solution would be to modify the question class code.  This is invasive because the introduced code may only be used by Lesson unless it was implemented in such a way that it would be viable for other uses.  Some of the foreseeable changes would include the following:&lt;br /&gt;
*Add methods to the &#039;&#039;default_questiontype&#039;&#039; class that would handle the default behavior for defining jumps.  Default behavior would be two jump definitions: one for correct answers and one for incorrect answers.&lt;br /&gt;
*Provide a place to store the defined jumps.&lt;br /&gt;
*In each question type, override the default methods in the &#039;&#039;default_questiontype&#039;&#039; class to suite the behavior of the question (optional for each question type).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pros for this implementation:&#039;&#039;&#039;&lt;br /&gt;
*All question type code is in one place.&lt;br /&gt;
*Clean implementation&lt;br /&gt;
*Better work-flow for question authoring&lt;br /&gt;
&#039;&#039;&#039;Cons:&#039;&#039;&#039;&lt;br /&gt;
*Potentially introduce code that is unusable by other modules, etc.&lt;br /&gt;
&lt;br /&gt;
====Noninvasive solution====&lt;br /&gt;
Lesson would extend all question types that to handle jumps.  Lesson would have its own &#039;&#039;type&#039;&#039; folder (mod/lesson/type).  This &#039;&#039;type&#039;&#039; folder would be organized the same as &#039;&#039;question/type&#039;&#039; directory.  Each question type would have its own folder and in that folder a &#039;&#039;questiontype.php&#039;&#039;.  So, &#039;&#039;mod/lesson/type/{questiontype}/questiontype.php&#039;&#039; where {questiontype} is replaced with each question type name. The &#039;&#039;questiontype.php&#039;&#039; would extend the original question type class and add the necessary methods for handling jumps.  The following methods would be added:&lt;br /&gt;
*&#039;&#039;&#039;print_jump_form:&#039;&#039;&#039; accepts the possible jump values from Lesson and then print (or return) the contents of a form.  This form would then be presented to the user to define the possible jumps.&lt;br /&gt;
*&#039;&#039;&#039;process_jump_form:&#039;&#039;&#039; accepts the form data from &#039;&#039;&#039;print_jump_form&#039;&#039;&#039; and organize it.  It then returns the organized data so that Lesson can store it.&lt;br /&gt;
*&#039;&#039;&#039;interpret_jump:&#039;&#039;&#039; accepts the user&#039;s answer to the question and the returned value from &#039;&#039;&#039;process_jump_form&#039;&#039;&#039;.  This method would determine which jump Lesson should use.&lt;br /&gt;
*&#039;&#039;&#039;restore_jumps:&#039;&#039;&#039; restore the returned value from &#039;&#039;&#039;process_jump_form&#039;&#039;&#039; during Lesson&#039;s restore routine.  Returns the restored value so that Lesson can store it.&lt;br /&gt;
&lt;br /&gt;
Example use:&lt;br /&gt;
&lt;br /&gt;
Two jump definitions: correct or incorrect:&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;print_jump_form&#039;&#039;&#039; would return the following form:&lt;br /&gt;
  Correct answer jump: [drop-down-menu-with-lesson-jumps]&lt;br /&gt;
  Wrong answer jump: [drop-down-menu-with-lesson-jumps]&lt;br /&gt;
*&#039;&#039;&#039;process_jump_form&#039;&#039;&#039; would accept the POST data from the above form and organize it in an array like this: array(&#039;correct&#039; =&amp;gt; [lesson-jump-code], &#039;wrong&#039; =&amp;gt; [lesson-jump-code]).  Where [lesson-jump-code] is a Lesson page id or jump code from the [drop-down-menu-with-lesson-jumps].  This array would be returned so that Lesson could store it.&lt;br /&gt;
*&#039;&#039;&#039;interpret_jump&#039;&#039;&#039; would accept the students answer and the above array.  It would return the [lesson-jump-code] either associated with &#039;&#039;correct&#039;&#039; or &#039;&#039;wrong&#039;&#039; based on the student&#039;s answer.&lt;br /&gt;
*&#039;&#039;&#039;restore_jumps&#039;&#039;&#039; would be called during the Lesson restore process and it would return the restored array so that Lesson could store it.  Example: if each answer had a jump then the array would be defined like this: array(answerid =&amp;gt; [lesson-jump-code] ... ).  The &#039;&#039;answerid&#039;&#039; would have to be mapped to the new answerid during the restore process.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pros for this implementation:&#039;&#039;&#039;&lt;br /&gt;
*Potential Lesson specific code remains in Lesson.&lt;br /&gt;
&#039;&#039;&#039;Cons:&#039;&#039;&#039;&lt;br /&gt;
*Work-flow would have an extra step (create question then define jumps).&lt;br /&gt;
*Creating new question types is more difficult.&lt;br /&gt;
&lt;br /&gt;
====A middle way====&lt;br /&gt;
&lt;br /&gt;
I (Tim) don&#039;t like either of the above two solutions. I don&#039;t like excessively lesson specific code in the question types, but it would be really bad if the code for a particular question type was not all in one place.&lt;br /&gt;
&lt;br /&gt;
I think that the solution is to add a new concept: question outcomes. It will take me a couple of paragraphs to explain what I mean with this.&lt;br /&gt;
&lt;br /&gt;
Currently, after a student attempts a question, you get stuff that falls into two categories:&lt;br /&gt;
# generic stuff, like the grade (both before and after applying the penalty factor), the penalty factor itself, the classification of the before grade as Correct/Partially Correct/Incorrect, a possible manual comment added by the teacher, the state of the question (saved, graded, manual graded, closed)&lt;br /&gt;
# question-type specific stuff, like the data in question_states-&amp;gt;answer column, or the feedback that is displayed on-screen.&lt;br /&gt;
&lt;br /&gt;
I think that in the generic stuff category, we need to add the new concept of &#039;outcome&#039;. This would be what the lesson module uses to select branches, and would probably also be useful to the quiz reports. It would categorise the student&#039;s answer into one of a number of categories, and the question type class would need to implement two new methods:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;array of strings&#039;&#039; all_outcomes($question)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;string&#039;&#039; outcome($question, $state)&lt;br /&gt;
&lt;br /&gt;
For multiple-choice (single response) this would return the answer selected. For shortanswer, numerical and calculated, this would return the answer that was matched. The generic implementation in the base class would probably have to just return &#039;incorrect&#039;/&#039;partiallycorrect&#039;/&#039;correct&#039; (that is the ids of the strings that are looked up in the language file. &lt;br /&gt;
&lt;br /&gt;
The lesson would then need to do branching be specifying where to go for each outcome. It would requre a multi-stage UI (create the question using the standard questionbank code, add the question to the lesson, link up the outcomes to jump targets). However, done well, this could be a very natural UI.&lt;br /&gt;
&lt;br /&gt;
The quiz UI is not perfect, but I think the way it is now: create a question, then add the question to the quiz, then adjust the grade for that question within the quiz, is very natural to use.&lt;br /&gt;
&lt;br /&gt;
====Migration of jumps====&lt;br /&gt;
Regardless of implementation, one problem remains: if each question potentially does not have a jump defined for each answer, then how are the currently defined jumps supposed to be migrated to the new question implementation (remember, Lesson questions have one jump defined for each answer regardless if it makes sense at all)?  The only solution that comes to mind is to have strict definitions for migration for the following [[Lesson#Types_of_questions_available_within_a_lesson|question types that exist in Lesson]]:&lt;br /&gt;
*Multichoice (single and multianswer)&lt;br /&gt;
*Matching&lt;br /&gt;
*Numerical&lt;br /&gt;
*Short Answer&lt;br /&gt;
*True/False&lt;br /&gt;
*Essay&lt;br /&gt;
&lt;br /&gt;
Note: the use of migration here refers to upgrading old Lessons &#039;&#039;&#039;and&#039;&#039;&#039; restoring older Lessons.&lt;br /&gt;
&lt;br /&gt;
===Branch tables===&lt;br /&gt;
Once a solution for the above problem has been found, I would like to discuss the possibility of replacing the functionality of [[Lesson#Branches_and_branch_tables|branch tables]] with the [[Question_types#Description|description question type]].  Reason for discussing this after a solution has been found is because the solution may influence the outcome to this problem.&lt;br /&gt;
&lt;br /&gt;
==New database schema==&lt;br /&gt;
&lt;br /&gt;
  ------------------      ------------------&lt;br /&gt;
  |                |      |                |&lt;br /&gt;
  | &#039;&#039;&#039;course_modules&#039;&#039;&#039; |      | [[#lesson_default|lesson_default]] |&lt;br /&gt;
  |                |      |                |&lt;br /&gt;
  ------------------      ------------------&lt;br /&gt;
          |                        |&lt;br /&gt;
          |                        |&lt;br /&gt;
   --------------           --------------&lt;br /&gt;
   |            |           |            |&lt;br /&gt;
   |   [[#lesson|lesson]]   |-----------|   &#039;&#039;&#039;course&#039;&#039;&#039;   |&lt;br /&gt;
   |            |           |            |&lt;br /&gt;
   --------------           --------------&lt;br /&gt;
          |&lt;br /&gt;
          |      ----------------------      ----------&lt;br /&gt;
          |      |                    |      |        |&lt;br /&gt;
          |------| [[#lesson_high_scores|lesson_high_scores]] |------|  &#039;&#039;&#039;user&#039;&#039;&#039;  |&lt;br /&gt;
          |      |                    |      |        |&lt;br /&gt;
          |      ----------------------      ----------&lt;br /&gt;
          |                                       |&lt;br /&gt;
          |      -----------------                |&lt;br /&gt;
          |      |               |                |&lt;br /&gt;
          |------| [[#lesson_grades|lesson_grades]] |----------------|&lt;br /&gt;
          |      |               |                |&lt;br /&gt;
          |      -----------------                |&lt;br /&gt;
          |                                       |&lt;br /&gt;
          |      -------------------              |&lt;br /&gt;
          |      |                 |--------------|    ---------------------&lt;br /&gt;
          |------| [[#lesson_attempts|lesson_attempts]] |                   |                   |&lt;br /&gt;
          |      |                 |-------------------| &#039;&#039;&#039;question_attempts&#039;&#039;&#039; |&lt;br /&gt;
          |      -------------------                   |                   |&lt;br /&gt;
          |                                            ---------------------&lt;br /&gt;
          |      ----------------&lt;br /&gt;
          |      |              |&lt;br /&gt;
          |------| [[#lesson_pages|lesson_pages]] |&lt;br /&gt;
                 |              |&lt;br /&gt;
                 ----------------&lt;br /&gt;
                        |      -----------------------------      ------------&lt;br /&gt;
                        |      |                           |      |          |&lt;br /&gt;
                        |------| [[#lesson_question_instances|lesson_question_instances]] |------| &#039;&#039;&#039;[[Quiz_database_structure#question|question]]&#039;&#039;&#039; |&lt;br /&gt;
                        |      |                           |      |          |&lt;br /&gt;
                        |      -----------------------------      ------------&lt;br /&gt;
                        |      &lt;br /&gt;
                        |      -------------------&lt;br /&gt;
                        |      |                 |&lt;br /&gt;
                        |------| [[#lesson_branches|lesson_branches]] |&lt;br /&gt;
                               |                 |&lt;br /&gt;
                               -------------------&lt;br /&gt;
*&#039;&#039;&#039;Note&#039;&#039;&#039;: tables in &#039;&#039;&#039;bold&#039;&#039;&#039; are standard in Moodle and are there only to show relations.&lt;br /&gt;
&lt;br /&gt;
==Table Descriptions==&lt;br /&gt;
&lt;br /&gt;
Key:&lt;br /&gt;
*PK = primary key&lt;br /&gt;
*FK = foreign key&lt;br /&gt;
&lt;br /&gt;
===lesson===&lt;br /&gt;
Primary module table&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*course: FK to course&lt;br /&gt;
*name: Name of the lesson&lt;br /&gt;
*practice: Flag for practice lessons&lt;br /&gt;
*modattempts&lt;br /&gt;
*password: Stores password for password protected lessons&lt;br /&gt;
*dependency: FK to [[#lesson|lesson]] table.  The lesson that this lesson is dependent upon&lt;br /&gt;
*conditions: conditions for the dependency&lt;br /&gt;
*grade: Max grade for the lesson&lt;br /&gt;
*cusom: custom scoring flag&lt;br /&gt;
*ongoing: display on going score flag&lt;br /&gt;
*usemaxgrade: use max grade or mean flag&lt;br /&gt;
*maxanswers: maximum answers for a question (would only be used by branch tables, remove?)&lt;br /&gt;
*maxattempts: number of attempts on a question&lt;br /&gt;
*review&lt;br /&gt;
*nextpagedefault: default flow control&lt;br /&gt;
*minquestions: minimum questions to answer&lt;br /&gt;
*maxpages&lt;br /&gt;
*time: allowed time in the lesson per attempt&lt;br /&gt;
*retake: allow student to retake&lt;br /&gt;
*activitylink: FK to course_modules table&lt;br /&gt;
*mediafile&lt;br /&gt;
*mediaheight: height of pop-up&lt;br /&gt;
*mediawidth: width of pop-up&lt;br /&gt;
*mediaclose: display close button for media&lt;br /&gt;
*slideshow: display branches in slide show mode flag&lt;br /&gt;
*width: width of slide show&lt;br /&gt;
*height: height of slide show&lt;br /&gt;
*bgcolor: background color of slide show&lt;br /&gt;
*displayleft: display left menu&lt;br /&gt;
*displayleftif: display left menu if student has already completed lesson flag&lt;br /&gt;
*progressbar: display progress bar flag&lt;br /&gt;
*highscores: high scores&lt;br /&gt;
*available: when lesson is available to student&lt;br /&gt;
*deadline: when lesson closes to student&lt;br /&gt;
*timemodified: last updated&lt;br /&gt;
&lt;br /&gt;
===lesson_default===&lt;br /&gt;
Lesson default settings for a course.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*same as the lesson table minus the following fields: name, dependency, activity, mediafile, available, deadline, and modified&lt;br /&gt;
&lt;br /&gt;
===lesson_high_scores===&lt;br /&gt;
Keep track of the lesson top scores.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*userid: FK to user table&lt;br /&gt;
*gradeid: FK to [[#lesson_grades|lesson_grades]] table&lt;br /&gt;
*nickname: user&#039;s nickname&lt;br /&gt;
&lt;br /&gt;
===lesson_grades===&lt;br /&gt;
User attempt grades.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*userid: FK to user table&lt;br /&gt;
*grade: attempt grade&lt;br /&gt;
*completed: time of completion&lt;br /&gt;
&lt;br /&gt;
===lesson_attempts===&lt;br /&gt;
Lesson attempts by users.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*uniqueid: FK to question_sessions table&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*userid: FK to user table&lt;br /&gt;
*path: users path through the lesson (comma separated list of page ids from the [[#lesson_pages|lesson_pages]] table)&lt;br /&gt;
*attempt: user&#039;s attempt count&lt;br /&gt;
*sumgrade: current total for grades&lt;br /&gt;
*timestart: starting time of attempt&lt;br /&gt;
*timefinish: ending time of attempt&lt;br /&gt;
*timemodified: last time updated&lt;br /&gt;
&lt;br /&gt;
===lesson_pages===&lt;br /&gt;
All content pages in lesson.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*prevpageid: FK to [[#lesson_pages|lesson_pages]] table&lt;br /&gt;
*nextpageid: FK to [[#lesson_pages|lesson_pages]] table&lt;br /&gt;
*type: values include&lt;br /&gt;
**branch&lt;br /&gt;
**endofbranch&lt;br /&gt;
**cluster&lt;br /&gt;
**endofcluster&lt;br /&gt;
**question&lt;br /&gt;
*display: display in left menu flag&lt;br /&gt;
*jumps: jumps related to this page&lt;br /&gt;
*timemodified: time last updated&lt;br /&gt;
&lt;br /&gt;
===lesson_question_instances===&lt;br /&gt;
Store relation between lesson pages that are questions and the question table.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*pageid: FK to [[#lesson_pages|lesson_pages]] table&lt;br /&gt;
*questionid: FK to [[Quiz_database_structure#question|question]] table&lt;br /&gt;
*grade: point value of the question&lt;br /&gt;
&lt;br /&gt;
===lesson_branches===&lt;br /&gt;
Lesson pages that are branch tables.&lt;br /&gt;
&lt;br /&gt;
Fields:&lt;br /&gt;
*id: PK&lt;br /&gt;
*lessonid: FK to [[#lesson|lesson]] table&lt;br /&gt;
*pageid: FK to [[#lesson_pages|lesson_pages]] table&lt;br /&gt;
*boilerplates: text of branch buttons&lt;br /&gt;
*layout: layout of branch buttons&lt;br /&gt;
&lt;br /&gt;
==Discussion==&lt;br /&gt;
If you would like to discuss topics on this page, please make a post in the [http://moodle.org/mod/forum/view.php?f=333 lesson forum].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Sorry, I did not see this until I had added my comments inline. I hope that is not too much of a problem.&#039;&#039;--[[User:Tim Hunt|Tim Hunt]]&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[http://moodle.org/mod/forum/discuss.php?d=42563 Original lesson forum discussion]&lt;br /&gt;
*[[Question_engine|Question engine]]&lt;br /&gt;
*[[Lesson|Lesson module]]&lt;br /&gt;
*[[Quiz database structure#Overview]] - now, at last, with a diagram of the structure in 1.7.&lt;br /&gt;
&lt;br /&gt;
[[Category:Future]]&lt;br /&gt;
[[Category:Moodle 2.0]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Modules]]&lt;br /&gt;
[[Category:Lesson]]&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User_talk:Bruno_Vernier&amp;diff=23453</id>
		<title>User talk:Bruno Vernier</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User_talk:Bruno_Vernier&amp;diff=23453"/>
		<updated>2006-07-11T05:24:48Z</updated>

		<summary type="html">&lt;p&gt;Bruno: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Bruno Vernier&lt;br /&gt;
&lt;br /&gt;
* teacher&lt;br /&gt;
&lt;br /&gt;
* school network admin&lt;br /&gt;
&lt;br /&gt;
* Vancouver BC, Canada&lt;/div&gt;</summary>
		<author><name>Bruno</name></author>
	</entry>
</feed>