avris / forms
Requires
- avris/container: ^1.0
- avris/http: ^4.0
- avris/localisator: ^4.0
- twig/twig: ^2.4
Requires (Dev)
- avris/micrus: ^4.0-dev
- phpunit/phpunit: ^6.4
- squizlabs/php_codesniffer: ^3.2
- symfony/var-dumper: ^4.0
Suggests
- avris/micrus: Web framework that this library was created for
README
表单很复杂。有许多事情你必须考虑:将现有对象(如果有的话)绑定到表单的每个单独字段,在用户提交表单后验证它们,如果无效,则重新显示它并绑定POST数据以及验证错误,将数据绑定回对象...
Avris Forms 添加了一个抽象层,处理所有这些。你只需要定义你需要字段列表及其配置选项。你将得到一个对象,它将为你处理所有这些。只需在控制器中处理它,并在视图中显示它。
安装
composer require avris/forms
配置
依赖项通过 DI 容器处理。你可以自己配置它或使用内置的 DI 构建器。
$formsDir = $projectDir . '/vendor/avris/forms';
$locale = 'en';
$builder = (new LocalisatorBuilder())
->registerExtension(new LocalisatorExtension($locale, [$formsDir . '/translations']))
->registerExtension(new FormsExtension(
[
UserForm::class => [],
PostForm::class => ['@logger'],
],
[CustomWidget::class => []]
));
$localisator = $builder->build(LocalisatorInterface::class);
$loader = new FilesystemLoader([$projectDir . '/templates', $formsDir . '/templates']);
self::$twig = new Environment($loader, [
'cache' => __DIR__ . '/_output/cache',
'debug' => true,
'strict_variables' => true,
]);
self::$twig->addExtension(new FormRenderer());
self::$twig->addExtension(new LocalisatorTwig($localisator));
$widgetFactory = $builder->build(WidgetFactory::class);
请注意,所有表单和部件都是服务,因此你可以简单地注入依赖项到它们中。
Twig 是渲染表单 HTML 所必需的。它期望有一个 l(string $key, array $replacements) 函数用于翻译。你可以自己提供它,或者使用如上所示的 Avris Localisator。
用法
表单定义
<?php
namespace App\Form;
use App\Entity\User;
use Avris\Forms\Assert as Assert;
use Avris\Forms\Security\CsrfProviderInterface;
use Avris\Forms\Widget as Widget;
use Avris\Forms\Form;
use Avris\Forms\WidgetFactory;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
class RegisterForm extends Form
{
/** @var EntityRepository */
private $repo;
// inject dependencies as with any other service
public function __construct(
WidgetFactory $widgetFactory,
CsrfProviderInterface $csrfProvider,
EntityManagerInterface $em
) {
parent::__construct($widgetFactory, $csrfProvider);
$this->repo = $em->getRepository(User::class);
}
public function configure()
{
$this
->add('username', Widget\Text::class, [
'placeholder' => 'Username can only contain latin letters and numbers',
], [
new Assert\NotBlank(),
new Assert\Regexp('^[A-Za-z0-9]+$', 'Username can only contain latin letters and numbers')),
new Assert\MinLength(5),
new Assert\MaxLength(25),
new Assert\Callback(
function (string $value) {
return !$this->repo->findOneBy(['username' => $value]);
},
'Username already exists in the database'
),
])
->add('email', Widget\Email::class, [], [
new Assert\NotBlank(),
new Assert\Callback(
function (string $value) {
return !$this->repo->findOneBy(['email' => $value]);
},
'Email already exists in the database'
),
])
->add('password', Widget\Password::class, [], [
new Assert\NotBlank(),
new Assert\MinLength(5),
new Assert\Callback(
function () {
return $this->getData()['password'] === $this->getData()['passwordRepeat'];
},
'Passwords don\'t match'
)
])
->add('passwordRepeat', Widget\Password::class, [], new Assert\NotBlank)
->add('source', Widget\Choice::class, [
'choices' => [
'facebook' => 'Facebook',
'twitter' => 'Twitter',
'friend' => 'Friend',
],
'add_empty' => true,
])
->add('agree', Widget\Checkbox::class, [
'label' => '',
'sublabel' => 'I agree to the terms and conditions')
], new Assert\NotBlank)
;
}
// just a custom helper
public function getPassword()
{
return $this->getData()['password'];
}
// if not specified, will fallback to the short class name of the object it's bound to
// it's useful to specify it, if you have multiple X-related forms on the same page (like Login and Register)
public function getName(): string
{
return 'Register';
}
}
使用表单
public function registerAction(WidgetFactory $widgetFactory, EntityManagerInterface $em, RequestInterface $request)
{
$form = $widgetFactory->build(RegisterForm::class);
$form->bindObject(new User());
$form->bindRequest($request);
if ($form->isValid()) {
$user = $form->buildObject();
$em->persist($post);
$em->flush();
return $this->redirectToRoute('myaccount');
}
return $this->render('User/register.html.twig', ['form' => $form]);
}
并在视图中这样显示
<form method="post" class="form">
{{ widget(form, form.name, 'Avris\\Forms\\Style\\Bootstrap') }}
<div class="form-group">
<button type="submit" class="btn btn-block btn-primary">Log in</button>
</div>
</form>
有关更多示例,请参阅 Micrus Demo 项目。
部件
add(string $name, string $type = Widget\Text::class, array $options = [], array $asserts = []) 方法让你可以将字段添加到你的表单中。$name 参数必须是唯一的,因为它将是对象的属性名。$type 是一个字符串,定义了应使用哪个部件。
Text(默认)数字整数电子邮件网址隐藏密码文本区域Checkbox,选项sublabel-- 在复选框旁边显示的文本
日期日期时间时间Choice-- 选项choices => [key => value, ...](required) -- 将向用户展示的选项列表;它可以是简单的键值列表,也可以是对象数组 -- 在第二种情况下,Avris Forms 将保留这些对象,但你需要提供一个将它们映射到键值列表的方法(对于键:选项keys,对于值:选项labels或方法__toString)add_empty => bool(default: false)multiple => bool(default: false) -- 如果用户可以选择一个或多个选项,expanded => bool(default: false) -- 如果应使用单个select控件或多个checkbox/radio控件,keys => callback(optional, required ifchoicesis an array of objects) -- 将对象映射到唯一的字符串标识符,例如:function (User $user) { return $user->getId(); }labels => callback(optional) -- 将对象映射到其文本表示形式(如果没有提供,则使用__toString)。
文件Custom-- 允许自定义 HTML,不将任何内容绑定到对象;选项template(required) - 要显示的 Twig 模板的名称
Csrf-- CSRF 保护,自动添加到每个表单,除非用选项csrf = false创建。Multiple-- 以子表单表格形式表示的值数组;选项widget(必需) -- 单个子表单的类名lineStyle(默认: Avris\Forms\Style\BootstrapInlineNoLabel)add => bool(默认: true)remove => bool|callback(默认: true)element_prototypeelement_optionselement_asserts
TextAddon(bootstrap),选项before(可选)after(可选)
NumberAddon(bootstrap),类似于TextAddon的选项
注意,由于表单作为小部件,您可以在不同的表单内轻松嵌套表单(无论是直接用于一对一关系,还是使用 Multiple 小部件用于一对多关系)。
所有小部件的选项
labelclassattr-- HTML 属性数组helperreadonlydefault-- 默认数据prototype-- 绑定的默认对象
自定义小部件
要创建您自己的小部件
- 创建一个扩展
Avris\Forms\Widget\Widget的类,其中包含所有逻辑(数据转换) - 在依赖注入(DI)中注册它
- 创建相应的 Twig 模板,例如
MyApp\Widget\Foo类应该有一个Form/MyApp/Widget/Foo.html.twig模板。
断言
可用的断言有
NotBlank电子邮件网址MaxLengthMinLengthRegexp数字整数MinMaxStepMinCountMaxCount日期日期时间时间MinDateMaxDateObjectValidatorCorrectPasswordChoiceCsrfMinCountMaxCountFile\FileFile\ImageFile\ExtensionFile\TypeFile\MaxHeightFile\MinHeightFile\MaxWidthFile\MinWidthFile\MaxSizeFile\MaxRatio
许多小部件会自动添加相关断言,因此您不必这样做。
样式
当将表单渲染为 HTML 时,您可以指定样式。例如,使用 `{{ widget(form, form.name, 'Avris\Forms\Style\Bootstrap2') }}`,每个小部件都被包裹在具有 2 列标签和 10 列小部件的 Bootstrap 类中。
内置样式有
DefaultStyleBootstrapBootstrap1Bootstrap2Bootstrap3BootstrapHalfBootstrapInlineBootstrapInlineNoLabelBootstrapMiniBootstrapNoLabel
您可以通过实现 Avris\Forms\Style\FormStyleInterface 来创建自己的样式。
框架集成
Micrus
尽管 Avris Forms 可以独立使用,但它们最初是作为 Micrus Framework 的一部分创建的。因此,与该框架的集成非常简单。只需在您的 App\App:registerModules 中注册模块即可。
yield new \Avris\Forms\FormsModule;
您可以在控制器中使用助手函数
/**
* @M\Route("/add", name="postAdd")
* @M\Route("/{uuid:id}/edit", name="postEdit")
*/
public function formAction(EntityManagerInterface $em, RequestInterface $request, Post $post = null)
{
if (!$post) {
$post = new Post($this->getUser());
}
$form = $this->form(PostForm::class, $post, $request); // creates a form and binds the object and the request
if ($this->handleForm($form, $post)) { // validates the form and if valid, binds the data back to $post
$post->handleFileUpload($this->getProjectDir());
$em->persist($post);
$em->flush();
return $this->redirectToRoute('postRead', ['id' => $post->getId()]);
}
return $this->render(['form' => $form], 'Post/form');
}
版权
- 作者: Andre Prusinowski (Avris.it)
- 许可: MIT