Using the repository:

/**
 * @Route("/post/{id}", name="blog_by_id", requirements={"id"="\\d+"})
 */
public function post(int $id): JsonResponse
{
    $repository = $this->getDoctrine()->getRepository(BlogPost::class);

    return $this->json(
        $repository->find($id)
    );
}

Using @ParamConverter implicitly:

/**
 * @Route("/post/{id}", name="blog_by_id", requirements={"id"="\\d+"})
 */
public function post(**BlogPost $post**): JsonResponse
{
    //Automatically gets the repository and does find($id);
    return $this->json(**$post**);
}

<aside> 📌 Notice that in the signature we replaced int $id with BlogPost $post Symfony uses the {id} in the @Route to know which parameter to filter by, being the equivalent of findOneBy(['id' => <contents of {id}>]);

</aside>

Using @ParamConverter explicitly:

/**
 * @Route("/post/{id}", name="blog_by_id", requirements={"id"="\\d+"})
 * **@ParamConverter("post", class="App:BlogPost")**
 */
public function post(**$post**): JsonResponse
{
    //Automatically gets the repository and does find($id);
    return $this->json($post);
}

<aside> 📌 Here we removed the type BlogPost from the signature and did the mapping using an annotation.

</aside>

Using @ParamConverter with manual mapping:

/**
 *@Route("/post/{**slug**}", name="blog_by_slug")
 *@ParamConverter("post", options={"mapping": {**"slug"**: "slug"}})
 */
public function postBySlug(BlogPost $post): JsonResponse
{
//Automatically gets the repository and does findOneBy(['slug' => $slug]);
    return $this->json($post);
}

<aside> 📌 In the mapping, the first parameter should match the route, while the second one should match the entity.

</aside>