Serialize Entity with ManyToMany relationship in Symfony 5

Viewed 143

I have a test app with a manytomany relationship and I can get a JSON correctly:

Job.php:

<?php

namespace App\Entity;

use Symfony\Component\Serializer\Annotation\Groups;
use App\Repository\JobRepository;
use Doctrine\ORM\Mapping as ORM;
use \App\Entity\Category;

/**
 * @ORM\Entity(repositoryClass=JobRepository::class)
 */
class Job
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

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

    /**
     * @ORM\ManyToMany(targetEntity="App\Entity\Category", inversedBy="jobs")
     * @ORM\JoinTable(
     *      name="jobs_categories",
     *      joinColumns={@ORM\JoinColumn(name="job_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="category_id", referencedColumnName="id")}
     * )
     */
    private $categories;

     /**
     * Constructor
     */
    public function __construct()
    {
        $this->categories = new \Doctrine\Common\Collections\ArrayCollection();
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }
    
    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    public function addCategory($categories): self
    {
        $this->categories[] = $categories;
    
        return $this;
    }

    public function removeCategory(Category $categories)
    {
        $this->categories->removeElement($categories);
    }

    public function getCategories()
    {
        return $this->categories;
    }

    public function __toString () {
        return $this->name;
    } 
}

Category.php

<?php

namespace App\Entity;

use App\Repository\CategoryRepository;
use Doctrine\ORM\Mapping as ORM;
use App\Entity\Job;

/**
 * @ORM\Entity(repositoryClass=CategoryRepository::class)
 */
class Category
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

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

    /**
     * @ORM\ManyToMany(targetEntity="Job", mappedBy="categories")
     */
    protected $jobs;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->jobs = new \Doctrine\Common\Collections\ArrayCollection();
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;

        return $this;
    }

    public function addJob(Job $post): self
    {
        $this->jobs[] = $post;

        return $this;
    }

    public function removeJob(Job $post)
    {
        $this->jobs->removeElement($post);
    }

    public function getJob()
    {
        return $this->jobs;
    }

    public function __toString () {
        return $this->name;
    } 
}

index.html (JQuery Ajax):

// Añadir tarea
$('#addJob').click(function () {
   var name = $('#name').val();
   var categories = [];
   var categoriesChbx = $(".category");

   if (name === "") {
       alert("Tienes que agregar una tarea."); // TODO
   } else {
       categoriesChbx.each(function () {
           var item = $(this);
           item.is(':checked') ? categories.push(item.attr("name")) : "";
      });

      var job = {
           name: name,
           categories: categories
      };
           var error = false;

           $.ajax({
               url: "{{ url('ajax_save_job') }}",
               type: "POST",
               dataType: "json",
               data: {
                   "job": job,
                   "error": error
              },
              async: true,
              success: function (data, error) {
                  console.log(data, error);

                  

                  if (error !== false) {
                      // TODO
                      errorDiv = $('.error');
                      errorDiv.html(error);
                      errorDiv.show();
                 }
             }
          });
      }
      return false;
});

BackendController.php (ajax method):

public function ajaxSaveJob ()
    {
        //dump($request->isXMLHttpRequest());
        if ($request->isXMLHttpRequest()) {         
            $jobArray = $request->request->get('job');

            $jobInclude = $this->getDoctrine()->getRepository(Job::class)->findOneBy(['name' => $jobArray['name']]);
            
            if (!isset($jobInclude)) {
                $job = new Job();

                $job->setName($jobArray['name']);

                if (isset($jobArray['categories'])) {
                    foreach ($jobArray['categories'] as $value) {
                        $category = new Category();
                        $category = $this->getDoctrine()->getRepository(Category::class)->findOneBy(['name' => $value]);
                        $job->addCategory($category);
                    }
                }


                $entityManager = $this->getDoctrine()->getManager();
                $entityManager->persist($job);
                $entityManager->flush();
              
                $encoders = [new JsonEncoder()];
                $normalizers = [new ObjectNormalizer()];
                $serializer = new Serializer($normalizers, $encoders);

                // Serializar objeto a JSON
                $jobJson = $serializer->serialize($job, 'json', [
                    'circular_reference_handler' => function ($object) {
                        return $object->getName();
                    }
                ]);
                dump($jobJson);

               return new JsonResponse([ 'data' => $jobJson, 'error' => false ]);
            }
            else {
                return new JsonResponse([ 'data' => '', 'error' => 'Ese taréa ya existe' ]);
            }

        }
        else {
            return new JsonResponse([ 'data' => '', 'error' => "Error: ..." ]); // TODO
        }
    }

I want to get somethink like this:

{
    "id":137,
    "name":"43545",
    "categories":["php", "javascript"]
}

But instead I get this:

{
    "id":142,
    "name":"Diego",
    "categories":[{"id":5,"name":"javascript","job":[{"id":103,"name":"5654","categories":[{"id":4,"name":"php","job":["5654",{"id":139,"name":"Mar\u00eda","categories":["php","javascript",{"id":6,"name":"css","job":[{"id":135,"name":"53453453","categories":["javascript","css"]},{"id":138,"name":"Fernando","categories":["javascript","css"]},"Mar\u00eda",{"id":140,"name":"Jose","categories":["css"]}]}]}]},"javascript"]},{"id":135,"name":"53453453","categories":["javascript",{"id":6,"name":"css","job":["53453453",{"id":138,"name":"Fernando","categories":["javascript","css"]},{"id":139,"name":"Mar\u00eda","categories":[{"id":4,"name":"php","job":[{"id":103,"name":"5654","categories":["php","javascript"]},"Mar\u00eda"]},"javascript","css"]},{"id":140,"name":"Jose","categories":["css"]}]}]},{"id":138,"name":"Fernando","categories":["javascript",{"id":6,"name":"css","job":[{"id":135,"name":"53453453","categories":["javascript","css"]},"Fernando",{"id":139,"name":"Mar\u00eda","categories":[{"id":4,"name":"php","job":[{"id":103,"name":"5654","categories":["php","javascript"]},"Mar\u00eda"]},"javascript","css"]},{"id":140,"name":"Jose","categories":["css"]}]}]},{"id":139,"name":"Mar\u00eda","categories":[{"id":4,"name":"php","job":[{"id":103,"name":"5654","categories":["php","javascript"]},"Mar\u00eda"]},"javascript",{"id":6,"name":"css","job":[{"id":135,"name":"53453453","categories":["javascript","css"]},{"id":138,"name":"Fernando","categories":["javascript","css"]},"Mar\u00eda",{"id":140,"name":"Jose","categories":["css"]}]}]},"Diego"]}]
}

¿How could you get only the categories associated with the currently handled job?

PS: Sorry for my bad English.

0 Answers
Related