A Simple RESTful API Tutorial with Symfony 3

Categories: PHP, Symfony, Tutorial

This article was written in/for Symfony 3.2.7

Symfony is a fantastic framework that provides an excellent starting point for a wide array of applications and websites. Today, we are going to build a simple RESTful API that will allow us to create content and then retrieve it.

Note: Most sections have both a screenshot followed by code.

Requirements

For this simple project you will need to have a few programs installed, most of which are already installed if you are on a Linux based machine (this includes Mac OSX)

What is Symfony

Symfony is a set of PHP Components, a Web Application framework, a Philosophy, and a Community — all working together in harmony. – http://symfony.com/what-is-symfony

What is an API

API’s (Application Programming Interface) is a set of rules, functions and tools used by developers to interact with, or create, software. The API describes what functionality is available and in what way you can utilize that functionality. Basically, an API is a set of guidelines the we, as developers, use to make our products. The API we are going to build will allow other developers to add a quote, along with the associated source to a database. The API user will also be able to retrieve a quote based on a unique identifier.

This is a simple example of how API’s can be used but it provides a foundation for both getting started with API’s and getting started with Symfony.

Please Note: This tutorial is meant to get you started and the end result is a fun application. This is not production ready (i.e. don’t put it up on a live server). There is no security built in and it would be fairly easy for your server to be compromised. Instead, use this tutorial to start you on your learning adventure. That being said, Have Fun!

Getting Started

Now that we have installed our required software, we can jump right in a create a new project. The first thing that we need to do is create our Symphony project. Thanks to the Symfony CLI (command line interface) we can start a project just by running on simple command.

In terminal, navigate to your desired directory and type


	symfony new basicrestapi

What Just Happened?

We just instructed our Symfony installer to locate and download the laster version of Symfony (for this tutorial we are using the latest version: 3.2.7)

Start the Server

In the root directory, run php bin/console server:start. This will start an instance of PHP’s Built-in web server. You will receive instructions on how to view the site when this command completes.


php bin/console server:start

Create a new Class

We are building an API that handles Famous Quotes. Let’s create a new directory /src/AppBundle/Controller/Api/v1/QuoteController.php. This class will house our methods for adding and retrieving quotes. This is where will we put all of the logic for our API.

Why did we add the extra directories?

We could have simply used /src/AppBundle/Controller/DefaultController.php. We didn’t necessarily need to add the extra directories. We’ve added them for future proofing. We may want additional functionality in our application that compliments our API (such as a website to explain usage) and we will almost certainly create a new version of our API (as noted above, this is not secure).

Once we’ve added in additional features and security we can set up Version 2 of our API here: /src/AppBundle/API/v2/QuoteController.php

Our Class Setup

Let’s create our class called QuoteController.php. We’ll add a namespace and setup our class.

	

namespace AppBundle\Controller\Api\v1;

class QuoteController
{

}
	

Extending the Symfony Framework Controller

Symfony has some really helpful functionality built in. In order to use this functionality, our class will need to EXTEND the SymfonyFramwork Controller. Don’t forget to use the correct use statement.


namespace AppBundle\Controller\Api\v1;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class QuoteController extends Controller
{

}

Creating our New Quote Method

We’ve set up our class, extended Symfony’s Controller. It’s time to write the logic needed to accept a new quote. Start by adding the newAction() method. We are using annotation to set the route and the POST method. When using annotation, we must add in the appropriate use statements (both of which are part of Symfony’s FrameworkExtraBundle).
New Action


namespace AppBundle\Controller\Api\v1;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;

class QuoteController extends Controller
{

    /**
     * @Route("/api/v1/quote")
     * @Method("POST")
     */
    public function newAction() {

    }

}

Retrieving information from the Url

We will be accepting new quotes from url string. We will expect a structure like http://example.com/api/v1/quote?source=Farnsworth&quote=Yes%20if%20by%20allow%20you%mean%20force. (%20 is the equivelent to a space when using percent encoding). We will use the Request component to allow our method to get the data from the url. We can pass in a parameter to our method called $request and then use the method, provided by Symfony, to get parts of the URL query string. Again, we must add the HttpFoundation Request use statement for use in our class.

Request and the query string

	
namespace AppBundle\Controller\Api\v1;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Component\HttpFoundation\Request;

class QuoteController extends Controller
{

    /**
     * @Route("/api/v1/quote")
     * @Method("POST")
     */
    public function newAction(Request $request) {

        $sourceQuery = $request->query->get('source');
        $quote = $request->query->get('quote');

    }

}
	

Great! Now what do we do?

We’ve set up our class and our function and we are getting the information from the query sting but what are we going to do with it? We haven’t set up any way to store this infomration. Well, that’s what we are about to do! We need to setup a database and a table to store our Famous Quotes. Thankfully, Symfony knows how common this is and has included some very helpful functionality that we can start using right away.

Doctrine

Doctrine ORM (Object-Relational Mapping) and DBAL (Database Abstraction Layer) are tools that we can use to interface with our database. These tools give us a LOT of assistance when interacting with the database. We could spend a long time getting started with Doctrine, but for the purposes of this article we will only briefly discuss a small portion.

We will use doctrine to set up our database, create a table, and automagically create some very handy getter and setter methods. Learn more about Doctrine at http://symfony.com/doc/current/doctrine.html

Configure our database settings

Open app/config/parameters.yml and adjust the database settings. For now, we can ignore most of the contents of this file and just focus on the database name, username and user password.

Create a MySQL database and then adjust the settings in this file. If you are on a Linux based machine you can probably leave the username as root and password as either root or null.

Parameters

Create the Database

Doctrine has a very handy cli (command line interface) that we can use to create our database using the information we just added to parameters.yml (which are actually variable used in app/config.yml). In the command line navigate to your project root and type php bin/console doctrine:database:create.
doctrine:database:create

	
php bin/console doctrine:database:create
	

Create an Entity

Now that we have our Database we will use Doctrine to create an Entity class and table for our Famour Quotes.

An entity is just a basic class used to hold data. This class will be auto-generated by Doctrine just by filling out a few questions in the command line. In the command line, type php bin/console doctrine:generate:entity

I would highly recommend creating entities “by hand” before relying on CLI functionality. That being said, in an effort to keep this post about API’s, we will use the CLI.

	
php bin/console doctrine:generate:entity
	

Follow the Prompts

We are creating a simple entity with two fields ‘quote’ and ‘source’. You will be prompted for several bits of additional information, we will stick with the defaults for now.

	
The Entity shortcut name: AppBundle:Quote

Configuration format (yml, xml, php, or annotation) [annotation]: annotation

New field name (press  to stop adding fields): quote
Field type [string]: string
Field length [255]: 255
Is nullable [false]: false
Unique [false]: false

New field name (press  to stop adding fields): source
Field type [string]: string
Field length [255]: 255
Is nullable [false]: false
Unique [false]: false
	

Check Out the Files

Doctrine used our input to create two new files: src/AppBundle/Entity/Quote.php and src/AppBundle/Repository/QuoteRepository.php. Take a second and look through these files. QuoteRepository.php is an empty class but Quote.php has created getter and setter methods for both fields. That’s quite a handy shortcut, isn’t it?

Creating a table from our Entity

We have an Entity class for our Quotes but we still can’t save to the database yet because we don’t have our table. We can use another helpful Doctrine CLI command to generate tables from Entity classes. doctrine:schema:update –force. This will create the Quote table in our database.

	
php bin/console doctrine:schema:update --force
	

Using our Entity

Now the we have our database, table and some helpful methods set up, we can start setting up our data to save it. Back in src/AppBundle/Controller/Api/v1/QuoteController.php we will use our new Quote entity and setters to begin saving the data.

Create a new Quote object the use the set methods to “set” the query string parameters to the object.

Set the values

	
// Create a new empty object
$quote = new Quote();

// Use methods from the Quote entity to set the values
$quote->setSource($sourceQuery);
$quote->setQuote($quoteQuery);
	

Save to the Database

It’s finally time to save our Quote! To do this we will use Doctrine’s Entity Manager, persist our object (save it), then flush (execute) the query.

save to the database

	
/**
 * @Route("/api/v1/quote")
 * @Method("POST")
 */
public function newAction(Request $request) {

    // Get the Query Parameters from the URL
    // We will trust that the input is safe (sanitized)
    $sourceQuery = $request->query->get('source');
    $quoteQuery = $request->query->get('quote');

    // Create a new empty object
    $quote = new Quote();

    // Use methods from the Quote entity to set the values
    $quote->setSource($sourceQuery);
    $quote->setQuote($quoteQuery);

    // Get the Doctrine service and manager
    $em = $this->getDoctrine()->getManager();

    // Add our quote to Doctrine so that it can be saved
    $em->persist($quote);

    // Save our quote
    $em->flush();

}
	

Send a Quick Response

Let’s send a message back that alerts the user that the quote was added to the database. In this tutorial, we won’t be verifying that the quote made it in to the database we are just assuming it is. You don’t want to do this in production.

Response

	
return new Response('It\'s probably been saved', 201);
	

Test this with Postman

Postman is a powerful tool for testing your API with a nice GUI (graphical user interface). We will be just scratching the surface with what Postman can do.

Open Postman the correct method. This should default to GET, but we will want to use POST for this test. Enter your URL. If you’ve been following along it should look something like: http://127.0.0.1:8000/api/v1/quote?source=Fry&quote=No%20Im%20doesnt

See the Results

We didn’t check to see if the post actually works but we are sending a response that says that it most-likely worked. We should see that when we click send.

Get A Quote

Create a new method called getAction. This will allow users to request a quote with a given ID (which was automatically created by doctrine as a unique field). We will need to write our method annotations similar to how we did with the newAction.

Because we need a variable in the route, we will use curly braces to denote that is not static. We will use the same variable name as a parameter in our Method. (Note: these must match exactly)

get action

	
/**
 * @Route("/api/v1/quote/{id}")
 * @Method("GET")
 * @param $id
 */
public function getAction($id) {

}
	

Get the Repository

Doctrine to the rescue again. This time we will use the Doctrine service to get the repository based on the id. We’ll use the findOneBy method and retrieve the first record with a matchig ID.

get the repository

	
$quote = $this->getDoctrine()
	->getRepository('AppBundle:Quote')
	->findOneBy(['id' => $id]);
	

What Does that return

We’ve receieved an object from doctrine! You’ll see below that we have an object with an id, source and a quote. The problem though is that they are all private so we can access them directly.

	
object(AppBundle\Entity\Quote)[444]
  private 'id' => int 1
  private 'quote' => string 'test quote one' (length=14)
  private 'source' => string 'Farnsworth' (length=10)
	

Entity helper methods

So what do we do now? Remember how Doctrine created a bunch of helper methods for us? We can use those! Take a second and go back to look at /src/AppBundle/Entity/Quote.php. We can use getQuote() and getSource(). We will save it into an array to make it easier in the future.

	
$quote = $this->getDoctrine()
    ->getRepository('AppBundle:Quote')
    ->findOneBy(['id' => $id]);

  $data = [
    'quote' => $quote->getQuote(),
    'source' => $quote->getSource(),
  ];
	

Return A JSON Response

Now we can return the data as a JSON encoded string using the Symfony Response object.

\

	
$quote = $this->getDoctrine()
    ->getRepository('AppBundle:Quote')
    ->findOneBy(['id' => $id]);

  $data = [
    'quote' => $quote->getQuote(),
    'source' => $quote->getSource(),
  ];

  return new Response(json_encode($data));
	

Test this in Postman

This time we will switch the method from POST to GET and supply a new url. If you have been following the tutorial the URL should be similar to: http://127.0.0.1:8000/api/v1/quote/1

You now having a working API!

That’s it. You have a simple RESTful API built in Symfony 3! I hope this tutorial got you started and demystified a little bit of Symfony. Building API’s can be fun. Symfony is a great tool and has a lot to offer.

Let me know how you like the tutorial. If you have questions or comments. Leave a reply below. Thanks!

8 responses to “A Simple RESTful API Tutorial with Symfony 3

  1. Good example of creating API without FOSRestBundle. I have only two notices.
    1. You could use Route Annotation to Your class to add prefix for all actions (/api/v1)
    2. You could use JsonResponse as a shortcut for Response(json_encode

    1. Ah… I just recently found out about JsonResponse (and a few other built-in response types). I’m not exactly sure how to prefix all actions with /api/v1/ but I’ll figure it out and update the code!

      Thanks @golabiusz!

  2. Hi, good tutorial, you can do this.

    EXAMPLE:

    /**
    * @Route(“/api/v1/quote”)
    */
    class QuoteController extends Controller
    {
    /**
    * @Route(“/”)
    * @Method(“POST”)
    */
    public function newAction() { . . . }
    }

  3. thank you so much… this is very useful for to begin understanding … I follow this tutorial and everythings works with symfony 3.4 and mysql

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.