-
-
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