Symfony2 / Doctrine, нужно ли поместить бизнес-логику в мой контроллер?А дублирующий контроллер? - PullRequest
20 голосов
/ 05 ноября 2011

У меня в приложении несколько сложный механизм ценообразования. Вот некоторые из моих бизнес-правил для определения стадии (объекты выделены жирным шрифтом ):

  • A Продукт может иметь уникальные ценовые баллы для данного клиента , веб-сайт или группа клиентов .
  • A Продукт может иногда иметь один или несколько дополнительных вариантов , которые могут иметь свои ценовые баллы или Правила цены .
  • A Продукт имеет одно Уникальное дополнение , выбранное пользователем, которое по сути является ценой и целым числом.

Прямо сейчас у меня есть 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 для извлечения данных, основанных на моей бизнес-логике в классах репозитория.

Я с радостью приветствую неправильные ответы, потому что действительно считаю, что это неправильно. Как мне исправить это? Каким-то прекрасным был бы сервис, который по сути выглядит так:

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

Но я понятия не имею, как это сделать в Symfony / Doctrine, особенно в том, как доступ к Doctrine и Repositories осуществляется в контроллерах.

Ответы [ 2 ]

29 голосов
/ 06 ноября 2011

Вы правы в том, что вся ваша бизнес-логика, которую можно повторно использовать, отправляется в службу, чтобы разные контроллеры могли повторно использовать код.

Вы уже ознакомились с "как создатьдокументация по сервису:

Документация по сервисному контейнеру

Я покажу вам скорость разгона.

В 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.
}

Наконец, чтобы получить ваш сервис от контроллера, вам просто нужноdo:

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

Существуют и другие способы, которыми вы можете модулировать службу, например не выгружать все ваши службы в config.yml, но все это объясняется в документации.Также обратите внимание, что вы можете добавить любую другую услугу, которую пожелаете, в свою службу, поэтому, если вам нужны такие вещи, как arguments: [@doctrine, @security.context, @validator], вы можете делать все эти вещи или даже: [@my_other_service].

Я подозреваю из вашего другого вопроса о введенииEntityManager, который вы, возможно, уже заметили, что это был путь, хотя!

Надеюсь, это все еще было полезно для вас!

10 голосов
/ 06 ноября 2011

Вы упростили свой пример, поэтому я не знаю всех подробностей, но вот мое решение, чтобы решить вашу проблему.

Обратите внимание, что на самом деле вам может потребоваться более одной службы, но вы должны получить идею на основе моего примера.

В основном следуйте принципу - один класс имеет одну ответственность.

Калькулятор цен вычисляет цену:

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 или вручную, в качестве параметра для метод расчета () .

Обратите внимание, что я ввел EntityManager в службу PriceCalculator , но вы можете определить поставщиков данных как службы и внедрить их вместо этого (для действительно сложных вещей).

Вы также можете отправить все запросы в репозитории и передать объекты в ваш PriceCalculator .

...