表单¶
概览¶
Phalcon 在Phalcon\Forms命名空间下提供组件,帮助开发人员创建和维护可用于在屏幕上渲染 HTML 元素并对接收的输入进行验证的表单。
<?php
use Phalcon\Forms\Form;
use Phalcon\Forms\Element\Text;
use Phalcon\Forms\Element\Select;
$form = new Form();
$form->add(
    new Text(
        'nameLast'
    )
);
$form->add(
    new Text(
        'nameFirst'
    )
);
$form->add(
    new Select(
        'phoneType',
        [
            1 => 'Home',
            2 => 'Work',
            3 => 'Mobile',
        ]
    )
);
在模板中:
<h1>
    Contacts
</h1>
<form method='post'>
    <p>
        <label>
            Last Name
        </label>
        <?php echo $form->render('nameLast'); ?>
    </p>
    <p>
        <label>
            First Name
        </label>
        <?php echo $form->render('nameFirst'); ?>
    </p>
    <p>
        <label>
            Gender
        </label>
        <?php echo $form->render('phoneType'); ?>
    </p>
    <p>
        <input type='submit' value='Save' />
    </p>
</form>
表单中的每个元素都可以按开发者的需要进行渲染。在内部,Phalcon\Tag被用来生成每个元素正确的 HTML,你可以将额外的 HTML 属性作为第二个参数传入。render():
<p>
    <label>
        Name
    </label>
    <?php 
        echo $form->render(
            'nameFirst', 
            [
                'maxlength'   => 30, 
                'placeholder' => 'First Name',
            ]
        ); ?>
</p>
HTML 属性也可以在元素定义中设置:
<?php
use Phalcon\Forms\Form;
$form = new Form();
$form->add(
    new Text(
        'nameFirst',
        [
            'maxlength'   => 30, 
            'placeholder' => 'First Name',
        ]
    )
);
方法¶
Phalcon\Forms\Form暴露了许多方法,用于帮助设置具有必要元素的表单,以便执行验证、渲染元素等操作。
构造函数。可选地接受一个对象,在内部读取该对象。如果该对象包含与表单中定义的元素名称匹配的属性,则这些元素将用实体对应属性的值填充。实体可以是像entity或甚至是Phalcon\Mvc\Model或甚至是\stdClass第二个参数是userOptions一个可选的用户自定义数据数组。 注意
如果表单有initialize方法存在,构造函数会自动使用相同的参数调用它。
<?php
use Phalcon\Forms\Element\Text;
use Phalcon\Forms\Element\Select;
use Phalcon\Forms\Form;
$form = new Form(
    null,
    [
        'phoneTypes' => [
            1 => 'Home',
            2 => 'Work',
            3 => 'Mobile',
        ],
    ]
);
$form->add(
    new Text(
        'nameLast'
    )
);
$form->add(
    new Text(
        'nameFirst'
    )
);
$options    = $this->getUserOptions();
$phoneTypes = $options['phoneTypes'] ?? [];
$form->add(
    new Select(
        'phoneType',
        $phoneTypes
    )
);
如果entity如果传递的不是一个对象,将会抛出一个Phalcon\Forms\Exception异常。
ElementInterface对象。第二个参数position(如果定义)是我们要添加的目标现有元素的名称。第三个布尔参数type如果设为true新元素将添加到由position定义的元素之前。如果没有设置或设置为null/false,新元素将添加到由position参数。 定义的元素之后。data是键/值组成的数组。这通常是$_POST数组。第二个参数entity是一个实体对象。如果实体对象的属性中包含与表单中定义的元素名称相匹配的属性,则这些元素将用实体对应属性的值填充。实体可以是像data数据库模型这样的对象。Phalcon\Mvc\Model或甚至是\stdClass。第三个参数whitelist是白名单元素的数组。如果whitelist数组中有任一元素的名称与data数组中的元素相同,则该元素将被忽略。 The bind方法接收第一个数组(例如$_POST)和一个实体对象(例如Invoices)。它遍历该数组,如果发现数组中某键存在于表单里,它会对该数组键值应用必要的过滤器(在表单中定义)。之后,它会检查实体对象(Invoices),并将此值分配给任何与数组键匹配的属性。如果有与数组键同名的 setter 方法存在,则会优先调用该方法(即name->setName())。这种方法允许我们快速过滤输入内容,并将过滤后的输入分配给传入的实体对象。
如果表单中没有元素,会抛出一个Phalcon\Forms\Exception异常。
将表单中的所有元素清除为其默认值。如果传入的参数fields是字符串类型,只有该字段会被清空。如果是数组类型,数组中的所有字段都会被清除。最后,如果不传任何参数,所有字段都会被清空。 返回表单中的元素数量 返回迭代器中的当前元素 根据元素的名称返回已添加到表单中的元素。如果表单中未找到该元素,会抛出一个Phalcon\Forms\Exception异常。 返回表单的动作 返回表单的属性集合。返回的对象是Phalcon\Html\Attributes. 返回添加到表单的表单元素 返回与模型相关的实体 从内部过滤数据获取值或调用getValue(name) 返回某个元素的标签。如果表单中未找到该元素,会抛出一个Phalcon\Forms\Exception异常。 返回在验证期间生成的消息 if (false === $form->isValid($_POST)) {
    $messages = $form->getMessages();
    foreach ($messages as $message) {
        echo $message, "<br>";
    }
}
Phalcon\Html\TagFactory对象 返回选项的值(如果存在)。如果该选项不存在,则返回defaultValue参数指定的默认值。 返回元素的选项 返回注册在表单中的验证器对象 从内部相关实体获取值或从默认值获取值 检查表单是否包含一个元素 检查是否为特定元素生成了消息 验证表单。第一个参数是用户提供的数据。这通常是$_POST数组。 第二个可选参数是entity(对象)。如果传入了该参数,组件内部将调用bind()方法,这个方法会:- 遍历传入的data- 检查从data中获取的元素是否存在于entity中(根据名称匹配)- 如果存在,再检查表单的白名单数组。如果该元素出现在白名单里,则不会对其进行更改 - 根据定义的过滤器(如果有的话)对来自data数组的该元素的值进行清理 - 如果存在的话,调用entity上的任何对应的 setter 方法 - 将值分配给entity.
上的同名属性bind()操作结束后,修改后的entity将传入beforeValidation事件(如果启用了事件),然后所有验证器将使用修改后的entity对象中的相关方法手动
注意
数据来验证表单。entity对象将会导致该对象被用户的输入所修改,如上所述。如果你不希望产生这种行为,可以在传递该对象之前克隆它,以保留原始对象的副本。
<?php
use MyApp\Models\Customers;
use Phalcon\Forms\Form;
$customer = Customers::findFirst();
$form = new Form($customer);
if (true === $form->isValid($_POST, $customer)) {
    $customer->save();
}
<label>HTML 标签中。比如可以是 CSS 类。如果该元素在表单中未找到,会抛出一个Phalcon\Forms\Exception异常。 将内部迭代指针移动到下一个位置 渲染表单中的特定项。可选的attributes数组参数可以用来传递要渲染元素所需的附加参数。如果该元素在表单中未找到,会抛出一个Phalcon\Forms\Exception异常。 从表单中移除一个元素 重绕内部迭代器 设置表单的操作 设置与模型相关的实体 设置表单属性集合 设置表单的Phalcon\Html\TagFactory表单的标签 设置表单中的验证对象。 设置表单的用户自定义选项 设置默认白名单 为表单设置用户自定义选项 返回迭代器中的当前元素是否有效 初始化¶
表单可以在表单类之外通过向其中添加元素进行初始化。但是,您可以通过在各自的类中实现表单来复用代码或组织您的表单类:
<?php
use MyApp\Models\PhoneTypes;
use Phalcon\Forms\Element\Select;
use Phalcon\Forms\Element\Text;
use Phalcon\Forms\Form;
class CustomersForm extends Form
{
    public function initialize()
    {
        $this->add(
            new Text(
                'nameLast'
            )
        );
        $this->add(
            new Text(
                'nameFirst'
            )
        );
        $this->add(
            new Select(
                'phoneType',
                PhoneTypes::find(),
                [
                    'emptyText'  => 'Select one...',
                    'emptyValue' => '',
                    'useEmpty'   => true,
                    'using'      => [
                        'typ_id',
                        'typ_name',
                    ],
                ]
            )
        );
    }
}
我们也可以在构造函数中传递一个用户自定义选项的数组,这会提供更多功能。
<?php
use MyApp\Models\Customers;
use Phalcon\Forms\Element\Hidden;
use Phalcon\Forms\Element\Text;
use Phalcon\Forms\Form;
class CustomersForm extends Form
{
    public function initialize(
        Customers $customer,
        array $options
    ) {
        $mode = $options['mode'] ?? 'view';
        if ('edit' === $mode) {
            $this->add(
                new Hidden(
                    'id'
                )
            );
        }
        $this->add(
            new Text(
                'nameLast'
            )
        );
        $this->add(
            new Text(
                'nameFirst'
            )
        );
    }
}
在实例化表单时,您将使用:
<?php
use MyApp\Models\Customers;
$form = new CustomersForm(
    new Customers(),
    [
        'mode' => 'edit',
    ]
);
options数组检查期间initialize方法中进行。代码会检查数组中是否存在mode元素,如果不存在,则默认使用view。如果存在mode是edit,我们将会添加一个Phalcon\Forms\Element\Hidden元素,并将实体的ID放入表单中。通过使用options数组,我们可以创建可重用的表单,并且可以传入表单所需的额外数据。 实体¶
诸如Phalcon\Mvc\Model模型、PHP类,甚至\stdClass对象之类的实体都可以传递给表单,以设置默认值或将表单的值赋给该对象。
<?php
use MyApp\Models\Customers;
use Phalcon\Forms\Form;
$customer = Customers::findFirst();
$form = new Form($customer);
$form->add(
    new Text(
        'nameFirst'
    )
);
$form->add(
    new Text(
        'nameLast'
    )
);
一旦表单渲染完成,如果未给元素分配默认值,则它将使用实体提供的值:
您还可以验证表单并将用户输入的值赋给实体,如下所示:
<?php
use MyApp\Models\Customers;
use Phalcon\Forms\Form;
$customer = Customers::findFirst();
$form = new Form($customer);
$form->bind($_POST, $customer);
if (true === $form->isValid()) {
    $customer->save();
}
在上面的示例中,我们获取了第一个Customer记录。我们将该对象传递给我们的表单,以填充初始值。随后调用bind方法并传入实体和$_POST数组。表单将自动从$_POST中过滤输入值,并将其赋给实体对象(Customers)。如果表单通过了验证,我们就可以保存该对象。
我们也可以使用PHP类作为实体:
<?php
class Preferences
{
    public string $timezone = 'Europe/Amsterdam';
    public string $receiveEmails = 'No';
}
使用此类作为实体,允许表单从中获取默认值:
<?php
$form = new Form(
    new Preferences()
);
$form->add(
    new Select(
        'timezone',
        [
            'America/New_York'  => 'New York',
            'Europe/Amsterdam'  => 'Amsterdam',
            'America/Sao_Paulo' => 'Sao Paulo',
            'Asia/Tokyo'        => 'Tokyo',
        ]
    )
);
$form->add(
    new Select(
        'receiveEmails',
        [
            'Yes' => 'Yes, please!',
            'No'  => 'No, thanks',
        ]
    )
);
实体可以实现优先级高于公共属性的getter方法。这些方法在生成值方面提供了更大的灵活性:
<?php
class Preferences
{
    public string $timezone;
    public string $receiveEmails;
    public function getTimezone(): string
    {
        return 'Europe/Amsterdam';
    }
    public function getReceiveEmails(): string
    {
        return 'No';
    }
}
getReceiveEmails和getTimezone方法将替代receiveEmails和timezone属性被使用。 注意
默认情况下,Phalcon\Forms\Form::bind()会绑定所有表单字段到模型。要仅绑定模型中存在的字段,请将phalcon.form.strict_entity_property_check的值设为1. 
元素¶
Phalcon 提供了一组内建的元素用于表单,所有这些元素都位于Phalcon\Forms\Element命名空间中:
| 名称 | 描述 | 
|---|---|
| Phalcon\Forms\Element\Check | 生成input[type=check]元素 |  
| Phalcon\Forms\Element\Date | 生成input[type=date]元素 |  
| Phalcon\Forms\Element\Email | 生成input[type=email]元素 |  
| Phalcon\Forms\Element\File | 生成input[type=file]元素 |  
| Phalcon\Forms\Element\Hidden | 生成input[type=hidden]元素 |  
| Phalcon\Forms\Element\Numeric | 生成input[type=number]元素 |  
| Phalcon\Forms\Element\Password | 生成input[type=password]元素 |  
| Phalcon\Forms\Element\Radio | 生成radio元素 |  
| Phalcon\Forms\Element\Select | 生成select元素 |  
| Phalcon\Forms\Element\Submit | 生成input[type=submit]元素 |  
| Phalcon\Forms\Element\Text | 生成input[type=text]元素 |  
| Phalcon\Forms\Element\TextArea | 生成textarea元素 |  
这些元素会透明地使用Phalcon\Html\TagFactory组件。
注意
关于HTML元素的更多信息,您可以查看我们的TagFactory 文档
注意
The Phalcon\Forms\Element\Check和Phalcon\Forms\Element\Radio类现在分别使用Phalcon\Html\Helper\Input\Checkbox和Phalcon\Html\Helper\Input\Radio。这些类使用checked和unchecked参数来设置每个控件的状态。如果checked参数与$value相同,则控件将被选中。如果存在unchecked参数,并且$value不等于checked参数。更多
The Phalcon\Forms\Element\Select支持useEmpty选项,以便在可用选项列表中使用空白元素。emptyText和emptyValue是可选参数,分别允许您自定义空白元素的文本和值。
您也可以通过继承Phalcon\Forms\Element\AbstractElement抽象类中的功能。
<?php
use Phalcon\Forms\Element\AbstractElement ;
class MyElement extends AbstractElement
{
    public function render($attributes = null)
    {
        $html = '';// HTML
        return $html;
    }
}
预留名称¶
由于表单的工作方式以及与元素的交互,某些名称是保留的,不能用作元素名称。这些名称包括:
actionattributesdielementsentityeventsmanagermessagesmessagesforlabeltagFactoryuseroptionuseroptionsvalidationvalue
这些名称对应于Form对象中的getter方法,或者来自Di容器的属性。
过滤¶
表单还能够在验证之前过滤数据。您可以在每个元素上设置过滤器:
<?php
use Phalcon\Filter\Filter;
use Phalcon\Forms\Element\Text;
use Phalcon\Forms\Form;
$form = new Form();
$name = new Text('nameLast');
$name->setFilters(
    [
        'string', // Filter::FILTER_STRING
        'trim',   // Filter::FILTER_TRIM
    ]
);
$form->add($name);
$email = new Text('email');
$email->setFilters(
    'email'
);
$form->add($email);
注意
关于过滤器的更多信息,您可以查看我们的 [Filter文档][filter-filter]
验证¶
Phalcon 表单与验证组件集成,提供即时验证功能。每个元素可以设置内置或自定义验证器:
<?php
use Phalcon\Forms\Element\Text;
use Phalcon\Validation\Validator\PresenceOf;
use Phalcon\Validation\Validator\StringLength;
$nameLast = new Text('nameLast');
$nameLast->addValidator(
    new PresenceOf(
        [
            'message' => 'The last name is required',
        ]
    )
);
$nameLast->addValidator(
    new StringLength(
        [
            'min'            => 10,
            'messageMinimum' => 'The last name is too short',
        ]
    )
);
$form->add($nameLast);
$nameFirst = new Text('nameFirst');
$nameFirst->addValidator(
    new StringLength(
        [
            'max'            => 20,
            'messageMaximum' => 'The first name is too long',
            'allowEmpty'     => true,
        ]
    )
);
$form->add($nameFirst);
然后可以根据用户输入的数据验证表单:
<?php
if (false === $form->isValid($_POST)) {
    $messages = $form->getMessages();
    foreach ($messages as $message) {
        echo $message, '<br>';
    }
}
验证器将按照它们注册的顺序执行。
默认情况下,表单中所有元素生成的消息会被合并,因此可以使用单一的foreach来遍历。您也可以获取特定元素的消息:
<?php
$messages = $form->getMessagesFor('nameLast');
foreach ($messages as $message) {
    echo $message, '<br>';
}
空值处理¶
您可以传递选项allowEmpty给任意内置验证器以忽略空值。allowEmpty选项也可以是一个字段名数组。匹配该数组中元素的字段会在其值为空时进行验证。true如果它们具有空值,则会触发验证失败。
<?php
use Phalcon\Forms\Element\Text;
use Phalcon\Filter\Validation\Validator\Regex;
$telephone = new Text('telephone');
$telephone->addValidator(
    new Regex(
        [
            'message'    => 'The telephone is required',
            'pattern'    => '/\+1 [0-9]+/',
            'allowEmpty' => true,
        ]
    )
);
$form->add($telephone);
验证失败时停止¶
如果希望在第一次验证失败时立即停止验证链,需要传递cancelOnFail选项。这在某个元素附加了多个验证器且您希望在第一个验证失败时通知用户而不再继续检查并添加更多错误信息的情况下特别有用。
<?php
use Phalcon\Forms\Form;
use Phalcon\Forms\Element\Text;
$form = new Form();
$lastName = new Text('lastName');
$lastName->addValidators(
    [
        new PresenceOf(
            [
                'message'      => 'Last Name is required',
                'cancelOnFail' => true,
            ]
        ),
        new StringLength(
            [
                'min'            => 3,
                'max'            => 255,
                'messageMaximum' => 'Last Name cannot be more than 255 characters',
                'messageMinimum' => 'Last Name cannot be less than 3 characters',
            ]
        ),
    ]
);
// Empty data
$form->isValid($_POST);
// Get the messages from the form
$messages = $form->getMessages();
echo count($messages); 
// 1 message
echo $messages[0]->getMessage();
// 'Last Name is required'
渲染¶
您可以非常灵活地渲染表单,下面的示例展示了如何使用标准流程渲染每个元素:
<form method='post'>
    <?php
        // Traverse the form
        foreach ($form as $element) {
            // Get any generated messages for the current element
            $messages = $form->getMessagesFor(
                $element->getName()
            );
            if (count($messages)) {
                // Print each element
                echo '<div class="messages">';
                foreach ($messages as $message) {
                    echo $message;
                }
                echo '</div>';
            }
            echo '<p>';
            echo '<label for="' . 
                    $element->getName() .
                 '">' .
                 $element->getLabel() .
                 '</label>' 
             ;
            echo $element;
            echo '</p>';
        }
    ?>
    <input type='submit' value='Send' />
</form>
或者在您的表单类中复用逻辑:
<?php
use Phalcon\Forms\Form;
class ContactForm extends Form
{
    public function initialize()
    {
        // ...
    }
    public function renderDecorated($name)
    {
        $element  = $this->get($name);
        $messages = $this->getMessagesFor(
            $element->getName()
        );
        if (count($messages)) {
            echo "<div class='messages'>";
            foreach ($messages as $message) {
                echo $this->flash->error($message);
            }
            echo '</div>';
        }
        echo '<p>';
        echo '<label for="' .
                $element->getName() .
             '">' .
             $element->getLabel() .
             '</label>';
        echo $element;
        echo '</p>';
    }
}
在视图中:
事件¶
当表单以类的形式实现时,回调函数:beforeValidation()和afterValidation()方法可以在表单的类中实现,以执行预验证和后验证:
<?php
use Phalcon\Forms\Form;
class ContactForm extends Form
{
    public function beforeValidation()
    {
    }
}
管理器¶
此组件提供了Phalcon\Forms\Manager开发者可以使用它来注册表单并通过服务定位器访问这些表单:
<?php
use Phalcon\Di\FactoryDefault;
use Phalcon\Forms\Manager;
$container = new FactoryDefault();
$container->set(
    'forms',
    function () {
        return new Manager();
    }
);
表单被添加到表单管理器中,并通过唯一名称进行引用:
通过唯一的名称,可以在应用程序的任何部分访问表单:
如果在管理器中找不到某个表单,则会抛出一个Phalcon\Forms\Exception异常。
异常¶
在Phalcon\Forms异常命名空间将被Phalcon\Forms\Exception。您可以使用这些异常来选择性地捕获仅从此组件抛出的异常。
<?php
use Phalcon\Forms\Exception;
use Phalcon\Forms\Manager;
use Phalcon\Mvc\Controller;
/**
 * @property Manager $forms
 */
class IndexController extends Controller
{
    public function index()
    {
        try {
            $this->forms->get('unknown-form');
        } catch (Exception $ex) {
            echo $ex->getMessage();
        }
    }
}
依赖注入¶
Phalcon\Forms\Form扩展了Phalcon\Di\Injectable,因此如果需要的话,您可以访问应用程序的服务:
<?php
use Phalcon\Forms\Element\Hidden;
use Phalcon\Forms\Element\Text;
use Phalcon\Forms\Form;
use Phalcon\Security;
/**
 * @property Security $security
 */
class ContactForm extends Form
{
    public function initialize()
    {
        // Set the same form as the entity
        $this->setEntity($this);
        // Add a text element to capture the 'email'
        $this->add(
            new Text(
                'email'
            )
        );
        // Add a text element to put a hidden CSRF
        $this->add(
            new Hidden(
                'csrf'
            )
        );
    }
    public function getCsrf()
    {
        return $this->security->getToken();
    }
}
额外资源¶
- Vökuró是一个使用表单构建器创建和管理表单的示例应用程序。