Skip to content

Merge docs #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Sep 4, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions api-bundle/angular-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# AngularJS integration

DunglasApiBundle works fine with [AngularJS](http://angularjs.org). The popular [Restangular](https://github.com/mgonto/restangular)
REST client library for Angular can easily be configured to handle the API format.

Here is a working Restangular config:

```javascript
'use strict';

var app = angular
.module('myAngularjsApp')
.config(['RestangularProvider', function (RestangularProvider) {
// The URL of the API endpoint
RestangularProvider.setBaseUrl('http://localhost:8000');

// JSON-LD @id support
RestangularProvider.setRestangularFields({
id: '@id'
});
RestangularProvider.setSelfLinkAbsoluteUrl(false);

// Hydra collections support
RestangularProvider.addResponseInterceptor(function (data, operation) {
// Remove trailing slash to make Restangular working
function populateHref(data) {
if (data['@id']) {
data.href = data['@id'].substring(1);
}
}

// Populate href property for the collection
populateHref(data);

if ('getList' === operation) {
var collectionResponse = data['hydra:member'];
collectionResponse.metadata = {};

// Put metadata in a property of the collection
angular.forEach(data, function (value, key) {
if ('hydra:member' !== key) {
collectionResponse.metadata[key] = value;
}
});

// Populate href property for all elements of the collection
angular.forEach(collectionResponse, function (value) {
populateHref(value);
});

return collectionResponse;
}

return data;
});
}])
;

```

Previous chapter: [Performances](performances.md)
45 changes: 45 additions & 0 deletions api-bundle/controllers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Controllers

The bundle provide a default controller class implementing CRUD operations: `Dunglas\ApiBundle\Controller\ResourceController`.
Basically this controller class extends the default controller class of the FrameworkBundle of Symfony providing implementations
of CRUD actions. It also provides convenient methods to retrieve the `Resource` class associated with the current request
and to serialize entities using normalizers provided by the bundle.

## Using a custom controller

When [the event system](the-event-system.md) is not enough, it's possible to use custom controllers.

Your custom controller should extend the `ResourceController` provided by this bundle.

Example of custom controller:

```php
<?php

// src/AppBundle/Controller/CustomController.php

namespace AppBundle\Controller;

use Dunglas\ApiBundle\Controller\ResourceController;
use Symfony\Component\HttpFoundation\Request;

class CustomController extends ResourceController
{
// Customize the AppBundle:Custom:custom action
public function getAction(Request $request, $id)
{
$this->get('logger')->info('This is my custom controller.');

return parent::getAction($request, $id);
}
}
```

Custom controllers are often used with [custom operations](operations.md). If you don't create a custom operation
for your custom controller, you need to register yourself that controller in the Symfony routing system and it will
appear in documentations.

Note that you shouldn't use `@Route` annotations, as this will cause bugs. The bundle auto-registers routes within Symfony2, so you don't need to use `@Route` annotations.

Previous chapter: [Controllers](controllers.md)<br>
Next chapter: [Using external (JSON-LD) vocabularies](external-vocabularies.md)
140 changes: 140 additions & 0 deletions api-bundle/data-providers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# Data providers

To retrieve data that will be exposed by the API, DunglasApiBundle use classes called data providers. A data provider
using [Doctrine ORM](http://www.doctrine-project.org/projects/orm.html) to retrieve data from a database is included with the bundle and enabled by default. This data provider
natively supports paged collection and filters. It can be used as is and fits perfectly with common usages.

But sometime, you want to retrieve data from other sources such as a webservice, ElasticSearch, MongoDB or another ORM.
Custom data providers can be used to do so. A project can include as much data providers as it needs. The first able to
retrieve data for a given resource will be used.

## Creating a custom data provider

Data providers must return collection of items and specific items for a given resource when requested. In the following
example, we will create a custom provider returning data from a static list of object. Fell free to adapt it to match your
own needs.

Let's start with the data provider itself:

```php
<?php

// src/AppBundle/DataProvider/StaticDataProvider.php

namespace AppBundle\DataProvider;

use Dunglas\ApiBundle\Api\ResourceInterface;
use Dunglas\ApiBundle\Model\DataProviderInterface;
use Symfony\Component\HttpFoundation\Request;

class StaticDataProvider implements DataProviderInterface
{
private $data;

public function __construct()
{
$this->data = [
'a1' => new MyEntity('a1', 1871),
'a2' => new MyEntity('a2', 1936),
];
}

public function getItem(ResourceInterface $resource, $id, $fetchData = false)
{
return isset($this->data[$id]) ? $this->data[$id] : null;
}

public function getCollection(ResourceInterface $resource, Request $request)
{
return $this->data;
}

public function supports(ResourceInterface $resource)
{
return 'AppBundle\Entity\MyEntity' === $resource->getEntityClass();
}
}
```

Then register that provider with a priority higher than the Doctrine ORM data provider:

```yaml

# app/config/services.yml

services:
my_custom_data_provider:
class: AppBundle\DataProvider\StaticDataProvider
tags: [ { name: "api.data_provider", priority: 1 } ]
```

This data provider is now up and running. It will take precedence over the default Doctrine ORM data provider for each resources
it supports (here, resources managing `AppBundle\Entity\MyEntity` entities).

## Returning a paged collection

The previous custom data provider return only full, non paged, collection. However for large collections, returning all
the data set in one response is often not possible.
The `getCollection()` method of data providers supporting paged collections must returns an instance of `Dunglas\ApiBundle\Model\PaginatorInterface`
instead of a standard array.

To create your own paginators, take a look at the Doctrine ORM paginator bridge: [`Dunglas\ApiBundle\Doctrine\Orm\Paginator`](/Doctrine/Orm/Paginator.php).

## Supporting filters

To be able [to filter collections](filters.md), the Data Provider must be aware of registered filters to the given resource.
The best way to learn how to create filter aware data provider is too look at the default Doctrine ORM dataprovider: [`Dunglas\ApiBundle\Doctrine\Orm\DataProvider`](/Doctrine/Orm/DataProvider.php).

## Extending the Doctrine Data Provider

The bundle is provided with a data provider leveraging the Doctrine ORM. This default data provider can be extended.

For performance reasons, [custom output walkers for the Doctrine ORM Paginator](http://www.doctrine-project.org/jira/browse/DDC-3282)
are disabled. It drastically improves performance when dealing with large collections. However it prevents advanced [filters](filters.md)
adding `HAVING` and `GROUP BY` clauses to DQL queries to work properly.

To enable custom output walkers, start by creating a custom data provider supporting the `AppBundle\Entity\MyEntity` class:

```php
<?php

// src/AppBundle/DataProvider/MyEntityDataProvider.php

namespace AppBundle\DataProvider;

use Dunglas\ApiBundle\Doctrine\Orm\DataProvider;
use Dunglas\ApiBundle\Model\DataProviderInterface;

class MyEntityDataProvider extends DataProvider
{
protected function getPaginator(QueryBuilder $queryBuilder)
{
$doctrineOrmPaginator = new DoctrineOrmPaginator($queryBuilder);
// Enable output walkers to make queries with HAVING and ORDER BY clauses working
$doctrineOrmPaginator->setUseOutputWalkers(true);

return new Paginator($doctrineOrmPaginator);
}

public function supports(ResourceInterface $resource)
{
return 'AppBundle\Entity\MyEntity' === $resource->getEntityClass();
}
}
```

Then register the data provider:

```yaml

# app/config/services.yml

services:
my_entity_data_provider:
parent: "api.doctrine.orm.data_provider"
class: AppBundle\DataProvider\MyEntityDataProvider
tags: [ { name: "api.data_provider", priority: 1 } ]
```

Previous chapter: [Operations](operations.md)<br>
Next chapter: [Filters](filters.md)
64 changes: 64 additions & 0 deletions api-bundle/external-vocabularies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Using external (JSON-LD) vocabularies

JSON-LD allows to define classes and properties of your API with open vocabularies such as [Schema.org](https://schema.org)
and [Good Relations](http://www.heppnetz.de/projects/goodrelations/).

DunglasApiBundle provides annotations usable on PHP classes and properties to specify a related external [IRI](http://en.wikipedia.org/wiki/Internationalized_resource_identifier).


```php
<?php

// src/AppBundle/Entity/Product.php

namespace AppBundle\Entity;

use Dunglas\ApiBundle\Annotation\Iri;

/**
* ...
* @Iri("https://schema.org/Product")
*/
class Product
{
// ...

/**
* ...
* @Iri("https://schema.org/name")
*/
public $name;
}
```

The generated JSON for products and the related context document will now use external IRIs according to the specified annotations:

`GET /products/22`

```json
{
"@context": "/contexts/Product",
"@id": "/product/22",
"@type": "https://schema.org/Product",
"name": "My awesome product",
// other properties
}
```

`GET /contexts/Product`

```json
{
"@context": {
"@vocab": "http://example.com/vocab#",
"hydra": "http://www.w3.org/ns/hydra/core#",
"name": "https://schema.org/name",
// Other properties
}
}
```

An extended list of existing open vocabularies is available on [the Linked Open Vocabularies (LOV) database](http://lov.okfn.org/dataset/lov/).

Previous chapter: [Controllers vocabularies](controllers.md)<br>
Next chapter: [Performances](performances.md)
Loading