avris/micrus-forms

此包已被弃用且不再维护。作者建议使用avris/forms包。

简单的表单抽象

维护者

详细信息

gitlab.com/Avris/Forms

源代码

问题

安装: 154

依赖: 2

建议者: 0

安全: 0

v4.0.1 2018-01-27 23:34 UTC

This package is auto-updated.

Last update: 2022-02-01 13:05:47 UTC


README

表单很复杂。有许多事情你必须考虑:将现有对象(如果有)绑定到表单的每个字段,在用户提交表单后验证它们,如果无效,则重新显示它,并绑定数据回对象...

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 示例项目

小部件

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) -- 是否使用一个或多个 selectcheckbox/radio 控件
    • keys => callback (optional, required if choices is 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_prototype
    • element_options
    • element_asserts
  • TextAddon (bootstrap), 选项
    • before (可选)
    • after (可选)
  • NumberAddon (bootstrap), 类似于 TextAddon 的选项

注意,由于表单也可以作为小部件,您可以在不同的表单内轻松嵌套表单(无论是用于一对一关系的直接嵌套,还是使用 Multiple 小部件用于一对多关系)。

所有小部件的选项

  • label
  • class
  • attr -- HTML属性数组
  • helper
  • readonly
  • default -- 默认数据
  • prototype -- 绑定的默认对象

自定义小部件

要创建您自己的小部件

  • 创建一个扩展 Avris\Forms\Widget\Widget 的类,其中包含所有逻辑(数据转换)
  • 在DI中注册它
  • 创建相应的Twig模板,例如,MyApp\Widget\Foo 类应该有一个 Form/MyApp/Widget/Foo.html.twig 模板。

断言

可用的断言有

  • NotBlank
  • 电子邮件
  • 网址
  • MaxLength
  • MinLength
  • Regexp
  • 数字
  • 整数
  • Min
  • Max
  • Step
  • MinCount
  • MaxCount
  • 日期
  • 日期时间
  • 时间
  • MinDate
  • MaxDate
  • ObjectValidator
  • CorrectPassword
  • Choice
  • Csrf
  • MinCount
  • MaxCount
  • File\File
  • File\Image
  • File\Extension
  • File\Type
  • File\MaxHeight
  • File\MinHeight
  • File\MaxWidth
  • File\MinWidth
  • File\MaxSize
  • File\MaxRatio

许多小部件会自动添加相关断言,因此您不必这样做。

样式

当将表单渲染为HTML时,您可以指定一个样式。例如,使用 `{{ widget(form, form.name, 'Avris\Forms\Style\Bootstrap2') }}`,每个小部件都被Bootstrap类包裹,标签有2列,小部件有10列。

内置样式有

  • DefaultStyle
  • Bootstrap
  • Bootstrap1
  • Bootstrap2
  • Bootstrap3
  • BootstrapHalf
  • BootstrapInline
  • BootstrapInlineNoLabel
  • BootstrapMini
  • BootstrapNoLabel

您可以通过实现 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');
}

版权