Returning to Symfony, building API and playing with AngularJS

It's been almost two months since I started working again, now I have two projects,
one of them using Symfony because my teammate in this project knew PHP, so it was the right choice.

Symfony has improved so much since I started using it a few years ago, now they use the package manager
called composer, it has a good template engine(Twig), and more. There are a lot of ways to do the same, so you could choose
the better solution for your current situation.

In our case we wanted to use Symfony with AngularJS, the Javascript client framework from Google, so a Restful API was necessary.
There is a really complete bundle (modules/plugins in Symfony) to achieve this, FOSRestBundle, but I thought it was too complex for what I wanted,
so I decided to use a mix with the @Template annotation in the Controller and define the routing with the _format special variable.

The controller would look more or less like this...


    ...

    /**
     * @Template()
     */
    public function indexAction()
    {
        $products = $this->getDoctrine()
                ->getRepository()
                ->find();

        return array("products" => $products);
    }

    /**
     *  @Template()
     */
    public function showAction($id)
    {
        $product = $this->getDoctrine()
                ->getRepository()
                ->findOneById($id);

        return array("product" => $product);
    }

    /**
     *  @Template()
     */
    public function newAction()
    {
        return array("product" => new Product());
    }

    /**
     *  @Template()
     */
    public function deleteAction($id)
    {
        $em = $this->getDoctrine()->getManager();
        $product = $em
                ->getRepository()
                ->find($id);

        if (!$product)
        {
            throw $this->createNotFoundException('msg');
        }

        $em->remove($product);
        $em->flush();

        return new RedirectResponse($this->generateUrl('product_index'));
    }

    /**
     *  @Template()
     */
    public function editAction($id)
    {
        return $this->render("MyBundle:Product:new.html.twig");
    }

    /**
     *  @Template()
     */
    public function saveAction($product_id=-1)
    {
        ... save stuff getting JSON object
    }

This would be a normal Controller defining CRUD actions for Product, we didn't need to define a template render here
because the @Template annotation will take care of finding the right template according to the name of the action
(*.html.twig or *.json.twig in my case). In the editAction we wanted to show a specific template so we didn't need to write the @Template annotation and we write the render explicitly.

About the model, for this to work is necessary for the Entity to implement the JsonSerializable interface (new in PHP 5.4),
and then create the jsonSerialize method returning the array.

Our routing file could be like this one:


    _product_index:
        pattern: /products/all
        defaults: { _controller: MyBundle:Product:index }
        requirements:
            _format: json
            _method: GET

    _product_new:
        pattern: /products/new
        defaults: {_controller: MyBundle:Product:new, _format: json }
        requirements:
            _method: GET

    product_get:
        pattern: /products/{id}
        defaults: {_controller: MyBundle:Product:show, _format: json }
        requirements:
            _method: GET

    product_create:
        pattern: /products/create
        defaults: { _controller: MyBundle:Product:save, _format: json}
        requirements:
            _method: POST

    product_save:
        pattern: /products/{id}
        defaults: { _controller: MyBundle:Product:save, _format: json}
        requirements:
            _method: PUT

    product_delete:
        pattern: /products/{id}
        defaults: { _controller: MyBundle:Product:delete, _format: json }
        requirements:
            _method: DELETE

To retrieve this data easily from Angular we would create a resource:


    angular.module('myServices', ['ngResource']).
        factory('Product', function($resource) {
            return $resource('/products/:productId', {}, {
                query: { method: 'GET', params: {productId: 'all'}, isArray: true},
                get: { method: 'GET'},
                save: { method : 'PUT'},
                create: { method : 'POST'},
                destroy: { method : 'DELETE'}
            });
        });

With this few lines we have all requests defined for Angular to retrieve and interact with data from server,
if you want to know more about Angular resources, there's good documentation in their site.

This is too long now so just a little tip, you could create the twig json template just
with this line of code...


    //Resources/.../product.json.twig
    \{\{ products|json_encode|raw \}\}

If you don't use the @Template annotation is not necessary to create this template, you could just serve a json
object using json_encode, but it's necessary with my approach.

Javier Aguirre

Read more posts by this author.

comments powered by Disqus