1

Dans Symfony, j'écris une API pour Angular2. J'utilise le FOSRestBundle avec le JMSSerializerBundle. Maintenant, j'ai une entité "Utilisateur" qui a un champ d'entité "adresse" avec une association OneToOne. Et j'ai du mal à enregistrer l'adresse de l'utilisateur.Symfony REST api - Champ d'association d'entité -> valeur non valide

Donc, je fais d'abord un GET de l'objet utilisateur et il renvoie l'objet entier avec l'adresse en json. Ensuite, je fais PUT demande avec ce même objet même. Dans ma fonction PUT-je utiliser un formulaire Symfony pour valider les données et là, il renvoie une erreur:

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

J'ai d'autres domaines sur mon entité utilisateur et ceux sont enregistrés parfaitement quand je quitte le champ d'adresse dans mon constructeur de formulaire. BTW: J'ai omis ces autres champs pour ne pas surcharger la quantité de code.

-je utiliser ces versions:

  • symfony: 3.1
  • jms/sérialiseur: 1.1
  • friendsofsymfony/repos paquet: 2,0

J'ai cherché pendant 2 jours maintenant et je ne peux pas trouver quelque chose qui m'aide avec cette question. J'utilise les transformations de date comme FOSRestBundle dit: http://symfony.com/doc/current/bundles/FOSRestBundle/2-the-view-layer.html#data-transformation

J'espère avoir formulé ma question assez bon et donné assez d'informations.

Voici mon code simplifié:

classe User:

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; 

Adresse Classe:

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; 

classe UserType:

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'; 
    } 
} 

classe UserController:

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("https://stackoverflow.com/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("https://stackoverflow.com/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; 
    } 

Et le JSON-objet que je GET et PUT ressemble à ceci:

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

mon 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" 

Répondre

0

je l'ai fait quelque chose comme ça et je l'ai fait sans aucun transformateur de modèle dans le type de formulaire. Voici mon code

UserType:

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

Type d'adresse:

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

Et l'objet JSON pour mettre

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