
Symfony how to use FOSRestBundle
In this post, I would like to talk about how to properly build the RESTfull API for AngularJS and other front-end frameworks with a backend on Symfony.
And, as you probably already guessed, I will use the FOSRestBundle - a wonderful bundle that will help us implement the backend.
There will be no examples of how to work with Angular, I will only describe working with Symfony FosRestBundle.
For work, we also need a JMSSerializerBundle for serializing data from Entity to JSON or other formats, excluding some fields for an entity (for example, a password for the API for the method of obtaining a list of users) and much more. You can read more in the documentation.
Installation and configuration
1) Download the required dependencies in our composer.json 2) Configuration
And now we are editing our config.yml.
First, we will configure our FOSRestBundle
body_listener includes EventListener to monitor what format the user response is needed, based on its Accept- * headers
view_response_listener - This setting allows you to simply return the View for a particular query
serializer.serialize_null - This setting tells that we also want so that NULL is serialized, like everything else, if it is not set or set to false, then all fields that are null simply will not be displayed in the server response.
PS: thank you for reminding lowadka
body_converter.rules - it contains an array for settings focused on a particular address, in this example we are for all requests that have the / api prefix, we will return JSON, in all other cases - html.
Now let's start customizing our JMSSerializeBundle
It makes sense to dwell on the moment with jms_serializer.metadata.directories , where we tell the serializer that the configuration for a particular class (entity) is located there or there :)
We agree that we need to display the entire list of users, I personally use FosUserBundle in my projects and here is my essence:
I give just an example of this entity, which inherits from the main FosUserBundle model. This is important because both classes will have to be configured separately for the JmsSerializerBundle.
So back to jms_serializer.metadata.directories :
Here we just indicate that for the AppBundle classes we will look for the configuration for serialization in app / config / serializer / AppBundle, and for FosUserBundle in app / config / serializer / FosUserBundle.
The configuration for the class will be automatically in the format:
For the class AppBundle \ Entity \ User - app / config / serializer / AppBundle / Entity.User. (Yml | xml | php)
For the class of the base model FosUserBundle - app / config / serializer / FosUserBundle / Model.User. (Yml | xml | php)
Personally, I prefer to use YAML. We will finally begin to tell the JMSSerializer how we need it to configure this or that class.
app / config / serializer / AppBundle / Entity.User.yml
app / config / serializer / FosUserBundle / Model.User.yml
Just like that, we were able to talk about what we want to see about the following response format from the server when receiving data from 1 user:
In principle, this configuration is not necessary to register and the server will return all data about the entity. Only in this case it is illogical for us to show many things, for example, such as a password. Therefore, I considered it necessary to demonstrate just such an implementation in this example.
Now let's start creating the controller.
First, create a route:
Pay attention to / api - do not forget to add it, but if you want to change, you will have to change the configuration for fos_rest in config.yml.
Now BackendUserBundle / Resources / config / routing.yml itself:
Now you can start creating the controller itself:
Note that we are now inheriting from FOS \ RestBundle \ Controller \ FOSRestController .
By the way, you probably noticed the annotation View (serializerGroups = {"user"}).
The fact is, because we want to see the data of App \ Entity \ User and the main FosUserBundle model, in which all other fields are stored, we need to create a specific group, in this case - “user”.
So, we have 2 actions getUserAction and getUsersAllAction. Now you will understand the essence of the specificity of the names of controller methods.
Let's make debug of all routes:
$ app / console debug: route | grep api
Get:
Consider the following example with new methods:
And, as you probably already guessed, I will use the FOSRestBundle - a wonderful bundle that will help us implement the backend.
There will be no examples of how to work with Angular, I will only describe working with Symfony FosRestBundle.
For work, we also need a JMSSerializerBundle for serializing data from Entity to JSON or other formats, excluding some fields for an entity (for example, a password for the API for the method of obtaining a list of users) and much more. You can read more in the documentation.
Installation and configuration
1) Download the required dependencies in our composer.json 2) Configuration
"friendsofsymfony/rest-bundle": "^1.7",
"jms/serializer-bundle": "^1.1"
// app/AppKernel.php
class AppKernel extends Kernel
{
public function registerBundles()
{
$bundles = array(
// ...
new JMS\SerializerBundle\JMSSerializerBundle(),
new FOS\RestBundle\FOSRestBundle(),
);
// ...
}
}
And now we are editing our config.yml.
First, we will configure our FOSRestBundle
fos_rest:
body_listener: true
view:
view_response_listener: true
serializer:
serialize_null: true
body_converter:
enabled: true
format_listener:
rules:
- { path: '^/api', priorities: ['json'], fallback_format: json, exception_fallback_format: html, prefer_extension: true }
- { path: '^/', priorities: [ 'html', '*/*'], fallback_format: html, prefer_extension: true }
body_listener includes EventListener to monitor what format the user response is needed, based on its Accept- * headers
view_response_listener - This setting allows you to simply return the View for a particular query
serializer.serialize_null - This setting tells that we also want so that NULL is serialized, like everything else, if it is not set or set to false, then all fields that are null simply will not be displayed in the server response.
PS: thank you for reminding lowadka
body_converter.rules - it contains an array for settings focused on a particular address, in this example we are for all requests that have the / api prefix, we will return JSON, in all other cases - html.
Now let's start customizing our JMSSerializeBundle
jms_serializer:
property_naming:
separator: _
lower_case: true
metadata:
cache: file
debug: "%kernel.debug%"
file_cache:
dir: "%kernel.cache_dir%/serializer"
directories:
FOSUserBundle:
namespace_prefix: FOS\UserBundle
path: %kernel.root_dir%/config/serializer/FosUserBundle
AppBundle:
namespace_prefix: AppBundle
path: %kernel.root_dir%/config/serializer/AppBundle
auto_detection: true
It makes sense to dwell on the moment with jms_serializer.metadata.directories , where we tell the serializer that the configuration for a particular class (entity) is located there or there :)
We agree that we need to display the entire list of users, I personally use FosUserBundle in my projects and here is my essence:
balance = $balance;
return $this;
}
/**
* Get balance
*
* @return integer
*/
public function getBalance()
{
return $this->balance;
}
}
I give just an example of this entity, which inherits from the main FosUserBundle model. This is important because both classes will have to be configured separately for the JmsSerializerBundle.
So back to jms_serializer.metadata.directories :
directories:
FOSUserBundle:
namespace_prefix: FOS\UserBundle
path: %kernel.root_dir%/config/serializer/FosUserBundle
AppBundle:
namespace_prefix: AppBundle
path: %kernel.root_dir%/config/serializer/AppBundle
Here we just indicate that for the AppBundle classes we will look for the configuration for serialization in app / config / serializer / AppBundle, and for FosUserBundle in app / config / serializer / FosUserBundle.
The configuration for the class will be automatically in the format:
For the class AppBundle \ Entity \ User - app / config / serializer / AppBundle / Entity.User. (Yml | xml | php)
For the class of the base model FosUserBundle - app / config / serializer / FosUserBundle / Model.User. (Yml | xml | php)
Personally, I prefer to use YAML. We will finally begin to tell the JMSSerializer how we need it to configure this or that class.
app / config / serializer / AppBundle / Entity.User.yml
AppBundle\Entity\User:
exclusion_policy: ALL
properties:
balance:
expose: true
app / config / serializer / FosUserBundle / Model.User.yml
FOS\UserBundle\Model\User:
exclusion_policy: ALL
group: user
properties:
id:
expose: true
username:
expose: true
email:
expose: true
balance:
expose: true
Just like that, we were able to talk about what we want to see about the following response format from the server when receiving data from 1 user:
{"id":1,"username":"admin","email":"admin","balance":0}
In principle, this configuration is not necessary to register and the server will return all data about the entity. Only in this case it is illogical for us to show many things, for example, such as a password. Therefore, I considered it necessary to demonstrate just such an implementation in this example.
Now let's start creating the controller.
First, create a route:
backend_user:
resource: "@BackendUserBundle/Resources/config/routing.yml"
prefix: /api
Pay attention to / api - do not forget to add it, but if you want to change, you will have to change the configuration for fos_rest in config.yml.
Now BackendUserBundle / Resources / config / routing.yml itself:
backend_user_users:
type: rest
resource: "@BackendUserBundle/Controller/UsersController.php"
prefix: /v1
Now you can start creating the controller itself:
getDoctrine()->getRepository('AppBundle:User')->findAll();
$view = $this->view($users, 200);
return $this->handleView($view);
}
/**
* @param $id
* @return \Symfony\Component\HttpFoundation\Response
* @View(serializerGroups={"user"})
*/
public function getUserAction($id)
{
$user = $this->getDoctrine()->getRepository('AppBundle:User')->find($id);
if (!$user instanceof User) {
throw new NotFoundHttpException('User not found');
}
$view = $this->view($user, 200);
return $this->handleView($view);
}
}
Note that we are now inheriting from FOS \ RestBundle \ Controller \ FOSRestController .
By the way, you probably noticed the annotation View (serializerGroups = {"user"}).
The fact is, because we want to see the data of App \ Entity \ User and the main FosUserBundle model, in which all other fields are stored, we need to create a specific group, in this case - “user”.
So, we have 2 actions getUserAction and getUsersAllAction. Now you will understand the essence of the specificity of the names of controller methods.
Let's make debug of all routes:
$ app / console debug: route | grep api
Get:
get_users_all GET ANY ANY /api/v1/users/all.{_format}
get_user GET ANY ANY /api/v1/users/{id}.{_format}
Consider the following example with new methods:
Напоминает Laravel Resource Controller, правда?
В комментариях показано по какому адресу и методу запроса будет выполнен тот или иной метод.
В следующий раз я расскажу вам о том, как правильно использовать FOSRestBundle для, например, вывода комментариев определенного пользователя по адресу: "/users/{id}/comments", создавать \ обновлять данные пользователей.