1

在 Symfony 中,我正在为 Angular2 编写一个 API。我将 FOSRestBundle 与 JMSSerializerBundle 一起使用。现在,我有一个实体“用户”,它有一个带有 OneToOne 关联的实体字段“地址”。而且我无法保存用户的地址。

所以我首先对用户对象进行 GET,它返回整个对象,地址为 json。然后我用那个完全相同的对象做 PUT 请求。在我的 PUT 函数中,我使用 Symfony 表单来验证数据并返回错误:

{
  "children": {
    "address": {
      "errors": [
        "This value is not valid."
      ]
    }
  }
}

我的用户实体上有一些其他字段,当我在表单构建器中省略地址字段时,这些字段被完美保存。顺便说一句:我省略了其他字段,以免代码量过多。

我使用这些版本:

  • 交响乐:3.1
  • jms/序列化器:1.1
  • Friendsofsymfony/rest-bundle: 2.0

我一直在寻找 2 天,但我找不到任何可以帮助我解决这个问题的东西。我使用像FOSRestBundle这样的日期转换说:http ://symfony.com/doc/current/bundles/FOSRestBundle/2-the-view-layer.html#data-transformation

我希望我的问题足够好,并提供了足够的信息。

这是我的简化代码:

用户等级:

use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;

/**
 * @ORM\Entity
 */
class User
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\OneToOne(targetEntity="Address", cascade={"persist", "remove"}, orphanRemoval=true)
     * @JMS\Type("AppBundle\Entity\Address")
     */
    private $address;

地址类:

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 */
class Address
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(type="string")
     */
    private $street;

    /**
     * @ORM\Column(type="string")
     */
    private $number;

    /**
     * @ORM\Column(type="string")
     */
    private $postalCode;

    /**
     * @ORM\Column(type="string")
     */
    private $city;

    /**
     * @ORM\Column(type="string")
     */
    private $country;

用户类型类:

use FOS\RestBundle\Form\Transformer\EntityToIdObjectTransformer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class UserType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // Data transformation needed for relationship entities
        $addressTransformer = new EntityToIdObjectTransformer($options['em'], 'AppBundle:Address');

        $builder
            ->add($builder->create('address', TextType::class)->addModelTransformer($addressTransformer))
        ;
    }

    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\User',
            'csrf_protection'   => false,
            'allow_extra_fields' => true,
            'em' => null
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'user';
    }
}

用户控制器类:

use AppBundle\Entity\User;
use AppBundle\Form\UserType;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\View\View;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;

/**
 * Class UserController
 * @package AppBundle\Controller
 */
class UserController extends Controller
{
    /**
     * @Rest\View
     * @Route("/users/{id}")
     * @Method("PUT")
     */
    public function putAction(Request $request, $id)
    {
        $user = $this->getEntity($id);
        $form = $this->createForm(UserType::class, $user, array(
            'method' => 'PUT',
            'em' => $this->getDoctrine()->getManager()
        ));

        $form->handleRequest($request);

        if ($form->isValid()) {
            $em = $this->getDoctrine()->getManager();

            $em->persist($user);
            $em->flush();

            $response = new Response();
            $response->setStatusCode(204);
            $response->setContent('User saved!');

            return $response;
        }

        return View::create($form, 400);
    }


    /**
     * @Rest\View
     * @Route("/users/{id}", requirements={"id": "\d+"})
     * @Method("GET")
     */
    public function getAction($id)
    {
        $user = $this->getEntity($id);

        return array('user' => $user);
    }

    /**
     * Get the User entity object by the given ID and return it
     *
     * @param $id
     *
     * @return User
     */
    private function getEntity($id)
    {
        $user = $this->getDoctrine()->getRepository('AppBundle:User')->find($id);

        if (!$user instanceof User) {
            throw new NotFoundHttpException('User not found');
        }

        return $user;
    }

我 GET 和 PUT 的 json-object 看起来像这样:

{
"user":
    {
        "id":1,
        "address": {
            "id":1,
            "street":"Teststreet",
            "number":"1",
            "postalCode":"9999",
            "city":"Citytest",
            "country":"Countrytest"
        }
    }
}

我的 config.yml:

fos_rest:
    param_fetcher_listener: true
    body_listener:
        array_normalizer: fos_rest.normalizer.camel_keys
    format_listener:
        rules:
            path: ^/
            fallback_format: json
            prefer_extension: false
            priorities: [json, xml]
    body_converter:
        enabled:              false
        validate:             false
    view:
        view_response_listener: force
        formats:
            json: true
            xml: true
        templating_formats:
            html: true
        force_redirects:
            html: true
        failed_validation: HTTP_BAD_REQUEST
        default_engine: twig
        mime_types:
            json: ['application/json', 'application/json;version=1.0', 'application/json;version=1.1']
    routing_loader:
        default_format: json
    serializer:
        serialize_null: true

nelmio_api_doc: ~

jms_serializer:
    metadata:
        directories:
            FOSUB:
                namespace_prefix: "FOS\\UserBundle"
                path: "%kernel.root_dir%/serializer/FOSUserBundle"
4

1 回答 1

0

我做过类似的事情,并且我在表单类型中没有任何模型转换器就做到了。这是我的代码

用户类型:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('address', AddressType::class)
    ;
}

地址类型:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('street', AddressType::class)
        ->add('number', AddressType::class)
        ->add('postalCode', AddressType::class)
        // ...
    ;
}

以及要放置的 json 对象

{
"user":
    {
        "address": {
            "street":"Teststreet",
            "number":"1",
            "postalCode":"9999",
            "city":"Citytest",
            "country":"Countrytest"
        }
    }
}
于 2016-07-24T05:06:24.760 回答