Search code examples
symfonysymfony4symfony-routing

How to return specific data using urls and routing in symfony 4 when making an API GET request?


I'm new to Symfony and trying to learn the basics. I recently saw this question and I wanted to learn how routing works. So I copied the Controller1.php from the question and changed it to UserController.php this:

<?php


namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

class UsersController extends AbstractController
{
    /**
     * @Route("/listOf/Users", methods={"GET"})
     * @param Request $request
     * @return JsonResponse
     */
    public function list(Request $request)
    {
        if (empty($request->headers->get('api-key'))) {
            return new JsonResponse(['error' => 'Please provide an API_key'], 401);
        }

        if ($request->headers->get('api-key') !== $_ENV['API_KEY']) {
            return new JsonResponse(['error' => 'Invalid API key'], 401);
        }

        return new JsonResponse($this->getDoctrine()->getRepository('App\Entity\User')->findAll());
    }
}

Which indeed, as OP claims, works fine and return the following (manually added data using Sequel Pro) list:

    [
    {
        "id": 14,
        "name": "user1 Name"
    },
    {
        "id": 226,
        "name": "user2 Name"
    },
    {
        "id": 383,
        "name": "user3 Name"
    }
    ]

So my next step was to learn how to adjust this list of users to return a specific user with a given id. So I followed the official Symfony Docs on Routing. So I changed the code to the following:

<?php


namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

class UsersController extends AbstractController
{
    /**
     * @Route("/listOf/Users/{IdUser}", requirements={"IdUser"="\d+"},  methods={"GET"})
     * @param Request $request
     * @param int $IdUser
     * @return JsonResponse
     */
    public function list(Request $request, int $IdUser)
    {
        if (empty($request->headers->get('api-key'))) {
            return new JsonResponse(['error' => 'Please provide an API_key'], 401);
        }

        if ($request->headers->get('api-key') !== $_ENV['API_KEY']) {
            return new JsonResponse(['error' => 'Invalid API key'], 401);
        }

        return new JsonResponse($this->getDoctrine()->getRepository('App\Entity\User\{IdUser}')->findAll());
    }
}

and tried to request the data of the user with the id 14, but this didn't work and yielded the following error:

Class App\Entity\User{IdUser} does not exist (500 Internal Server Error)

What more changes do I need to do to be able to do what I'm trying to do?

This is my User.php entity:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 */
class User implements \JsonSerializable
{

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

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

    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 jsonSerialize()
    {
        return get_object_vars($this);
    }
}

And my UserRepository.php has nothing beside the automatically generated code in it.

Edit: My first request which worked was of the form: http://domainName.local:80/listOf/Users and my second one was: http://domainName.local:80/listOf/Users/14


Solution

  • As promised earlier - here's why it does not work and how to make it work.

    Let's examine the code blow:

    $this->getDoctrine()->getRepository('App\Entity\User\{IdUser}')->findAll();
    

    Basically you're saying: doctrine, give me the repository that is responsible for handling the entity App\Entity\User\{IdUser} literally and ofc there is no such entity class. What you really want is the repo for App\Entity\User.

    The string you pass to the getRepository() method always has to be the fully qualified class name of an entity - period. To ensure you never have any typos here, it's quite helpful to use the class constant of the entity, which looks like so

    $repo = $this->getDoctrine()->getRepository(App\Entity\User::class);
    

    Once you have the repository, you can call it's different methods as shown in the doctrine documentation here https://www.doctrine-project.org/api/orm/latest/Doctrine/ORM/EntityRepository.html In your case, you have the variable $IdUser, which you want to be mapped to the db column/entity property id of the user class. Since you know that you want exactly this one user with the id 14, all you have to do is tell the repo to find exactly one which looks like this.

    // here's the example for your specific case
    $user = $repo->findOneBy(['id' => $IdUser]);
    
    // another example could be e.g. to search a user by their email address
    $user = $repo->findOneBy(['email' => $email]);
    
    // you can also pass multiple conditions to find*By methods
    $user = $repo->findOneBy([
        'first_name' => $firstName,
        'last_name' => $lastName,
    ]);
    

    Hopefully, this was more helpful than confusing =)