Symfony:关于格式化参数的逻辑应该在存储库还是控制器中?

时间:2017-06-26 15:55:06

标签: php symfony doctrine-orm doctrine

根据Symfony的方式,最佳做法是什么?

选项1:

class MyController extends Controller
{
    public function myAction(...)
    {
        // ..
        $example = $this->getDoctrine()->getRepository('AppBundle:Contract')->getContracts(array(
            'company_id'                => $company->getId(),
            'contract_month_date_start'  => date('Y-m-d', strtotime('first day of this month', $contractDate->getTimestamp())),
            'contract_month_date_end'    => date('Y-m-d', strtotime('last day of this month', $contractDate->getTimestamp())),
        ));
        // ..
    }
}

class ExampleRepository extends EntityRepository
{
    public function getContracts($options)
    {

        //..

        $qb->select('contract')
            ->from('AppBundle:Contract', 'contract')
            ->where('contract.companyId = :company_id')
            ->andWhere('contract.startDate < :contract_month_date_end')
            ->andWhere('contract.endDate < :contract_month_date_end')
            ->setParameter('company_id', $options['company_id'])
            ->setParameter('contract_month_date_start', $options['contract_month_date_start'])
            ->setParameter('contract_month_date_end', $options['contract_month_date_end']);

        //..
    }
}

选项2:

class MyController extends Controller
{
    public function myAction(...)
    {
        // ..
        $example = $this->getDoctrine()->getRepository('AppBundle:Contract')->getContracts(array(
            'company'      => $company,
            'contractDate' => $contractDate
        ));
        // ..
    }
}


class ExampleRepository extends EntityRepository
{
    public function getContracts($options)
    {

        //..

        $company_id                = $options['company']->getId();
        $contract_month_date_start = date('Y-m-d', strtotime('first day of this month', $options['contractDate']));
        $contract_month_date_end   = date('Y-m-d', strtotime('last day of this month', $options['contractDate']));

        $qb->select('contract')
            ->from('AppBundle:Contract', 'contract')
            ->where('contract.companyId = :company_id')
            ->andWhere('contract.startDate < :contract_month_date_end')
            ->andWhere('contract.endDate < :contract_month_date_end')
            ->setParameter('company_id', $company_id)
            ->setParameter('contract_month_date_start', $contract_month_date_start)
            ->setParameter('contract_month_date_end', $contract_month_date_end);

        //..
    }
}

我觉得第二个更好(使用存储库的不同控制器之间的重复代码更少)。但我也觉得它对存储库负有太多责任。

3 个答案:

答案 0 :(得分:1)

Symfony best practices对您的任务不清楚,但恕我直言以下声明可以映射到您的第二个选项

  

总的来说,这意味着您应该积极地将业务逻辑与框架分离,同时积极地将控制器和路由耦合到框架,以便充分利用它。

正如您已经说过的那样,第二个选项使控制器保持更薄并将逻辑移动到存储库级别,这是 Symfony方式一种完全有效的方法。

best practice statement为选项2

添加了一些额外支持
  

在Symfony应用程序中,业务逻辑是您为应用程序编写的所有自定义代码,并非特定于框架(例如路由和控制器)。用作服务的域类,Doctrine实体和常规PHP类是业务逻辑的良好示例。

如果您需要在应用程序周围的不同位置使用此格式,则另一种方法可能是创建一个服务类,您可以将其注入到您需要的存储库中。

答案 1 :(得分:1)

  

TL; DR:在您的示例中,理想情况下您的格式化逻辑应该是   在中间层处理。

以下是一些改进:

首先,不要从控制器获取存储库。相反,将其声明为服务(例如,在app/config/services.yml中)。

此外,您可以使用新图层抽象访问您的数据(=您的存储库)。沟通将这样完成:

Controller&lt; =&gt; Business layer&lt; =&gt; Data access

  • Controller:负责调用业务层的正确方法并处理响应(呈现HTML,返回JSON等)。
  • 业务层:负责您应用的逻辑
  • 数据访问:负责查询数据库(例如)并获取数据而不包含任何业务逻辑。只能由业务层访问。

使用这样的体系结构将帮助您拥有更具可扩展性的应用程序,而无需花费太长时间来实现它。

在您的代码中,它看起来如下所示:

您的控制器,负责&#34;路由&#34;用户对正确服务的请求

class MyController extends Controller
{
    public function myAction()
    {
        // Accessing the contract manager, previously declared as a service in services.yml
        $contractManager = $this->get('manager.contract');
        $contracts = $contractManager->getMonthlyContracts($company);
    }
}

您的业务层,负责执行逻辑:

// This class must be declared as a service in services.yml
class ContractManager {
    private $contractRepository;

    public function __construct(ContractRepository $contractRepository)
    {
        $this->contractRepository = $contractRepository;
    }

    public function getMonthlyContracts(Company $company)
    {
        // The business layer is responsible for the logic of fetching the data. Therefore, its methods are business-related (here "find the monthly contracts for company X", which is very specific).
        // It is its role to set the start and end dates, not the controller's
        $contracts = $this->contractRepository->findByCompany(
            $company,
            new \DateTime('first day of this month'),
            new \DateTime('last day of this month')
        );

        // Do some logic with the contracts if required...

        return $contracts;
    }
}

您的存储库,负责访问数据:

class ContractRepository extends EntityRepository
{
    // The repository handles basic access to data without any logic, hence the generic "findByCompany" method name
    public function findByCompany(Company $company, \DateTime $from, \DateTime $to)
    {
        $qb->select('contract')
            ->from('AppBundle:Contract', 'contract')
            ->where('contract.company = :company')
            ->andWhere('contract.startDate > :from')
            ->andWhere('contract.endDate < :to')
            ->setParameter('company', $company)
            ->setParameter('from', $from)
            ->setParameter('to', $to);

        //...
    }
}

请查看Symfony doc,了解如何在services.yml中将存储库声明为服务。

答案 2 :(得分:0)

我更喜欢在表示层中执行此类工作,如果您使用的是TWIG,则应该查看date格式过滤器,或者如果它是api,您可以使用{来工作到表示层{3}}例如。

通过这种方式,业务逻辑不会改变,也不会影响表示逻辑的更改

我的两分钱。