### Install Laminas HAL with Composer Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/README.md Installs the laminas-api-tools/api-tools-hal package using the Composer package manager. ```console composer require laminas-api-tools/api-tools-hal ``` -------------------------------- ### Create Resource Listener (PHP) Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/basics/example.rst Implements a resource listener for handling 'create', 'fetch', and 'fetchAll' events. It uses a PersistenceInterface to interact with the data store and throws exceptions for errors. Requires Laminas Framework 2.2.0+. ```php namespace Paste; use Laminas\ApiTools\Rest\Exception\CreationException; use Laminas\ApiTools\Rest\Exception\DomainException; use Laminas\ApiTools\Rest\ResourceEvent; use Laminas\EventManager\AbstractListenerAggregate; use Laminas\EventManager\EventManagerInterface; class PasteResourceListener extends AbstractListenerAggregate { protected $persistence; public function __construct(PersistenceInterface $persistence) { $this->persistence = $persistence; } public function attach(EventManagerInterface $events) { $this->listeners[] = $events->attach('create', array($this, 'onCreate')); $this->listeners[] = $events->attach('fetch', array($this, 'onFetch')); $this->listeners[] = $events->attach('fetchAll', array($this, 'onFetchAll')); } public function onCreate(ResourceEvent $e) { $data = $e->getParam('data'); $paste = $this->persistence->save($data); if (!$paste) { throw new CreationException(); } return $paste; } public function onFetch(ResourceEvent $e) { $id = $e->getParam('id'); $paste = $this->persistence->fetch($id); if (!$paste) { throw new DomainException('Paste not found', 404); } return $paste; } public function onFetchAll(ResourceEvent $e) { return $this->persistence->fetchAll(); } } ``` -------------------------------- ### Configure Routing (PHP) Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/basics/example.rst Defines routing configurations for a Laminas application, including a top-level route for a web UI and a 'Segment' route for the RESTful API endpoint '/api/pastes[/:id]'. ```php 'router' => array('routes' => array( 'paste' => array( 'type' => 'Literal', 'options' => array( 'route' => '/paste', 'controller' => 'Paste\PasteController', // for the web UI ), 'may_terminate' => true, 'child_routes' => array( 'api' => array( 'type' => 'Segment', 'options' => array( 'route' => '/api/pastes[/:id]', 'controller' => 'Paste\ApiController', ), ), ), ), )), ``` -------------------------------- ### Example OPTIONS Request and Response Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/options.rst Demonstrates a typical OPTIONS HTTP request to a resource URI and the expected response, including the 'Allow' header specifying permitted HTTP methods. ```http OPTIONS /api/user Host: example.org ``` ```http HTTP/1.1 200 OK Allow: GET, POST ``` -------------------------------- ### Define Persistence Interface (PHP) Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/basics/example.rst Defines the basic interface for data persistence operations like saving and fetching resources. This interface is language-agnostic regarding the actual storage mechanism. ```php namespace Paste; interface PersistenceInterface { public function save(array $data); public function fetch($id); public function fetchAll(); } ``` -------------------------------- ### Returning an ApiProblem Instance Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/ref/alternate-resource-return-values.rst This PHP code example demonstrates how to instantiate and return an ApiProblem object with various parameters, including status code, detail message, description URI, title, and additional properties. ```php use PhlyRestfully\ApiProblem; return new ApiProblem( 418, 'Exceeded rate limit', $urlHelper('api/help', array('resource', 'error_418')), "I'm a teapot", array( 'user' => $user, 'limit' => '60/hour', ) ); ``` -------------------------------- ### Configure API Controller Resources (PHP) Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/basics/example.rst Configures API Tools REST resources, specifying the controller, identifier, listener, and HTTP methods for collection and resource operations. Sets page size for collections. ```php 'api-tools-rest' => array( 'resources' => array( 'Paste\ApiController' => array( 'identifier' => 'Pastes', 'listener' => 'Paste\PasteResourceListener', 'resource_identifiers' => array('PasteResource'), 'collection_http_options' => array('get', 'post'), 'collection_name' => 'pastes', 'page_size' => 10, 'resource_http_options' => array('get'), ``` -------------------------------- ### HAL Resources: Embedded Resources Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/halprimer.rst Demonstrates how to include other resources within a HAL resource using the '_embedded' property, including examples of single and arrayed embedded resources. ```APIDOC ## HAL Resources: Embedded Resources ### Description HAL allows for the embedding of other resources within a parent resource using the `_embedded` property. This property contains named resources, which can be either a single resource object or an array of resource objects. Each embedded resource must also adhere to the HAL structure, including a `_links` property with a `self` link. ### Method N/A ### Endpoint N/A ### Parameters N/A ### Request Example ```json { "_links": { "self": { "href": "http://example.org/api/user/matthew" } }, "id": "matthew", "name": "Matthew Weier O'Phinney", "_embedded": { "contacts": [ { "_links": { "self": { "href": "http://example.org/api/user/mac_nibblet" } }, "id": "mac_nibblet", "name": "Antoine Hedgecock" }, { "_links": { "self": { "href": "http://example.org/api/user/spiffyjr" } }, "id": "spiffyjr", "name": "Kyle Spraggs" } ], "website": { "_links": { "self": { "href": "http://example.org/api/locations/mwop" } }, "id": "mwop", "url": "http://www.mwop.net" } } } ``` ### Response #### Success Response (200) - **_links** (object) - Relational links. - **id** (string) - User ID. - **name** (string) - User name. - **_embedded** (object) - Contains embedded resources. - **contacts** (array) - An array of embedded contact resources. - Each contact object includes `_links`, `id`, and `name`. - **website** (object) - An embedded website resource. - Includes `_links`, `id`, and `url`. #### Response Example ```json { "_links": { "self": { "href": "http://example.org/api/user/matthew" } }, "id": "matthew", "name": "Matthew Weier O'Phinney", "_embedded": { "contacts": [ { "_links": { "self": { "href": "http://example.org/api/user/mac_nibblet" } }, "id": "mac_nibblet", "name": "Antoine Hedgecock" }, { "_links": { "self": { "href": "http://example.org/api/user/spiffyjr" } }, "id": "spiffyjr", "name": "Kyle Spraggs" } ], "website": { "_links": { "self": { "href": "http://example.org/api/locations/mwop" } }, "id": "mwop", "url": "http://www.mwop.net" } } } ``` ``` -------------------------------- ### Hierarchical Resource Routing Example Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/ref/advanced-routing.rst Demonstrates routing configuration for parent and child resources, resolving potential ID conflicts by using distinct identifier names. ```APIDOC ## POST /users ### Description This section outlines how to configure routing for parent and child resources to avoid identifier conflicts. It demonstrates a refactored routing structure where distinct identifier names are used for parent and child resources. ### Method POST (demonstrative for a resource creation context) ### Endpoint `/users[/:user_id]` and `/addresses[/:address_id]` ### Parameters #### Path Parameters - **user_id** (string) - Optional - Identifier for the user resource. - **address_id** (string) - Optional - Identifier for the address resource. #### Request Body (Not specified in the provided text, depends on the specific resource) ### Request Example ```json { "example": "request body not provided" } ``` ### Response #### Success Response (200) (Not specified in the provided text) #### Response Example ```json { "example": "response body not provided" } ``` ``` -------------------------------- ### API-Problem JSON Payload Example Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/problems.rst An example of an HTTP response adhering to the API-Problem specification for reporting errors. This format includes fields like 'describedBy', 'title', 'status', and 'detail' to convey error information. ```http HTTP/1.1 500 Internal Error Content-Type: application/problem+json { "describedBy": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html", "detail": "Status failed validation", "status": 500, "title": "Internal Server Error" } ``` -------------------------------- ### PHP Module Class for API Resource Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/basics/example.rst This PHP code defines a Laminas module class, including configurations for routing, autoloading, and service factories. It specifically shows how to create a factory for a 'PasteResourceListener' that depends on a 'PastePersistenceInterface'. This is crucial for integrating custom resources with the api-tools-hal library. ```php namespace Paste; class Module { public function getConfig() { return include __DIR__ . '/config/module.config.php'; } public function getAutoloaderConfig() { return array( 'Laminas\Loader\StandardAutoloader' => array( 'namespaces' => array( __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__, ), ), ); } public function getServiceConfig() { return array('factories' => array( 'Paste\PasteResourceListener' => function ($services) { $persistence = $services->get('Paste\PersistenceInterface'); return new PasteResourceListener($persistence); }, )); } } ``` -------------------------------- ### Configuration-driven Hydrator Maps Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/ref/hydrators.rst Provides an example of how to configure hydrator mappings and a default hydrator via application configuration files (e.g., `config/autoload/api-tools-rest.global.php`). This allows for a declarative approach to hydrator management. ```php return array( 'api-tools-rest' => array( 'renderer' => array( 'default_hydrator' => 'ArraySerializable', 'hydrators' => array( 'MyResourcesFoo' => 'ObjectProperty', 'MyResourcesBar' => 'Reflection', ), ), ), ); ``` -------------------------------- ### Example Pagination Links in Response (JSON) Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/ref/collections-and-pagination.rst Illustrates the structure of relational links included in an API response when pagination is enabled. These links typically point to the current page, previous page, next page, first page, and last page of the collection. ```json { "_links": { "self": { "href": "http://example.org/api/paste?page=17" }, "prev": { "href": "http://example.org/api/paste?page=16" }, "next": { "href": "http://example.org/api/paste?page=18" }, "first": { "href": "http://example.org/api/paste" }, "last": { "href": "http://example.org/api/paste?page=300" } }, // ... } ``` -------------------------------- ### Configure Child Resource Routing (PHP) Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/ref/child-resources.rst Defines the routing configuration for parent and child resources. This example shows how 'users' can have nested 'addresses' as child routes, specifying segment types and controllers. ```php 'users' => array( 'type' => 'Segment', 'options' => array( 'route' => '/users[/:user_id]', 'controller' => 'UserResourceController', ), 'may_terminate' => true, 'child_routes' => array( 'addresses' => array( 'type' => 'Segment', 'options' => array( 'route' => '/addresses[/:address_id]', 'controller' => 'UserAddressResourceController', ), ), ), ), ``` -------------------------------- ### Basic Segment Route for Resource Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/ref/advanced-routing.rst Defines a basic Segment route for a standalone resource, including an optional 'id' parameter. This is the recommended starting point for resource routing. ```php 'route' => '/resource[/:id]' ``` -------------------------------- ### Get HalLinks from ViewHelperManager Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/ref/hydrators.rst Retrieves the HalLinks plugin instance from the application's ViewHelperManager. This is typically done within an event listener like onBootstrap. ```php $app = $e->getApplication(); $services = $app->getServiceManager(); $helpers = $services->get('ViewHelperManager'); $halLinks = $helpers->get('HalLinks'); ``` -------------------------------- ### Retrieve Resource Identifier for HAL Links Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/ref/advanced-routing.rst This example shows how to attach listeners to the 'getIdFromResource' event of the LaminasApiToolsRestPluginHalLinks to provide custom logic for retrieving a resource's identifier. This is crucial for generating 'self' relational links for specific resource types like User and UserAddress. ```php $sharedEvents->attach('LaminasApiToolsRestPluginHalLinks', 'getIdFromResource', function ($e) { $resource = $e->getParam('resource'); if (!$resource instanceof User) { return; } return $resource->user_id; }, 100); ``` ```php $sharedEvents->attach('LaminasApiToolsRestPluginHalLinks', 'getIdFromResource', function ($e) { $resource = $e->getParam('resource'); if (!$resource instanceof UserAddress) { return; } return $resource->address_id; }, 100); ``` -------------------------------- ### HAL Primer: Introduction Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/halprimer.rst Explains the core principles of HAL, including its role in achieving Richardson Maturity Model Level 3 by incorporating relational links and standard structures for embedded resources. ```APIDOC ## Introduction to HAL ### Description HAL (Hypermedia Application Language) is an open specification for structuring RESTful resources. It emphasizes the use of relational links within resources to facilitate discoverability and navigation, aligning with Level 3 of the Richardson Maturity Model. APIs using HAL typically expose resources via HTTP, utilize HTTP verbs for manipulation, and provide canonical links to themselves and related resources. ### Method N/A ### Endpoint N/A ### Parameters N/A ### Request Example N/A ### Response N/A ``` -------------------------------- ### Get HalLinks from ControllerPluginManager Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/ref/hydrators.rst Retrieves the HalLinks plugin instance from the application's ControllerPluginManager. This is useful for accessing HalLinks functionality within controllers. ```php $app = $e->getApplication(); $services = $app->getServiceManager(); $plugins = $services->get('ControllerPluginManager'); $halLinks = $plugins->get('HalLinks'); ``` -------------------------------- ### POST Request for Resource Creation (HAL) Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/halprimer.rst Demonstrates a POST request to create a new user resource. It includes setting the Accept and Content-Type headers, and provides the resource data without HAL-specific links. The Content-Type uses a vendor-specific mediatype. ```http POST /api/user Accept: application/json Content-Type: application/vnd.example.user+json { "id": "matthew", "name": "Matthew Weier O'Phinney", "contacts": [ { "id": "mac_nibblet", }, { "id": "spiffyjr", } ], "website": { "id": "mwop", } } ``` -------------------------------- ### Create HalCollection with Links and Metadata (PHP) Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/ref/alternate-resource-return-values.rst Illustrates creating a HalCollection with custom routes, parameters, options, metadata attributes, and relational links. ```php use PhlyRestfully\HalCollection; use PhlyRestfully\Link; // Assume $users is an iterable set of users for seeding the collection. $collection = new HalCollection($users); $collection->setCollectionRoute('api/user'); // Assume that we need to specify a version within the URL: $collection->setCollectionRouteParams(array( 'version' => 2, )); // Tell the router to allow query parameters when generating the URI: $collection->setCollectionRouteOptions(array( 'query' => true, )); // Set the resource route, params, and options $collection->setResourceRoute('api/user'); $collection->setResourceRouteParams(array( 'version' => 2, )); $collection->setResourceRouteOptions(array( 'query' => null, // disable query string params )); // Set the collection name: $collection->setCollectionName('users'); // Set some attributes: current page, total number of pages, total items: $collection->setAttributes(array( 'page' => $page, // assume we have this from somewhere else 'pages_count' => count($users), 'users_count' => $users->countAllItems(), )); // Add some links $selfLink = new Link('self'); $selfLink->setRoute('api/user', array(), array('query' => true)); $docsLink = new Link('describedBy'); $docsLink->setRoute('api/help', array('resource' => 'users')); $links = $collection->getLinks(); $links->add($selfLink) ->add($docsLink); ``` -------------------------------- ### Create HalResource with Links (PHP) Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/ref/alternate-resource-return-values.rst Demonstrates creating a HalResource instance with a user object and its ID, and then adding 'self' and 'describedBy' links. ```php use PhlyRestfully\HalResource; use PhlyRestfully\Link; // Create the HAL resource // Assume $user is an object representing a user we want to // render; we could have also used an associative array. $halResource = new HalResource($user, $user->getId()); // Create some links $selfLink = new Link('self'); $selfLink->setRoute('user', array('user_id' => $user->getId())); $docsLink = new Link('describedBy'); $docsLink->setRoute('api/help', array('resource' => 'user')); $links = $halResource->getLinks(); $links->add($selfLink) ->add($docsLink); ``` -------------------------------- ### Configure Resource Controllers (PHP) Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/ref/child-resources.rst Sets up RESTful resource controllers, specifying their listener classes, collection names, HTTP methods, and other options. This is crucial for defining how resources are handled. ```php return array( // ... 'api-tools-rest' => array( 'resources' => array( 'UserResourceController' => array( 'listener' => 'UserListener', 'collection_name' => 'users', 'collection_http_options' => array('get', 'post'), 'resource_http_options' => array('get', 'patch', 'put', 'delete'), 'page_size' => 30, ), 'UserAddressResourceController' => array( 'listener' => 'UserAddressListener', 'collection_name' => 'addresses', 'collection_http_options' => array('get', 'post'), 'resource_http_options' => array('get', 'patch', 'put', 'delete'), ), ), ), ); ``` -------------------------------- ### Get HalLinks from Controller Plugin Method Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/ref/hydrators.rst Retrieves the HalLinks plugin instance directly from a controller instance. This is the most direct way when working within a controller's event scope. ```php $controller = $e->getTarget(); $halLinks = $controller->plugin('HalLinks'); ``` -------------------------------- ### Create Laminas API Tools ResourceController Instance (PHP) Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/basics/controllers.rst This snippet demonstrates how to manually create and configure a Laminas API Tools ResourceController instance. It involves setting up a persistence listener, an event manager, a Resource, and configuring the controller with route, collection name, page size, and allowed HTTP methods for collections and resources. ```php return array( 'controllers' => array( 'PasteController' => function ($controllers) { $services = $controllers->getServiceLocator(); $persistence = $services->get('PastePersistenceListener'); $events = $services->get('EventManager'); $events->setIdentifiers('PasteResource`); $events->attach($persistence); $resource = new LaminasApiToolsRestResource(); $resource->setEventManager($events); $controller = new LaminasApiToolsRestResourceController('PasteController'); $controller->setResource($resource); $controller->setRoute('paste/api'); $controller->setCollectionName('pastes'); $controller->setPageSize(30); $controller->setCollectionHttpOptions(array( 'GET', 'POST', )); $controller->setResourceHttpOptions(array( 'GET', )); return $controller; }, ), ); ``` -------------------------------- ### HAL Resources: Basic Structure Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/halprimer.rst Illustrates the fundamental structure of a HAL JSON resource, requiring a '_links' property with at least a 'self' relational link. ```APIDOC ## HAL Resources: Basic Structure ### Description A minimal HAL JSON resource must include a `_links` property. This property contains relational links, with a `self` link being mandatory. The `self` link provides the canonical URI for the resource. ### Method N/A ### Endpoint N/A ### Parameters N/A ### Request Example ```json { "_links": { "self": { "href": "http://example.org/api/user/matthew" } }, "id": "matthew", "name": "Matthew Weier O'Phinney" } ``` ### Response #### Success Response (200) - **_links** (object) - Contains relational links for the resource. - **self** (object) - The canonical URI for the resource. - **href** (string) - The URI of the resource. - **id** (string) - The identifier of the user. - **name** (string) - The name of the user. #### Response Example ```json { "_links": { "self": { "href": "http://example.org/api/user/matthew" } }, "id": "matthew", "name": "Matthew Weier O'Phinney" } ``` ``` -------------------------------- ### ApiProblem Constructor Signature Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/ref/alternate-resource-return-values.rst This snippet shows the constructor signature for the ApiProblem class, outlining the parameters required for creating an API problem response. ```php public function __construct( $status, // HTTP status code used for the response $detail, // Summary of what happened $describedBy = null, // URI to a description of the problem $title = null, // Generic title for the problem array $additional = array() // Additional properties to include in the payload ); ``` -------------------------------- ### OPTIONS /api/user Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/options.rst Demonstrates how to use the OPTIONS HTTP method to discover allowed HTTP verbs for a specific API resource. ```APIDOC ## OPTIONS /api/user ### Description This endpoint allows clients to inquire about the permitted HTTP methods for a given resource. ### Method OPTIONS ### Endpoint /api/user ### Response #### Success Response (200) - **Allow** (string) - A comma-separated list of allowed HTTP methods (e.g., GET, POST). #### Response Example ```http HTTP/1.1 200 OK Allow: GET, POST ``` ### Error Handling #### Error Response (405 Method Not Allowed) - **Allow** (string) - A comma-separated list of allowed HTTP methods when an unsupported method is attempted. #### Response Example ```http HTTP/1.1 405 Not Allowed Allow: GET, POST ``` ``` -------------------------------- ### Injecting 'describedby' Link into Resources/Collections Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/ref/advanced-rendering.rst Shows how to attach listeners to the 'renderResource' and 'renderCollection' events of the HalLinks plugin. This allows for programmatic injection of relational links, such as 'describedby', into resources or collections before they are rendered. ```php $sharedEvents->attach( 'PhlyRestfully\Plugin\HalLinks', array('renderResource', 'renderCollection'), function ($e) { $resource = $e->getParam('resource', false); $collection = $e->getParam('collection', false); if (!$resource && !$collection) { return; } if ($resource && !$resource instanceof \My\User) { return; } if ($collection && !$collection instanceof \My\Users) { return; } if ($collection) { $resource = $collection; } $links = $resource->getLinks(); $links->add(\PhlyRestfully\Link::factory(array( 'rel' => 'describedby', 'url' => 'http://example.com/api/help/resources/user', ))); } ); ``` -------------------------------- ### HAL JSON Resource with Self Link Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/halprimer.rst Demonstrates a minimal HAL JSON resource structure, requiring a '_links' property with a 'self' relational link. ```json { "_links": { "self": { "href": "http://example.org/api/user/matthew" } }, "id": "matthew", "name": "Matthew Weier O'Phinney" } ``` -------------------------------- ### Configure Laminas API Tools Resources via Abstract Factory (PHP) Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/basics/controllers.rst This PHP code demonstrates how to configure resources for Laminas API Tools using an abstract factory. It shows the structure within the 'api-tools-rest' configuration key, specifying controller class, event identifier, listener service, resource identifiers, accept criteria, collection HTTP options, and collection name. ```php return array( 'api-tools-rest' => array( 'resources' => array( // Key is the service name for the controller; value is // configuration 'MyApi\Controller\Contacts' => array( // Name of the controller class to use, if other than // Laminas\ApiTools\Rest\ResourceController. Must extend // Laminas\ApiTools\Rest\ResourceController, however, to be valid. // (OPTIONAL) 'controller_class' => 'Laminas\ApiTools\Rest\ResourceController', // Event identifier for the resource controller. By default, // the resource name is used; you can use a different // identifier via this key. // (OPTIONAL) 'identifier' => 'Contacts', // Name of the service locator key OR the fully qualified // class name of the resource listener (latter works only if // the class has no required arguments in the constructor). // (REQUIRED) 'listener' => 'MyApi\Resource\Contacts', // Event identifiers for the composed resource. By default, // the class name of the listener is used; you can add another // identifier, or an array of identifiers, via this key. // (OPTIONAL) 'resource_identifiers' => array('ContactsResource'), // Accept criteria (which accept headers will be allowed) // (OPTIONAL) 'accept_criteria' => array( 'Laminas\ApiTools\Rest\View\RestfulJsonModel' => array( 'application/json', 'text/json', ), ), // HTTP options for resource collections // (OPTIONAL) 'collection_http_options' => array('get', 'post'), // Collection name (OPTIONAL) 'collection_name' => 'contacts', ), ), ), ); ``` -------------------------------- ### POST /api/user Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/halprimer.rst Creates a new user resource. The request body should contain user details without HAL links, and the API will assign them upon creation. The response includes the created resource with HAL links and embedded resources. ```APIDOC ## POST /api/user ### Description Creates a new user resource. The request body should contain user details without HAL links, and the API will assign them upon creation. The response includes the created resource with HAL links and embedded resources. ### Method POST ### Endpoint /api/user ### Parameters #### Headers - **Accept**: application/json, application/hal+json - **Content-Type**: application/vnd.example.user+json #### Request Body - **id** (string) - Required - The unique identifier for the user. - **name** (string) - Required - The name of the user. - **contacts** (array) - Optional - An array of contact resources embedded within the user. - **id** (string) - Required - The unique identifier for the contact. - **website** (object) - Optional - The website resource embedded within the user. - **id** (string) - Required - The unique identifier for the website. ### Request Example ```json { "id": "matthew", "name": "Matthew Weier O'Phinney", "contacts": [ { "id": "mac_nibblet" }, { "id": "spiffyjr" } ], "website": { "id": "mwop" } } ``` ### Response #### Success Response (201 Created) - **_links** (object) - Contains HAL links for the resource. - **self** (object) - Link to the resource itself. - **href** (string) - The URL of the resource. - **id** (string) - The unique identifier for the user. - **name** (string) - The name of the user. - **_embedded** (object) - Contains embedded resources. - **contacts** (array) - An array of embedded contact resources. - **_links** (object) - Contains HAL links for the contact. - **self** (object) - Link to the contact resource. - **href** (string) - The URL of the contact. - **id** (string) - The unique identifier for the contact. - **name** (string) - The name of the contact. - **website** (object) - The embedded website resource. - **_links** (object) - Contains HAL links for the website. - **self** (object) - Link to the website resource. - **href** (string) - The URL of the website. - **id** (string) - The unique identifier for the website. - **url** (string) - The URL of the website. #### Response Example ```json { "_links": { "self": { "href": "http://example.org/api/user/matthew" } }, "id": "matthew", "name": "Matthew Weier O'Phinney", "_embedded": { "contacts": [ { "_links": { "self": { "href": "http://example.org/api/user/mac_nibblet" } }, "id": "mac_nibblet", "name": "Antoine Hedgecock" }, { "_links": { "self": { "href": "http://example.org/api/user/spiffyjr" } }, "id": "spiffyjr", "name": "Kyle Spraggs" } ], "website": { "_links": { "self": { "href": "http://example.org/api/locations/mwop" } }, "id": "mwop", "url": "http://www.mwop.net" } } } ``` ``` -------------------------------- ### Handle User Not Found Exception (PHP) Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/basics/resources.rst Demonstrates how to fetch a user using an ActiveRecord-like pattern. If the user is not found, it throws a custom DomainException with specific error details and a link to an error description URI. It returns the fetched user object if found. ```php // Assume an ActiveRecord-like pattern here for simplicity $user = User::fetch($id); if (!$user) { $ex = new DomainException('User not found', 404); $ex->setDescibedBy('http://example.org/api/errors/user-not-found'); $ex->setTitle('User not found'); throw $ex; } return $user; } ``` -------------------------------- ### HAL Hypermedia Type Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/halprimer.rst Details the hypermedia types defined by HAL for XML and JSON, with a focus on the 'application/hal+json' media type for JSON APIs. ```APIDOC ## HAL Hypermedia Type ### Description HAL defines two hypermedia types: one for XML and one for JSON. The primary media type for JSON-based HAL APIs is "application/hal+json". This type is typically used for resources returned by the API, as relational links are generally not submitted during resource creation, update, or deletion operations. ### Method N/A ### Endpoint N/A ### Parameters N/A ### Request Example N/A ### Response N/A ``` -------------------------------- ### ResourceController Configuration Options Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/basics/controllers.rst This section details the various configuration options available for the ResourceController, allowing customization of HAL-based API interactions. ```APIDOC ## ResourceController Configuration Options ### Description Provides a comprehensive overview of configuration settings for the `LaminasApiToolsHalResourceController`. These options allow fine-grained control over how the controller interacts with resources and collections, particularly in the context of HAL. ### Method N/A (Configuration) ### Endpoint N/A (Configuration) ### Parameters #### Configuration Options - **`collection_query_whitelist`** (string|array) - Optional - Specifies query parameters to be injected into collection links. Defaults to 'page'. - **`content_type`** (array) - Optional - Defines content types the controller responds to, mapping Content-Type headers to HAL representations. Example: ```php ResourceController::CONTENT_TYPE_JSON => [ 'application/json', 'application/hal+json', 'text/json', ] ``` - **`route_identifier_name`** (string) - Optional - The name of the route's identifier parameter (e.g., 'contact_id'). - **`page_size`** (int) - Optional - The default number of items to return per page for collections. Defaults to 30. - **`page_size_param`** (string) - Optional - The query string parameter name that allows clients to specify the page size. If not set, clients cannot control page size via query parameters. - **`resource_http_options`** (array) - Optional - Allowed HTTP methods for individual resource operations (e.g., 'get', 'patch', 'put', 'delete'). - **`route_name`** (string) - Required - The name of the route associated with this resource (e.g., 'api/contacts'). ### Request Example N/A (Configuration) ### Response N/A (Configuration) ``` -------------------------------- ### Handle 'create' event with validation and exceptions - PHP Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/basics/resources.rst This listener handles the 'create' event. It retrieves data from the event, attempts to create a User object, validates it, and throws a CreationException with details if validation fails. Otherwise, it persists the user. ```php function ($e) { $data = $e->getParam('data'); // Assume an ActiveRecord-like pattern here for simplicity $user = User::factory($data); if (!$user->isValid()) { $ex = new CreationException('New user failed validation', 400); $ex->setAdditionalDetails($user->getMessages()); $ex->setDescibedBy('http://example.org/api/errors/user-validation'); $ex->setTitle('Validation error'); throw $ex; } $user->persist(); return $user; } ``` -------------------------------- ### HAL Collections Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/halprimer.rst Explains how HAL represents collections, typically as arrays of embedded resources, and includes standard pagination links ('first', 'last', 'next', 'prev') and metadata like 'count' and 'total'. ```APIDOC ## HAL Collections ### Description HAL represents collections as arrays of embedded resources. A collection resource commonly includes a `self` link, along with standard pagination links such as `first`, `last`, `next`, and `prev`. It may also contain metadata about the collection, like the number of items returned (`count`) and the total number of available items (`total`). ### Method N/A ### Endpoint N/A ### Parameters N/A ### Request Example ```json { "_links": { "self": { "href": "http://example.org/api/user?page=3" }, "first": { "href": "http://example.org/api/user" }, "prev": { "href": "http://example.org/api/user?page=2" }, "next": { "href": "http://example.org/api/user?page=4" }, "last": { "href": "http://example.org/api/user?page=133" } }, "count": 3, "total": 498, "_embedded": { "users": [ { "_links": { "self": { "href": "http://example.org/api/user/mwop" } }, "id": "mwop", "name": "Matthew Weier O'Phinney" }, { "_links": { "self": { "href": "http://example.org/api/user/mac_nibblet" } }, "id": "mac_nibblet", "name": "Antoine Hedgecock" }, { "_links": { "self": { "href": "http://example.org/api/user/spiffyjr" } }, "id": "spiffyjr", "name": "Kyle Spraggs" } ] } } ``` ### Response #### Success Response (200) - **_links** (object) - Relational and pagination links. - **self** (object) - Link to the current collection page. - **first** (object) - Link to the first page of the collection. - **prev** (object) - Link to the previous page of the collection. - **next** (object) - Link to the next page of the collection. - **last** (object) - Link to the last page of the collection. - **count** (integer) - The number of resources in the current response. - **total** (integer) - The total number of resources available in the collection. - **_embedded** (object) - Contains the collection of embedded resources. - **users** (array) - An array of user resources. - Each user object includes `_links`, `id`, and `name`. #### Response Example ```json { "_links": { "self": { "href": "http://example.org/api/user?page=3" }, "first": { "href": "http://example.org/api/user" }, "prev": { "href": "http://example.org/api/user?page=2" }, "next": { "href": "http://example.org/api/user?page=4" }, "last": { "href": "http://example.org/api/user?page=133" } }, "count": 3, "total": 498, "_embedded": { "users": [ { "_links": { "self": { "href": "http://example.org/api/user/mwop" } }, "id": "mwop", "name": "Matthew Weier O'Phinney" }, { "_links": { "self": { "href": "http://example.org/api/user/mac_nibblet" } }, "id": "mac_nibblet", "name": "Antoine Hedgecock" }, { "_links": { "self": { "href": "http://example.org/api/user/spiffyjr" } }, "id": "spiffyjr", "name": "Kyle Spraggs" } ] } } ``` ``` -------------------------------- ### Handling Disallowed HTTP Methods Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/options.rst Illustrates the correct HTTP response for disallowed methods, returning a 405 Not Allowed status code and indicating the allowed methods via the 'Allow' header. ```http HTTP/1.1 405 Not Allowed Allow: GET, POST ``` -------------------------------- ### HAL JSON Collection with Pagination Links Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/halprimer.rst Shows a HAL JSON representation of a collection, including pagination links ('first', 'prev', 'next', 'last') and metadata like 'count' and 'total'. ```json { "_links": { "self": { "href": "http://example.org/api/user?page=3" }, "first": { "href": "http://example.org/api/user" }, "prev": { "href": "http://example.org/api/user?page=2" }, "next": { "href": "http://example.org/api/user?page=4" }, "last": { "href": "http://example.org/api/user?page=133" } }, "count": 3, "total": 498, "_embedded": { "users": [ { "_links": { "self": { "href": "http://example.org/api/user/mwop" } }, "id": "mwop", "name": "Matthew Weier O'Phinney" }, { "_links": { "self": { "href": "http://example.org/api/user/mac_nibblet" } }, "id": "mac_nibblet", "name": "Antoine Hedgecock" }, { "_links": { "self": { "href": "http://example.org/api/user/spiffyjr" } }, "id": "spiffyjr", "name": "Kyle Spraggs" } ] } } ``` -------------------------------- ### Configure Metadata and Hydrator Maps (PHP) Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/ref/child-resources.rst Configures metadata maps for resources, including hydrator types, route identifier names, and routes. This enables correct data hydration and URL generation for resources and collections. ```php return array( // ... 'api-tools-rest' => array( // ... 'metadata_map' => array( 'User' => array( 'hydrator' => 'ClassMethods', 'route_identifier_name' => 'user_id', 'route' => 'users', ), 'UserAddress' => array( 'hydrator' => 'ObjectProperty', 'route_identifier_name' => 'address_id', 'route' => 'users/addresses', ), 'UserAddresses' => array( 'route_identifier_name' => 'address_id', 'route' => 'users/addresses', 'is_collection' => true, 'route_options' => array('query' => true), ), ), ), ); ``` -------------------------------- ### Mapping PHP Properties to HAL Embedded Resources Source: https://github.com/laminas-api-tools/api-tools-hal/blob/1.11.x/docs/ref/embedding-resources.rst This PHP code demonstrates how to explicitly map object properties to HAL embedded resources. It shows how to create HalResource and HalCollection objects from the User's URL and phones properties, respectively, ensuring they are correctly rendered in the HAL output. ```php $user = $persistence->fetch($id); $user->addresses = new HalResource($user->url, $user->url->url_id); $user->phones = new HalCollection($user->phones, 'api/user/phone'); ```