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);
        //..
    }
}

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

tl; dr:在您的示例中,您的格式逻辑理想情况应该是 在中间层中处理

这里有一些改进:

首先,请勿从控制器中获取存储库。而是将其声明为服务(例如,在您的app/config/services.yml中(。

另外,您可以使用新图层抽象对数据(=存储库(的访问。沟通会这样进行:

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

  • 控制器:负责调用业务层和处理响应的正确方法(渲染HTML,返回JSON等(。
  • 业务层:对您的应用的逻辑负责。
  • 数据访问:负责查询您的数据库并获取数据,而不包括任何业务逻辑。只能由业务层访问。

使用这种体系结构将帮助您使用更可扩展的应用程序而无需花费太长时间实施。

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

您的控制器,负责"路由"用户的请求到正确的服务

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中的服务。

Symfony最佳实践在您的任务上尚不清楚,但是以恕我直言,可以将以下语句映射到您的第二个选项

总的来说,这意味着您应该积极地将业务逻辑从框架中解脱出来,同时,请积极地将控制器和路由到框架中,以充分利用它。

正如您已经说过的,第二个选项将控制器保持较薄,并将逻辑移至存储库级别,这是 symfony Way 一种完全有效的方法。

这个最佳练习声明为选项2

增加了一些支持

在Symfony应用程序中,Business Logic是您为应用程序编写的所有自定义代码(例如路由和控制器(。域类,学说实体和常规的PHP课程用作服务是商业逻辑的好示例。

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

我更喜欢在演示层中执行此类工作,如果您使用的是Twig,则应查看日期格式过滤器或API,您可以工作到以JMS序列化为例的演示层。

以这种方式,业务逻辑不会改变,也不会影响演示逻辑的更改

我的两分钱。

最新更新