1

In Symfony schreibe ich eine API für Angular2. Ich benutze das FOSRestBundle mit dem JMSSerializerBundle. Jetzt habe ich eine Entität "Benutzer", die ein Entitätsfeld "Adresse" mit einer OneToOne-Zuordnung hat. Und ich habe Probleme beim Speichern der Adresse des Benutzers.Symfony REST API - Entity Association Feld -> Wert ist nicht gültig

Also mache ich zuerst ein GET des Benutzerobjekts und es gibt das ganze Objekt mit der Adresse als json zurück. Dann mache ich PUT Anfrage mit genau demselben Objekt. In meiner Funktion PUT verwende ich eine Symfony Form die Daten zu überprüfen und es gibt sie einen Fehler:

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

Ich habe einige andere Felder auf meiner Benutzereinheit und diejenigen perfekt werden gespeichert, wenn ich das Adressfeld auslassen in meinem Form-Builder. BTW: Ich habe diese anderen Felder weggelassen, um die Menge an Code nicht zu überladen.

Ich benutze diese Versionen:

  • symfony 3.1
  • jms/Serializer: 1.1
  • friendsofsymfony/rest-Bundle: 2,0

Ich habe seit 2 Tagen Suche jetzt und ich kann nichts finden, was mir bei diesem Problem hilft. Ich benutze die Datumstransformationen wie FOSRestBundle sagt: http://symfony.com/doc/current/bundles/FOSRestBundle/2-the-view-layer.html#data-transformation

Ich hoffe, ich formulierte meine Frage gut genug und gab genug Informationen.

Hier ist meine vereinfachte Code:

Benutzerklasse:

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; 

Klasse Adresse:

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; 

Usertype Klasse:

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

Usercontroller Klasse:

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

Und das Json-Objekt, das ich GET und PUT sieht wie folgt aus:

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

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

Antwort

0

Ich habe so etwas getan, und ich tat es ohne Modelltransformator im Formulartyp. Hier ist mein Code

Usertype:

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

Typ Adresse:

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

Und das JSON-Objekt zu setzen

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