I start to create a RESTFul API using Symfony2 framework. It will return data only in JSON format. I'm trying to create a method in action controller that returns a list of users. The problem is that my code is too excess and is not elegant. I think that I do something wrong because simple SQL queries will be more shorter and be clearer than my code.
My method should get 4 input parameters from URL: limit, offset, sort field and sort direction. The output should be a JSON with array of users. I want to configure the fields in JSON using Symfony2 serializers. And I want to return total quantity of users in "X-Total-Count" header.
As I understood it is better to use FOSRestBundle. So, I tried this:
<?php
namespace AppBundle\Controller;
use FOS\RestBundle\Controller\FOSRestController;
use Symfony\Component\HttpFoundation\Request;
use FOS\RestBundle\Controller\Annotations;
use FOS\RestBundle\Request\ParamFetcherInterface;
class UserController extends FOSRestController
{
/**
* List all notes.
*
* @Annotations\QueryParam(name="_page", requirements="\d+", default=1, nullable=true, description="Page number.")
* @Annotations\QueryParam(name="_perPage", requirements="\d+", default=30, nullable=true, description="Limit.")
* @Annotations\QueryParam(name="_sortField", nullable=true, description="Sort field.")
* @Annotations\QueryParam(name="_sortDir", nullable=true, description="Sort direction.")
*
* @Annotations\View()
*
* @param Request $request the request object
* @param ParamFetcherInterface $paramFetcher param fetcher service
*
* @return array
*/
public function getUsersAction(Request $request, ParamFetcherInterface $paramFetcher)
{
$sortField = $paramFetcher->get('_sortField');
$sortDir = $paramFetcher->get('_sortDir');
$page = $paramFetcher->get('_page');
$limit = $paramFetcher->get('_perPage');
$orderBy = null;
if ($sortField && $sortDir) {
$orderBy = [
$sortField => $sortDir
];
}
$offset = ($page - 1) * $limit;
$repository = $this->getDoctrine()
->getRepository('AppBundle:User');
$users = $repository->findBy([], $orderBy, $limit, $offset);
return $users;
}
...
}
I think that this code is good because it is short and I can use serializer. And I can configure wich fields should be in response. But in this example I don't have a response object and I can't set "X-Total-Count" header. Also I don't know how to get total rows quantity.
I tried another one case:
<?php
namespace AppBundle\Controller;
use Symfony\Component\HttpFoundation\JsonResponse;
use FOS\RestBundle\Controller\FOSRestController;
use Symfony\Component\HttpFoundation\Request;
use FOS\RestBundle\Controller\Annotations;
use FOS\RestBundle\Request\ParamFetcherInterface;
use Doctrine\ORM\Tools\Pagination\Paginator;
class UserController extends FOSRestController
{
/**
* List all users.
*
* @Annotations\QueryParam(name="_page", requirements="\d+", default=1, nullable=true, description="Page number.")
* @Annotations\QueryParam(name="_perPage", requirements="\d+", default=30, nullable=true, description="Limit.")
* @Annotations\QueryParam(name="_sortField", nullable=true, description="Sort field.")
* @Annotations\QueryParam(name="_sortDir", nullable=true, description="Sort direction.")
*
* @param Request $request the request object
* @param ParamFetcherInterface $paramFetcher param fetcher service
*
* @return array
*/
public function getUsersAction(Request $request, ParamFetcherInterface $paramFetcher)
{
$sortField = $paramFetcher->get('_sortField');
$sortDir = $paramFetcher->get('_sortDir');
$page = $paramFetcher->get('_page');
$limit = $paramFetcher->get('_perPage');
$offset = ($page - 1) * $limit;
$em = $this->getDoctrine()->getEntityManager();
$qb = $em->createQueryBuilder();
$qb->select('u')
->from('AppBundle:User', 'u');
if ($sortField && $sortDir) {
$qb->orderBy('u.' . $sortField, $sortDir);
}
$query = $qb->getQuery();
$query->setFirstResult($offset)
->setMaxResults($limit);
$paginator = new Paginator($query);
$totalCount = $paginator->count();
$users = $query->getResult();
$response = new JsonResponse($users);
$response->headers->set('X-Total-Count', $totalCount);
return $response;
}
...
}
In this case I have a response object, I can set a "X-Total-Count" header. I can get total rows quantity using Paginator. But I can't use serializer like in previous example. And I think that this code is too excess and is not elegant. I use query builder, query, paginator just for getting a list of users.
Please, tell me how to get the list of users using simple, elegant code.
Instead of just returning $users like in the first code sample, you can do it like this:
$view = $this
->view($users, 200);
->setHeader('X-Total-Count', $totalCount);
return $this->handleView($view);
Also you could create your repository that extends base repository, and move your pagination logic there. That way you controller would be very thin.