Doctrine in Apigility Documentation

This will document how to create an API with Doctrine and Apigility.

Introduction

The landscape of API strategies grows every day. In the field of PHP there are strategies from simple REST-only resources to fully Richardson Maturity Model Level 3 API engines. Apigility falls into the Level 3 category.

An API will serve data and Doctrine in Apigility tries, at it’s core, to map Doctrine entities to API resources. So a Doctrine enabled resource in Apigility will provide GET, POST, PUT, PATCH, and DELETE. It also enables the ORM entity relationships to embed related data in a HAL (Hypertext Application Language) response. This allows complex joins between entities to be represented in the API response.

When should you use Doctrine in Apigility?

When you have a Doctrine project with properly mapped associations, metadata, between entities Doctrine in Apigility will give you a powerful head-start toward building an API. Correct metadata is absolutly core to building an API with this tool. To help design your ORM Skipper is strongly recommended.

You can use Doctrine in Apigility to serve pieces of your schema by filtering with hydrator strategies or you can serve your entire schema in an “Open-schema API” where relationships between entities are fully explored in the HAL _embedded data.

If you’re familiar with the benefits of ORM and will use it in your project and you require a fully-fledged API engine than this API strategy may what you’re looking for.

Note

Maintained by API Skeletons.

About

Much of this document was written by Tom H Anderson and some material was compiled from other sources in the Apigility realm.

Tom was the original author of zfcampus/zf-apigility-doctrine and has created many supporting libraries for Doctrine in Apigility. He contributed much to the early development of Apigility and continues to give to open source in PHP.

As soon as Apigility was announced developers were asking for Doctrine support though none of us really knew what that ment. What was created serves as a strong platform for an ORM based API.

Note

Maintained by API Skeletons.

Authentication

The official Apigility documentation describes the lightweight authentication infrastructure included with Apigility and how it is intended to be extended. Extending this infrastructure is exactly what is required for Doctrine in Apigility.

When building an API application the central authentication is usually done through an OAuth2 implicit and/or authorization_code grant type. To support authorization you must redirect users to a login page when a user tries to authenticate through these routes.

api-skeletons/zf-oauth2-doctrine-identity replaces the default AuthenticatedIdentity provided by Apigility. This class ->getIdentity() returns the AccessToken granted through OAuth2. From the Access Token you can fetch the authenticated User entity and OAuth2 Client entity.

Authentication should occur every time a user tries to access a route they are not authorized for and the user is not already Authenticated. To trigger Authentication you must have authorization setup for your resources.

Note

Maintained by API Skeletons.

Authorization

api-skeletons/zf-oauth2-doctrine-permissions-acl implements ACL resources for a User with a many-to-many relationship to a Role entity. It works in tandem with other zf-oauth2-doctrine libraries to create easy to implement authorization in a Doctrine in Apigility API.

Note

Maintained by API Skeletons.

OAuth2 Adapter

api-skeletons/zf-oauth2-doctrine

This module provides a Doctrine adapter for zfcampus/zf-mvc-auth and zfcampus/zf-oauth2 and entity definitions for all aspects of OAuth2 including Authorization Code, Access Tokens, Refresh Tokens, JWT & JTI, and Scopes.

A Skipper module is provided to embed in your Entity Relationship Diagram.

Entity Relationship Diagram

https://raw.githubusercontent.com/API-Skeletons/zf-oauth2-doctrine/master/media/oauth2-doctrine-erd.png

Entity Relationship Diagram created with Skipper

The ERD module is located at media/OAuth2-orm.module.xml and is intended to be embedded in the ERD for your project.

Relations

The User entity is provided by your application and a dynamic join is made at runtime when the metadata is gathered for Doctrine. There is a dynamic join with Client, AuthorizationCode, AccessToken and RefreshToken.

The central OAuth2 entity is the Client. There is a dynamic join from the Client entity to the configured User entity. For every application owned by a User there will be a Client entry. The User referenced from a Client is the User who owns that Client.

The AuthorizationCode entity is used when a User connects from an application using OAuth2. The reference to the User entity from the AuthorizationCode entity is for the User that is trying to log into the Client referenced from the AuthorizationCode entity. The same is true for the AccessToken and RefreshToken entities.

The Scope entities are many to many relationships for Client, AuthorizationCode, AccessToken and RefreshToken. Scopes dictate what permissions a Client has into the API.

There is a one to one relationship from Client to PublicKey. This is because the encryption side of OAuth2 is less common and to encapsulate the encryption fields into the PublicKey entity. The JWT and JTI entities provide full support and their use in encryption falls outside the scope of this documentation.

Database Table Namespaces

All OAuth2 tables are suffixed with _OAuth2 such as Client_OAuth2. You can change these if you override. It is recommended your database table names match your entity names to provide canonical naming across your application. See also bushbaby/zf-oauth2-doctrine-mutatetablenames.

Note

Maintained by API Skeletons.

Application Configuration

Installation

Installation of this module uses composer. For composer documentation, please refer to getcomposer.org.

composer require api-skeletons/zf-oauth2-doctrine "^1.0"

Add this module to your application’s configuration:

'modules' => [
   ...
   'ZF\OAuth2\Doctrine',
],

Global Configuration

Copy config/oauth2.doctrine-orm.global.php.dist to your autoload directory and rename to oauth2.doctrine-orm.global.php This config has multiple sections for multiple adapters. Out of the box this module provides a default adapter. You will need to edit this file with at least your User entity, which is not provided.

zfcampus/zf-mvc-auth Configuration

By default this module includes a oauth2.doctrineadapter.default adapter. The adapter is used to create storage from services. Add this configuration to your config/autoload/zf-mvc-auth-oauth2-override.global.php:

'zf-mvc-auth' => array(
    'authentication' => array(
        'adapters' => array(
            'oauth2_doctrine' => array(
                'adapter' => 'ZF\\MvcAuth\\Authentication\\OAuth2Adapter',
                'storage' => array(
                    'storage' => 'oauth2.doctrineadapter.default',
                    'route' => '/oauth',
                ),
            ),
        ),
    ),
),

zfcampus/zf-oauth2 Configuration

Add the default storage adapter to the zf-oauth default storage. zfcampus/zf-oauth2 provides an oauth2.local.php file. This repository’s recommendation is to create a new config/autoload/oauth2.global.php file and set the following configuration as well as any OAuth2 server settings e.g. allow_implicit:

'zf-oauth2' => array(
    'storage' => 'oauth2.doctrineadapter.default',

Note

Maintained by API Skeletons.

Module Configuration

Using Default Entities

Details for creating your database with the included entities are outside the scope of this project. Generally this is done through doctrine/doctrine-orm-module with php public/index.php orm:schema-tool:create

By default this module uses the entities provided but you may use the adapter with your own entites (and map them in the mapping config section) by toggling this flag:

'zf-oauth2-doctrine' => [
    'default' => [
        'enable_default_entities' => true,

Customizing Many to One Mapping

If you need to customize the call to mapManyToOne, which creates the dynamic joins to the User entity from the default entites, you may add any parameters to the ['dynamic_mapping']['default_entity']['additional_mapping_data'] element. An example for a User entity with a primary key of user_id which does not conform to the metadata naming strategy is added to each entity:

'refresh_token_entity' => [
    'entity' => 'ZF\OAuth2\Doctrine\Entity\RefreshToken',
    'field' => 'refreshToken',
    'additional_mapping_data' => [
        'joinColumns' => [
            [
                'name' => 'user_id',
                'referencedColumnName' => 'user_id',
            ],
        ],
    ],
],

Identity field on User entity

By default this Doctrine adapter retrieves the user by the username field on the configured User entity. If you need to use a different or multiple fields you may do so via the auth_identity_fields key. For example, ZfcUser allows users to authenticate by username and/or email fields.

An example to match ZfcUser auth_identity_fields configuration:

'zf-oauth2-doctrine' => [
    'default' => [
        'auth_identity_fields' => ['username', 'email'],

Note

Maintained by API Skeletons.

User Entity Configuration

This repository supplies every entity you need to implement OAuth2 except the User entity. The reason is so the User entity can be decoupled from the OAuth2 Doctrine repository instead to be linked dynamically at run time. This allows, among other benefits, the ability to create an ERD without modifying the OAuth2-orm.module.xml module.

The User entity must implement ZF\OAuth2\Doctrine\Entity\UserInterface

The User entity for the unit test for this module is a good template to start from: https://github.com/api-skeletons/zf-oauth2-doctrine/blob/master/test/asset/module/Doctrine/src/Entity/User.php

Note

Maintained by API Skeletons.

Events

Events are fully supported. Return values are used if propagation is stopped allowing you to write your own handlers for any OAuth2 Adapter method. Each function which implements the OAuth2 interfaces may be attached to and optionally overridden.

Example Event Attachment

Module.php onBootstrap:

$doctrineOAuth2Adapter = $e->getParam('application')
    ->getServiceManager()
    ->get('oauth2.doctrineadapter.default')
     ;
$listenerAggregate = new \Application\EventSubscriber\OAuth2AggregateListener($objectManager);
$doctrineOAuth2Adapter->getEventManager()->attachAggregate($listenerAggregate);

ApplicationEventSubscriberOAuth2AggregateListener:

namespace Application\EventSubscriber;

use Zend\EventManager\Event;
use Zend\EventManager\AbstractListenerAggregate;
use Zend\EventManager\EventManagerInterface;

class OAuth2AggregateListener extends AbstractListenerAggregate
{
    protected $handlers = array();
    protected $logInAs;

    public function attach(EventManagerInterface $events)
    {
        $this->handlers[] = $events->attach('checkUserCredentials', array($this, 'checkUserCredentials'));
    }

    /**
     * Do work such as non-standard encrypted password checking
     */
    public function checkUserCredentials(Event $e)
    {
        if ($e->getParams()['username'] == 'specialUser') {
            $e->stopPropagation();

            return true;
        }
    }
}

Note

Maintained by API Skeletons.

zf-doctrine-apigility Integration

Validate zf-apigility-doctrine resources

To validate the OAuth2 session with Query Create Filters and Query Providers implement ZF\OAuth2\Doctrine\OAuth2ServerInterface and use ZF\OAuth2\Doctrine\OAuth2ServerTrait. Then call $result = $this->validateOAuth2($scope); in the filter function.

Note

Maintained by API Skeletons.

Override Default Entities

This example shows how to override default entities. The same procedure is used any time default entities will be overridden.

First you’ll need a new module for the modified xml. Call it OAuth2. Next copy the zf-oauth2-doctrine/config/orm/*.xml into your new module OAuth2/config/orm (create this directory).

Edit the xml as needed. Next edit your oauth2.doctrine-orm.global.php file and set ‘enable_default_entities’ => false You may need to modify the config file to match the new xml.

Now in OAuth2/config/module.config.php add the Doctrine config and alias the xml:

'doctrine' => array(
    'driver' => array(
        'oauth2_driver' => array(
            'class' => 'Doctrine\\ORM\\Mapping\\Driver\\XmlDriver',
            'paths' => array(
                0 => __DIR__ . '/orm',
            ),
        ),
        'orm_default' => array(
            'class' => 'Doctrine\\ORM\\Mapping\\Driver\\DriverChain',
            'drivers' => array(
                'ZF\\OAuth2\\Doctrine\\Entity' => 'oauth2_driver',
            ),
        ),
    ),
),

Note

Maintained by API Skeletons.

Multiple OAuth2 Instances

You may have multiple sets of distinct OAuth2 servers on the same application instance. This describes how to create a second OAuth2 server with it’s own entities.

Create a new module called SecondOAuth2 and copy the entities from zf-oauth2-doctrine into it’s src/SecondOAuth2/Entity directory. Change all the namespaces to SecondOAuth2 and this includes autogenerated fully qualified name spaces in the add and remove functions for collections.

Copy the ORM XML data from zf-oauth2-doctrine to SecondOAuth2/config/orm and edit it for the new module. Be sure to change the table names. Add doctrine config to module.config.php

Copy this code into the SecondOAuth2 Module.php:

public function onBootstrap(MvcEvent $e)
{
    /** @var ServiceLocatorInterface $serviceManager */
    $serviceManager = $e->getParam('application')->getServiceManager();
    $serviceManager->get('oauth2.doctrineadapter.second')->bootstrap($e);
}

public function getServiceConfig()
{
    return [
        'factories' => [
            'oauth2.doctrineadapter.second' => function ($serviceManager) {
                /** @var ServiceLocatorInterface | ContainerInterface $serviceManager */
                $globalConfig = $serviceManager->get('Config');
                $config = new Config($globalConfig['zf-oauth2-doctrine']['second']);
                /** @var DoctrineAdapterFactory $factory */
                $factory = $serviceManager->get(DoctrineAdapterFactory::class);
                $factory->setConfig($config);

                return $factory->createService($serviceManager);
            }
        ],
    ];
}

Create a copy of the default config in oauth2.doctrine-orm.global.php and name the block ‘second’. Create a new User entity and assign that entity to the default group. There should not be a reason to have two OAuth2 servers connected to the same User entity. Change namespaces as needed.

Finally follow the configuration section of this documentation to configure the second OAuth2 server.

Note

Maintained by API Skeletons.

Add-Ons

api-skeletons/zf-oauth2-doctrine-console

Console routes to manage OAuth2 entities. Add and list Clients, Scopes, and more from the command line.

api-skeletons/zf-oauth2-doctrine-identity

This should have been a part of zf-oauth2-doctrine from the beginning but because zf-oauth2-doctrine is so mature identity mapping has been implemented in this add-on repository. This will change the AuthenticatedIdentity of [zfcampus/zf-mvc-auth](https://github.com/zfcampus/zf-mvc-auth) to a Doctrine enabled identity with quick access to the User, Client, and AccessToken entities.

api-skeletons/zf-oauth2-doctrine-permissions-acl

ACL Permissions and Authenticated Identity management. This uses interfaces rather than defining entities to create guards on resources based on [api-skeletons/zf-oauth2-doctrine-identity](https://github.com/API-Skeletons/zf-oauth2-doctrine-identity)

bushbaby/zf-oauth2-doctrine-mutatetablenames

Allows configuration of the table names that this module uses without overriding default entities.

Note

Maintained by API Skeletons.

Note

Maintained by API Skeletons.

OAuth2 Adapter Console Management

api-skeletons/zf-oauth2-doctrine-console

About

This repository provides console routes to manage a headless OAuth2 server.

Installation

Installation of this module uses composer. For composer documentation, please refer to getcomposer.org:

composer require api-skeletons/zf-oauth2-doctrine-console "*"

Add this module to your application’s configuration:

'modules' => array(
   ...
   'ZF\OAuth2\Doctrine\Console',
),

Console Routes

  • oauth2:client:create Create a new client with or without a user.
  • oauth2:client:update Update a client.
  • oauth2:client:delete Delete a client.
  • oauth2:client:list List all clients.
  • oauth2:scope:create Create a scope.
  • oauth2:scope:update Update a scope.
  • oauth2:scope:delete Delete a scope.
  • oauth2:scope:list List all scopes.
  • oauth2:public-key:create Create the public/private key record for the given client. This data is used to sign JWT access tokens. Each client may have only one key pair.
  • oauth2:public-key:delete Remove the key pair public key from a client.
  • oauth2:jwt:create Create a new JWT for a given client. This JWT will be used by an oauth2 connection requesting a grant_type of urn:ietf:params:oauth:grant-type:jwt-bearer. Creating the JWT puts the oauth2 connection request’s public key in place in the OAuth2 tables.
  • oauth2:jwt:delete Delete a JWT.
  • oauth2:jwt:list List all JWT.

For the connecting side of JWT, zf-oauth2-client provides a command line tool to generate a JWT reqeust. See also http://bshaffer.github.io/oauth2-server-php-docs/grant-types/jwt-bearer/

Note

Maintained by API Skeletons.

Hydrator Strategies

api-skeletons/zf-doctrine-hydrator

A collection of hydrator strategies for phpro/zf-doctrine-hydration-module written for use with zfcampus/zf-apigility-doctrine

Installation

Installation of this module uses composer. For composer documentation, please refer to getcomposer.org:

composer require api-skeletons/zf-doctrine-hydrator "*"

Add this module to your application’s configuration:

'modules' => array(
   ...
   'ZF\Doctrine\Hydrator',
),

StrategyCollectionExtract

Extract a collection. Often when this is used the entities in the collection have a many to one relationship with the entity the collection belongs to. In this case it is recommended you use the EntityLink for the parent on the child.

How to use these hydration strategies

In your configuration for your Doctrine in Apigility API the doctrine-hydrator section add a strategy to an entity. In this example an Artist is a member of a Band which, by example, is a many to one relationship (an artist can only have one band):

'doctrine-hydrator' => array(
    'DatabaseApi\\V1\\Rest\\Album\\SongHydrator' => array(
        'entity_class' => 'Database\\Entity\\Song',
        'object_manager' => 'doctrine.entitymanager.orm_default',
        'by_value' => true,
        'use_generated_hydrator' => true,
    ),
    'DatabaseApi\\V1\\Rest\\Album\\AlbumHydrator' => array(
        'entity_class' => 'Database\\Entity\\Album',
        'object_manager' => 'doctrine.entitymanager.orm_default',
        'by_value' => true,
        'strategies' => array(
            'artist' => 'ZF\Doctrine\Hydrator\Strategy\EntityLink',
            'song' => 'ZF\Doctrine\Hydrator\Strategy\CollectionLink',
        ),
        'use_generated_hydrator' => true,
    ),
    'DatabaseApi\\V1\\Rest\\Artist\\ArtistHydrator' => array(
        'entity_class' => 'Database\\Entity\\Artist',
        'object_manager' => 'doctrine.entitymanager.orm_default',
        'by_value' => true,
        'strategies' => array(
            'album' => 'ZF\Doctrine\Hydrator\Strategy\CollectionExtract',
        ),
        'use_generated_hydrator' => true,
    ),

When an Artist is queried all Albums for the Artist will be returned. For each Album the Artist will be returned only as a self link. The result of a call to https://api/artist/1 will look like:

{
    "id": 1,
    "name": "Soft Cell",
    "_embedded": {
        "album": [
            {
                "id": 1,
                "name": "Non-Stop Erotic Cabaret",
                "_embedded": {
                    "artist": {
                        "_links": {
                            "self": "https://api/artist/1"
                        }
                    },
                    "song": {
                        "_links": {
                            "self": "https://api/song?filter%5B0%5D%5Bfield%5D=album&filter%5B0%5D%5Btype%5D=eq&filter%5B0%5D%5Bvalue%5D=1"
                        }
                    }
                },
                "_links": {
                    "self": "https://api/album/1"
                }
            }
        ],
    },
    "_links": {
        "self": "https://api/artist/1"
    }
}

Note

Maintained by API Skeletons.

Filtering and Sorting

zfcampus/zf-doctrine-querybuilder

This library provides query builder directives from array parameters. This library was designed to apply filters from an HTTP request to give an API fluent filter and order-by dialects.

This library implements rich filter and sorting access to developers implementing your API.

Note

Maintained by API Skeletons.

Note

Maintained by API Skeletons.