diff --git a/.travis.yml b/.travis.yml index e6ca74f0..7fee4b96 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,6 @@ matrix: - php: 7 - php: hhvm allow_failures: - - php: 7 - php: hhvm notifications: diff --git a/README.md b/README.md index 1a17a69b..a9e23adb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ -Apigility with Doctrine +Doctrine in Apigility ============================== [![Build status](https://api.travis-ci.org/zfcampus/zf-apigility-doctrine.svg)](http://travis-ci.org/zfcampus/zf-apigility-doctrine) +[![Total Downloads](https://poser.pugx.org/zfcampus/zf-apigility-doctrine/downloads)](https://packagist.org/packages/zfcampus/zf-apigility-doctrine) This module provides the classes for integrating Doctrine with Apigility. @@ -40,7 +41,7 @@ for the object manager. `/apigility/api/module[/:name]/doctrine[/:controller_service_name]` -This is a Doctrine resource route _like_ Apigility Rest `/apigility/api/module[/:name]/rest[/:controller_service_name]` +This is a Doctrine resource route _like_ Apigility Rest `/apigility/api/module[/:name]/rest[/:controller_service_name]` To create a resource do not include `[/:controller_service_name]` POST Parameters @@ -135,23 +136,12 @@ setting the DoctrineResource::setMultiKeyDelimiter($value) Complex queries through route parameters ---------------------------------------- -You may specify multiple route parameters and as long as the route -matches then the route parameter names will be matched to the entity. +NO LONGER SUPPORTED. As of version 2.0.4 this functionality has been removed from +this module. The intended use of this module is a 1:1 mapping of entities to resources +and using subroutes is not in the spirit of this intention. It is STRONGLY recommended +you use [zfcampus/zf-doctrine-querybuilder](https://github.com/zfcampus/zf-doctrine-querybuilder) +for complex query-ability. -For instance, a route of ```/api/artist/:artist_id/album/:album_id``` mapped to the *Album* -entity will filter the *Album* for field names. So, given an album with id, name, and artist -fields the album_id matches to the resoruce configuration and will be queried by key -and the artist is a field on album and will be queried by criteria so the final query -would be - -```php -$objectManager->getRepository('Album')->findOneBy( - 'id' => :album_id, - 'artist' => :artist_id -); -``` - -The album(_id) is not a field on the *Album* entity and will be ignored. Query Providers @@ -197,7 +187,7 @@ When the query provider is registered attach it to the doctrine-connected resour Query Create Filters ============== -In order to filter or change data sent to a create statement before it is used to hydrate the entity you may use a query create filter. Create filters are very similar to *Query Providers* in their implementation. +In order to filter or change data sent to a create statement before it is used to hydrate the entity you may use a query create filter. Create filters are very similar to *Query Providers* in their implementation. Create filters take the data as a parameter and return the data, modified or filtered. diff --git a/composer.json b/composer.json index dc7262df..e19fd7b4 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "zfcampus/zf-apigility-doctrine", "type": "library", - "description": "Doctrine for Apigility", + "description": "Doctrine in Apigility", "keywords": [ "apigility", "doctrine", @@ -12,18 +12,12 @@ "authors": [ { "name": "Tom Anderson", - "email": "tanderson@soliantconsulting.com" + "email": "tom.h.anderson@gmail.com" } ], - "extra": { - "branch-alias": { - "dev-master": "1.1-dev", - "dev-develop": "1.2-dev" - } - }, "require-dev": { - "doctrine/doctrine-mongo-odm-module": "^0.9", - "doctrine/doctrine-orm-module": "^0.9", + "doctrine/doctrine-mongo-odm-module": "~0.10 || ^1.0", + "doctrine/doctrine-orm-module": "^0.10", "doctrine/mongodb-odm": "^1", "phpunit/phpunit": "^4.7", "squizlabs/php_codesniffer": "^2.3.0", @@ -31,14 +25,14 @@ "zendframework/zend-log": "~2.4", "zendframework/zend-serializer": "~2.4", "zendframework/zend-test": "~2.4", - "zendframework/zend-stdlib": "2.4.9", - "zfcampus/zf-apigility-admin": "^1.1", + "zendframework/zend-stdlib": "~2.4", + "zfcampus/zf-apigility-admin": "~1.4", "zendframework/zend-i18n": "~2.4" }, "require": { - "php": ">=5.4", - "phpro/zf-doctrine-hydration-module": "^0.1.5", - "zfcampus/zf-apigility": "^1.0" + "php": ">=5.4 || ^7.0", + "phpro/zf-doctrine-hydration-module": "^2.0", + "zfcampus/zf-apigility": "^1.1" }, "suggest": { "doctrine/doctrine-orm-module": "For ORM mapping", diff --git a/src/Admin/Model/DoctrineMetadataServiceResource.php b/src/Admin/Model/DoctrineMetadataServiceResource.php index 4c3ab6a1..37e1ef36 100644 --- a/src/Admin/Model/DoctrineMetadataServiceResource.php +++ b/src/Admin/Model/DoctrineMetadataServiceResource.php @@ -9,10 +9,9 @@ use ZF\ApiProblem\ApiProblem; use ZF\Rest\AbstractResourceListener; use Zend\ServiceManager\ServiceManager; -use Zend\ServiceManager\ServiceManagerAwareInterface; use Exception; -class DoctrineMetadataServiceResource extends AbstractResourceListener implements ServiceManagerAwareInterface +class DoctrineMetadataServiceResource extends AbstractResourceListener { protected $serviceManager; diff --git a/src/Admin/Model/DoctrineRestServiceModel.php b/src/Admin/Model/DoctrineRestServiceModel.php index ac35776e..ac5b6ee0 100644 --- a/src/Admin/Model/DoctrineRestServiceModel.php +++ b/src/Admin/Model/DoctrineRestServiceModel.php @@ -22,10 +22,10 @@ use ZF\Rest\Exception\CreationException; use Zf\Apigility\Admin\Model\ModuleEntity; use Zend\ServiceManager\ServiceManager; -use Zend\ServiceManager\ServiceManagerAwareInterface; use ZF\ApiProblem\ApiProblem; -class DoctrineRestServiceModel implements EventManagerAwareInterface, ServiceManagerAwareInterface +class DoctrineRestServiceModel implements + EventManagerAwareInterface { /** * @var ConfigResource diff --git a/src/Admin/Model/DoctrineRestServiceModelFactory.php b/src/Admin/Model/DoctrineRestServiceModelFactory.php index f01692b9..2f0b1a66 100644 --- a/src/Admin/Model/DoctrineRestServiceModelFactory.php +++ b/src/Admin/Model/DoctrineRestServiceModelFactory.php @@ -9,9 +9,8 @@ use ZF\Apigility\Admin\Exception; use ZF\Apigility\Admin\Model\RpcServiceModelFactory; use Zend\ServiceManager\ServiceManager; -use Zend\ServiceManager\ServiceManagerAwareInterface; -class DoctrineRestServiceModelFactory extends RpcServiceModelFactory implements ServiceManagerAwareInterface +class DoctrineRestServiceModelFactory extends RpcServiceModelFactory { const TYPE_DEFAULT = 'ZF\Apigility\Doctrine\Admin\Model\DoctrineRestServiceModel'; diff --git a/src/Admin/Module.php b/src/Admin/Module.php index c4cde452..07568435 100644 --- a/src/Admin/Module.php +++ b/src/Admin/Module.php @@ -11,6 +11,7 @@ use Zend\ModuleManager\Feature\DependencyIndicatorInterface; use Zend\ModuleManager\Feature\ServiceProviderInterface; use Zend\ServiceManager\Exception\ServiceNotCreatedException; +use ZF\Apigility\Doctrine\Admin\Model\DoctrineMetadataServiceResource; class Module implements ConfigProviderInterface, @@ -53,11 +54,15 @@ public function getConfig() public function getServiceConfig() { return array( - 'invokables' => array( - 'ZF\Apigility\Doctrine\Admin\Model\DoctrineMetadataServiceResource' => - 'ZF\Apigility\Doctrine\Admin\Model\DoctrineMetadataServiceResource', - ), 'factories' => array( + // This resource pulls the object manager dynamically + // so it needs access to the service manager + 'ZF\Apigility\Doctrine\Admin\Model\DoctrineMetadataServiceResource' => function ($services) { + $instance = new DoctrineMetadataServiceResource(); + $instance->setServiceManager($services); + + return $instance; + }, 'ZF\Apigility\Doctrine\Admin\Model\DoctrineAutodiscoveryModel' => function ($services) { if (!$services->has('Config')) { // @codeCoverageIgnoreStart @@ -96,12 +101,15 @@ public function getServiceConfig() __NAMESPACE__ . '\Model\DoctrineRestServiceModel::onFetch' ); - return new Model\DoctrineRestServiceModelFactory( + $instance = new Model\DoctrineRestServiceModelFactory( $modulePathSpec, $configFactory, $sharedEvents, $moduleModel ); + $instance->setServiceManager($services); + + return $instance; }, 'ZF\Apigility\Doctrine\Admin\Model\DoctrineRestServiceResource' => function ($services) { if (!$services->has('ZF\Apigility\Doctrine\Admin\Model\DoctrineRestServiceModelFactory')) { diff --git a/src/Server/Controller/RpcController.php b/src/Server/Controller/RpcController.php deleted file mode 100644 index 07479239..00000000 --- a/src/Server/Controller/RpcController.php +++ /dev/null @@ -1,95 +0,0 @@ -params()->fromRoute('parent_id'); - if (!$parentId) { - // @codeCoverageIgnoreStart - return new ApiProblemResponse( - new ApiProblem(400, "Parent ID is required") - ); - } - // @codeCoverageIgnoreEnd - - $childId = $this->params()->fromRoute('child_id'); - - $config = $this->getServiceLocator()->get('Config'); - $zfRpcDoctrineControllerArrayKey = array_search(get_class($this), $config['controllers']['invokables']); - - $associationConfig = $config['zf-rpc-doctrine-controller'][$zfRpcDoctrineControllerArrayKey]; - $metadataConfig = $config['zf-hal']['metadata_map'][$associationConfig['source_entity']]; - $hydratorConfig = $config['doctrine-hydrator'][$metadataConfig['hydrator']]; - - $objectManager = $this->getServiceLocator()->get($hydratorConfig['object_manager']); - $metadataFactory = $objectManager->getMetadataFactory(); - - // Find target entity controller to dispatch - foreach ($config['zf-rest'] as $controllerName => $controllerConfig) { - if ($associationConfig['target_entity'] == $controllerConfig['entity_class']) { - $targetRouteParam = $controllerConfig['route_identifier_name']; - break; - } - } - - // Find source entity field name for target - $sourceMetadata = $metadataFactory->getMetadataFor($associationConfig['source_entity']); - foreach ($sourceMetadata->associationMappings as $mapping) { - if ($mapping['sourceEntity'] == $associationConfig['source_entity'] - and $mapping['targetEntity'] == $associationConfig['target_entity'] - and $mapping['fieldName'] == $associationConfig['field_name'] - ) { - $sourceField = $mapping['mappedBy']; - break; - } - } - - $query = array(); - - if ($childId) { - // Verify child is a child of parent - $child = $objectManager->getRepository($associationConfig['target_entity'])->findOneBy( - array( - 'id' => $childId, - $sourceField => $parentId, - ) - ); - - if ($child) { - $this->getRequest()->setMethod('GET'); - $hal = $this->forward()->dispatch( - $controllerName, - array( - $targetRouteParam => $childId, - ) - ); - $renderer = $this->getServiceLocator()->get('ZF\Hal\JsonRenderer'); - $data = json_decode($renderer->render($hal), true); - - return $data; - } else { - // @codeCoverageIgnoreStart - return new ApiProblemResponse( - new ApiProblem(400, 'Resource not found.') - ); - } - // @codeCoverageIgnoreEnd - } else { - $query[] = array('type' => 'eq', 'field' => $sourceField, 'value' => $parentId); - - $this->getRequest()->setMethod('GET'); - $hal = $this->forward()->dispatch($controllerName, array()); - $renderer = $this->getServiceLocator()->get('ZF\Hal\JsonRenderer'); - $data = json_decode($renderer->render($hal), true); - - return $data; - } - } -} diff --git a/src/Server/Event/Listener/CollectionListener.php b/src/Server/Event/Listener/CollectionListener.php index 7545033a..9c19f246 100644 --- a/src/Server/Event/Listener/CollectionListener.php +++ b/src/Server/Event/Listener/CollectionListener.php @@ -13,8 +13,8 @@ use Zend\InputFilter\InputFilterInterface; use Zend\ServiceManager\ServiceLocatorInterface; use Zend\Stdlib\ArrayObject; -use Zend\Stdlib\Hydrator\HydratorAwareInterface; -use Zend\Stdlib\Hydrator\HydratorInterface; +use Zend\Hydrator\HydratorAwareInterface; +use Zend\Hydrator\HydratorInterface; use ZF\Apigility\Doctrine\Server\Event\DoctrineResourceEvent; use ZF\Apigility\Doctrine\Server\Exception\InvalidArgumentException; diff --git a/src/Server/Resource/DoctrineResource.php b/src/Server/Resource/DoctrineResource.php index c084133d..dbc85856 100644 --- a/src/Server/Resource/DoctrineResource.php +++ b/src/Server/Resource/DoctrineResource.php @@ -20,8 +20,8 @@ use Zend\EventManager\EventManagerAwareInterface; use Zend\EventManager\EventManagerInterface; use Zend\Stdlib\ArrayUtils; -use Zend\Stdlib\Hydrator\HydratorAwareInterface; -use Zend\Stdlib\Hydrator\HydratorInterface; +use Zend\Hydrator\HydratorAwareInterface; +use Zend\Hydrator\HydratorInterface; use Zend\EventManager\SharedEventManager; use Traversable; use ReflectionClass; @@ -268,34 +268,6 @@ public function getMultiKeyDelimiter() return $this->multiKeyDelimiter; } - /** - * For /multi/1/keyed/2/routes/3 the route parameter - * names may include an id suffix (e.g. id, _id, Id) - * and this will be striped to create criteria - * - * Example - * $objectManager->getRepository(...)->findOneBy( - * 'multi' => 1, - * 'keyed' => 2, - * 'routes' => 3 - * ); - * - * @var string - */ - protected $stripRouteParameterSuffix = '_id'; - - public function setStripRouteParameterSuffix($value) - { - $this->stripRouteParameterSuffix = $value; - - return $this; - } - - public function getStripRouteParameterSuffix() - { - return $this->stripRouteParameterSuffix; - } - /** * @var HydratorInterface */ @@ -744,13 +716,6 @@ protected function findEntity($id, $method, $data = null) * Append query selection parameters by route match. */ foreach ($allowedRouteParams as $routeMatchParam => $value) { - if ($this->getStripRouteParameterSuffix() === substr( - $routeMatchParam, - -1 * strlen($this->getStripRouteParameterSuffix()) - )) { - $routeMatchParam = substr($routeMatchParam, 0, -1 * strlen($this->getStripRouteParameterSuffix())); - } - if (in_array($routeMatchParam, $associationMappings) || in_array($routeMatchParam, $fieldNames)) { $criteria[$routeMatchParam] = $value; } diff --git a/src/Server/Resource/DoctrineResourceFactory.php b/src/Server/Resource/DoctrineResourceFactory.php index a475f78d..7f9ea926 100644 --- a/src/Server/Resource/DoctrineResourceFactory.php +++ b/src/Server/Resource/DoctrineResourceFactory.php @@ -8,7 +8,7 @@ use Zend\ServiceManager\Exception\ServiceNotCreatedException; use Zend\ServiceManager\Exception\ServiceNotFoundException; use Zend\ServiceManager\ServiceLocatorInterface; -use Zend\Stdlib\Hydrator\HydratorInterface; +use Zend\Hydrator\HydratorInterface; use ZF\Apigility\Doctrine\Server\Collection\Query; use RuntimeException; diff --git a/test/src/Server/ODM/CRUD/CRUDTest.php b/test/src/Server/ODM/CRUD/CRUDTest.php index d39974d0..ab9f6f45 100644 --- a/test/src/Server/ODM/CRUD/CRUDTest.php +++ b/test/src/Server/ODM/CRUD/CRUDTest.php @@ -61,7 +61,6 @@ public function testCreate() $this->getRequest()->setContent('{"name": "ArtistOne","createdAt": "2011-12-18 13:17:17"}'); $this->dispatch('/test/meta'); $body = json_decode($this->getResponse()->getBody(), true); - $this->assertEquals('ArtistOne', $body['name']); $this->assertEquals(201, $this->getResponseStatusCode()); $this->validateTriggeredEvents(array( diff --git a/test/src/Server/ORM/CRUD/CRUDTest.php b/test/src/Server/ORM/CRUD/CRUDTest.php index dac7616c..d98f0c88 100644 --- a/test/src/Server/ORM/CRUD/CRUDTest.php +++ b/test/src/Server/ORM/CRUD/CRUDTest.php @@ -651,15 +651,5 @@ public function testRpcController() $this->assertEquals(201, $this->getResponseStatusCode()); $albumId = $body['id']; - - $this->getRequest()->setMethod(Request::METHOD_GET); - $this->getRequest()->setContent(null); - $this->dispatch("/test/artist/$artistId/album"); - $body = json_decode($this->getResponse()->getBody(), true); - $this->assertEquals(2, $body['total_items']); - - $this->dispatch("/test/artist/$artistId/album/$albumId"); - $body = json_decode($this->getResponse()->getBody(), true); - $this->assertEquals('AlbumTwo', $body['name']); } }