, (*1)
Orbitale ApiBundle
Allows the developer to create simple webservices based on an entities list in configuration., (*2)
Installation
With Composer, (*3)
Just write this command line instruction if Composer is installed in your Symfony root directory :, (*4)
composer require orbitale/api-bundle
I recommend to use Composer, as it is the best way to keep this repository update on your application.
You can easily download Composer by checking the official Composer website, (*5)
Initialization
You need to initiate some settings to make sure the bundle is configured properly., (*6)
-
First, register the required bundles in the kernel:, (*7)
<?php
# app/AppKernel.php
class AppKernel extends Kernel
{
public function registerBundles() {
$bundles = array(
// ...
new Orbitale\Bundle\ApiBundle\OrbitaleApiBundle(),
-
Import the routing in your application:, (*8)
# app/config/routing.yml
orbitale_api:
resource: "@OrbitaleApiBundle/Controller/"
type: annotation
prefix: /api/
The prefix is up to you, but it's important that the app has to be solitary in its relative path, for it to work in the best way., (*9)
- Done ! The rest is all specific configuration for your application.
Usage
Add your first entity in the config:, (*10)
# app/config/config.yml
orbitale_api:
services:
posts: { entity: AppBundle\Entity\Post }
Run your server:, (*11)
$ php app/console server:run
Navigate to its generated url http://127.0.0.1/app_dev.php/api/post
., (*12)
Now you should see something like this:, (*13)
{"posts":[]}
If you do see this, it means that the API generator is working !, (*14)
HTTP methods
The generator handles GET, POST (update), PUT (insert) and DELETE HTTP methods., (*15)
GET routes
-
orbitale_api_cget
: /{serviceName}
, (*16)
This route allows to get a collection of objects of the specified entity., (*17)
The received collection has the same key as the serviceName
, and is an array of objects., (*18)
-
orbitale_api_get
: /{serviceName}/{id}
, (*19)
This route retrieves a single object with its primary key (even if this primary key is not called id
). The received element will have the same key as the serviceName
but with removed trailing 's' at the end of it (for example, posts
will become post
)., (*20)
All the received attributes in the object will follow your ExclusionPolicy
and different Expose
or Exclude
settings in the jms_serializer
., (*21)
If you need more information about exposing or not some fields, you can check JMSSerializer's documentation, (*22)
-
orbitale_api_get_subrequest
: /{serviceName}/{id}/{subElement}
, (*23)
This is the great point of this Api generator., (*24)
This route can retrieve any element recursively depending on three parameters:, (*25)
- The parameter has to be a valid entity attribute.
- If the attribute is a collection, you can fetch one element in this collection by appending its primary key.
- The value must not be null (or it'll return an empty value).
For example, if your Page
entity has a title
attribute, you can type this url: http://127.0.0.1:8000/api/pages/1/title
., (*26)
And you may see something like this:, (*27)
{"page.1.title":"Default page"}
As the subRequest
is managed recursively, you really can navigate in a complex object like this:
http://127.0.0.1:8000/api/pages/1/children/2/category/name
.
It will then retrieve datas in the specified order :, (*28)
- Get the
page
element with primary key 1
.
- Get its element
children
, which is a collection of Page
.
- Retrieve the one with the primary key
2
.
- Get the
Page 2
's category object.
- Get the category name.
The output may look like this:, (*29)
{"page.1.children.2.category.name": "Default category"}
The object's key is the compilation of your request, so you can check whether it exists in your code, and if it does, it means that it's a valid object., (*30)
POST and PUT routes
The POST route is only used to UPDATE datas.
The PUT route is only used to INSERT datas., (*31)
Basically, the PUT route works the same than the POST route, but it won't merge any entity in the database. It will instead fill an empty entity, validate it, and if the object is valid, persist it., (*32)
- POST:
orbitale_api_post
: /{serviceName}/{id}
- PUT:
orbitale_api_put
: /{serviceName}
The route will first search for an entity of serviceName
with primary key id
., (*33)
The json
object
The API will search for a json
parameter in POST datas. If it's a string, it's automatically transformed into a Json object., (*34)
This json
object is a transposition of your entity serialized object, with its values., (*35)
It means that you can use your entity attributes as they're shown by the Api generator.
It will simply merge
(if POST) or fill an empty object (if PUT) to the database object with your json object, so it won't modify other parameters., (*36)
The mapping
object
Additionally, the Api will search for a mapping
object. This object is mandatory, and it defines all the fields you want to, (*37)
For example, if you only want to modify a Page
title
attribute, you can use this json object:, (*38)
{ "json": { "title": "New title !" }, "mapping": { "title": true } }
Then, you will change the title, and you won't modify any other data directly., (*39)
The entire object will be then sent to validation through the Symfony's validator
service, so you can see if the object is wrongly updated or not, by simply using your usual validation annotations or files., (*40)
If the object is not valid, the API will send you all the error messages sent by the validator
service, for you to handle them in front (or back, depending on how you manage to use this bundle)., (*41)
The output is the newly updated or inserted object., (*42)
Mapping-specific behavior
Sometimes you use camelCase
, sometime snake_case
, and jms_serializer
can be configured differently in different apps., (*43)
This is why some attributes may have their name changed through the serialization process., (*44)
One simple case :, (*45)
In your Page
entity, you have a ManyToOne
relationship with a Category
object., (*46)
Your mapping looks like this:, (*47)
# AppBundle\Entity\Page.php
// ...
/**
* @ORM\ManyToOne(targetEntity="AppBundle\Entity\Category")
*/
protected $pageCategory;
When retrieving a Page
object with the API, you'll see this :, (*48)
{ "page": { "title": "Page title", "page_category": { "id": 1, "name": "Default category" } } }
This can have some breaks, does it?, (*49)
Then, when you send your JSON
object to any of the PUT or POST methods, you'll have to tell the API that the fields have different names., (*50)
For example, in a POST request:, (*51)
{
"json": { "page_category": { "id": 1, "name": "Sefault category" } },
"mapping": { "page_category": { "objectField": "pageCategory" } }
}
With this special mapping for page_category
, you will tell the API that the page_category
json attribute corresponds to a pageCategory
attribute in your Doctrine entity., (*52)
Relationships
As no automatic cascading operation is made, you'll have to specify it in your Entity mapping., (*53)
Plus, the object must be an existing object, or you can have some unexpected behavior., (*54)
Secure your API
In the configuration reference you may see a allowed_origins
attribute., (*55)
This attribute is used to check whether the asker has rights to view this API or not. It's especially useful to refuse connections from some IP addresses, and from unwanted AJAX requests (CORS is not managed, you'll have to use another bundle for that)., (*56)
By default, in dev
environment, localhosts are automatically added to the allowed_origins
array., (*57)
The current server IP address is also added to the allowed_origins
, for you to make requests to your own API from your own server., (*58)
You can add other IPs or domain names in this attribute like this:, (*59)
# app/config/config.yml
orbitale_api:
allowed_origins:
- 1.2.3.4
- my.domain.com
This is a basic security system. If you want more security, you'll have to extend the ApiController and override the checkAsker
method, and also change the routing namespace to your own controller., (*60)