2

I am trying to submit a JSON through a complex form. I can't figure out what I am missing. The "normal" form is functioning. I am able to get serialized data with groups.

Class TaskBoard

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

    /**
     * @var \DateTime $createdTime createdTime
     * 
     * @ORM\Column(type="datetime")
     * @Assert\DateTime()
     */
    private $createdTime;

    /**
     * @var \DateTime $lastUpdatedTime lastUpdatedTime
     * 
     * @ORM\Column(type="datetime", nullable=true)
     * @Assert\DateTime()
     */
    private $lastUpdatedTime;

    /**
     * @var string $name name
     * 
     * @ORM\Column(type="string", length=20)
     * @Assert\Type("string")
     * @Assert\NotBlank()
     * @Assert\Length(
     *      min = 2,
     *      max = 20,
     *      minMessage = "The name must be at least {{ limit }} characters long",
     *      maxMessage = "The name cannot be longer than {{ limit }} characters"
     * )
     */
    private $name;

    /**
     * @var App\Entity\User $user user
     * 
     * @ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="taskboards", cascade={"persist"})
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     */
    private $user;

    /**
     * @var string $description description
     * 
     * @ORM\Column(type="text", nullable=true)
     * @Assert\Type("string")
     */
    private $description;

    /**
     * @var App\Entity\Status $status status
     * 
     * @ORM\ManyToOne(targetEntity="App\Entity\Status", inversedBy="taskboards", cascade={"persist"})
     * @ORM\JoinColumn(name="status_id", referencedColumnName="id", nullable=false)
     */
    private $status;

    /**
     * @var boolean $completed completed
     * 
     * @ORM\Column(type="boolean")
     */
    private $completed;

    /**
     * @var \DateTime $deadLine deadLine
     * 
     * @ORM\Column(type="date", nullable=true)
     * @Assert\DateTime()
     * @Assert\GreaterThanOrEqual("today")
     */
    private $deadLine;

Class Status

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

    /**
     * @ORM\Column(type="string", length=20)
     */
    private $name;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\TaskBoard", mappedBy="status", cascade={"persist"})
     */
    public $taskboards;

Class User

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

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\TaskBoard", mappedBy="user", cascade={"persist"})
     */
    public $taskboards;

Form

class TaskBoardType extends AbstractType {

    public function buildForm(FormBuilderInterface $builder, array $options) {
        $builder
                ->add('name', TextType::class)
                ->add('user', EntityType::class, array(
                    'class' => User::class,
                    'choice_label' => 'username',
                        )
                )
                ->add('description', TextareaType::class, array(
                    'required' => false
                ))
                ->add('status', EntityType::class, array(
                    'class' => Status::class,
                    'choice_label' => 'name',
                        )
                )
                ->add('deadLine', DateTimeType::class)
        ;
    }

    public function configureOptions(OptionsResolver $resolver) {
        $resolver->setDefaults([
            'data_class' => TaskBoard::class,
            'csrf_protection' => false,
        ]);
    }

Controller

class TaskBoardAPIController extends AbstractController {

    public function postTaskBoard(Request $request) {

        $form = $this->createForm(TaskBoardType::class, new TaskBoard());

        $data = json_decode(
                $request->getContent(), true
        );

        var_dump($data);

        $form->submit($data);

        if (!$form->isValid()) {
            return new JsonResponse(
                    [
                'status' => 'error',
                'errors' => $form->getErrors(),
                'form' => $form,
                    ], JsonResponse::HTTP_BAD_REQUEST
            );
        }

        $this->entityManager->persist($form->getData());
        $this->entityManager->flush();

        return new JsonResponse(
                [
            'status' => 'ok',
                ], JsonResponse::HTTP_CREATED
        );
    }

JSON sent

{
    "name": "XXX",
    "user": {
        "id": 1,
        "username": "BFA"
    },
    "description": "XXXXXXXXXXXXXXX",
    "status": {
        "id": 1,
        "name": "To Do"
    },
    "completed": false
}

The form is not valid and blank in the JsonResponse.

I based myself on : https://codereviewvideos.com/course/beginners-guide-back-end-json-api-front-end-2018/video/symfony-4-json-api-form-submission and Deserialize an entity with a relationship with Symfony Serializer Component

Thanks for your help.

2 Answers 2

1

What was wrong was the JSON input.

The form does this :

class TaskBoardType extends AbstractType {

public function buildForm(FormBuilderInterface $builder, array $options) {
    $builder
            ->add('name', TextType::class)
            ->add('user', EntityType::class, array(
                'class' => User::class,
                'choice_label' => 'username',
                    )
            )
            ->add('description', TextareaType::class, array(
                'required' => false
            ))
            ->add('status', EntityType::class, array(
                'class' => Status::class,
                'choice_label' => 'name',
                    )
            )
            ->add('deadLine', DateTimeType::class)
    ;
}

public function configureOptions(OptionsResolver $resolver) {
    $resolver->setDefaults([
        'data_class' => TaskBoard::class,
        'csrf_protection' => false,
    ]);
}

When checking the code generated from the form, this is the result :

<div>
   <label for="task_board_user" class="required">User</label>
   <select id="task_board_user" name="task_board[user]">
      <option value="1">XXX</option>
      <option value="2">XXX</option>
   </select>
</div>

Thus the form is expecting directly an INT/ID.

By changing the JSON as follow it goes through validation :

{
    "name": "XXXO",
    "user": 1,
    "description": "XXXXXXXXXXXXXXX",
    "status": 1
}
Sign up to request clarification or add additional context in comments.

Comments

0

Your forgot to handle and get form submission. Note that you will need to get request data using something like

    $form = $this->createFormBuilder($task)
        ... add fields
        ->getForm();

    $form->handleRequest($request); // handling request

    if ($form->isSubmitted() && $form->isValid()) {
        // $form->getData() holds the submitted values
        // but, the original `$task` variable has also been updated
        $task = $form->getData();

        // ... perform some action, such as saving the task to the database
        // for example, if Task is a Doctrine entity, save it!
        // $entityManager = $this->getDoctrine()->getManager();
        // $entityManager->persist($task);
        // $entityManager->flush();

        return $this->redirectToRoute('...');
    }

for more, see Handling Form Submissions

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.