2017 © Pedro Peláez
 

library zend-rest-module

A Zend module for creating RESTful web services.

image

aeris/zend-rest-module

A Zend module for creating RESTful web services.

  • Wednesday, October 7, 2015
  • by eschwartz
  • Repository
  • 7 Watchers
  • 1 Stars
  • 3,525 Installations
  • PHP
  • 1 Dependents
  • 0 Suggesters
  • 1 Forks
  • 1 Open issues
  • 9 Versions
  • 33 % Grown

The README.md

ZendRestModule

A Zend Framework 2 module to help you create RESTful web services., (*1)


Installation

ZendRestModule can be installed via composer., (*2)

php composer.phar require aeris/zend-rest-module

Then add 'Aeris\ZendRestModule' to the 'modules' config in /config/application.config.php., (*3)

Features

ZendRestModule provides a number of features to facilitate creating a RESTful web service., (*4)

  • Entity serialization
  • RESTful Exception handling
  • Test Helpers

Serializer

ZendRestModule uses the popular JMS Serializer to serialize and deserialize application models., (*5)

The JMS Serializer allows you to use Annotations, XML, or YML to configure how objects are serialized and deserialized from raw data., (*6)

While you can use the JMS serializer on its own, ZendRestModule introduces behaviors to make serialization/deserialization a lot easier in the context of Zend APIs., (*7)

Here's how a RESTful controller might look without the ZendRestModule:, (*8)

Automatic Enitity Serialization

class AnimalRestController extends AbstractRestfulController {
    public function get($id) {
        // Grab the animal entity from the database
        $animal = $this
            ->animalRepository
            ->find($id);

        // Serialize the animal into a JSON string
        $jsonString = $this
            ->serviceManager
            ->get('the_jms_serializer_service_I_configured_on_my_own')
            ->serialize($animal, 'json');

        // But JsonModel expects an array, so we need
        // deserialize the JSON string back into a php array.
        $serializedData = json_decode($jsonString);

        return new JsonModel($serializedData);
    }
}

Using the ZendRestModule, you can simply return the raw model:, (*9)

class AnimalRestController extends AbstractRestfulController {
    public function get($id) {
        return $this
            ->animalRepository
            ->find($id);
    }
}

The ZendRestModule will intercept the return value, convert the model to a SerializedJsonModel, then serialize the data according to your JMS Serializer configuration., (*10)

Deserialize onto Existing Entities

Out of the box, the JMS Serializer allows you to deserialize raw data into entity objects., (*11)

class AnimalRestController extends AbstractRestfulController {
    public function create($data) {
        // Deserialize the raw JSON data into 
        // a MyApp\Entity\Animal object
        $animal = $this->serviceManager
            ->get('Aeris\ZendRestModule\Serializer')
            ->deserialize($data, 'MyApp\Entity\Animal');

        $this->saveToDb($animal);

        return $animal;
    }
}

ZendRestModule includes a JMS object constructor extension, which allows you to deserialize data onto an existing entity. This is very useful for PUT requests, where the request data my not be a fully defined entity., (*12)

class AnimalRestController extends AbstractRestfulController {
    public function update($id, $data) {
        $animal = $this->entityManager
            ->getRepo('MyApp\Entity\Animal')
            ->find($id);

        // Update the existing animal from the
        // PUT data
        $this->serviceManager
            ->get('Aeris\ZendRestModule\Serializer')
            ->deserialize($data, $animal);

        $this->saveToDb($animal);

        return $animal;
    } 

}

See this article for a long rant about how and why this works., (*13)

Serialization Groups

The JMS Serializer allows you to configure serialization groups for entities. This is useful for setting what data different request return. For example, I may only want to see an animal's id and name in a getList response, but see more details in a get response:, (*14)

use JMS\Serializer\Annotation as JMS;

class Animal {
    /**
     * @JMS\Groups({"animalSummary", "animalDetails"})
     * @var int
     */
    public $id;

    /**
     * @JMS\Groups({"animalSummary", "animalDetails"})
     * @var string
     */
    public $name;

    /**
     * @JMS\Groups({"animalDetails"})
     * @var string
     */
    public $species;

    /**
     * @JMS\Groups({"animalDetails"})
     * @var string
     */
    public $color;

    /**
     * @JMS\Groups({"dates"})
     * @var
     */
    public $birthDate;
}

The Aeris\ZendRestModule\View\Annotation\Groups annotation allows you to configure which serialization groups will be used for each controller action., (*15)

use Aeris\ZendRestModule\View\Annotation as View;

class AnimalRestController extends AbstractRestfulController {

  /**
  * @View\Groups({"animalDetails", "dates"})
  */
  public function get($id) {
    return $this->entityManager->find($id);
  }

  /**
  * @View\Groups({"animalSummary"})
  */
  public function getList() {
    return $this->entityManager->findAll();
  }
}

You can also configure serialization groups in the zend_rest config:, (*16)

[
    'controllers' => [
        'invokables' => [
            'MyApp\Animals\AnimalRest' => '\MyApp\Animals\AnimalRestController',
        ]
    ],
    'zend_rest' => [
        'controllers' => [
            'MyApp\Animals\AnimalRest' => [
                'serialization_groups' => [
                    'get' => ['animalDetails', 'dates'],
                    'getList' => ['animalSummary']
                ]
            ]
        ]
    ]
]

Other Serializer Components

DateTimeTimestampHandler

Serializes/deserializes between unix timestamps and \DateTime objects., (*17)

class Animal {
    /**
     * @JMS\Type("DateTimeTimestamp")
     *
     * @var \DateTime
     */
    public $birthDate;
}

The serializer will now deserialize birthDate timestamps into \DateTime objects, and serialize birthDate as a timestamp., (*18)

Serializer Configuration Reference

[
    'zend_rest' => [
        'serializer' => [
            // Implementations of \JMS\Serializer\Handler\SubscribingHandlerInterface
            // See http://jmsyst.com/libs/serializer/master/handlers
            'handlers' => [
                // Registers the DateTimeTimestamp Handler by default,
                '\Aeris\ZendRestModule\Service\Serializer\Handler\DateTimeTimestampHandler',
            ],

            // Event subscribers
            // implementing \JMS\Serializer\EventDispatcher\EventSubscriberInterface.
            // May be services or fully qualified class names.
            // See http://jmsyst.com/libs/serializer/master/event_system
            'subscribers' => [
                'MyApp\Serializer\Subscriber\MyAwesomeEventSubscriber'
            ],

            // Event listeners,
            // categorized by event name.
            // See http://jmsyst.com/libs/serializer/master/event_system
            'listeners' => [
                'serializer.pre_serialize' => [
                    function(\JMS\Serializer\EventDispatcher\PreSerializeEvent $event) {
                        // some fantastic event handling logic
                    },
                ],
            ],

            // An implementation of \JMS\Serializer\Naming\PropertyNamingStrategyInterface
            // The '\Aeris\ZendRestModule\Service\Serializer\Naming\IdenticalPropertyNamingStrategy` (default)
            // fixes a bug in the `\JMS\Serializer\Naming\IdenticalPropertyNamingStrategy`
            // See https://github.com/schmittjoh/serializer/issues/334
            'naming_strategy' => '\Aeris\ZendRestModule\Service\Serializer\Naming\IdenticalPropertyNamingStrategy',

            // An implementation of \JMS\Serializer\Construction\ObjectConstructorInterface
            // The 'Aeris\ZendRestModule\Serializer\Constructor\InitializedObjectConstructor' (default)
            // allows data to be deserialized onto existing entities.
            'object_constructor' => 'Aeris\ZendRestModule\Serializer\Constructor\InitializedObjectConstructor',

            // Set to false to disable the @MaxDepth annotation.
            // ZendRestModule sets this to true by default.
            // Note, however, that the JMSSerializers sets this to false by default.
            'enable_max_depth' => true,
        ]
    ],
]

RESTful Exception Handling

ZendRestModule catches errors and exceptions thrown during the MVC event cycle, and converts the errors into JSON responses., (*19)

Example

This example configures JSON output for errors occuring in the Animals Web Service., (*20)

// zend-rest.config.php
'zend_rest' => [
    'errors' => [
        [
            // The `error` can be a Zend\Mvc error string
            // or the class name of an Exception
            'error' => \Zend\Mvc\Application::ERROR_ROUTER_NO_MATCH,

            // HTTP response code
            'http_code' => 404,

            The `error.code` property of the JSON response object
            'application_code' => 'invalid_request',

            The `error.details` property of the JSON response object
            'details' => 'The requested endpoint or action is invalid and not supported.',
        ],
        [
            'error' => 'MyApp\Exception\RidiculousAnimalException',
            'http_code' => 418,
            'applicationCode' => 'ridiculous_animal_error',
            'details' => 'The animal you have requested is too riduculous for our web service.',
            'on_error' => function(RestErrorEvt $evt) {
                // This is a good place to log errors
                $exception = $evt->getError();
                error_log($exception);

                // You can also modify the error view model
                $viewModel = $evt->getViewModel();
                $errorObject = $viewModel->getVariable('error');

                $errorObject['animal'] = $exception->getAnimal();
                $viewModel->setVariable('error', $errorObject);
            }
        ],
        [
            // You should always include a fallback '\Exception' handler.
            'error' => '\Exception',
            'http_code' => 500,
            'application_code' => 'uh_oh',
            'details' => 'whoops!',
        ]
    ]
]
class AnimalRestController extends AbstractRestfulController {
    public function get($id) {
        if ($id === 'narwhal') {
            throw new RidiculousAnimalException("narwhal");
        }

        return $this->animalRepo->find($id);
    }
}

A GET request to /animals/narwhal would return a JSON object so:, (*21)

HTTP Code 418
{
    "error": {
        "code": "ridiculous_animal_error",
        "details": "The animal you have requested is too riduculous for our web service.",
        "animal": "narwhal"
    }
}

The error would also be written to the server log, by way of the on_error callback., (*22)

Similarly a request to /not/an/endpoint would return a 404 error, with a invalid_request JSON application code., (*23)

AbstractTestCase

The \Aeris\ZendRestModuleTest\AbstractTestCase is an extension of the \Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase. It provides a few utilities for testing restful APIs, and a number of new assertions. It is not necessary to use this test case class when working with the ZendRestModule., (*24)

Configuration Reference

[
    'zend_rest' => [
        // Required.
        'cache_dir' => __DIR__ . '/cache'

        // Set to true to disable caching.
        'debug' => false,


        // See "Exception Handling" documentation
        'errors' => [
          [
              'error' => \Zend\Mvc\Application::ERROR_ROUTER_NO_MATCH,
              'http_code' => 404,
              'application_code' => 'invalid_request',
              'details' => 'The requested endpoint or action is invalid and not supported.',
              'on_error' => function(RestErrorEvt $evt) {
                error_log("Someone requested an invalid endpoint.");
              }
          ]
        ],

        'controllers' => [
            // See "Serialization Groups" documentation.
            'serialization_groups' => [
                'MyApp\Controller\UserRest' => [
                    'get' => ['details', 'timestamps'],
                    'getList' => ['summary']
                ]
            ] 
        ],

        // See "Serializer" documentation
        'serializer' => [
            'handlers' [
                '\Aeris\ZendRestModule\Service\Serializer\Handler\DateTimeTimestampHandler',
            ],
            'naming_strategy' => '\Aeris\ZendRestModule\Service\Serializer\Naming\IdenticalPropertyNamingStrategy',
            'object_constructor' => 'Aeris\ZendRestModule\Serializer\Constructor\InitializedObjectConstructor',
            'enable_max_depth' => true,
        ]
    ]
]

Have fun!, (*25)

The Versions

07/10 2015

dev-master

9999999-dev

A Zend module for creating RESTful web services.

  Sources   Download

GPL

The Requires

 

The Development Requires

by Edan Schwartz

api rest zf2 zend restful

07/10 2015

v1.1.4

1.1.4.0

A Zend module for creating RESTful web services.

  Sources   Download

GPL

The Requires

 

The Development Requires

by Edan Schwartz

api rest zf2 zend restful

06/08 2015

1.1.3

1.1.3.0

A Zend module for creating RESTful web services.

  Sources   Download

GPL

The Requires

 

The Development Requires

by Edan Schwartz

api rest zf2 zend restful

05/03 2015

1.1.2

1.1.2.0

A Zend module for creating RESTful web services.

  Sources   Download

GPL

The Requires

 

The Development Requires

by Edan Schwartz

api rest zf2 zend restful

05/03 2015

dev-feature-cell-phone-serializer

dev-feature-cell-phone-serializer

A Zend module for creating RESTful web services.

  Sources   Download

GPL

The Requires

 

The Development Requires

by Edan Schwartz

api rest zf2 zend restful

05/02 2015

dev-development

dev-development

A Zend module for creating RESTful web services.

  Sources   Download

GPL

The Requires

 

The Development Requires

by Edan Schwartz

api rest zf2 zend restful

05/02 2015

1.1.1

1.1.1.0

A Zend module for creating RESTful web services.

  Sources   Download

GPL

The Requires

 

The Development Requires

by Edan Schwartz

api rest zf2 zend restful

29/01 2015

1.1.0

1.1.0.0

A Zend module for creating RESTful web services.

  Sources   Download

GPL

The Requires

 

The Development Requires

by Edan Schwartz

api rest zf2 zend restful

16/01 2015

1.0.0

1.0.0.0

A Zend module for creating RESTful web services.

  Sources   Download

GPL

The Requires

 

The Development Requires

by Edan Schwartz

api rest zf2 zend restful