一个很好的模式来管理我的控制器api数据调用

时间:2016-02-12 13:20:05

标签: php symfony design-patterns

我已经接管了Symfony 2.6项目,并且我正忙着尽可能地重构胖控制器。

目前,所有控制器的jist都遵循类似的结构

/**
 * @Method({"GET"})
 * @Route("/", name="homepage")
 * @Template()
 */
public function indexAction() 
{
    //Initialize the API
    $api = $this->get('core_bundle.api_wrapper')->getApi();

    //Retrieve data from various end points
    $result_set = $api->get('/rest/resource');
    $result_set2 = $api->get('/rest/resource2');
    $result_set3 = $api->get('/rest/resource3');
    //etc.. etc..

    //Once all api calls have been completed, do some manipulation of the data if required

    $result_set = DataHelper::getInstance()->setReadFlags($result_set);

    //The api resultset keys are not always standard (It's just the way it is Stackoverflow, I cannot change the API code)
    $result_set2 = Standardize::getInstance()->standardizeKeys($result_set2);
    $result_set2 = DataHelper::getInstance()->setReadFlags($result_set2);

    //Create the social links from the existing data
    $result_set3 = SocialHelper::getInstance()->createSocialLinks($result_set3);

    return [$result_set, $result_set2, $result_set3];
}

我努力寻找适用于此类情景的良好模式。我需要清理控制器并从中移除尽可能多的业务逻辑,因为有些控制器有100多行代码。

我遇到的问题是:

  1. 每个控制器都可以对API进行多次调用以获取数据。数据并不总是相关的,有时调用依赖于用户角色和/或先前API调用的密钥。
  2. 在返回View Response之前,需要以某种方式操作每个结果集。
  3. 我觉得创建多个模型然后传入API Result集,然后调用一个函数来操作每个结果集将导致一个较小的控制器操作,但是因为我将多次调用多个模型而感到困惑每个结果集。

    如果我想与单一责任保持一致,最终将不得不创建许多模型类。

    任何指导都将不胜感激。

2 个答案:

答案 0 :(得分:3)

您应该创建一个服务,根据需要将端点传递给它,将逻辑输入并将数据返回给控制器。

像这样,您的控制器将只返回包含您的服务提取的数据的响应 它会非常轻松。

服务可能如下所示:

<?php

namespace AppBundle\Services;

class EndpointManager
{
    public function __construct(YourApiService $apiWrapper) 
    {
        $this->apiWrapper = $apiWrapper;
    }

    public function fetch(array $endpoints)
    {
        $data = array();    

        // Do your logic here
        foreach ($endpoints as $end) {
            $data[$end] = $apiWrapper->get($end);
        }
        // ...

        // Return the data fetched
        return $data;
    }
}

使用您需要的所有服务作为参数声明它:

services:
    endpoint_manager:
        class: AppBundle\Services\EndpointManager
        arguments:
            apiWrapper: "@core_bundle.api_wrapper"

(如果传递更多参数,请不要忘记在服务的构造函数中设置它)

然后,使用它:

$apiManager = $this->get('endpoint_manager');

return $apiManager->fetch(['api/endpoint1', 'api/endpoint2']);

有关更多信息,请参阅Services documentation

希望这会对你有所帮助。

答案 1 :(得分:2)

与许多事情一样,这取决于项目的细节。 @chalasr有一些非常好的建议。

我会考虑定义我的controllers as services。这将允许您使用构造函数注入替换许多这些get调用,并可能稍微清理一下。

然后我会更进一步,用操作类替换我的控制器类。我怀疑你的一些控制器实现了多个动作,制作了相当大的文件?虽然有些行为是相似的,但它们的不同之处足以引起一些混乱?

动作类与控制器类的不同之处在于动作类实现一个且仅一个动作方法。该方法仍然使用请求作为输入生成响应。最终会有更多的课程,但每个课程都变得更小,只关注一件事。我还发现,一旦代码被分解为自己的类,通常可以共享来自不同控制器类的动作代码。

另外,我还会将您的单身人士(DataHelper,SocialHelper)定义为Symfony服务,只是为了保持一致性。