-1

I have an entity Professionnal with Jobs in API Plateform project 3.2.

When I call host/api/professionnals I got a result like this :

{
    "data": [
        {
            "id": "/api/professionnals/1",
            "type": "Professionnal",
            "attributes": {
                "email": "[email protected]",
                "firstname": "JOHN",
                "lastname": "DOE"
            },
            "relationships": {
                "jobs": {
                    "data": [
                        {
                            "type": "Job",
                            "id": "/api/jobs/1"
                        },
                        {
                            "type": "Job",
                            "id": "/api/jobs/2"
                        }
                    ]
                }
            }
        },
        ...
    ]
}

I would like the job label property but it seems to be more difficult with relation ManyToMany in API Plateform to get it.

Professionnal.php Entity

<?php

namespace App\Entity;

use ...

#[ApiResource(normalizationContext: ['groups' => ['professionnal:read']])]
#[ORM\Entity(repositoryClass: ProfessionnalRepository::class)]
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_EMAIL', fields: ['email'])]
class Professionnal implements UserInterface, PasswordAuthenticatedUserInterface {
    
    ...

    #[Assert\NotBlank]
    #[Assert\Email]
    #[ORM\Column(length: 180, unique: true)]
    #[Groups(['professionnal:read'])]
    private ?string $email = null;

    ...
    
    #[ORM\Column(length: 150)]
    #[Groups(['professionnal:read'])]
    private ?string $firstname = null;

    #[ORM\Column(length: 255)]
    #[Groups(['professionnal:read'])]
    private ?string $lastname = null;

    ...

    /**
     * @var Collection<int, Job>
     */
    #[ORM\ManyToMany(targetEntity: Job::class, inversedBy: 'professionnals', fetch: "EAGER")]
    #[ORM\JoinTable(name: 'professionnal_job')]
    #[ORM\JoinColumn(name: 'professionnal_id', referencedColumnName: 'id', nullable: false)]
    #[ORM\InverseJoinColumn(name: 'job_id', referencedColumnName: 'id', nullable: false)]
    #[Groups(['professionnal:read'])]
    private Collection $jobs;


    public function __construct() {
    
        $this->jobs = new ArrayCollection();
    }

    ...
    
    /**
     * @return Job[]
     */
    #[Groups(['professionnal:read'])]
    public function getJobs(): array {
        return $this->jobs->getValues();
    }


}

Job.php Entity

<?php

namespace App\Entity;

...

#[ORM\Entity(repositoryClass: JobRepository::class)]
#[ApiResource()]
class Job {
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 255)]
    #[Groups(['professionnal:read', 'job:read'])]
    private ?string $label = null;


    /**
     * @var Collection<int, Professionnal>
     */
    #[ORM\ManyToMany(targetEntity: Professionnal::class, mappedBy: 'jobs')]
    private Collection $professionnals;

    public function __construct() {
        $this->professionnals = new ArrayCollection();
    }

    public function __toString(): string {
        return (is_null($this->getLabel())) ? '' : $this->getLabel();
    }

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

    public function getLabel(): ?string {
        return $this->label;
    }

    public function setLabel(string $label): static {
        $this->label = $label;

        return $this;
    }


    /**
     * @return Collection<int, Professionnal>
     */
    public function getProfessionnals(): Collection {
        return $this->professionnals;
    }

    ...
}

I tried to add this config code without success :/

api_platform:
    eager_loading:
        max_joins: 100

Someone has got an idea ? Should I find a workaround or there is a proper solution to do this ?

1 Answer 1

0

I found a solution based on custom DTO and Provider. Basicaly, API Plateform does not offer an easy way yet to get object properties from an ManyToMany relation.

So...

  1. Make a DTO and Provider

  2. Replace #[ApiResource] with #[GetCollection(output: ProfessionalDTO::class, provider: ProfessionalsProvider::class)]

  3. with jobs property as Collection create a new method.

    public function getJobs(): array { return $this->jobs->getValues(); }

  4. Declare the provider in services.yaml

Professional.php

namespace App\Entity;

use ...
use ApiPlatform\Metadata\GetCollection;

#[GetCollection(output: ProfessionalDTO::class, provider: ProfessionalsProvider::class)]
#[ORM\Entity(repositoryClass: ProfessionalRepository::class)]
#[ORM\UniqueConstraint(name: 'UNIQ_IDENTIFIER_EMAIL', fields: ['email'])]
class Professional implements UserInterface, PasswordAuthenticatedUserInterface {
    
    ...

    #[Assert\NotBlank]
    #[Assert\Email]
    #[ORM\Column(length: 180, unique: true)]
    #[Groups(['professional:read'])]
    private ?string $email = null;

    ...
    
    #[ORM\Column(length: 150)]
    #[Groups(['professional:read'])]
    private ?string $firstname = null;

    #[ORM\Column(length: 255)]
    #[Groups(['professional:read'])]
    private ?string $lastname = null;

    ...

    /**
     * @var Collection<int, Job>
     */
    #[ORM\ManyToMany(targetEntity: Job::class, inversedBy: 'professionals', fetch: "EAGER")]
    #[ORM\JoinTable(name: 'professional_job')]
    #[ORM\JoinColumn(name: 'professional_id', referencedColumnName: 'id', nullable: false)]
    #[ORM\InverseJoinColumn(name: 'job_id', referencedColumnName: 'id', nullable: false)]
    #[Groups(['professional:read'])]
    private Collection $jobs;


    public function __construct() {
    
        $this->jobs = new ArrayCollection();
    }

    ...
    
    /**
     * @return Job[]
     */
    public function getJobs(): array {
        return $this->jobs->getValues();
    }


}

src/Dto/ProfessionalDTO.php

namespace App\Dto;

use App\Entity\Professional;

class ProfessionalDTO {
    public string $id;
    public string $email;
    public string $firstname;
    public string $lastname;
    public array $jobs;

    public function __construct(Professional $professional) {
        $this->id = $professional->getId();
        $this->email = $professional->getEmail();
        $this->firstname = $professional->getFirstname();
        $this->lastname = $professional->getLastname();

        $this->jobs = [];
        foreach ($professional->getJobs() as $job) {
            $this->jobs[] = [
                    'id' => $job->getId(),
                    'label' => $job->getLabel()
            ];
        }
    
    }
}

src/State/ProfessionalsProvider.php

<?php

namespace App\State;

use ApiPlatform\Metadata\Operation;
use ApiPlatform\State\ProviderInterface;
use App\Dto\ProfessionalDTO;
use App\Entity\Professional;
use App\Repository\ProfessionalRepository;

class ProfessionalsProvider implements ProviderInterface {

    private ProfessionalRepository $professionalRepository;

    public function __construct(ProfessionalRepository $professionalRepository)
    {
        $this->professionalRepository = $professionalRepository;
    }

    public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null {
        $professionals = $this->professionalRepository->findAll();
        return array_map(
                fn(Professional $professional) => new ProfessionalDTO($professional),
                $professionals
        );
    }
}

services.yaml.

App\State\ProfessionalsProvider:
    arguments:
        $professionalRepository: '@App\Repository\ProfessionalRepository'
Sign up to request clarification or add additional context in comments.

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.