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)
- PHP
- MySQL
- Symfony http://symfony.com/doc/current/setup.html
- Postman http://getpostman.com
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).
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"e=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.
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.
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.
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.
// 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.
/**
* @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.
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"e=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)
/**
* @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.
$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!
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
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!
Hi, good tutorial, you can do this.
EXAMPLE:
/**
* @Route(“/api/v1/quote”)
*/
class QuoteController extends Controller
{
/**
* @Route(“/”)
* @Method(“POST”)
*/
public function newAction() { . . . }
}
I’ll have to try this out. I’m assuming the newAction @Route will expand on the class @Route?
Easy and at the point, no steps omitted. Thanks!
Nice Article ! i am also working restful api with symfony and publish article on that
Iink: https://www.cloudways.com/blog/rest-api-in-symfony-3-1/
Great article! It goes much more in depth! Thanks for sharing!
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