Symfony2/Doctrine, нужно поставить бизнес-логику в контроллер? И дублирующий контроллер?
У меня несколько сложный механизм ценообразования в моем приложении. Вот некоторые из моих бизнес-правил для настройки этапа (сущности полужирный):
- Продукт может иметь уникальные Ценовые точки для данного Клиента, Веб-сайт или Клиента Группа.
- Продукт может иногда иметь один или несколько дополнительных параметров, которые могут иметь свои собственные Ценовые точки или Правила цены. > .
- Продукт имеет одно Уникальное дополнение, выбранное пользователем, которое по существу является ценой и целым числом.
Прямо сейчас у меня есть EntityRepository для Price Points, чтобы существенно определить правильную цену для базового продукта. То же самое относится к Уникальное дополнение и Параметры.
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 всюду, чтобы вытащить данные на основе моей бизнес-логики в классы репозитория.
Я с радостью приветствую urdoingitwrong ответы, потому что я действительно думаю, что это неправильно. Как я могу это исправить? Что-то красивое было бы сервисом, который по сути выглядит следующим образом:
$pricer = getPricerService->Pricer($Entities,$postdata,$etc);
$unitPrice = $pricer->getUnitPrice();
$totalPrice = $pricer->getTotalPrice();
$optionsPrice = $pricer->getOptionsPrice();
Но я не знаю, как это сделать в Symfony/Doctrine, особенно в том, как доступ к Doctrine и Repositories осуществляется в контроллерах.
Ответы
Ответ 1
Вы правы, что у вас должна быть вся ваша повторно используемая бизнес-логика обработана службой, чтобы разные контроллеры могли повторно использовать код.
Вы проверили документацию "Как создать сервис":
Документация контейнерного контейнера
Я покажу вам скорость.
В 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 вы, возможно, уже блестели, это был путь!!
Надеюсь, это было по-прежнему полезно для вас!
Ответ 2
Вы упростили свой пример, поэтому я не знаю всех подробностей, но здесь мой вопрос, чтобы решить вашу проблему.
Обратите внимание, что вам может понадобиться больше одной службы, но вы должны получить идею на основе моего примера.
В основном следуйте принципу: у одного класса есть одна ответственность.
Калькулятор цен вычисляет цену:
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.