英文:
How to call a PHP function from Moodle mustache template?
问题
我正在尝试通过下拉菜单为 Moodle 区块插件内的课程构建一个筛选功能。根据我所了解的,应该按以下方式进行:
- 在模板中使用 select 元素
- 在 js 文件中处理 select 元素的 onchange 事件
- 从事件处理程序调用带有查询参数的 PHP 文件
- 在 PHP 函数中使用查询参数
到目前为止,我已成功实现了这个方法的前两个步骤,但是步骤 3 和 4 还没有完成。
当使用 .load()
函数调用带有查询参数的 PHP 文件时,它不会重定向到该页面,但直接在浏览器中打开 URL 时却有效。
为了筛选课程,我编写了一个 controller.php
文件来处理数据库请求。控制器在我的插件基础文件中是必需的。但是当我调用基础文件时,它显示它缺少它继承的 block_base
类。
现在我想知道我是否犯了错误,或者我是否选择了错误的方法。任何帮助都将不胜感激。
我使用了 这个答案 来帮助实现这个功能。
以下是我的代码:
filter.mustache
<select name="language-select" data-action="language-select">
{{#languages}}
<option value="{{.}}">{{.}}</option>
{{/languages}}
</select>
filter.js
window.onload = init;
function init() {
/* 加载基础插件文件并将语言作为查询参数发送 */
$('[data-action="language-select"]').change(function() {
console.log($('[data-action="language-select"]').val());
$('[data-action="language-select"]').load('/blocks/course_overview_page/block_course_overview_page.php?language=' + $('[data-action="language-select"]').val());
});
}
block_course_overview_page.php
require_once(__DIR__ . '/db/controller.php');
require_once($CFG->libdir . '/pagelib.php');
class block_course_overview_page extends block_base {
public function get_content() {
global $CFG, $OUTPUT, $PAGE;
if ($this->content !== null) {
return $this->content;
}
$this->content = new stdClass();
/* 加载处理事件的 js 脚本 */
$PAGE->requires->js(new moodle_url('/blocks/course_overview_page/js/filter.js'));
$languageoptions = get_languages();
/* 从查询参数获取语言 */
$selectedlanguage = optional_param('language', 0, PARAM_INT);
if ($selectedlanguage) {
get_filtered_courses($selectedlanguage);
}
/* 数据对象用于包装模板内迭代的课程列表 */
$data = (object)[
'courses' => array_values(get_all_courses_for_overview()),
'languages' => $languageoptions
];
/* 渲染模板并传递数据 */
$this->content->text = $OUTPUT->render_from_template('block_course_overview_page/cards', $data);
return $this->content;
}
}
英文:
I am trying to build a filter function via dropdown menus for courses inside a Moodle block plugin. From what I've learned, the way to go would be to
- Use select element in template
- Handle select element's onchange event in js file
- Call PHP file with query param from event handler
- Use query param in PHP function
So far I have succeeded to implement this approach apart from step 3 and 4.
When calling the PHP file with the load function .load('/blocks/course_overview_page/block_course_overview_page.php?language=' + $('[data-action="language-select"]').val())
, it does not redirect to that page, but it works when I directly open the URL in the browser.
To filter the courses I have written a controller.php file to handle database requests. The controller is required in my plugin base file. But when I call the base file it displays the error that it's missing the block_base class that it extends from.
Now I wonder if I made a mistake or if I even chose the wrong approach. Any help is appreciated.
I used this answer to help implement this.
Here is my code:
filter.mustache
<select name="language-select" data-action="language-select">
{{#languages}}
<option value="{{.}}">{{.}}</option>
{{/languages}}
</select>
filter.js
window.onload = init;
function init() {
/* load base plugin file and send language as query param */
$('[data-action="language-select"]').change(function() {
console.log($('[data-action="language-select"]').val());
$('[data-action="language-select"]').load('/blocks/course_overview_page/block_course_overview_page.php?language=' + $('[data-action="language-select"]').val());
});
}
block_course_overview_page.php
require_once(__DIR__ . '/db/controller.php');
require_once($CFG->libdir . '/pagelib.php');
class block_course_overview_page extends block_base {
public function get_content() {
global $CFG, $OUTPUT, $PAGE;
if ($this->content !== null) {
return $this->content;
}
$this->content = new stdClass();
/* load js script to handle events */
$PAGE->requires->js(new moodle_url('/blocks/course_overview_page/js/filter.js'));
$languageoptions = get_languages();
/* get language from query params */
$selectedlanguage = optional_param('language', 0, PARAM_INT);
if ($selectedlanguage) {
get_filtered_courses($selectedlanguage);
}
/* data object is necessary to wrap courses list for iteration inside template */
$data = (object)[
'courses' => array_values(get_all_courses_for_overview()),
'languages' => $languageoptions
];
/* render template and pass data */
$this->content->text = $OUTPUT->render_from_template('block_course_overview_page/cards', $data);
return $this->content;
}
答案1
得分: 1
使用 Moodle Fragments API 通过 JS 调用 Mustache 中的 PHP 函数。
有关 Fragments 的更多信息,请阅读官方文档。
https://docs.moodle.org/dev/Fragment
以下是在 Moodle 中从 Mustache 调用 PHP 函数的示例。
- 在你的组件/插件的 lib.php 文件中创建一个 fragment 输出函数
替换你的组件和函数名称。
function YOUR_COMPONENT_output_fragment_FUNCTIONNAME() {
// 你的函数代码在这里。
}
在你的情况下,应该是
function block_course_overview_output_fragment_filter($args) {
// 你的代码在这里。
$language = $args['language'];
}
- 从你的 mustache 文件中调用 fragment。在 filter.js 中包含你的 js 代码。
从 js 或 mustache 中调用 fragment。
Fragment.loadFragment("ComponentName", "PHPFunctionName", ContextID, args);
将以下代码添加到你的 mustache 文件的底部。
{{#js}}
require(['jquery', 'core/fragment'], function($, Fragment) {
window.onload = init;
function init() {
/* 加载基础插件文件并将语言作为查询参数发送 */
$('[data-action="language-select"]').change(function() {
console.log($('[data-action="language-select"]').val());
// 在模板渲染期间传递系统上下文 ID。
var contextid = "{{contextid}}";
var args = {
"language": $('[data-action="language-select"]').val()
};
Fragment.loadFragment('block_course_overview', 'filter', contextid, args).then((html, js) => {
// 你可以在变量 "html" 中获取返回的数据
$('[data-action="language-select"]').html(html);
})
});
}
})
{{/js}}
英文:
Use the Moodle Fragments API to call the php function from Mustache using JS.
For more about fragments. Please read the official documention.
https://docs.moodle.org/dev/Fragment
Here the example to call PHP function from Mustache in Moodle.
- Create a fragment output function in your component/plugin lib.php
Replace your component and function name.
function YOUR_COMPONENT_output_fragment_FUNCTIONNAME() {
// Your function code goes here.
}
In your case, it should be
function block_course_overview_output_fragment_filter($args) {
// Your code goes here.
$language = $args['language];
}
- Call the fragment from your mustache. Included your js code from filter.js.
Fragment call from js or mustache.
Fragment.loadFragment("ComponentName", "PHPFunctionName", ContextID, args);
Add the below code to the bottom of your mustache file.
{{#js}}
require(['jquery', 'core/fragment'], function($, Fragment) {
window.onload = init;
function init() {
/* load base plugin file and send language as query param */
$('[data-action="language-select"]').change(function() {
console.log($('[data-action="language-select"]').val());
// Pass the system context id during the template render.
var contextid = "{{contextid}}";
var args = {
"language": $('[data-action="language-select"]').val()
};
Fragment.loadFragment('block_course_overview', 'filter', contextid, args).then((html, js) => {
// You can get the return data in variable "html"
$('[data-action="language-select"]').html(html);
})
});
}
})
{{/js}}
答案2
得分: 0
以下是已翻译好的部分:
-
当前首选的方法(大多数时候核心使用的方法)是:
-
为您的插件创建一个 Web 服务(或者如果有合适的核心 Web 服务,则使用它)。
-
在您的插件 JavaScript 中(最好使用 ES6 语法编写,不使用 jQuery),导入 core/ajax 和 core/templates。例如:
import Templates from "core/templates";
import Ajax from "core/ajax";
- 从 JavaScript 调用您的服务并对结果执行一些操作(例如为视图渲染模板):
const request = {
methodname: 'name_of_the_web_service',
args: { some: params, go: here }
};
Ajax.call([request])[0]
.then((result) => {
return Templates.render('your_plugin/your_template_name', result);
})
.then(({ html, js }) => {
// 对已渲染的 HTML 和 JavaScript 执行一些操作。
});
请注意,代码中的引号已根据需要进行了修复,以使其保持语法正确。
英文:
The preferred way to do this currently (and the way core does this most of the time):
-
Create a web service for your plugin (or use an existing core web service if one is suitable).
-
In your plugin js (which ideally would be written using ES6 syntax and would not be using JQuery), import core/ajax and core/templates. E.g.
import Templates from "core/templates"; import Ajax from "core/ajax";
-
Call your service from the JS and do something with the result (like render a template for your view):
const request = { methodname: 'name_of_the_web_service', args: {some: params, go: here} }; Ajax.call([request])[0] .then((result) => { return Templates.render('your_plugin/your_template_name', result); .then(({html, js}) => { // Do something with the rendered html and js. }); });
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论