有几种方法可以在phalcon中实现这种行为。在任何事情之前,我们需要了解视图和视图助手在phalcon中的工作方式。如果您仔细观察,您会注意到,
.volt
和
.phtml
都直接访问
DI
。
例如,在volt中,您可以访问
flash
服务,并通过调用以下方式输出其消息:
{{ flash.output() }}
,它将转换为phtml:
<?php echo $this->flash->output(); ?>
因此,我的解决方案集中在在DI中定义一个volt可以访问的新服务上。在CakePHP中,
postLink()
的语法类似于:
echo $this->Form->postLink()
,而该函数实际上是在名为
FormHelper
的类中定义的。因此,我的解决方案将做同样的事情,定义一个名为
FormHelper
的类,然后将其注入到视图中,名称为
Form
。
1.创建一个
app/helpers/
目录。
2.更新您的
app/config/config.php
文件,添加对我们新目录的引用:
'helpersDir'=> APP_PATH . '/app/helpers/'
3.更新您的
app/config/loader.php
文件,添加
$config->application->helpersDir
到注册的目录中。
4.创建一个名为
app/helpers/FormHelper.php
的新文件。
5.将
以下代码复制粘贴到文件中。
<?php
use Phalcon\Tag;
class FormHelper extends Tag
{
protected $_lastAction = '';
public function dottedNameToBracketNotation($name)
{
$parts=explode('.',$name);
$first = array_shift($parts);
$name=$first . ($parts ? '[' . implode('][', $parts) . ']' : '');
return $name;
}
protected function flatten(array $data, $separator = '.')
{
$result = [];
$stack = [];
$path = null;
reset($data);
while (!empty($data)) {
$key = key($data);
$element = $data[$key];
unset($data[$key]);
if (is_array($element) && !empty($element)) {
if (!empty($data)) {
$stack[] = [$data, $path];
}
$data = $element;
reset($data);
$path .= $key . $separator;
} else {
$result[$path . $key] = $element;
}
if (empty($data) && !empty($stack)) {
list($data, $path) = array_pop($stack);
reset($data);
}
}
return $result;
}
protected function _confirm($message, $okCode, $cancelCode = '', $options = [])
{
$message = json_encode($message);
$confirm = "if (confirm({$message})) { {$okCode} } {$cancelCode}";
if (isset($options['escape']) && $options['escape'] === false) {
$confirm = $this->h($confirm);
}
return $confirm;
}
public function h($text, $double = true, $charset = 'UTF-8')
{
return htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, $charset, $double);
}
protected function _lastAction($url)
{
$action = $url;
$query = parse_url($action, PHP_URL_QUERY);
$query = $query ? '?' . $query : '';
$this->_lastAction = parse_url($action, PHP_URL_PATH) . $query;
}
public function postLink($title, $url = null, array $options = [])
{
$out='';
$options += ['block' => null, 'confirm' => null];
$requestMethod = 'POST';
if (!empty($options['method'])) {
$requestMethod = strtoupper($options['method']);
unset($options['method']);
}
$confirmMessage = $options['confirm'];
unset($options['confirm']);
$formName = str_replace('.', '', uniqid('post_', true));
$formOptions = [
'name' => $formName,
'style' => 'display:none;',
'method' => 'post',
];
if (isset($options['target'])) {
$formOptions['target'] = $options['target'];
unset($options['target']);
}
$formOptions[0]=$url;
$out.=$this->form($formOptions);
$out .= $this->hiddenField(['_method','value' => $requestMethod]);
$fields = [];
if (isset($options['data']) && is_array($options['data'])) {
foreach ($this->flatten($options['data']) as $key => $value) {
$out .= $this->hiddenField([$this->dottedNameToBracketNotation($key),'value' => $value]);
}
unset($options['data']);
}
$out .= $this->endForm();
if ($options['block']) {
if ($options['block'] === true) {
$options['block'] = __FUNCTION__;
}
$out = '';
}
unset($options['block']);
$url = '#';
$onClick = 'document.' . $formName . '.submit();';
if ($confirmMessage) {
$options['onclick'] = $this->_confirm($confirmMessage, $onClick, '', $options);
} else {
$options['onclick'] = $onClick . ' ';
}
$options['onclick'] .= 'event.returnValue = false; return false;';
$options[0]=$url;
$options[1]=$title;
$options[2]=false;
$out .= $this->linkTo($options);
return $out;
}
}
- 编辑您的
app/config/services.php
文件并添加以下内容:
$di->set('Form',function () {
return new FormHelper();
});
如果你愿意,可以将“Form”改为小写字母,两者都可以。我将其大写是为了更接近CakePHP的语法。请注意,当尝试访问服务时,Volt区分大小写,但phtml会将其转换为小写。
- 编辑您要测试代码的模板,例如
app/views/index/test.volt
- 将以下代码复制粘贴到其中:
{{ Form.postLink(' Delete','',['confirm':'Are you sure you want to delete #4?','data':['a':['b','c']]]) }}
Alternatively for phtml, use:
<?php echo $this->form->postLink(' Delete', '', array('confirm' => 'Are you sure you want to delete #4?', 'data' => array('a' => array('b', 'c')))); ?>
运行它,观看它施展其魔力,只需通过地址栏访问
/index/test
来呈现你的index/test.volt模板。(确保你在index控制器中定义了这样一个操作)
至于其他解决方案,你也可以使用
$compiler->addFunction()
,逐个将函数添加到volt中。手册中给出了
$compiler->addFunction('shuffle', 'str_shuffle');
的示例。你可以尝试在DI中覆盖"tag"的factoryDefault,并使用我们已经定义的扩展tag的helper。所以你只需要像这样将它从"form"改为"tag":
$di->set('tag',function () {return new FormHelper();});
但是,正如你所看到的,它不会将
postLink()
函数作为函数使volt可用,你仍然需要将其访问为
tag.postLink()
。而所有的\Phalcon\Tag函数实际上都被硬编码进了volt引擎中。你可以通过查看
\Phalcon\Mvc\View\Engine\Volt\Compiler
类的zephir源代码来清楚地看到这一点,该代码在
这里可用。为了方便起见,并以防链接失效,我在这里发布了一个片段,显示volt中的"tag"函数实际上是硬编码进去的:
if method_exists(className, method) {
let arrayHelpers = this->_arrayHelpers;
if typeof arrayHelpers != "array" {
let arrayHelpers = [
"link_to": true,
"image": true,
"form": true,
"select": true,
"select_static": true,
"submit_button": true,
"radio_field": true,
"check_field": true,
"file_field": true,
"hidden_field": true,
"password_field": true,
"text_area": true,
"text_field": true,
"email_field": true,
"date_field": true,
"tel_field": true,
"numeric_field": true,
"image_input": true
];
let this->_arrayHelpers = arrayHelpers;
}
if isset arrayHelpers[name] {
return "$this->tag->" . method . "(array(" . arguments . "))";
}
return "$this->tag->" . method . "(" . arguments . ")";
}
如果您想通过扩展\Phalcon\Tags
类来添加更多方法,那么就没有这个功能。但是,如volt文档页面所示,可以注册自定义扩展以使用volt。文档给出如下示例:$compiler->addExtension(new PhpFunctionExtension());
其中类的源代码为:
<?php
class PhpFunctionExtension
{
public function compileFunction($name, $arguments)
{
if (function_exists($name)) {
return $name . '('. $arguments . ')';
}
}
}
这将允许您访问任何您想要的函数,而无需手动注册可能需要的每个可能的函数。您可以通过尝试在volt中访问
str_shuffle
来测试,就像我们之前使用
$compiler->addFunction('shuffle', 'str_shuffle');
一样,但这次不需要注册它。
在其他解决方案方面,您还可以尝试将CakePHP和PhalconPHP集成在一起,并尝试从PhalconPHP调用CakePHP的视图助手,但这时您会遇到一个问题,即CakePHP无法理解您在Phalcon中配置的路由器设置。但是,如果您坚定不移,您可以编写所有的路由和配置文件,并在PhalconPHP旁边运行它,但我强烈反对这种拼凑的解决方法。最后,如果您了解该函数的工作原理并且几乎不使用它,您可以直接在HTML中硬编码。老实说,对我来说,CakePHP的逻辑看起来并不那么合理,因为它必须通过插入表单来破坏您的HTML文档,这可能会影响您的布局。我认为,如果我们已经在使用JavaScript,那么使用JavaScript动态生成表单,然后在单击按钮时将其附加到
<body>
并提交我们刚刚动态创建的表单会更有意义。但是,您想要一个CakePHP实现,因此我尽可能接近他们使用的逻辑进行了编码。就支持所有功能(例如
block
)而言,它并不完美,但应该满足大多数需求。
我总是可以修改我的实现,但我认为它很好地演示了如何与Phalcon一起使用,适用于那些从CakePHP迁移的人。