Phalcon PHP如何在JavaScript确认对话框中发布链接

8
我正在开发一个CRUD系统,使用的是Phalcon PHP(版本1.3.4)。
我的目标是创建一个链接(删除行),在单击时要求确认(JavaScript确认框),然后转到该链接(请求类型POST)。
所以假设用户点击了“删除行”按钮。
  1. JavaScript确认:“确定要删除此行吗?”
  2. 用户点击“是”
  3. 网页进行POST到“/users/delete/1”
我知道CakePHP有一个函数(FormHelper :: postLink())可以完全做到这一点。
我想知道Phalcon PHP是否也有类似的函数。
2个回答

5
我看到实现你想要的有三种可能性。一种是在Volt模板中创建,第二种是向你的View添加函数。第三种方法最接近于我理解的你的愿望,即扩展Phalcon标签助手,这是我在这里描述的部分。
Phalcon有自己的标签助手,允许您轻松创建某些元素。 postLink不是其中实现的部分,但您可以轻松实现它。在我的示例中,我有一个名为Application的命名空间,其中包含一个从\Phalcon\Tag继承的Tag类。这是本教程的基础。
// Tag.php

namespace Application;

class Tag extends \Phalcon\Tag
{

    static public function postLink() {
        return '<strong>TEST TAG</strong>';
    }
}

为了强制Phalcon DI使用此类,需要通过手动声明它作为新的DI服务来覆盖其标准声明:
// services.php
$di['tag'] = function() {
    return new \Application\Tag();
};

您可以通过在Volt模板中键入{{ tag.postLink() }}或在phtml模板中使用$this->tag->postLink()来测试其是否正常工作。

现在,您可以填写Tag::postLink()方法的HTML和所需参数:

namespace Application;

class Tag extends \Phalcon\Tag
{

    static $forms = [];

    static public function postLink($title, $url, $options = array()) {

        // random & unique form ID
        while ($randId = 'f_' . mt_rand(9000, 999999)) {
            if (!isset(self::$forms[$randId])) {
                self::$forms[$randId] = true;
                break;
            }
        }

        // dialog message
        $dialogMessage = isset($options['message']) && $options['message'] ?  $options['message'] : 'Are you sure you want to go on?';


        $html = <<<HTML

            <form action="{$url}" method="post" id="{$randId}">
                <!-- maybe not necessary part -->
                <input type="hidden" name="confirmed" value="1" />
            </form>
            <a href="#" onclick="javascript: confirm('{$dialogMessage}') ? document.forms['{$randId}'].submit() : false;">{$title}</a>

HTML;

        return $html;
    }
}

现在你可以像这样运行它:
{{ tag.postLink('delete', '/users/delete/1') }}

{% set formOptions = ['message' : 'Are you sure you want to delete user Kialia Kuliambro?'] %}
{{ tag.postLink('delete', '/users/delete/1', formOptions) }}

{{ tag.postLink('delete', '/users/delete/1', ['message' : 'Are you sure you want to delete user Kialia Kuliambro?']) }}

享受扩展的乐趣吧 :)

4
有几种方法可以在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;//Router::url($url, true);
        $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();
        //This is currently unsupported
        if ($options['block']) {
            if ($options['block'] === true) {
                $options['block'] = __FUNCTION__;
            }
            //$this->_View->append($options['block'], $out);
            $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;

    }

}
  1. 编辑您的 app/config/services.php 文件并添加以下内容:
$di->set('Form',function () {
return new FormHelper();
});

如果你愿意,可以将“Form”改为小写字母,两者都可以。我将其大写是为了更接近CakePHP的语法。请注意,当尝试访问服务时,Volt区分大小写,但phtml会将其转换为小写。

  1. 编辑您要测试代码的模板,例如app/views/index/test.volt
  2. 将以下代码复制粘贴到其中:
{{ 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
{
    /**
     * This method is called on any attempt to compile a function call
     */
    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迁移的人。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接