simplethings/form-serializer-bundle

基于表单类型将对象图序列化和反序列化为XML/JSON

v0.2 2012-07-19 11:57 UTC

This package is auto-updated.

Last update: 2024-09-11 01:45:34 UTC


README

帮助解决Serializer/Form组件API不匹配的Bundle。这种不匹配会导致控制器中的代码无法重用,使每个应用都通过重复实现一切而膨胀。目前几乎不可能在同一个控制器操作中重用REST-API调用和基于HTML/表单的提交。此外,所有当前的序列化组件都存在一个共同缺陷:它们无法将对象图反序列化(更新)。更新对象图是Form组件已经解决的问题(完美!)。

此bundle通过挂钩到Form框架来解决这些问题。它允许使用表单类型实现对象的序列化器。对于反序列化,它使用与“常规”表单请求完全相同的API。

Form组件是一个非常优秀的序列化库,但到目前为止,它只有一个实现:HTML表单。此bundle添加了对将序列化编码器挂钩到此过程的支持,默认支持XML和JSON。

新的表单选项

使用此bundle,您将获得新的表单类型选项。重写“form”字段以具有以下附加配置键

  • serialize_xml_name - 指定根XML名称或列表条目XML名称的元素,具体取决于其在父或子元素上的定义。(默认:entry)
  • serialize_xml_value - 如果为true,此字段将是父字段的XML值。如果嵌套类型具有一些属性和一个值,则很有用。(默认:false)
  • serialize_xml_attribute - 如果为true,此字段将作为父元素上的属性在XML中渲染,而不是作为元素。(默认:false)
  • serialize_xml_inline - 如果为true,则不会为元素集合渲染集合包装元素。如果为false,则包装所有元素。(默认:true)
  • serialize_name - 如果需要从默认的驼峰式命名策略(将驼峰式转换为下划线)中偏离,则指定序列化形式中元素的名称。(默认:false)
  • serialize_only - 如果为true,则字段将从FormView中删除,因此只存在于序列化数据(json,xml)中

用法

此bundle在Symfony DIC中定义了一个新的服务以序列化表单

class UserController extends Controller
{
    public function showAction(User $user)
    {
        $serializer = $this->get('form_serializer');
        $xml = $serializer->serialize($user, new UserType(), 'xml');

        return new Response($xml, 200, array('Content-Type' => 'text/xml'));
    }
}

FormListener的API在它实现的FormSerializerInterface上进行了文档说明

interface FormSerializerInterface
{
    /**
     * Serialize a list of objects, where each element is serialized based on a
     * form type.
     *
     * @param array|Traversable $list
     * @param FormTypeInterface $type
     * @param string $format
     * @param string $xmlRootName
     *
     * @return string
     */
    public function serializeList($list, $type, $format, $xmlRootName = 'entries');

    /**
     * Serialize an object based on a form type, form builder or form instance.
     *
     * @param mixed $object
     * @param FormTypeInterface|FormBuilderInterface|FormInterface $typeBuilder
     * @param string $format
     *
     * @return string
     */
    public function serialize($object, $typeBuilder, $format);
}

此bundle还在表单框架内部注册了一个监听器,将XML和JSON请求绑定到表单。只需像以下示例中那样调用$form->bind($request)即可。

如果您想将JMS Serializer基于配置转换为FormTypes,可以使用包含的命令

php app/console simplethings:convert-jms-metadata "className"

由于JMS Serializer会自动为每个类构建元数据,因此您可以使用此命令为您生成任何现有类的表单类型。

配置

默认DIC(config.yml)配置

simple_things_form_serializer:
    include_root_in_json: false
    application_xml_root_name: ~
    naming_strategy: camel_case
    encoders:
        xml: true
        json: true

名为simple_things_form_serializer.encoder的依赖注入标签,用于添加更多编码器。

示例

考虑一个通常的表单,扩展了一些有关序列化的详细信息

namespace Acme\DemoBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class UserType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('username', 'text')
            ->add('email', 'email')
            ->add('country', 'entity')
            ->add('addresses', 'collection', array('type' => 'address', 'serialize_xml_name' => 'address'))
            ->add('created', 'datetime', array('read_only' => true))
        ;
    }

    public function getName()
    {
        return 'user';
    }

    public function setDefaultOptions(OptionsResolverInterface $options)
    {
        $options->setDefaults(array(
            'data_class' => 'Acme\DemoBundle\Entity\User',
            'serialize_xml_name' => 'user',
        ));
    }
}

使用序列化器

$serializer = $this->get('form_serializer');
$data       = $serializer->serialize($user, new UserType(), 'xml');

生成

<user>
    <username>beberlei</username>
    <email>kontakt@beberlei.de</email>
    <country>de</country>
    <addresses>
        <address>
            <street>Foostreet 1</street>
        </address>
    </addresses>
    <created>2012-07-10</created>
</user>

或者如果您使用JSON

{
    "user": {
        "username": "beberlei",
        "email": "kontakt@beberlei.de",
        "country": "de",
        "addresses": [
            {"street": "Foostreet 1"}
        ],
        "created": "2012-07-10"
    }
}

反序列化看起来很熟悉

class UserController extends Controller
{
    /**
     * @Method("POST")
     */
    public function editAction(Request $request)
    {
        $em = $this->get('doctrine.orm.default_entity_manager');

        $user = $em->find('Acme\DemoBundle\Entity\User', $request->get('id'));
        $form = $this->createForm(new UserType(), $user);

        $form->bind($request);

        if ( ! $form->isValid()) {
            return $this->renderFormFailure("MyBundle:User:edit.html.twig", $form, array('user' => $user));
        }

        // do some business logic here

        $em->flush();

        return $this->formRedirect($form, $this->generateUrl('user_show', array('id' => $user->getId()), 201);
    }

    /* either render the form errors as xml/json or the html form again based on " _format" */
    public function renderFormFailure($template, FormInterface $form, $parameters)
    {
    }

    /* redirect OR 201 created, based on the "_format" */
    public function formRedirect()
    {
    }
}

这几乎就像一本教科书中的表单请求。唯一不同的是,我们必须使用“renderFormView”和“formRedirect”方法来生成响应对象。

  • renderFormView将根据响应格式决定要执行的操作。
    1. 当格式为html时,显示一个表单
    2. 当传递的表单尚未绑定时,显示HTTP 405错误
    3. 显示HTTP 412预条件失败,表单错误序列化为XML或JSON
  • formRedirect将根据响应格式进行决策
    1. 如果其是html(或配置选项use_forwards = false),则重定向到指定的URL
    2. 如果其是xml或json(并且配置选项use_forwards = true),则转发到路由