Moodle【导出器】是接收数据并将其序列化为一个简单的预定义结构的类。它们确保输出的数据格式统一,易于维护。它们也用于生成外部函数的签名(参数和返回值)
外部函数定义在moodle/lib/externallib.php
在处理外部函数(Ajax,Web服务,...)和渲染方法时,我们经常遇到同一个对象被使用并从多个位置输出的情况。当这种情况出现时,我们的对象的代码会被重复序列化,或者变得不一致我们用【导出器】来解决这个问题,它清楚地定义了它输出的数据,并包含将输入数据转换成输出数据的逻辑,它可以自动生成外部函数所需的结构,如果需要导出新属性,则只需将其添加到导出器类中,并且【导出器】的所有用法都会自动继承此添加的属性
基类被定义在:moodle33\lib\classes\external\exporter.php
protected $related 用于避免DB查询的相关对象的列表 protected $data 这个输出器的数据 (值:stdClass|array) //保存持久对象和相关对象 public function __construct($data, $related = array()) //函数以适合于mustache模板的格式导出渲染器数据。这意味着原始记录是在to_record中生成的,但是,所有的字符串都是通过外部格式文本(或外部格式字符串)正确地传递的 final public function export(renderer_base $output) //函数猜测正确的上下文,返回到系统上下文 protected function get_context() //在导出时获得附加的值 protected function get_other_values(renderer_base $output) //获取此导出器的读取属性定义。 Read属性将模型(persistent或stdClass)的默认属性与{@link self :: define_other_properties()}定义的属性相结合 final public static function read_properties_definition() //获取用于创建和更新结构的导出器的属性定义,读取结构由以下方式返回:{@link self :: read_properties_definition()} final public static function properties_definition() //返回仅用于显示的附加属性列表 protected static function define_other_properties() //返回属性列表。返回实例化导出器时所需的属性列表,以及它将同时导出的属性列表 protected static function define_properties() //返回与此持久性相关的对象列表,只有在这里列出的对象可以缓存在这个对象中 protected static function define_related() //获取上下文结构 final protected static function get_context_structure() //获取格式字段名 final protected static function get_format_field($definitions, $property) //得到结构格式 final protected static function get_format_structure($property, $definition, $required = VALUE_REQUIRED) //返回创建的结构 final public static function get_create_structure() //返回读取的结构
final public static function get_read_structure() //从一组属性(递归)返回读取结构
final protected static function get_read_structure_from_properties($properties, $required = VALUE_REQUIRED, $default = null) //返回更新的结构
final public static function get_update_structure()
定义属性
define_properties()方法返回实例化导出器时所需的属性列表,以及它将同时导出的属性列表
protected static function define_properties() {
return array(
'id' => array(
'type' => PARAM_INT
),
'username' => array(
'type' => PARAM_ALPHANUMEXT
),
);
}
这些属性将允许我们为外部函数生成一个创建或更新结构,稍后再介绍。
哦,如果你使用持久性,你不需要这样做:瞧瞧这个-导出和持久。
如果你有一个持续的,你想要导出输出,所有的工作已经完成了。如果扩展class core\external\persistent_exporter,并添加方法define_class(),则所有持久性属性将自动添加到导出器
class status_exporter extends \core\external\persistent_exporter { /**
* Returns the specific class the persistent should be an instance of.
*
* @return string
*/
protected static function define_class() {
return \some\namespace\status::class; // PHP 5.5+ only
} }
属性特性
每个属性都使用以下属性进行配置:
- type
- 唯一的强制性属性。它必须是许多PARAM_ *常量之一,或者是一组属性。
- default
- 未提供值时的默认值。未指定时,需要一个值。
- null
- 无论是常量NULL_ALLOWED还是NULL_NOT_ALLOWED,都会告知是否接受空值。这个默认为NULL_NOT_ALLOWED。
- optional
- 该财产是否可以完全省略。默认为false。
- multiple
- 这个属性是否会有更多的条目。默认为false。
尽管这不是一个规则,但建议标准属性(通过与附加属性相反)仅使用上面的type属性,并且只使用PARAM_ *常量。
附加属性:
附加属性的列表被视为额外的属性,不需要给导出器输出它们。它们是从提供的数据(以及相关的对象,后面会详细介绍)中动态生成的。例如,如果我们希望我们的导出器将URL提供给用户的配置文件,我们不需要开发者事先传递它,我们可以基于已经提供的ID生成它。
附加属性只包含在对象的读取结构(get_read_structure和read_properties_definition)中,因为它们是动态生成的。不需要也不需要创建或更新对象
/**
* Return the list of additional properties. * @return array
*/
protected static function define_other_properties() {
return array(
'profileurl' => array(
'type' => PARAM_URL
),
'statuses' => array(
'type' => status_exporter::read_properties_definition(),
'multiple' => true,
'optional' => true
),
);
}
上面的代码片段定义了我们将始终在属性profileurl下导出一个URL ,并且我们将导出或不导出status_exporters的列表。正如你所看到的,这个类型可以使用另一个导出器的读取属性,它允许导出器被嵌套。
如果您定义了其他属性,则还必须添加逻辑来导出它们。这是通过将get_other_values(renderer_base $ output)方法添加到导出器来完成的。这是一个我们忽略状态的例子,因为它们是可选的
/**
* Get the additional values to inject while exporting.
*
* @param renderer_base $output The renderer.
* @return array Keys are the property names, values are their values.
*/
protected function get_other_values(renderer_base $output) {
$profileurl = new moodle_url('/user/profile.php', ['id' => $this->data->id]);
return [
'profileurl' => $profileurl->out(false)
];
}
重要说明:其他属性不能覆盖标准属性,所以请确保名称不冲突。
相关的对象
有时我们需要出口商内部的更多信息来导出。这可能与上下文一样简单,但在导出其他属性时也可以使用其他对象。尽可能避免调用API,或者在导出时查询数据库。出口商可以在循环中使用,因此后续查询可能会显着影响性能,因此需要相关的对象。
相关对象需要在出口商内部进行定义,即确保它们总是被提供并且是正确的类型。在一些情况下,通常当相关对象不存在时,它们可以被标记为可选的。例如,如果我们有一个问题导出器,可选的相关对象可能是同行评审者,因为我们并不总是有同行评审者。
使用方法protected static define_related(),如下所示。键是相关对象的任意名称,值是该类的完全限定名称。班级名称后面可以跟[]和/或?分别表示这些对象的列表,以及一个可选的相关内容
/**
* Returns a list of objects that are related.
*
* @return array
*/
protected static function define_related() {
return array(
'context' => 'context', // Must be an instance of context.
'statuses' => 'some\\namespace\\status[]', // Must be an array of status instances.
'mother' => 'family\\mother?', // Can be a mother instance, or null.
'brothers' => 'family\\brother[]?', // Can be null, or an array of brother instances.
);
}
实例化时,我们将相关的对象给导出者,如下所示:
$data = (object) ['id' => 123, 'username' => 'batman'];
$relateds = [
'context' => context_system::instance(),
'statuses' => [
new some\namespace\status('Hello'),
new some\namespace\status('World!'),
],
'mother' => null,
'brothers' => null
];
$ue = new user_exporter($data, $relateds);
使用导出器
一旦我们有一个简约的出口商设置,这里是如何使用它。
class user_exporter extends core\external\exporter { /**
* Return the list of properties.
*
* @return array
*/
protected static function define_properties() {
return array(
'id' => array(
'type' => PARAM_INT
),
'username' => array(
'type' => PARAM_ALPHANUMEXT
),
);
} }
导出数据
$data = (object) ['id' => 123, 'username' => 'batman']; // The only time we can give data to our exporter is when instantiating it.
$ue = new user_exporter($data); // To export, we must pass the reference to a renderer.
$data = $ue->export($OUTPUT);
如果我们打印$data的内容,我们将获得这个:
stdClass对象
(
[id] => 123
[username] =>蝙蝠侠
)
现在,我同意这并不令人印象深刻。但是等到你阅读了关于自动格式化文本①,以及在外部函数中的使用②。
①遵守文本格式规则: 如果我们在导出过程中必须通过$OUTPUT,那是因为我们正在为您自动处理文本格式。记住函数format_text()和format_string()?它们被用来对用户通常提交的内容应用过滤器,还可以将其从一些给定的格式转换为HTML。
导出时,导出器会查看所有属性的类型。当它找到PARAM_TEXT类型的属性时,它将使用format_string()。但是,如果使用PARAM_RAW找到一个属性,并且存在另一个同名的属性,但以format结尾,则将使用format_text()。如果您不熟悉在Moodle中输出文本的规则,请参阅输出函数和最常用的PARAM_ *类型
'description' => array(
'type' => PARAM_RAW,
),
'descriptionformat' => array(
'type' => PARAM_INT,
),
添加上面的两个属性,让我们看看当用户的描述是以Markdown格式,然后导出它时会发生什么
$data = (object) [
'id' => 123,
'username' => 'batman',
'description' => 'Hello __world__!',
'descriptionformat' => FORMAT_MARKDOWN
];
$ue = new user_exporter($data, ['context' => context_user::instance(123)]);
$data = $ue->export($OUTPUT);
不出所料,这就是它出来的结果:
stdClass Object
(
[id] => 123
[username] => 蝙蝠侠
[description] => <p>Hello <strong>world</strong>!</p>
[descriptionformat] => 1 // Corresponds to FORMAT_HTML.
)
Psst ...我们已经在上面作弊了。你有没有注意到我们已经通过了导出器的上下文?我们通过了一个相关的对象。
②外部函数里
假设我们有一个外部函数get_users,它返回一个用户列表。现在我们只想导出用户ID和他们的用户名,所以我们将使用我们的导出器。我们要求我们的导出器为我们创造外部结构:
public static function get_users_returns() {
return external_multiple_structure(
user_exporter::get_read_structure()
);
}
现在这样做了,我们必须使用我们的导出器来导出我们用户的数据
public static function get_users() {
global $DB, $PAGE;
$output = $PAGE->get_renderer('core');
$users = $DB->get_records('user', null, '', 'id, username', 0, 10); // Get 10 users.
$result = [];
foreach ($users as $userdata) {
$exporter = new user_exporter($userdata);
$result[] = $exporter->export($output);
}
return $result;
}
最后,如果您有另一个外部函数来创建用户,则可以使用导出器来获取传入数据的结构。如果你希望你的外部函数需要更多的信息来创建你的用户,这将有助于你的需求的增长。以下表示外部函数需要字段“username”在通过键“user”中传递。在创建和更新结构,包括所有的标准性能,而不是其他的附加属性。请注意,创建结构不包括id属性。使用user_exporter :: get_update_structure()是否更新用户,从而接收ID。
public static function create_user_parameters() {
return new external_function_parameters([
'user' => user_exporter::get_create_structure()
]);
} public static function create_user($user) {
// Mandatory parameters validation.
$params = self::validate_parameters(self::create_user_parameters(), ['user' => $user]);
$user = $params['user'];
...
}
重要提示:在参数中使用时,导出器的结构必须总是被包含在另一个关键字的下方,高于我们所使用的用户。否则这将不会灵活,并且可能不会为某些Web服务协议生成有效的结构