Symfony2 / Doctrine,必须将业务逻辑放在我的控制器中?和复制控制器?

时间:2011-11-05 17:00:37

标签: model-view-controller orm doctrine-orm symfony business-logic

我的应用程序中有一些复杂的定价机制 - 以下是我设置阶段的一些业务规则(实体粗体):

  • 对于客户网站客户,产品可能具有唯一价位即可。
  • 产品有时可能有一个或多个其他选项,可能有自己的价格点 < strong>价格规则。
  • 产品有一个唯一添加由用户选择,实质上是一个价格和一个整数。

现在,我有价格点的EntityRepository,从根本上确定基本产品的正确价格点。 唯一添加选项也是如此。

PricePointRepository

public function getThePrice($Product, $qty, $Website, $Customer = null) 
{
    //all logic to get product price for this given instance goes here. Good.
}

控制器(简化)

public function indexAction() 
{
    $Product = $em->dostuffwithpostdata;
    $qty = POST['qty']; //inb4insecure trolls
    $Website = $em->dostuff();
    $Customer = (if user is logged in, return their object with $em, otherwise null as it is a guest or public person); // No business logic here, just understanding the request.

    $price = $em->getRepository(PricePointRepository)->getThePrice($Product,$qty,Website,$Customer);

    $Options[] = $em->dostuffwithPOSTdata;
    $optionsPrice = 0;
    //Below is some logic directly related to pricing the product. 
    foreach($Options as $option) {
        if($option->hasRule()) {
            $optionsPrice += $ruleprice; //after some other stuff of course)
        } else {
            $optionsPrice += $em->getRepository(OptionPricePoints)->getPrice($option->getID(),$qty);
        }
    }

    $uniqueAdditionPrice = $em->stuff;

    $finalprice = $price + $optionsPrice + $uniqueAdditionPrice; //This is logic related to how I price this type of product!
    $unitprice = $finalprice / $qty;

    //twig stuff to render and show $finalprice, $unitprice, $uniqueAdditionPrice
}

这仅适用于产品页面。当我需要重新使用逻辑时,当我进入购物车,保存订单等时会发生什么。如您所见,我始终使用Doctrine来根据存储库类中的业务逻辑来提取数据。

我很高兴地欢迎urwingit错误答案,因为我确实认为这是错误的。我该如何解决这个问题?美丽的东西将是一种服务,基本上是这样的:

$pricer = getPricerService->Pricer($Entities,$postdata,$etc);
$unitPrice = $pricer->getUnitPrice();
$totalPrice = $pricer->getTotalPrice();
$optionsPrice = $pricer->getOptionsPrice();

但我不知道如何在Symfony / Doctrine中执行此操作,尤其是在控制器中访问Doctrine和Repositories的方式。

2 个答案:

答案 0 :(得分:29)

您应该将所有可重复使用的业务逻辑归结为服务,以便不同的控制器可以重复使用代码。

您是否查看了“如何创建服务”文档:

Service Container Documentation

我会尽快给你速度。

在config.yml中,您需要定义您的服务:

services:
    pricing_service:
        class: Acme\ProductBundle\Service\PricingService
        arguments: [@doctrine]

然后你只需要制作一个标准的PHP类来代表你的服务:

namespace Acme\ProductBundle\Service;

class PricingService {

    private $doctrine;        

    function __construct($doctrine) {
        $this->doctrine = $doctrine; // Note that this was injected using the arguments in the config.yml
    }

    // Now the rest of your functions go here such as "getUnitPrice" etc etc.
}

最后,您需要从控制器获取服务:

$pricingService = $this->get('pricing_service');

还有其他方法可以模块化服务,例如不将所有服务转储到config.yml中,但所有这些都在文档中进行了解释。另请注意,您可以将任何其他服务注入您的服务,因此如果您需要arguments: [@doctrine, @security.context, @validator]之类的内容,您可以完成所有这些工作,甚至:[@my_other_service]

我怀疑你注入EntityManager的其他问题,你可能已经知道这是可行的方法!

希望这对你有用!

答案 1 :(得分:10)

你简化了你的例子,所以我真的不知道所有的细节,但这是解决问题的方法。

请注意,您实际上可能需要多个服务,但您应该根据我的示例获得想法。

基本遵循原则 - 一个班级有一个应对能力。

价格计算器计算价格:

namespace MyNamespace;

class PriceCalculator
{
    private $entityManager = null;

    public function __construct(Doctrine\ORM\EntityManager $entityManager)
    {
        $this->entityManager = $entityManager;
    }

    /**
     * @return PriceInterface
     */
    public function calculate()
    {
        // do your stuff and return Price
    }
}

价格由PriceInterface描述:

namespace MyNamespace;

interface PriceInterface
{
    public function getUnitPrice();

    public function getTotalPrice();

    public function getOptionsPrice();
}

价格计算器服务依赖于实体管理器:

my_namespace.price_calculator:
  class:     MyNamespace\PriceCalculator
  arguments: [ @doctrine.orm.default_entity_manager ]

控制器使用价格计算器服务来获取价格:

public function indexAction() 
{
    $priceCalculator = $this->get('my_namespace.price_calculator');
    $price = $priceCalculator->calculate();

    $unitPrice = $price->getUnitPrice();
    $totalPrice = $price->getTotalPrice();
    $optionsPrice = $price->getOptionsPrice();
}

如果您需要请求或其他服务,您可以使用DIC注入它们或手动将其作为 calculate()方法的参数注入。

请注意,我将 EntityManager 注入 PriceCalculator 服务,但您可以将数据提供程序定义为服务并将其注入(对于非常复杂的事情)。

您还可以将所有查询推送到存储库并将实体传递给 PriceCalculator