-
-
Save webdevilopers/5ac1f30d88102e24df87 to your computer and use it in GitHub Desktop.
<?php | |
namespace Sps\Bundle\CalculationBundle\Handler; | |
use Doctrine\ORM\EntityManager; | |
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; | |
use Symfony\Component\HttpFoundation\Session\Session; | |
use Symfony\Component\Translation\Translator; | |
use Sps\Bundle\CalculationBundle\Entity\DormerCalculationPrice; | |
abstract class AbstractCreateCalculationHandler | |
{ | |
const DECIMALS = 2; | |
private $entityManager; | |
private $tokenStorage; | |
private $session; | |
private $translator; | |
protected $translationDomain; | |
protected $calculation; | |
private $variables; | |
public function __construct(EntityManager $entityManager, TokenStorageInterface $tokenStorage, Session $session, Translator $translator) | |
{ | |
$this->entityManager = $entityManager; | |
$this->tokenStorage = $tokenStorage; | |
$this->session = $session; | |
$this->translator = $translator; | |
} | |
public function handle($command) | |
{ | |
$this->calculation = $command->calculation; | |
$this->calculate(); | |
$this->save(); | |
} | |
public function getEntityManager() { | |
return $this->entityManager; | |
} | |
public function getTokenStorage() { | |
return $this->tokenStorage; | |
} | |
public function getSession() { | |
return $this->session; | |
} | |
public function getTranslator() { | |
return $this->translator; | |
} | |
public function getTranslationDomain() { | |
return $this->translationDomain; | |
} | |
public function getCalculation() { | |
return $this->calculation; | |
} | |
public function getPartner() | |
{ | |
return $this->getEntityManager() | |
->getRepository('SpsBaseBundle:Partner') | |
->find($this->getTokenStorage()->getToken()->getUser()->getId()); | |
} | |
/** | |
* | |
* @param type $message | |
* @param array $parameters | |
* @param type $type | |
*/ | |
public function addMessage($message, array $parameters = array(), $type = 'notice') | |
{ | |
// bootstrap: notice, error / warning, success | |
$this->session->getFlashBag()->add( | |
$type, $this->translator | |
->trans($message, $parameters, $this->getTranslationDomain())); | |
} | |
public function getMessages() | |
{ | |
return $this->session->getFlashBag()->all(); | |
} | |
public function getVariables() { | |
return $this->variables; | |
} | |
public function getVariable($key) | |
{ | |
if (!isset($this->variables[$key])) { | |
throw new \InvalidArgumentException("Variable `$key` does not exist."); | |
} | |
return $this->variables[$key]; | |
} | |
public function hasVariable($key) | |
{ | |
return isset($this->variables[$key]) ? true : false; | |
} | |
public function setVariable($key, $value) | |
{ | |
$this->variables[$key] = $value; | |
} | |
public function getPrice($key) | |
{ | |
return $this->calculation->getPrice($key); | |
} | |
public function hasPrice($key) | |
{ | |
return $this->calculation->hasPrice($key); | |
} | |
public function setPrice($key, $value) | |
{ | |
$this->calculation->setPrice($key, $value); | |
} | |
public function addPrice($key, $subtotal, $quantity = 1, $showInOffer = false) | |
{ | |
$total = $subtotal*$quantity; | |
$dormerCalculationPrice = new DormerCalculationPrice; | |
$dormerCalculationPrice->setName($key); | |
$dormerCalculationPrice->setValue($subtotal); | |
$dormerCalculationPrice->setQuantity($quantity); | |
$dormerCalculationPrice->setTotal($total); | |
$dormerCalculationPrice->setShowInOffer($showInOffer); | |
$this->getCalculation()->addPrice($dormerCalculationPrice); | |
} | |
private function round($price) | |
{ | |
return round($price, self::DECIMALS); | |
} | |
protected function save() | |
{ | |
$this->getEntityManager()->persist($this->getCalculation()); | |
$this->getEntityManager()->flush(); | |
} | |
} |
services: | |
sps.calculation.abstract_create_calculation_handler: | |
abstract: true | |
class: Sps\Bundle\CalculationBundle\Handler\AbstractCreateCalculationHandler | |
arguments: | |
- "@doctrine.orm.entity_manager" | |
- "@security.token_storage" | |
- "@session" | |
- "@translator.default" | |
sps.calculation.create_dormer_calculation_handler: | |
class: Sps\Bundle\CalculationBundle\DormerCalculation\CreateDormerCalculationHandler | |
parent: sps.calculation.abstract_create_calculation_handler | |
tags: | |
- { name: command_handler, handles: Sps\Bundle\CalculationBundle\DormerCalculation\CreateDormerCalculation } |
<?php | |
namespace Sps\Bundle\CalculationBundle\DormerCalculation; | |
use Sps\Bundle\CalculationBundle\Entity\DormerCalculation; | |
class CreateDormerCalculation | |
{ | |
public $calculation; | |
public function __construct(DormerCalculation $dormerCalculation) | |
{ | |
if (null === $dormerCalculation) { | |
throw new \InvalidArgumentException('Missing required "dormerCalculation" parameter'); | |
} | |
$this->calculation = $dormerCalculation; | |
} | |
} |
<?php | |
namespace Sps\Bundle\CalculationBundle\DormerCalculation; | |
use Sps\Bundle\CalculationBundle\Handler\AbstractCreateCalculationHandler; | |
use Sps\Bundle\BaseBundle\Entity\DormerCalculation; | |
use Sps\Bundle\CalculationBundle\Entity\DormerCalculationChargeRate; | |
use Sps\Bundle\BaseBundle\Entity\DormerCalculationPrice; | |
class CreateDormerCalculationHandler extends AbstractCreateCalculationHandler | |
{ | |
// A lot of traits with a lot of sub-calculations re-used for other calculation types | |
use CalculateMounting; | |
use CalculateConstructionElement; | |
use CalculateConstructionSite; | |
use CalculateDelivery; | |
use CalculateDormer; | |
use CalculateDormerWindow; | |
use CalculateDownspout; | |
use CalculateGutter; | |
use CalculateOverhang; | |
const WINDOW_DIMENSIONS_ROUNDING_FACTOR = 10; | |
public function calculate() | |
{ | |
// Do a lot of calculation with values from the entity | |
// But since there will be no more getters and setters | |
// and not the entity but the command will be data_class | |
// of the form and validated should these vars go into | |
// the command too (redundant?)? | |
// Or only into the command and then set them as valid values on the entity | |
// via special method setMeasurements($widht, $height) <- value object?! | |
$width = $this->getCalculation()->getWidth(); | |
$height = $this->getCalculation()->getHeight(); | |
$quantity = $this->getCalculation()->getQuantity(); | |
// In the end set a lot of prices on the entity | |
$total = ($width*$height)*$quantity*1000; | |
$this->getCalculation()->addPrice('total', $total); | |
} | |
} |
<?php | |
namespace Sps\Bundle\CalculationBundle\Entity; | |
use Doctrine\ORM\Mapping as ORM; | |
use Doctrine\Common\Collections\ArrayCollection; | |
/** | |
* DormerCalculation | |
* | |
* @ORM\Entity | |
*/ | |
class DormerCalculation | |
{ | |
/** | |
* @var integer $id | |
* | |
* @ORM\Column(name="id", type="integer", precision=0, scale=0, nullable=false, unique=false) | |
* @ORM\Id | |
* @ORM\GeneratedValue(strategy="IDENTITY") | |
*/ | |
private $id; | |
/** | |
* @var string $quantity | |
* | |
* @ORM\Column(name="quantity", type="integer") | |
*/ | |
private $quantity; | |
/** | |
* @var string $height | |
* | |
* @ORM\Column(name="h", type="float") | |
*/ | |
private $height; | |
/** | |
* @var string $width | |
* | |
* @ORM\Column(name="w", type="float") | |
*/ | |
private $width; | |
/** | |
* @ORM\OneToMany(targetEntity="DormerCalculationPrice", | |
* mappedBy="dormerCalculation", cascade="persist", indexBy="name", fetch="EAGER" | |
* ) | |
*/ | |
private $prices; | |
public function __construct($lotsOfValues) | |
{ | |
// Do not use getters and setters on entity, put all vars here via constructor | |
// or method e.g. addPrice() ?! | |
// Use a single $lotsOfValues var if there are a lot?! | |
} | |
public function addPrice(DormerCalculationPrice $price) { | |
$this->prices[$price->getName()] = $price; | |
$price->setDormerCalculation($this); | |
} | |
} |
<?php | |
use Sps\Bundle\CalculationBundle\Entity\DormerCalculation; | |
use Sps\Bundle\CalculationBundle\Form\DormerCalculation as CalculationForm; | |
use Sps\Bundle\CalculationBundle\DormerCalculation\CreateDormerCalculation; | |
class DefaultController extends Controller | |
{ | |
/** | |
* @Route("/calculation/{type}") | |
* @Template() | |
*/ | |
public function indexAction($type) | |
{ | |
// ... After form process this will be the valid entity | |
// Validation should be moved from entity to command (=> data_class) instead | |
$dormerCalculation = new DormerCalculation(); | |
// @todo Handle form, then populate entity and pass it to command bus and handler | |
$createDormerCalculation = new CreateDormerCalculation($dormerCalculation); | |
$createDormerCalculationHandler = $this->get('sps.calculation.create_dormer_calculation_handler'); | |
$createDormerCalculationHandler->handle($createDormerCalculation); | |
// Get ID from entity and redirect, no return values required | |
} | |
} |
If I get it right I should not use the entity
for data_class
in my form. I should use the command instead.
And it is the command that will have getters
and setters
for the properties.
Furthermore it is the command that will validate - ergo I could add my constraints to the properties for the form to use it.
When the handler is done I need to change properties on the entity. Some properties that were already defined on the command e.g. $width
, $height
. If I understood it right this is typical for command bus design and some properties simply repeat?
See: http://verraes.net/2013/04/decoupling-symfony2-forms-from-entities/#comment-1098270880 @mathiasverraes
But instead of using setters
and getters
on the entity I should create real-life-methods e.g.:
setMeasurements($width, $height)
or even better pass a value object e.g. DormerMeasurements
that has the properties width
and height
?
Or is my general approach wrong and I should not use dormerCalculation
as my entity but dormer
itself and use a method calculate
ot add a dormerCalculation
entity on it?
Thanks to @yvoyer for his improved example:
Original twitter discussion @gnugat @chrisguitarguy:
https://twitter.com/webdevilopers/status/700783882732486656