Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/231.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 如何使用基于ZfcBase DbMapper的模型在敏捷性驱动的应用程序中构建嵌套响应?_Php_Zend Framework2_One To Many_Datamapper_Laminas Api Tools - Fatal编程技术网

Php 如何使用基于ZfcBase DbMapper的模型在敏捷性驱动的应用程序中构建嵌套响应?

Php 如何使用基于ZfcBase DbMapper的模型在敏捷性驱动的应用程序中构建嵌套响应?,php,zend-framework2,one-to-many,datamapper,laminas-api-tools,Php,Zend Framework2,One To Many,Datamapper,Laminas Api Tools,我正在开发一个RESTful web应用程序——由驱动并基于。对于模型层,我使用了。该模型基本上由两个实体组成:Project和Image(1:n),目前的实现方式如下: ProjectCollection extends Paginator ProjectEntity ProjectMapper extends AbstractDbMapper ProjectService implements ServiceManagerAwareInterface ProjectServiceFactor

我正在开发一个RESTful web应用程序——由驱动并基于。对于模型层,我使用了。该模型基本上由两个实体组成:
Project
Image
1:n
),目前的实现方式如下:

ProjectCollection extends Paginator
ProjectEntity
ProjectMapper extends AbstractDbMapper
ProjectService implements ServiceManagerAwareInterface
ProjectServiceFactory implements FactoryInterface
use ZF\Hal\Collection

...

$images = new Collection($arrayOfImages);

$project['images'] = $images;
图像的结构相同

当请求资源(
/projects[/:id]
)时,响应的项目实体应包含其
图像
实体的列表

那么,这个
1:n
结构应该如何实现呢?

子问题:

  • [
    DbMapper
    ]是否提供了一些“魔法”来“自动”检索这样的树结构,而无需编写
    JOIN
    s(或使用ORM)

  • [
    Apigility
    ]是否为构建嵌套响应提供了一些“魔力”



  • 编辑

    我目前得到的输出是:

    /projects/:id

    {
        "id": "1",
        "title": "...",
        ...
        "_embedded": {
            "images": [
                {
                    "id": "1",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/1"
                        }
                    }
                },
                {
                    "id": "2",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/2"
                        }
                    }
                },
                {
                    "id": "3",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/3"
                        }
                    }
                }
            ]
        },
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects/1"
            }
        }
    }
    
    {
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects?page=1"
            },
            "first": {
                "href": "http://myproject-api.misc.loc/projects"
            },
            "last": {
                "href": "http://myproject-api.misc.loc/projects?page=24"
            },
            "next": {
                "href": "http://myproject-api.misc.loc/projects?page=2"
            }
        },
        "_embedded": {
            "projects": [
                {
                    "id": "1",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/1"
                        }
                    }
                },
                {
                    "id": "2",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/2"
                        }
                    }
                },
                {
                    "id": "3",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/3"
                        }
                    }
                }
            ]
        },
        "page_count": 24,
        "page_size": 3,
        "total_items": 72
    }
    
    public function findAll() {
        $select = $this->getSelect();
        $adapter = $this->getDbAdapter();
        $paginatorAdapter = new DbSelect($select, $adapter);
        $collection = new ProjectCollection($paginatorAdapter);
        return $collection;
    }
    
    return array(
        ...
        'zf-hal' => array(
            'metadata_map' => array(
                ...
                'Portfolio\\V2\\Rest\\Project\\ProjectEntity' => array(
                    'entity_identifier_name' => 'id',
                    'route_name' => 'portfolio.rest.project',
                    'route_identifier_name' => 'id',
                    'hydrator' => 'Portfolio\\V2\\Rest\\Project\\ProjectHydrator',
                ),
                'Portfolio\\V2\\Rest\\Project\\ProjectCollection' => array(
                    'entity_identifier_name' => 'id',
                    'route_name' => 'portfolio.rest.project',
                    'route_identifier_name' => 'id',
                    'is_collection' => true,
                ),
                ...
            ),
        ),
    );
    
    class Module implements ApigilityProviderInterface {
    
        ...
    
        public function getHydratorConfig() {
            return array(
                'factories' => array(
                    // V2
                    'Portfolio\\V2\\Rest\\Project\\ProjectHydrator' => function(ServiceManager $serviceManager) {
                        $projectHydrator = new ProjectHydrator();
                        $projectHydrator->setImageService($serviceManager->getServiceLocator()->get('Portfolio\V2\Rest\ImageService'));
                        return $projectHydrator;
                    }
                ),
            );
        }
    
        ...
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use Zend\Stdlib\Hydrator\ClassMethods;
    use Portfolio\V2\Rest\Image\ImageService;
    
    class ProjectHydrator extends ClassMethods {
    
        /**
         * @var ImageService
         */
        protected $imageService;
    
        /**
         * @return ImageService the $imageService
         */
        public function getImageService() {
            return $this->imageService;
        }
    
        /**
         * @param ImageService $imageService
         */
        public function setImageService(ImageService $imageService) {
            $this->imageService = $imageService;
            return $this;
        }
    
        /*
         * Doesn't need to be implemented:
         * the ClassMethods#hydrate(...) handle the $data already as wished.
         */
        /*
        public function hydrate(array $data, $object) {
            $object = parent::hydrate($data, $object);
            if ($object->getId() !== null) {
                $images = $this->imageService->getImagesForProject($object->getId());
                $object->setImages($images);
            }
            return $object;
        }
        */
    
        /**
         * @see \Zend\Stdlib\Hydrator\ClassMethods::extract()
         */
        public function extract($object) {
            $array = parent::extract($object);
            if ($array['id'] !== null) {
                $images = $this->imageService->getImagesForProject($array['id']);
                $array['images'] = $images;
            }
            return $array;
        }
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use Zend\ServiceManager\ServiceLocatorInterface;
    
    class ProjectMapperFactory {
    
        public function __invoke(ServiceLocatorInterface $serviceManager) {
            $mapper = new ProjectMapper();
            $mapper->setDbAdapter($serviceManager->get('PortfolioDbAdapter_V2'));
            $mapper->setEntityPrototype($serviceManager->get('Portfolio\V2\Rest\Project\ProjectEntity'));
            $projectHydrator = $serviceManager->get('HydratorManager')->get('Portfolio\\V2\\Rest\\Project\\ProjectHydrator');
            $mapper->setHydrator($projectHydrator);
            return $mapper;
        }
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use ZfcBase\Mapper\AbstractDbMapper;
    use Zend\Paginator\Adapter\DbSelect;
    use Zend\Db\ResultSet\HydratingResultSet;
    
    class ProjectMapper extends AbstractDbMapper {
    
        ...
    
        /**
         * Provides a collection of all the available projects.
         *
         * @return \Portfolio\V2\Rest\Project\ProjectCollection
         */
        public function findAll() {
            $resultSetPrototype = new HydratingResultSet(
                $this->getHydrator(),
                $this->getEntityPrototype()
            );
            $select = $this->getSelect();
            $adapter = $this->getDbAdapter();
            $paginatorAdapter = new DbSelect($select, $adapter, $resultSetPrototype);
            $collection = new ProjectCollection($paginatorAdapter);
            return $collection;
        }
    
        /**
         * Provides a project by ID.
         *
         * @param int $id
         * @return \Portfolio\V2\Rest\Project\ProjectEntity
         */
        public function findById($id) {
            $select = $this->getSelect();
            $select->where(array(
                'id' => $id,
            ));
            $entity = $this->select($select)->current();
            return $entity;
        }
    
        ...
    
    }
    
    因此,它适用于单个对象。但不适用于集合,其中单个项目包括更多集合:

    /projects

    {
        "id": "1",
        "title": "...",
        ...
        "_embedded": {
            "images": [
                {
                    "id": "1",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/1"
                        }
                    }
                },
                {
                    "id": "2",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/2"
                        }
                    }
                },
                {
                    "id": "3",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/3"
                        }
                    }
                }
            ]
        },
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects/1"
            }
        }
    }
    
    {
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects?page=1"
            },
            "first": {
                "href": "http://myproject-api.misc.loc/projects"
            },
            "last": {
                "href": "http://myproject-api.misc.loc/projects?page=24"
            },
            "next": {
                "href": "http://myproject-api.misc.loc/projects?page=2"
            }
        },
        "_embedded": {
            "projects": [
                {
                    "id": "1",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/1"
                        }
                    }
                },
                {
                    "id": "2",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/2"
                        }
                    }
                },
                {
                    "id": "3",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/3"
                        }
                    }
                }
            ]
        },
        "page_count": 24,
        "page_size": 3,
        "total_items": 72
    }
    
    public function findAll() {
        $select = $this->getSelect();
        $adapter = $this->getDbAdapter();
        $paginatorAdapter = new DbSelect($select, $adapter);
        $collection = new ProjectCollection($paginatorAdapter);
        return $collection;
    }
    
    return array(
        ...
        'zf-hal' => array(
            'metadata_map' => array(
                ...
                'Portfolio\\V2\\Rest\\Project\\ProjectEntity' => array(
                    'entity_identifier_name' => 'id',
                    'route_name' => 'portfolio.rest.project',
                    'route_identifier_name' => 'id',
                    'hydrator' => 'Portfolio\\V2\\Rest\\Project\\ProjectHydrator',
                ),
                'Portfolio\\V2\\Rest\\Project\\ProjectCollection' => array(
                    'entity_identifier_name' => 'id',
                    'route_name' => 'portfolio.rest.project',
                    'route_identifier_name' => 'id',
                    'is_collection' => true,
                ),
                ...
            ),
        ),
    );
    
    class Module implements ApigilityProviderInterface {
    
        ...
    
        public function getHydratorConfig() {
            return array(
                'factories' => array(
                    // V2
                    'Portfolio\\V2\\Rest\\Project\\ProjectHydrator' => function(ServiceManager $serviceManager) {
                        $projectHydrator = new ProjectHydrator();
                        $projectHydrator->setImageService($serviceManager->getServiceLocator()->get('Portfolio\V2\Rest\ImageService'));
                        return $projectHydrator;
                    }
                ),
            );
        }
    
        ...
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use Zend\Stdlib\Hydrator\ClassMethods;
    use Portfolio\V2\Rest\Image\ImageService;
    
    class ProjectHydrator extends ClassMethods {
    
        /**
         * @var ImageService
         */
        protected $imageService;
    
        /**
         * @return ImageService the $imageService
         */
        public function getImageService() {
            return $this->imageService;
        }
    
        /**
         * @param ImageService $imageService
         */
        public function setImageService(ImageService $imageService) {
            $this->imageService = $imageService;
            return $this;
        }
    
        /*
         * Doesn't need to be implemented:
         * the ClassMethods#hydrate(...) handle the $data already as wished.
         */
        /*
        public function hydrate(array $data, $object) {
            $object = parent::hydrate($data, $object);
            if ($object->getId() !== null) {
                $images = $this->imageService->getImagesForProject($object->getId());
                $object->setImages($images);
            }
            return $object;
        }
        */
    
        /**
         * @see \Zend\Stdlib\Hydrator\ClassMethods::extract()
         */
        public function extract($object) {
            $array = parent::extract($object);
            if ($array['id'] !== null) {
                $images = $this->imageService->getImagesForProject($array['id']);
                $array['images'] = $images;
            }
            return $array;
        }
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use Zend\ServiceManager\ServiceLocatorInterface;
    
    class ProjectMapperFactory {
    
        public function __invoke(ServiceLocatorInterface $serviceManager) {
            $mapper = new ProjectMapper();
            $mapper->setDbAdapter($serviceManager->get('PortfolioDbAdapter_V2'));
            $mapper->setEntityPrototype($serviceManager->get('Portfolio\V2\Rest\Project\ProjectEntity'));
            $projectHydrator = $serviceManager->get('HydratorManager')->get('Portfolio\\V2\\Rest\\Project\\ProjectHydrator');
            $mapper->setHydrator($projectHydrator);
            return $mapper;
        }
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use ZfcBase\Mapper\AbstractDbMapper;
    use Zend\Paginator\Adapter\DbSelect;
    use Zend\Db\ResultSet\HydratingResultSet;
    
    class ProjectMapper extends AbstractDbMapper {
    
        ...
    
        /**
         * Provides a collection of all the available projects.
         *
         * @return \Portfolio\V2\Rest\Project\ProjectCollection
         */
        public function findAll() {
            $resultSetPrototype = new HydratingResultSet(
                $this->getHydrator(),
                $this->getEntityPrototype()
            );
            $select = $this->getSelect();
            $adapter = $this->getDbAdapter();
            $paginatorAdapter = new DbSelect($select, $adapter, $resultSetPrototype);
            $collection = new ProjectCollection($paginatorAdapter);
            return $collection;
        }
    
        /**
         * Provides a project by ID.
         *
         * @param int $id
         * @return \Portfolio\V2\Rest\Project\ProjectEntity
         */
        public function findById($id) {
            $select = $this->getSelect();
            $select->where(array(
                'id' => $id,
            ));
            $entity = $this->select($select)->current();
            return $entity;
        }
    
        ...
    
    }
    
    编辑为:

    public function getProjects() {
        $projects = $this->getMapper()->findAll();
        foreach ($projects as $key => $project) {
            $images = $this->getImageService()->getImagesForProject($project['id']);
            $projects[$key]['images'] = $images;
        }
        return $projects;
    }
    
    public function findAll() {
        $select = $this->getSelect();
        $adapter = $this->getDbAdapter();
        $paginatorAdapter = new DbSelect($select, $adapter);
        // @todo Replace the constants with data from the config and request.
        $projects = $paginatorAdapter->getItems(0, 2);
        $projects = $projects->toArray();
        return $projects;
    }
    
    以及
    项目映射器#findAll()

    {
        "id": "1",
        "title": "...",
        ...
        "_embedded": {
            "images": [
                {
                    "id": "1",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/1"
                        }
                    }
                },
                {
                    "id": "2",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/2"
                        }
                    }
                },
                {
                    "id": "3",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/3"
                        }
                    }
                }
            ]
        },
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects/1"
            }
        }
    }
    
    {
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects?page=1"
            },
            "first": {
                "href": "http://myproject-api.misc.loc/projects"
            },
            "last": {
                "href": "http://myproject-api.misc.loc/projects?page=24"
            },
            "next": {
                "href": "http://myproject-api.misc.loc/projects?page=2"
            }
        },
        "_embedded": {
            "projects": [
                {
                    "id": "1",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/1"
                        }
                    }
                },
                {
                    "id": "2",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/2"
                        }
                    }
                },
                {
                    "id": "3",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/3"
                        }
                    }
                }
            ]
        },
        "page_count": 24,
        "page_size": 3,
        "total_items": 72
    }
    
    public function findAll() {
        $select = $this->getSelect();
        $adapter = $this->getDbAdapter();
        $paginatorAdapter = new DbSelect($select, $adapter);
        $collection = new ProjectCollection($paginatorAdapter);
        return $collection;
    }
    
    return array(
        ...
        'zf-hal' => array(
            'metadata_map' => array(
                ...
                'Portfolio\\V2\\Rest\\Project\\ProjectEntity' => array(
                    'entity_identifier_name' => 'id',
                    'route_name' => 'portfolio.rest.project',
                    'route_identifier_name' => 'id',
                    'hydrator' => 'Portfolio\\V2\\Rest\\Project\\ProjectHydrator',
                ),
                'Portfolio\\V2\\Rest\\Project\\ProjectCollection' => array(
                    'entity_identifier_name' => 'id',
                    'route_name' => 'portfolio.rest.project',
                    'route_identifier_name' => 'id',
                    'is_collection' => true,
                ),
                ...
            ),
        ),
    );
    
    class Module implements ApigilityProviderInterface {
    
        ...
    
        public function getHydratorConfig() {
            return array(
                'factories' => array(
                    // V2
                    'Portfolio\\V2\\Rest\\Project\\ProjectHydrator' => function(ServiceManager $serviceManager) {
                        $projectHydrator = new ProjectHydrator();
                        $projectHydrator->setImageService($serviceManager->getServiceLocator()->get('Portfolio\V2\Rest\ImageService'));
                        return $projectHydrator;
                    }
                ),
            );
        }
    
        ...
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use Zend\Stdlib\Hydrator\ClassMethods;
    use Portfolio\V2\Rest\Image\ImageService;
    
    class ProjectHydrator extends ClassMethods {
    
        /**
         * @var ImageService
         */
        protected $imageService;
    
        /**
         * @return ImageService the $imageService
         */
        public function getImageService() {
            return $this->imageService;
        }
    
        /**
         * @param ImageService $imageService
         */
        public function setImageService(ImageService $imageService) {
            $this->imageService = $imageService;
            return $this;
        }
    
        /*
         * Doesn't need to be implemented:
         * the ClassMethods#hydrate(...) handle the $data already as wished.
         */
        /*
        public function hydrate(array $data, $object) {
            $object = parent::hydrate($data, $object);
            if ($object->getId() !== null) {
                $images = $this->imageService->getImagesForProject($object->getId());
                $object->setImages($images);
            }
            return $object;
        }
        */
    
        /**
         * @see \Zend\Stdlib\Hydrator\ClassMethods::extract()
         */
        public function extract($object) {
            $array = parent::extract($object);
            if ($array['id'] !== null) {
                $images = $this->imageService->getImagesForProject($array['id']);
                $array['images'] = $images;
            }
            return $array;
        }
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use Zend\ServiceManager\ServiceLocatorInterface;
    
    class ProjectMapperFactory {
    
        public function __invoke(ServiceLocatorInterface $serviceManager) {
            $mapper = new ProjectMapper();
            $mapper->setDbAdapter($serviceManager->get('PortfolioDbAdapter_V2'));
            $mapper->setEntityPrototype($serviceManager->get('Portfolio\V2\Rest\Project\ProjectEntity'));
            $projectHydrator = $serviceManager->get('HydratorManager')->get('Portfolio\\V2\\Rest\\Project\\ProjectHydrator');
            $mapper->setHydrator($projectHydrator);
            return $mapper;
        }
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use ZfcBase\Mapper\AbstractDbMapper;
    use Zend\Paginator\Adapter\DbSelect;
    use Zend\Db\ResultSet\HydratingResultSet;
    
    class ProjectMapper extends AbstractDbMapper {
    
        ...
    
        /**
         * Provides a collection of all the available projects.
         *
         * @return \Portfolio\V2\Rest\Project\ProjectCollection
         */
        public function findAll() {
            $resultSetPrototype = new HydratingResultSet(
                $this->getHydrator(),
                $this->getEntityPrototype()
            );
            $select = $this->getSelect();
            $adapter = $this->getDbAdapter();
            $paginatorAdapter = new DbSelect($select, $adapter, $resultSetPrototype);
            $collection = new ProjectCollection($paginatorAdapter);
            return $collection;
        }
    
        /**
         * Provides a project by ID.
         *
         * @param int $id
         * @return \Portfolio\V2\Rest\Project\ProjectEntity
         */
        public function findById($id) {
            $select = $this->getSelect();
            $select->where(array(
                'id' => $id,
            ));
            $entity = $this->select($select)->current();
            return $entity;
        }
    
        ...
    
    }
    
    编辑为:

    public function getProjects() {
        $projects = $this->getMapper()->findAll();
        foreach ($projects as $key => $project) {
            $images = $this->getImageService()->getImagesForProject($project['id']);
            $projects[$key]['images'] = $images;
        }
        return $projects;
    }
    
    public function findAll() {
        $select = $this->getSelect();
        $adapter = $this->getDbAdapter();
        $paginatorAdapter = new DbSelect($select, $adapter);
        // @todo Replace the constants with data from the config and request.
        $projects = $paginatorAdapter->getItems(0, 2);
        $projects = $projects->toArray();
        return $projects;
    }
    
    现在我得到了想要的输出:

    {
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects"
            }
        },
        "_embedded": {
            "projects": [
                {
                    "id": "1",
                    "title": "...",
                    ...
                    "_embedded": {
                        "images": [
                            {
                                "id": "1",
                                "project_id": "1",
                                "title": "...",
                                ...
                                "_links": {
                                    "self": {
                                        "href": "http://myproject-api.misc.loc/images/1"
                                    }
                                }
                            },
                            {
                                ...
                            },
                            {
                                ...
                            }
                        ]
                    },
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/1"
                        }
                    }
                },
                {
                    "id": "2",
                    "title": "...",
                    ...
                    "_embedded": {
                        "images": [
                            ...
                        ]
                    },
                    ...
                }
            ]
        },
        "total_items": 2
    }
    

    但这是一个有点糟糕的解决方案,不是吗?我实际上在做的是:我只是在替换Apigility数据检索功能的一部分。。。无论如何,我不喜欢这个解决方案,我想找到一个更好的解决方案(“符合Apigility的解决方案”)。

    我没有使用db mapper的经验,但我想我可以为您回答问题2

    如果提取的项目资源(数组)具有一个键
    images
    ,该键保存类型为
    Hal\Collection
    的对象,它将自动提取该集合,并按照您在Hal示例中所示进行渲染

    之所以出现这种“魔力”,是因为在
    Hal.php
    中的
    renderEntity
    方法中调用了
    extractedembeddedcollection

    编辑 你写下你想要的:

    ["images": {...}, {...}, {...}]
    
    但你真正应该做到的是:

    {
        "id": "2",
        "title": "...",
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects/2"
            }
        },
        "_embedded": {
            "images": [ 
                {...}, 
                {...}, 
                {...}
            ]
        }
    }
    
    如何提取对象?您是否在元数据映射中注册了一个查询器

    您应该尝试返回如下内容:

    ProjectCollection extends Paginator
    ProjectEntity
    ProjectMapper extends AbstractDbMapper
    ProjectService implements ServiceManagerAwareInterface
    ProjectServiceFactory implements FactoryInterface
    
    use ZF\Hal\Collection
    
    ...
    
    $images = new Collection($arrayOfImages);
    
    $project['images'] = $images;
    

    然后它应该会起作用(我不知道如何解释它)。

    我终于找到了解决方案。(再次感谢@在GitHub上的支持。)简言之,我们的想法是在水合步骤上使用嵌套的(
    图像
    )项目列表来丰富(
    项目
    )列表项目。我其实不太喜欢这种方式,因为对我来说,在水合水平上有太多的模型逻辑。但它是有效的。我们开始:

    /module/Portfolio/config/module.config.php

    {
        "id": "1",
        "title": "...",
        ...
        "_embedded": {
            "images": [
                {
                    "id": "1",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/1"
                        }
                    }
                },
                {
                    "id": "2",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/2"
                        }
                    }
                },
                {
                    "id": "3",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/3"
                        }
                    }
                }
            ]
        },
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects/1"
            }
        }
    }
    
    {
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects?page=1"
            },
            "first": {
                "href": "http://myproject-api.misc.loc/projects"
            },
            "last": {
                "href": "http://myproject-api.misc.loc/projects?page=24"
            },
            "next": {
                "href": "http://myproject-api.misc.loc/projects?page=2"
            }
        },
        "_embedded": {
            "projects": [
                {
                    "id": "1",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/1"
                        }
                    }
                },
                {
                    "id": "2",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/2"
                        }
                    }
                },
                {
                    "id": "3",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/3"
                        }
                    }
                }
            ]
        },
        "page_count": 24,
        "page_size": 3,
        "total_items": 72
    }
    
    public function findAll() {
        $select = $this->getSelect();
        $adapter = $this->getDbAdapter();
        $paginatorAdapter = new DbSelect($select, $adapter);
        $collection = new ProjectCollection($paginatorAdapter);
        return $collection;
    }
    
    return array(
        ...
        'zf-hal' => array(
            'metadata_map' => array(
                ...
                'Portfolio\\V2\\Rest\\Project\\ProjectEntity' => array(
                    'entity_identifier_name' => 'id',
                    'route_name' => 'portfolio.rest.project',
                    'route_identifier_name' => 'id',
                    'hydrator' => 'Portfolio\\V2\\Rest\\Project\\ProjectHydrator',
                ),
                'Portfolio\\V2\\Rest\\Project\\ProjectCollection' => array(
                    'entity_identifier_name' => 'id',
                    'route_name' => 'portfolio.rest.project',
                    'route_identifier_name' => 'id',
                    'is_collection' => true,
                ),
                ...
            ),
        ),
    );
    
    class Module implements ApigilityProviderInterface {
    
        ...
    
        public function getHydratorConfig() {
            return array(
                'factories' => array(
                    // V2
                    'Portfolio\\V2\\Rest\\Project\\ProjectHydrator' => function(ServiceManager $serviceManager) {
                        $projectHydrator = new ProjectHydrator();
                        $projectHydrator->setImageService($serviceManager->getServiceLocator()->get('Portfolio\V2\Rest\ImageService'));
                        return $projectHydrator;
                    }
                ),
            );
        }
    
        ...
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use Zend\Stdlib\Hydrator\ClassMethods;
    use Portfolio\V2\Rest\Image\ImageService;
    
    class ProjectHydrator extends ClassMethods {
    
        /**
         * @var ImageService
         */
        protected $imageService;
    
        /**
         * @return ImageService the $imageService
         */
        public function getImageService() {
            return $this->imageService;
        }
    
        /**
         * @param ImageService $imageService
         */
        public function setImageService(ImageService $imageService) {
            $this->imageService = $imageService;
            return $this;
        }
    
        /*
         * Doesn't need to be implemented:
         * the ClassMethods#hydrate(...) handle the $data already as wished.
         */
        /*
        public function hydrate(array $data, $object) {
            $object = parent::hydrate($data, $object);
            if ($object->getId() !== null) {
                $images = $this->imageService->getImagesForProject($object->getId());
                $object->setImages($images);
            }
            return $object;
        }
        */
    
        /**
         * @see \Zend\Stdlib\Hydrator\ClassMethods::extract()
         */
        public function extract($object) {
            $array = parent::extract($object);
            if ($array['id'] !== null) {
                $images = $this->imageService->getImagesForProject($array['id']);
                $array['images'] = $images;
            }
            return $array;
        }
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use Zend\ServiceManager\ServiceLocatorInterface;
    
    class ProjectMapperFactory {
    
        public function __invoke(ServiceLocatorInterface $serviceManager) {
            $mapper = new ProjectMapper();
            $mapper->setDbAdapter($serviceManager->get('PortfolioDbAdapter_V2'));
            $mapper->setEntityPrototype($serviceManager->get('Portfolio\V2\Rest\Project\ProjectEntity'));
            $projectHydrator = $serviceManager->get('HydratorManager')->get('Portfolio\\V2\\Rest\\Project\\ProjectHydrator');
            $mapper->setHydrator($projectHydrator);
            return $mapper;
        }
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use ZfcBase\Mapper\AbstractDbMapper;
    use Zend\Paginator\Adapter\DbSelect;
    use Zend\Db\ResultSet\HydratingResultSet;
    
    class ProjectMapper extends AbstractDbMapper {
    
        ...
    
        /**
         * Provides a collection of all the available projects.
         *
         * @return \Portfolio\V2\Rest\Project\ProjectCollection
         */
        public function findAll() {
            $resultSetPrototype = new HydratingResultSet(
                $this->getHydrator(),
                $this->getEntityPrototype()
            );
            $select = $this->getSelect();
            $adapter = $this->getDbAdapter();
            $paginatorAdapter = new DbSelect($select, $adapter, $resultSetPrototype);
            $collection = new ProjectCollection($paginatorAdapter);
            return $collection;
        }
    
        /**
         * Provides a project by ID.
         *
         * @param int $id
         * @return \Portfolio\V2\Rest\Project\ProjectEntity
         */
        public function findById($id) {
            $select = $this->getSelect();
            $select->where(array(
                'id' => $id,
            ));
            $entity = $this->select($select)->current();
            return $entity;
        }
    
        ...
    
    }
    
    Portfolio\Module

    {
        "id": "1",
        "title": "...",
        ...
        "_embedded": {
            "images": [
                {
                    "id": "1",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/1"
                        }
                    }
                },
                {
                    "id": "2",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/2"
                        }
                    }
                },
                {
                    "id": "3",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/3"
                        }
                    }
                }
            ]
        },
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects/1"
            }
        }
    }
    
    {
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects?page=1"
            },
            "first": {
                "href": "http://myproject-api.misc.loc/projects"
            },
            "last": {
                "href": "http://myproject-api.misc.loc/projects?page=24"
            },
            "next": {
                "href": "http://myproject-api.misc.loc/projects?page=2"
            }
        },
        "_embedded": {
            "projects": [
                {
                    "id": "1",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/1"
                        }
                    }
                },
                {
                    "id": "2",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/2"
                        }
                    }
                },
                {
                    "id": "3",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/3"
                        }
                    }
                }
            ]
        },
        "page_count": 24,
        "page_size": 3,
        "total_items": 72
    }
    
    public function findAll() {
        $select = $this->getSelect();
        $adapter = $this->getDbAdapter();
        $paginatorAdapter = new DbSelect($select, $adapter);
        $collection = new ProjectCollection($paginatorAdapter);
        return $collection;
    }
    
    return array(
        ...
        'zf-hal' => array(
            'metadata_map' => array(
                ...
                'Portfolio\\V2\\Rest\\Project\\ProjectEntity' => array(
                    'entity_identifier_name' => 'id',
                    'route_name' => 'portfolio.rest.project',
                    'route_identifier_name' => 'id',
                    'hydrator' => 'Portfolio\\V2\\Rest\\Project\\ProjectHydrator',
                ),
                'Portfolio\\V2\\Rest\\Project\\ProjectCollection' => array(
                    'entity_identifier_name' => 'id',
                    'route_name' => 'portfolio.rest.project',
                    'route_identifier_name' => 'id',
                    'is_collection' => true,
                ),
                ...
            ),
        ),
    );
    
    class Module implements ApigilityProviderInterface {
    
        ...
    
        public function getHydratorConfig() {
            return array(
                'factories' => array(
                    // V2
                    'Portfolio\\V2\\Rest\\Project\\ProjectHydrator' => function(ServiceManager $serviceManager) {
                        $projectHydrator = new ProjectHydrator();
                        $projectHydrator->setImageService($serviceManager->getServiceLocator()->get('Portfolio\V2\Rest\ImageService'));
                        return $projectHydrator;
                    }
                ),
            );
        }
    
        ...
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use Zend\Stdlib\Hydrator\ClassMethods;
    use Portfolio\V2\Rest\Image\ImageService;
    
    class ProjectHydrator extends ClassMethods {
    
        /**
         * @var ImageService
         */
        protected $imageService;
    
        /**
         * @return ImageService the $imageService
         */
        public function getImageService() {
            return $this->imageService;
        }
    
        /**
         * @param ImageService $imageService
         */
        public function setImageService(ImageService $imageService) {
            $this->imageService = $imageService;
            return $this;
        }
    
        /*
         * Doesn't need to be implemented:
         * the ClassMethods#hydrate(...) handle the $data already as wished.
         */
        /*
        public function hydrate(array $data, $object) {
            $object = parent::hydrate($data, $object);
            if ($object->getId() !== null) {
                $images = $this->imageService->getImagesForProject($object->getId());
                $object->setImages($images);
            }
            return $object;
        }
        */
    
        /**
         * @see \Zend\Stdlib\Hydrator\ClassMethods::extract()
         */
        public function extract($object) {
            $array = parent::extract($object);
            if ($array['id'] !== null) {
                $images = $this->imageService->getImagesForProject($array['id']);
                $array['images'] = $images;
            }
            return $array;
        }
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use Zend\ServiceManager\ServiceLocatorInterface;
    
    class ProjectMapperFactory {
    
        public function __invoke(ServiceLocatorInterface $serviceManager) {
            $mapper = new ProjectMapper();
            $mapper->setDbAdapter($serviceManager->get('PortfolioDbAdapter_V2'));
            $mapper->setEntityPrototype($serviceManager->get('Portfolio\V2\Rest\Project\ProjectEntity'));
            $projectHydrator = $serviceManager->get('HydratorManager')->get('Portfolio\\V2\\Rest\\Project\\ProjectHydrator');
            $mapper->setHydrator($projectHydrator);
            return $mapper;
        }
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use ZfcBase\Mapper\AbstractDbMapper;
    use Zend\Paginator\Adapter\DbSelect;
    use Zend\Db\ResultSet\HydratingResultSet;
    
    class ProjectMapper extends AbstractDbMapper {
    
        ...
    
        /**
         * Provides a collection of all the available projects.
         *
         * @return \Portfolio\V2\Rest\Project\ProjectCollection
         */
        public function findAll() {
            $resultSetPrototype = new HydratingResultSet(
                $this->getHydrator(),
                $this->getEntityPrototype()
            );
            $select = $this->getSelect();
            $adapter = $this->getDbAdapter();
            $paginatorAdapter = new DbSelect($select, $adapter, $resultSetPrototype);
            $collection = new ProjectCollection($paginatorAdapter);
            return $collection;
        }
    
        /**
         * Provides a project by ID.
         *
         * @param int $id
         * @return \Portfolio\V2\Rest\Project\ProjectEntity
         */
        public function findById($id) {
            $select = $this->getSelect();
            $select->where(array(
                'id' => $id,
            ));
            $entity = $this->select($select)->current();
            return $entity;
        }
    
        ...
    
    }
    
    Portfolio\V2\Rest\Project\projector

    {
        "id": "1",
        "title": "...",
        ...
        "_embedded": {
            "images": [
                {
                    "id": "1",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/1"
                        }
                    }
                },
                {
                    "id": "2",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/2"
                        }
                    }
                },
                {
                    "id": "3",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/3"
                        }
                    }
                }
            ]
        },
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects/1"
            }
        }
    }
    
    {
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects?page=1"
            },
            "first": {
                "href": "http://myproject-api.misc.loc/projects"
            },
            "last": {
                "href": "http://myproject-api.misc.loc/projects?page=24"
            },
            "next": {
                "href": "http://myproject-api.misc.loc/projects?page=2"
            }
        },
        "_embedded": {
            "projects": [
                {
                    "id": "1",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/1"
                        }
                    }
                },
                {
                    "id": "2",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/2"
                        }
                    }
                },
                {
                    "id": "3",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/3"
                        }
                    }
                }
            ]
        },
        "page_count": 24,
        "page_size": 3,
        "total_items": 72
    }
    
    public function findAll() {
        $select = $this->getSelect();
        $adapter = $this->getDbAdapter();
        $paginatorAdapter = new DbSelect($select, $adapter);
        $collection = new ProjectCollection($paginatorAdapter);
        return $collection;
    }
    
    return array(
        ...
        'zf-hal' => array(
            'metadata_map' => array(
                ...
                'Portfolio\\V2\\Rest\\Project\\ProjectEntity' => array(
                    'entity_identifier_name' => 'id',
                    'route_name' => 'portfolio.rest.project',
                    'route_identifier_name' => 'id',
                    'hydrator' => 'Portfolio\\V2\\Rest\\Project\\ProjectHydrator',
                ),
                'Portfolio\\V2\\Rest\\Project\\ProjectCollection' => array(
                    'entity_identifier_name' => 'id',
                    'route_name' => 'portfolio.rest.project',
                    'route_identifier_name' => 'id',
                    'is_collection' => true,
                ),
                ...
            ),
        ),
    );
    
    class Module implements ApigilityProviderInterface {
    
        ...
    
        public function getHydratorConfig() {
            return array(
                'factories' => array(
                    // V2
                    'Portfolio\\V2\\Rest\\Project\\ProjectHydrator' => function(ServiceManager $serviceManager) {
                        $projectHydrator = new ProjectHydrator();
                        $projectHydrator->setImageService($serviceManager->getServiceLocator()->get('Portfolio\V2\Rest\ImageService'));
                        return $projectHydrator;
                    }
                ),
            );
        }
    
        ...
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use Zend\Stdlib\Hydrator\ClassMethods;
    use Portfolio\V2\Rest\Image\ImageService;
    
    class ProjectHydrator extends ClassMethods {
    
        /**
         * @var ImageService
         */
        protected $imageService;
    
        /**
         * @return ImageService the $imageService
         */
        public function getImageService() {
            return $this->imageService;
        }
    
        /**
         * @param ImageService $imageService
         */
        public function setImageService(ImageService $imageService) {
            $this->imageService = $imageService;
            return $this;
        }
    
        /*
         * Doesn't need to be implemented:
         * the ClassMethods#hydrate(...) handle the $data already as wished.
         */
        /*
        public function hydrate(array $data, $object) {
            $object = parent::hydrate($data, $object);
            if ($object->getId() !== null) {
                $images = $this->imageService->getImagesForProject($object->getId());
                $object->setImages($images);
            }
            return $object;
        }
        */
    
        /**
         * @see \Zend\Stdlib\Hydrator\ClassMethods::extract()
         */
        public function extract($object) {
            $array = parent::extract($object);
            if ($array['id'] !== null) {
                $images = $this->imageService->getImagesForProject($array['id']);
                $array['images'] = $images;
            }
            return $array;
        }
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use Zend\ServiceManager\ServiceLocatorInterface;
    
    class ProjectMapperFactory {
    
        public function __invoke(ServiceLocatorInterface $serviceManager) {
            $mapper = new ProjectMapper();
            $mapper->setDbAdapter($serviceManager->get('PortfolioDbAdapter_V2'));
            $mapper->setEntityPrototype($serviceManager->get('Portfolio\V2\Rest\Project\ProjectEntity'));
            $projectHydrator = $serviceManager->get('HydratorManager')->get('Portfolio\\V2\\Rest\\Project\\ProjectHydrator');
            $mapper->setHydrator($projectHydrator);
            return $mapper;
        }
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use ZfcBase\Mapper\AbstractDbMapper;
    use Zend\Paginator\Adapter\DbSelect;
    use Zend\Db\ResultSet\HydratingResultSet;
    
    class ProjectMapper extends AbstractDbMapper {
    
        ...
    
        /**
         * Provides a collection of all the available projects.
         *
         * @return \Portfolio\V2\Rest\Project\ProjectCollection
         */
        public function findAll() {
            $resultSetPrototype = new HydratingResultSet(
                $this->getHydrator(),
                $this->getEntityPrototype()
            );
            $select = $this->getSelect();
            $adapter = $this->getDbAdapter();
            $paginatorAdapter = new DbSelect($select, $adapter, $resultSetPrototype);
            $collection = new ProjectCollection($paginatorAdapter);
            return $collection;
        }
    
        /**
         * Provides a project by ID.
         *
         * @param int $id
         * @return \Portfolio\V2\Rest\Project\ProjectEntity
         */
        public function findById($id) {
            $select = $this->getSelect();
            $select->where(array(
                'id' => $id,
            ));
            $entity = $this->select($select)->current();
            return $entity;
        }
    
        ...
    
    }
    
    Portfolio\V2\Rest\Project\ProjectMapperFactory

    {
        "id": "1",
        "title": "...",
        ...
        "_embedded": {
            "images": [
                {
                    "id": "1",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/1"
                        }
                    }
                },
                {
                    "id": "2",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/2"
                        }
                    }
                },
                {
                    "id": "3",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/3"
                        }
                    }
                }
            ]
        },
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects/1"
            }
        }
    }
    
    {
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects?page=1"
            },
            "first": {
                "href": "http://myproject-api.misc.loc/projects"
            },
            "last": {
                "href": "http://myproject-api.misc.loc/projects?page=24"
            },
            "next": {
                "href": "http://myproject-api.misc.loc/projects?page=2"
            }
        },
        "_embedded": {
            "projects": [
                {
                    "id": "1",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/1"
                        }
                    }
                },
                {
                    "id": "2",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/2"
                        }
                    }
                },
                {
                    "id": "3",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/3"
                        }
                    }
                }
            ]
        },
        "page_count": 24,
        "page_size": 3,
        "total_items": 72
    }
    
    public function findAll() {
        $select = $this->getSelect();
        $adapter = $this->getDbAdapter();
        $paginatorAdapter = new DbSelect($select, $adapter);
        $collection = new ProjectCollection($paginatorAdapter);
        return $collection;
    }
    
    return array(
        ...
        'zf-hal' => array(
            'metadata_map' => array(
                ...
                'Portfolio\\V2\\Rest\\Project\\ProjectEntity' => array(
                    'entity_identifier_name' => 'id',
                    'route_name' => 'portfolio.rest.project',
                    'route_identifier_name' => 'id',
                    'hydrator' => 'Portfolio\\V2\\Rest\\Project\\ProjectHydrator',
                ),
                'Portfolio\\V2\\Rest\\Project\\ProjectCollection' => array(
                    'entity_identifier_name' => 'id',
                    'route_name' => 'portfolio.rest.project',
                    'route_identifier_name' => 'id',
                    'is_collection' => true,
                ),
                ...
            ),
        ),
    );
    
    class Module implements ApigilityProviderInterface {
    
        ...
    
        public function getHydratorConfig() {
            return array(
                'factories' => array(
                    // V2
                    'Portfolio\\V2\\Rest\\Project\\ProjectHydrator' => function(ServiceManager $serviceManager) {
                        $projectHydrator = new ProjectHydrator();
                        $projectHydrator->setImageService($serviceManager->getServiceLocator()->get('Portfolio\V2\Rest\ImageService'));
                        return $projectHydrator;
                    }
                ),
            );
        }
    
        ...
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use Zend\Stdlib\Hydrator\ClassMethods;
    use Portfolio\V2\Rest\Image\ImageService;
    
    class ProjectHydrator extends ClassMethods {
    
        /**
         * @var ImageService
         */
        protected $imageService;
    
        /**
         * @return ImageService the $imageService
         */
        public function getImageService() {
            return $this->imageService;
        }
    
        /**
         * @param ImageService $imageService
         */
        public function setImageService(ImageService $imageService) {
            $this->imageService = $imageService;
            return $this;
        }
    
        /*
         * Doesn't need to be implemented:
         * the ClassMethods#hydrate(...) handle the $data already as wished.
         */
        /*
        public function hydrate(array $data, $object) {
            $object = parent::hydrate($data, $object);
            if ($object->getId() !== null) {
                $images = $this->imageService->getImagesForProject($object->getId());
                $object->setImages($images);
            }
            return $object;
        }
        */
    
        /**
         * @see \Zend\Stdlib\Hydrator\ClassMethods::extract()
         */
        public function extract($object) {
            $array = parent::extract($object);
            if ($array['id'] !== null) {
                $images = $this->imageService->getImagesForProject($array['id']);
                $array['images'] = $images;
            }
            return $array;
        }
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use Zend\ServiceManager\ServiceLocatorInterface;
    
    class ProjectMapperFactory {
    
        public function __invoke(ServiceLocatorInterface $serviceManager) {
            $mapper = new ProjectMapper();
            $mapper->setDbAdapter($serviceManager->get('PortfolioDbAdapter_V2'));
            $mapper->setEntityPrototype($serviceManager->get('Portfolio\V2\Rest\Project\ProjectEntity'));
            $projectHydrator = $serviceManager->get('HydratorManager')->get('Portfolio\\V2\\Rest\\Project\\ProjectHydrator');
            $mapper->setHydrator($projectHydrator);
            return $mapper;
        }
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use ZfcBase\Mapper\AbstractDbMapper;
    use Zend\Paginator\Adapter\DbSelect;
    use Zend\Db\ResultSet\HydratingResultSet;
    
    class ProjectMapper extends AbstractDbMapper {
    
        ...
    
        /**
         * Provides a collection of all the available projects.
         *
         * @return \Portfolio\V2\Rest\Project\ProjectCollection
         */
        public function findAll() {
            $resultSetPrototype = new HydratingResultSet(
                $this->getHydrator(),
                $this->getEntityPrototype()
            );
            $select = $this->getSelect();
            $adapter = $this->getDbAdapter();
            $paginatorAdapter = new DbSelect($select, $adapter, $resultSetPrototype);
            $collection = new ProjectCollection($paginatorAdapter);
            return $collection;
        }
    
        /**
         * Provides a project by ID.
         *
         * @param int $id
         * @return \Portfolio\V2\Rest\Project\ProjectEntity
         */
        public function findById($id) {
            $select = $this->getSelect();
            $select->where(array(
                'id' => $id,
            ));
            $entity = $this->select($select)->current();
            return $entity;
        }
    
        ...
    
    }
    
    Portfolio\V2\Rest\Project\ProjectMapper

    {
        "id": "1",
        "title": "...",
        ...
        "_embedded": {
            "images": [
                {
                    "id": "1",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/1"
                        }
                    }
                },
                {
                    "id": "2",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/2"
                        }
                    }
                },
                {
                    "id": "3",
                    "project_id": "1",
                    "title": "...",
                    ...
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/images/3"
                        }
                    }
                }
            ]
        },
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects/1"
            }
        }
    }
    
    {
        "_links": {
            "self": {
                "href": "http://myproject-api.misc.loc/projects?page=1"
            },
            "first": {
                "href": "http://myproject-api.misc.loc/projects"
            },
            "last": {
                "href": "http://myproject-api.misc.loc/projects?page=24"
            },
            "next": {
                "href": "http://myproject-api.misc.loc/projects?page=2"
            }
        },
        "_embedded": {
            "projects": [
                {
                    "id": "1",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/1"
                        }
                    }
                },
                {
                    "id": "2",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/2"
                        }
                    }
                },
                {
                    "id": "3",
                    "title": "...",
                    ... <-- HERE I WANT TO GET ["images": {...}, {...}, {...}]
                    "_links": {
                        "self": {
                            "href": "http://myproject-api.misc.loc/projects/3"
                        }
                    }
                }
            ]
        },
        "page_count": 24,
        "page_size": 3,
        "total_items": 72
    }
    
    public function findAll() {
        $select = $this->getSelect();
        $adapter = $this->getDbAdapter();
        $paginatorAdapter = new DbSelect($select, $adapter);
        $collection = new ProjectCollection($paginatorAdapter);
        return $collection;
    }
    
    return array(
        ...
        'zf-hal' => array(
            'metadata_map' => array(
                ...
                'Portfolio\\V2\\Rest\\Project\\ProjectEntity' => array(
                    'entity_identifier_name' => 'id',
                    'route_name' => 'portfolio.rest.project',
                    'route_identifier_name' => 'id',
                    'hydrator' => 'Portfolio\\V2\\Rest\\Project\\ProjectHydrator',
                ),
                'Portfolio\\V2\\Rest\\Project\\ProjectCollection' => array(
                    'entity_identifier_name' => 'id',
                    'route_name' => 'portfolio.rest.project',
                    'route_identifier_name' => 'id',
                    'is_collection' => true,
                ),
                ...
            ),
        ),
    );
    
    class Module implements ApigilityProviderInterface {
    
        ...
    
        public function getHydratorConfig() {
            return array(
                'factories' => array(
                    // V2
                    'Portfolio\\V2\\Rest\\Project\\ProjectHydrator' => function(ServiceManager $serviceManager) {
                        $projectHydrator = new ProjectHydrator();
                        $projectHydrator->setImageService($serviceManager->getServiceLocator()->get('Portfolio\V2\Rest\ImageService'));
                        return $projectHydrator;
                    }
                ),
            );
        }
    
        ...
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use Zend\Stdlib\Hydrator\ClassMethods;
    use Portfolio\V2\Rest\Image\ImageService;
    
    class ProjectHydrator extends ClassMethods {
    
        /**
         * @var ImageService
         */
        protected $imageService;
    
        /**
         * @return ImageService the $imageService
         */
        public function getImageService() {
            return $this->imageService;
        }
    
        /**
         * @param ImageService $imageService
         */
        public function setImageService(ImageService $imageService) {
            $this->imageService = $imageService;
            return $this;
        }
    
        /*
         * Doesn't need to be implemented:
         * the ClassMethods#hydrate(...) handle the $data already as wished.
         */
        /*
        public function hydrate(array $data, $object) {
            $object = parent::hydrate($data, $object);
            if ($object->getId() !== null) {
                $images = $this->imageService->getImagesForProject($object->getId());
                $object->setImages($images);
            }
            return $object;
        }
        */
    
        /**
         * @see \Zend\Stdlib\Hydrator\ClassMethods::extract()
         */
        public function extract($object) {
            $array = parent::extract($object);
            if ($array['id'] !== null) {
                $images = $this->imageService->getImagesForProject($array['id']);
                $array['images'] = $images;
            }
            return $array;
        }
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use Zend\ServiceManager\ServiceLocatorInterface;
    
    class ProjectMapperFactory {
    
        public function __invoke(ServiceLocatorInterface $serviceManager) {
            $mapper = new ProjectMapper();
            $mapper->setDbAdapter($serviceManager->get('PortfolioDbAdapter_V2'));
            $mapper->setEntityPrototype($serviceManager->get('Portfolio\V2\Rest\Project\ProjectEntity'));
            $projectHydrator = $serviceManager->get('HydratorManager')->get('Portfolio\\V2\\Rest\\Project\\ProjectHydrator');
            $mapper->setHydrator($projectHydrator);
            return $mapper;
        }
    
    }
    
    namespace Portfolio\V2\Rest\Project;
    
    use ZfcBase\Mapper\AbstractDbMapper;
    use Zend\Paginator\Adapter\DbSelect;
    use Zend\Db\ResultSet\HydratingResultSet;
    
    class ProjectMapper extends AbstractDbMapper {
    
        ...
    
        /**
         * Provides a collection of all the available projects.
         *
         * @return \Portfolio\V2\Rest\Project\ProjectCollection
         */
        public function findAll() {
            $resultSetPrototype = new HydratingResultSet(
                $this->getHydrator(),
                $this->getEntityPrototype()
            );
            $select = $this->getSelect();
            $adapter = $this->getDbAdapter();
            $paginatorAdapter = new DbSelect($select, $adapter, $resultSetPrototype);
            $collection = new ProjectCollection($paginatorAdapter);
            return $collection;
        }
    
        /**
         * Provides a project by ID.
         *
         * @param int $id
         * @return \Portfolio\V2\Rest\Project\ProjectEntity
         */
        public function findById($id) {
            $select = $this->getSelect();
            $select->where(array(
                'id' => $id,
            ));
            $entity = $this->select($select)->current();
            return $entity;
        }
    
        ...
    
    }
    

    正如我在GitHub上的帖子中所说的,如果能从Apigility核心团队得到反馈就太好了,无论这个解决方案是否“符合Apigility标准”,如果不是,还有什么更好的/正确的解决方案。

    感谢您的回答!我终于让它工作了--几乎。。。我的实体
    Project
    具有属性images,但其类型为
    Zend\Paginator\Paginator
    。它适用于单个对象:这意味着,
    /project/:id
    提供一个项目及其映像:
    {“id”:“1”,“title”:“foo”,“_embedded”:{“images”:[{image1},{image2},{image3}}
    。(这对我来说意味着:
    Paginator
    作为集合父类的变体可以工作。)但它不适用于
    /project
    ——当我请求项目列表时,它的项不包含嵌入的图像列表(请参见编辑)。你知道,如何让它也工作吗?为了避免误解,我将我的评论部分编号:1<代码>你想写什么就写什么。。。但是你真正的目标是…
    ——确切地说,我指的是嵌入
    \u的正确
    HAL
    格式。2.“您在元数据映射中注册了水合器了吗?”--是的,我正在使用
    Zend\\Stdlib\\hydrator\\ClassMethods
    。3.“如何提取对象?”--请查看我的编辑。4.您的收藏建议:问题是,我无法遍历
    ZF\Hal\collection
    并修改其项目。您好,我面临着同样的问题,到目前为止,您是否找到了合适的解决方案?您好@JimitShah,这对我来说是一个合适的解决方案。我仍然不知道是否有更好/更优雅的,但至少这一个有效。