跳转到内容

表单


概览

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暴露了许多方法,用于帮助设置具有必要元素的表单,以便执行验证、渲染元素等操作。

public function __construct(
    mixed $entity = null, 
    array $userOptions = []
)
构造函数。可选地接受一个对象,在内部读取该对象。如果该对象包含与表单中定义的元素名称匹配的属性,则这些元素将用实体对应属性的值填充。实体可以是像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异常。

public function add(
    ElementInterface $element, 
    string $position = null, 
    bool $type = null
): Form
添加一个元素到表单中。第一个参数是一个ElementInterface对象。第二个参数position(如果定义)是我们要添加的目标现有元素的名称。第三个布尔参数type如果设为true新元素将添加到由position定义的元素之前。如果没有设置或设置为null/false,新元素将添加到由position参数。

public function bind(
    array $data, 
    mixed $entity, 
    array $whitelist = []
): Form
定义的元素之后。data是键/值组成的数组。这通常是$_POST数组。第二个参数entity是一个实体对象。如果实体对象的属性中包含与表单中定义的元素名称相匹配的属性,则这些元素将用实体对应属性的值填充。实体可以是像data数据库模型这样的对象。Phalcon\Mvc\Model或甚至是\stdClass。第三个参数whitelist是白名单元素的数组。如果whitelist数组中有任一元素的名称与data数组中的元素相同,则该元素将被忽略。

The bind方法接收第一个数组(例如$_POST)和一个实体对象(例如Invoices)。它遍历该数组,如果发现数组中某键存在于表单里,它会对该数组键值应用必要的过滤器(在表单中定义)。之后,它会检查实体对象(Invoices),并将此值分配给任何与数组键匹配的属性。如果有与数组键同名的 setter 方法存在,则会优先调用该方法(即name->setName())。这种方法允许我们快速过滤输入内容,并将过滤后的输入分配给传入的实体对象。

<?php

$form->bind($_POST, $customer);

if (true === $form->isValid()) {
    $customer->save();
}

如果表单中没有元素,会抛出一个Phalcon\Forms\Exception异常。

public function clear(mixed $fields = null): Form
将表单中的所有元素清除为其默认值。如果传入的参数fields是字符串类型,只有该字段会被清空。如果是数组类型,数组中的所有字段都会被清除。最后,如果不传任何参数,所有字段都会被清空。

public function count(): int
返回表单中的元素数量

public function current(): ElementInterface | bool
返回迭代器中的当前元素

public function get(string $name): ElementInterface
根据元素的名称返回已添加到表单中的元素。如果表单中未找到该元素,会抛出一个Phalcon\Forms\Exception异常。

public function getAction(): string
返回表单的动作

public function getAttributes(): Attributes
返回表单的属性集合。返回的对象是Phalcon\Html\Attributes.

public function getElements(): ElementInterface[]
返回添加到表单的表单元素

public function getEntity()
返回与模型相关的实体

public function getFilteredValue(string $name): mixed | null
从内部过滤数据获取值或调用getValue(name)

public function getLabel(string $name): string
返回某个元素的标签。如果表单中未找到该元素,会抛出一个Phalcon\Forms\Exception异常。

public function getMessages(): Messages | array
返回在验证期间生成的消息

if (false === $form->isValid($_POST)) {
    $messages = $form->getMessages();
    foreach ($messages as $message) {
        echo $message, "<br>";
    }
}

public function getMessagesFor(string $name): Messages
返回为特定元素生成的消息

public function getTagFactory(): TagFactory | null
返回Phalcon\Html\TagFactory对象

public function getUserOption(
    string option, 
    mixed defaultValue = null
): mixed
返回选项的值(如果存在)。如果该选项不存在,则返回defaultValue参数指定的默认值。

public function getUserOptions(): array
返回元素的选项

public function getValidation(): ValidationInterface
返回注册在表单中的验证器对象

public function getValue(string $name): mixed | null
从内部相关实体获取值或从默认值获取值

public function has(string $name): bool
检查表单是否包含一个元素

public function hasMessagesFor(string $name): bool
检查是否为特定元素生成了消息

public function isValid(
    array $data = null, 
    object $entity = null,
    array $whitelist = []
): bool
验证表单。第一个参数是用户提供的数据。这通常是$_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();
}

public function key(): int
返回迭代器中的当前位置/键

public function label(
    string $name, 
    array $attributes = null
): string
生成表单中添加的某个元素的带有 HTML 的标签。第一个参数是元素的名称,第二个参数则是包含可选参数的数组,这些参数需要被添加到<label>HTML 标签中。比如可以是 CSS 类。如果该元素在表单中未找到,会抛出一个Phalcon\Forms\Exception异常。

public function next(): void
将内部迭代指针移动到下一个位置

public function render(
    string $name, 
    array $attributes = []
): string
渲染表单中的特定项。可选的attributes数组参数可以用来传递要渲染元素所需的附加参数。如果该元素在表单中未找到,会抛出一个Phalcon\Forms\Exception异常。

public function remove(string $name): bool
从表单中移除一个元素

public function rewind(): void
重绕内部迭代器

public function setAction(string $action): Form
设置表单的操作

public function setEntity(object $entity): Form
设置与模型相关的实体

public function setAttributes(
    Attributes> $attributes
): AttributesInterface
设置表单属性集合

public function setTagFactory(TagFactory $tagFactory): Form
设置表单的Phalcon\Html\TagFactory表单的标签

public function setValidation(
    ValidationInterface $validation
);
设置表单中的验证对象。

public function setUserOption(
    string $option, 
    mixed $value
): Form
设置表单的用户自定义选项

public function setWhitelist(array $whitelist): Form
设置默认白名单

public function setUserOptions(array $options): Form
为表单设置用户自定义选项

public function valid(): bool
返回迭代器中的当前元素是否有效

初始化

表单可以在表单类之外通过向其中添加元素进行初始化。但是,您可以通过在各自的类中实现表单来复用代码或组织您的表单类:

<?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。如果存在modeedit,我们将会添加一个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 echo $form->render('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';
    }
}
对于上面的实体类,getReceiveEmailsgetTimezone方法将替代receiveEmailstimezone属性被使用。

注意

默认情况下,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\CheckPhalcon\Forms\Element\Radio类现在分别使用Phalcon\Html\Helper\Input\CheckboxPhalcon\Html\Helper\Input\Radio。这些类使用checkedunchecked参数来设置每个控件的状态。如果checked参数与$value相同,则控件将被选中。如果存在unchecked参数,并且$value不等于checked参数。更多

The Phalcon\Forms\Element\Select支持useEmpty选项,以便在可用选项列表中使用空白元素。emptyTextemptyValue是可选参数,分别允许您自定义空白元素的文本和值。

您也可以通过继承Phalcon\Forms\Element\AbstractElement抽象类中的功能。

<?php

use Phalcon\Forms\Element\AbstractElement ;

class MyElement extends AbstractElement
{
    public function render($attributes = null)
    {
        $html = '';// HTML

        return $html;
    }
}

预留名称

由于表单的工作方式以及与元素的交互,某些名称是保留的,不能用作元素名称。这些名称包括:

  • action
  • attributes
  • di
  • elements
  • entity
  • eventsmanager
  • messages
  • messagesfor
  • label
  • tagFactory
  • useroption
  • useroptions
  • validation
  • value

这些名称对应于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>';
    }
}

在视图中:

<?php

echo $form->renderDecorated('nameLast');
echo $form->renderDecorated('nameFirst');

事件

当表单以类的形式实现时,回调函数: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();
    }
);

表单被添加到表单管理器中,并通过唯一名称进行引用:

<?php

$this
    ->forms
    ->set(
        'login',
        new LoginForm()
    )
;

通过唯一的名称,可以在应用程序的任何部分访问表单:

<?php

$loginForm = $this->forms->get('login');

echo $loginForm->render();

如果在管理器中找不到某个表单,则会抛出一个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ó是一个使用表单构建器创建和管理表单的示例应用程序。
无噪 Logo
无噪文档
25 年 6 月翻译
版本号 5.9
文档源↗