ResourceControllersΒΆ

While the Resource hands off work to your domain logic, PhlyRestfully\ResourceController mediates between the incoming request and the Resource, as well as ensures an appropriate response payload is created and returned.

For the majority of cases, you should be able to use the ResourceController unmodified; you will only need to provide it with a Resource instance and some configuration detailing what Content-Types to respond to, what constitutes an acceptable Accept header, and what HTTP methods are valid for both collections and individual resources.

A common factory for a ResourceController instance might look like the following:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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 PhlyRestfully\Resource();
            $resource->setEventManager($events);

            $controller = new PhlyRestfully\ResourceController('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;
        },
    ),
);

Essentially, three steps are taken:

  • A listener is pulled from the service manager, and injected into a new event manager instance.
  • A Resource instance is created, and injected with the event manager instance.
  • A ResourceController instance is created, and injected with the Resource instance and some configuration.

Considering that most ResourceController instances follow the same pattern, PhlyRestfully provides an abstract factory for controllers that does the work for you. To use it, you will provide a resources subkey in your phlyrestfully configuration, with controller name/configuration pairs. As an example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// In a module's configuration, or the autoloadable configuration of your
// application:
return array(
    'phlyrestfully' => 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
                // PhlyRestfully\ResourceController. Must extend
                // PhlyRestfully\ResourceController, however, to be valid.
                // (OPTIONAL)
                'controller_class' => 'PhlyRestfully\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(
                    'PhlyRestfully\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',

                // Query parameter or array of query parameters that should be
                // injected into collection links if discovered in the request.
                // By default, only the "page" query parameter will be present.
                // (OPTIONAL)
                'collection_query_whitelist' => 'sort',

                // Content types to respond to
                // (OPTIONAL)
                'content_type' => array(
                    ResourceController::CONTENT_TYPE_JSON => array(
                        'application/json',
                        'application/hal+json',
                        'text/json',
                    ),
                ),

                // If a custom identifier_name is used
                // (OPTIONAL)
                'identifier_name'  => 'contact_id',

                // Number of items to return per page of a collection
                // (OPTIONAL)
                'page_size'  => 30,

                // Query string parameter that will indicate number of items
                // per page of results. If this is set, and the parameter is
                // passed, it will be used in favor of the page_size.
                // Leaving it unset will disable the ability of the client
                // to set the page size via query string parameter.
                // (OPTIONAL)
                'page_size_param' => 'page_size',

                // HTTP options for individual resources
                // (OPTIONAL)
                'resource_http_options'   => array('get', 'patch', 'put', 'delete'),

                // name of the route associated with this resource
                // (REQUIRED)
                'route_name' => 'api/contacts',
            ),
        ),
    ),
);

The options defined above cover every available configuration option of the ResourceController, and ensure that your primary listener for the Resource is attached. Additionally, it ensures that both your Resource and ResourceController have defined identifiers for their composed event manager instances, allowing you to attach shared event listeners - which can be useful for implementing logging, caching, authentication and authorization checks, etc.

Note that the above configuration assumes that you are defining a Zend\EventManager\ListenerAggregateInterface implementation to attach to the Resource. This is a good practice anyways, as it keeps the logic encapsulated, and allows you to have stateful listeners – which is particularly useful as most often you will consume a mapper or similar within your listeners in order to persist resources or fetch resources from persistence.