1

我正在开发基于Zend Framework 2的Apigility驱动应用程序。

目前,我正在将在数据库中检索到的数据直接发送到客户端:请求进来,MyResource#fetch(...)或被MyResource#fetchAll(...)触发并调用MyService类上的适当方法,该方法调用MyMapper以使用其方法(如findFooByBar(...).

现在我想在发送响应之前处理数据。我怎样才能做到这一点?


Apigility ZF HAL 文档显示,如何访问已检索并发送到客户端的实体数据。好吧,我试过了。对于此类任务,它很丑陋并且代码很多。而且......它不起作用。但是我想在这里发布我的尝试:

namespace Portfolio;

...

class Module implements ApigilityProviderInterface {

    private $serviceManager;

    public function onBootstrap(MvcEvent $event) {
        $application = $event->getTarget();
        $this->serviceManager = $serviceManager = $application->getServiceManager();
        $viewHelperManager = $serviceManager->get('ViewHelperManager');
        $hal = $viewHelperManager->get('Hal');
        $hal->getEventManager()->attach('renderEntity', array($this, 'onRenderEntity'));
        $hal->getEventManager()->attach('renderCollection', array($this, 'onRenderCollection'));
    }

    public function onRenderEntity($event) {
        $entity = $event->getParam('entity');
        if ($entity->entity instanceof ProjectEntity) {
            $projectEntity = $entity->entity;
            $imageCollection = $this->tempCreateimagesForProject(
                $event, $entity->entity->getId()
            );
            $projectEntity->setImages($imageCollection);
            $event->setParam('entity', $projectEntity);
        }
    }

    public function onRenderCollection($event) {
        $collection = $event->getParam('collection');
        $projectCollection = $collection->getCollection();
        if ($projectCollection instanceof ProjectCollection) {
            foreach ($projectCollection as $key => $projectItem) {
                $tempProject = $projectCollection->getItem($key);
                $tempProject->append(
                    ['images' => $this->tempCreateimagesForProject($tempProject->offsetGet('id'))]
                );
                $projectCollection->getItem($key)->offsetSet($key, $tempProject);
            }
        }
    }

    private function tempCreateimagesForProject(Event $event, $projectId) {
        $imageService = $this->serviceManager->get('Portfolio\V2\Rest\ImageService');
        $imageCollection = $imageService->getImagesForProject($projectId);
        return $imageCollection;
    }

    ...

}
4

3 回答 3

0

我认为使用renderEntityandrenderCollection事件不是添加这种资源特定逻辑的正确位置。它更适合更一般的更改或附带的定制。

您可以将此逻辑添加到资源侦听器中。因此,在您的类中的fetchfetchAll方法中,您MyResource可以添加您当前在这些onRenderEntityonRenderCollection方法中添加的自定义代码。

所以是这样的:

class MyResource extends AbstractResourceListener
{
    /**
     * Your image service dependency
     */
    protected $imageService;

    /* ... */

    public function fetch($id)
    {
        $project = $this->projectMapper->fetch($id);

        $imageCollection = $this->imageService->getImagesForProject($project);

        $project->setImages($imageCollection);
        return $project;
    }

    /* ... */

    public function fetchAll($params = array())
    {
        $projects = $this->projectMapper->fetchAll();

        foreach ($projects as $key => $project) {
            $imageCollection = $this->imageService->getImagesForProject($project);
            $project->setImages($imageCollection);
        }

        return $projects;
    }

    /* ... */
}
于 2015-03-07T14:48:47.890 回答
0

一种可能的解决方案是处理 Hydrator 中的数据。所以我们编写了一个自定义的 Hydrator 类,并使用其中的嵌套对象和列表来丰富项目。它看起来像这样:

Portfolio\V2\Rest\Project\ProjectHydrator

...

class ProjectHydrator extends ClassMethods {

    /**
     * @var ImageService
     */
    protected $imageService;

    ...

    /*
     * 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;
    }

}

这不是一个很好的解决方案,因为那时模型/数据检索逻辑的一部分被移至 hydrator。但它有效。这里展示了这种方法的实现,这里是 GitHub 上对这个主题的讨论。

于 2015-03-31T14:11:13.370 回答
0

如果您正在使用ClassMethodsHydrator 并且您Collection扩展\Zend\Paginator\Paginator了一个很好的解决方案而不会失去集合的一致性并且不更改任何人的代码是覆盖您的getCurrentItems()方法。

public class MyResourceCollection // most likely extends Paginator
{
    public function getCurrentItems()
    {
        // getting the original items
        $items = parent::getCurrentItems();
        // as we work with objects $item will be an object
        // when working with objects we use references to them not clones of objects
        // so changes to $item are also present in the collection
        foreach ($collection as $item) {
            $oldSomething = $item->getSomething();
            $item->setSomething('['.$oldSomething.']');
        }
        // $items are now changed, return them
        return $items;
    }
}

我已经命名了密钥,以免与其他地方something的方法混淆。getValue

这使得something值看起来像[something].

于 2017-02-07T00:55:12.110 回答