我有一个有两个字段的表单。带有验证器的InputFilter应用于它。它工作正常。然后我将字段移动到字段集并将字段集添加到表单。现在不存在字段的赋值验证器。验证器对象isValid
方法根本不会被触发。那么如何将InputFilter验证器应用于字段集中的字段?在这里你是班级:
文本类验证器
namespace Application\Validator;
use Zend\Validator\StringLength;
use Zend\Validator\ValidatorInterface;
class Text implements ValidatorInterface
{
protected $stringLength;
protected $messages = [];
public function __construct()
{
$this->stringLengthValidator = new StringLength();
}
public function isValid($value, $context = null)
{
if (empty($context['url'])) {
if (empty($value)) return false;
$this->stringLengthValidator->setMin(3);
$this->stringLengthValidator->setMax(5000);
if ($this->stringLengthValidator->isValid($value)) {
return true;
}
$this->messages = $this->stringLengthValidator->getMessages();
return false;
}
if (!empty($value)) return false;
return true;
}
public function getMessages()
{
return $this->messages;
}
}
测试类InputFilter
namespace Application\Filter;
use Application\Fieldset\Test as Fieldset;
use Application\Validator\Text;
use Application\Validator\Url;
use Zend\InputFilter\InputFilter;
class Test extends InputFilter
{
public function init()
{
$this->add([
'name' => Fieldset::TEXT,
'required' => false,
'allow_empty' => true,
'continue_if_empty' => true,
'validators' => [
['name' => Text::class],
],
]);
$this->add([
'name' => Fieldset::URL,
'required' => false,
'allow_empty' => true,
'continue_if_empty' => true,
'validators' => [
['name' => Url::class],
],
]);
}
}
测试类Fieldset
namespace Application\Fieldset;
use Zend\Form\Fieldset;
class Test extends Fieldset
{
const TEXT = 'text';
const URL = 'url';
public function init()
{
$this->add([
'name' => self::TEXT,
'type' => 'textarea',
'attributes' => [
'id' => 'text',
'class' => 'form-control',
'placeholder' => 'Type text here',
'rows' => '6',
],
'options' => [
'label' => self::TEXT,
],
]);
$this->add([
'name' => self::URL,
'type' => 'text',
'attributes' => [
'id' => 'url',
'class' => 'form-control',
'placeholder' => 'Type url here',
],
'options' => [
'label' => self::URL,
],
]);
}
}
测试类表单
namespace Application\Form;
use Application\Fieldset\Test as TestFieldset;
use Zend\Form\Form;
class Test extends Form
{
public function init()
{
$this->add([
'name' => 'test',
'type' => TestFieldset::class,
'options' => [
'use_as_base_fieldset' => true,
],
]);
$this->add([
'name' => 'submit',
'attributes' => [
'type' => 'submit',
'value' => 'Send',
],
]);
}
}
TestController类
namespace Application\Controller;
use Application\Form\Test as Form;
use Zend\Debug\Debug;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class TestController extends AbstractActionController
{
private $form;
public function __construct(Form $form)
{
$this->form = $form;
}
public function indexAction()
{
if ($this->getRequest()->isPost()) {
$this->form->setData($this->getRequest()->getPost());
Debug::dump($this->getRequest()->getPost());
if ($this->form->isValid()) {
Debug::dump($this->form->getData());
die();
}
}
return new ViewModel(['form' => $this->form]);
}
}
TestControllerFactory类
namespace Application\Factory;
use Application\Controller\TestController;
use Application\Form\Test;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
class TestControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$form = $container->get('FormElementManager')->get(Test::class);
return new TestController($form);
}
}
测试课
namespace Application\Factory;
use Application\Filter\Test as Filter;
use Application\Entity\Form as Entity;
use Application\Form\Test as Form;
use Interop\Container\ContainerInterface;
use Zend\Hydrator\ClassMethods;
use Zend\ServiceManager\Factory\FactoryInterface;
class Test implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
return (new Form())
->setHydrator($container
->get('HydratorManager')
->get(ClassMethods::class))
->setObject(new Entity())
->setInputFilter($container->get('InputFilterManager')->get(Filter::class));
}
}
测试场集
namespace Application\Factory;
use Application\Entity\Fieldset as Entity;
use Application\Fieldset\Test as Fieldset;
use Interop\Container\ContainerInterface;
use Zend\Hydrator\ClassMethods;
use Zend\ServiceManager\Factory\FactoryInterface;
class TestFieldset implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
return (new Fieldset())
->setHydrator($container->get('HydratorManager')->get(ClassMethods::class))
->setObject(new Entity());
}
}
我通过添加setInputFilter()
相应地将fieldset类更新为@Nukeface建议。但它没有奏效。它甚至没有执行InpuFilter类init
方法。也许我做错了:
<?php
namespace Application\Fieldset;
use Application\Filter\Test as Filter;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterAwareTrait;
class Test extends Fieldset
{
use InputFilterAwareTrait;
const TEXT = 'text';
const URL = 'url';
public function init()
{
$this->add([
'name' => self::TEXT,
'type' => 'textarea',
'attributes' => [
'id' => 'text',
'class' => 'form-control',
'placeholder' => 'Type text here',
'rows' => '6',
],
'options' => [
'label' => self::TEXT,
],
]);
$this->add([
'name' => self::URL,
'type' => 'text',
'attributes' => [
'id' => 'url',
'class' => 'form-control',
'placeholder' => 'Type url here',
],
'options' => [
'label' => self::URL,
],
]);
$this->setInputFilter(new Filter());
}
}
答案 0 :(得分:0)
只需将InputFilterProviderInterface
类用于您的字段集即可。这为您的fieldset实现了getInputFilterSpecification
方法,该方法执行此方法中提到的输入过滤器。
class MyFieldset extends Fieldset implements InputFilterProviderInterface
{
public function init()
{
$this->add([
'name' => 'textfield',
'type' => Text::class,
'attributes' => [
...
],
'options' => [
...
]
]);
}
public function getInputFilterSpecification()
{
return [
'textfield' => [
'required' => true,
'filters' => [
...
],
'validators' => [
[
'name' => YourTextValidator::class,
'options' => [
...
],
],
],
],
];
}
}
只要在表单中添加此字段集,绑定的过滤器和验证器就会在表单的isValid
方法调用中执行。
答案 1 :(得分:0)
之前尝试过一个答案并且用完了字符(30k限制),所以created a repo instead。回购包含下面答案的抽象,这是一个有效的例子。
你的问题表明你有正确的想法,只是还没有实施。它还包含一些错误,例如为Fieldset名称设置FQCN。希望下面的内容可以帮助您正常运行。
作为一个用例,我们将有一个基本的地址表单。国家,时区和其他事物的关系,我将超出范围。为了更深入地和嵌套Fieldsets(还有收藏集),我将引用您的回购。
首先创建基本设置。创建实体和配置。
namespace Demo\Entity;
class Address
{
protected $id; // int - primary key - unique - auto increment
protected $street; // string - max length 255 - not null
protected $number; // int - max length 11 - not null
protected $city; // string - max length 255 - null
// getters/setters/annotation/et cetera
}
要以通用且可重复使用的方式处理此问题,我们需要:
要在Zend Framework中将这些绑定在一起,需要在配置中注册这些。使用明确的命名,您可以添加这些命名。如果您正在使用像PhpStorm这样的IDE作为IDE,那么您可能希望将其保留到最后,因为可以为您生成use
语句。
由于这是一个解释,我现在向您展示。将其添加到模块的配置中:
// use statements here
return [
'controllers' => [
'factories' => [
AddressController::class => AddressControllerFactory::class,
],
],
'form_elements' => [ // <-- note: both Form and Fieldset classes count as Form elements
'factories' => [
AddressForm::class => AddressFormFactory::class,
AddressFieldset::class => AddressFieldsetFactory::class,
],
],
'input_filters' => [ // <-- note: input filter classes only!
'factories' => [
AddressFormInputFilter::class => AddressFormInputFilterFactory::class,
AddressFieldsetInputFilter::class => AddressFieldsetInputFilterFactory::class,
],
],
'view_manager' => [
'template_map' => [
'addressFormPartial' => __DIR__ . '/../view/partials/address-form.phtml',
],
];
首先,我们创建Fieldset(和Factory)类。这是因为它包含了我们将要处理的实际对象。
// other use statements for Elements
use Zend\Form\Fieldset;
class AddressFieldset extends Fieldset
{
public function init()
{
parent::init(); // called due to inheritance
$this->add([
'name' => 'id',
'type' => Hidden::class,
]);
$this->add([
'name' => 'street',
'required' => true,
'type' => Text::class,
'options' => [
'label' => 'Name',
],
'attributes' => [
'minlength' => 1,
'maxlength' => 255,
],
]);
$this->add([
'name' => 'number',
'required' => true,
'type' => Number::class,
'options' => [
'label' => 'Number',
],
'attributes' => [
'step' => 1,
'min' => 0,
],
]);
$this->add([
'name' => 'city',
'required' => false,
'type' => Text::class,
'options' => [
'label' => 'Name',
],
'attributes' => [
'minlength' => 1,
'maxlength' => 255,
],
]);
}
}
// other use statements
use Zend\ServiceManager\Factory\FactoryInterface;
class AddressFieldsetFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$this->setEntityManager($container->get(EntityManager::class));
/** @var AddressFieldset $fieldset */
$fieldset = new AddressFieldset($this->getEntityManager(), 'address');
$fieldset->setHydrator(
new DoctrineObject($this->getEntityManager())
);
$fieldset->setObject(new Address());
return $fieldset;
}
}
上面我们创建了Fieldset。这允许在表单中生成Fieldset。同时,Zend Framework还为每种输入类型设置了默认值(例如'type' => Text::class
)。但是,如果我们想要将它验证为我们自己的,更严格的标准,我们需要覆盖默认值。为此,我们需要一个InputFilter类。
// other use statements
use Zend\InputFilter\InputFilter;
class AddressFieldsetInputFilter extends InputFilter
{
public function init()
{
parent::init(); // called due to inheritance
$this->add([
'name' => 'id',
'required' => true,
'filters' => [
['name' => ToInt::class],
],
'validators' => [
['name' => IsInt::class],
],
]);
$this->add([
'name' => 'street',
'required' => true,
'filters' => [
['name' => StringTrim::class], // remove whitespace before & after string
['name' => StripTags::class], // remove unwanted tags
[ // if received is empty string, set to 'null'
'name' => ToNull::class,
'options' => [
'type' => ToNull::TYPE_STRING, // also supports other types
],
],
],
'validators' => [
[
'name' => StringLength::class, // set min/max string length
'options' => [
'min' => 1,
'max' => 255,
],
],
],
]);
$this->add([
'name' => 'number',
'required' => true,
'filters' => [
['name' => ToInt::class], // received from HTML form always string, have it cast to integer
[
'name' => ToNull::class, // if received is empty string, set to 'null'
'options' => [
'type' => ToNull::TYPE_INTEGER,
],
],
],
'validators' => [
['name' => IsInt::class], // check if actually integer
],
]);
$this->add([
'name' => 'city',
'required' => false, // <-- not required
'filters' => [
['name' => StringTrim::class], // remove whitespace before & after string
['name' => StripTags::class], // remove unwanted tags
[ // if received is empty string, set to 'null'
'name' => ToNull::class,
'options' => [
'type' => ToNull::TYPE_STRING, // also supports other types
],
],
],
'validators' => [
[
'name' => StringLength::class, // set min/max string length
'options' => [
'min' => 1,
'max' => 255,
],
],
],
]);
}
}
// other use statements
use Zend\ServiceManager\Factory\FactoryInterface;
class AddressFieldsetInputFilterFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
// Nothing else required in this example. So it's as plain as can be.
return new AddressFieldsetInputFilter();
}
}
因此。上面我们创建了Fieldset,它的InputFilter和2个必需的Factory类。这已经允许我们做很多事情,例如:
use Zend\Form\Form;
use Zend\InputFilter\InputFilterAwareInterface;
// other use statements
class AddressForm extends Form implements InputFilterAwareInterface
{
public function init()
{
//Call parent initializer. Check in parent what it does.
parent::init();
$this->add([
'type' => Csrf::class,
'name' => 'csrf',
'options' => [
'csrf_options' => [
'timeout' => 86400, // day
],
],
]);
$this->add([
'name' => 'address',
'type' => AddressFieldset::class,
'options' => [
'use_as_base_fieldset' => true,
],
]);
$this->add([
'name' => 'submit',
'type' => Submit::class,
'attributes' => [
'value' => 'Save',
],
]);
}
}
use Zend\ServiceManager\Factory\FactoryInterface;
// other use statements
class AddressFormFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
/** @var AbstractForm $form */
$form = new AddressForm('address', $this->options);
$form->setInputFilter(
$container->get('InputFilterManager')->get(ContactFormInputFilter::class);
);
return $form;
}
}
我只会显示AddressController#addAction
use Zend\Mvc\Controller\AbstractActionController;
// other use statements
class AddressController extends AbstractActionController
{
protected $addressForm; // + getter/setter
protected $entityManager; // + getter/setter
public function __construct(
EntityManager $entityManager,
AddressForm $form
) {
$this->entityManager = $entityManager;
$this->addressForm = $form;
}
// Add your own: index, view, edit and delete functions
public function addAction () {
/** @var AddressForm $form */
$form = $this->getAddressForm();
/** @var Request $request */
$request = $this->getRequest();
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
$entity = $form->getObject();
$this->getEntityManager()->persist($entity);
try {
$this->getEntityManager()->flush();
} catch (\Exception $e) {
$this->flashMessenger()->addErrorMessage($message);
return [
'form' => $form,
'validationMessages' => $form->getMessages() ?: '',
];
}
$this->flashMessenger()->addSuccessMessage(
'Successfully created object.'
);
return $this->redirect()->route($route, ['param' => 'routeParamValue']);
}
$this->flashMessenger()->addWarningMessage(
'Your form contains errors. Please correct them and try again.'
);
}
return [
'form' => $form,
'validationMessages' => $form->getMessages() ?: '',
];
}
}
class AddressControllerFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
/** @var AddressController $controller */
$controller = new AddressController(
$container->get(EntityManager::class),
$container->get('FormElementManager')->get(AddressForm::class);
);
return $controller;
}
}
$this->headTitle('Add address');
$form->prepare();
echo $this->form()->openTag($form);
echo $this->formRow($form->get('csrf'));
echo $this->formRow($form->get('address')->get('id'));
echo $this->formRow($form->get('address')->get('street'));
echo $this->formRow($form->get('address')->get('number'));
echo $this->formRow($form->get('address')->get('city'));
echo $this->formRow($form->get('submit'));
echo $this->form()->closeTag($form);
要使用此部分,请在add.phtml
视图中使用:
<?= $this->partial('addressFormPartial', ['form' => $form]) ?>
这段代码将与上面的Controller代码中演示的addAction
一起使用。
希望您发现这有用;-)如果您还有任何疑问,请不要犹豫。