php - Implement a State Pattern with Doctrine ORM -
i have class using state pattern. here's simple example
/** * @enitity **/ class door { protected $id; protected $state; public function __construct($id, doorstate $state) public function setstate(doorstate $state) { $this->state = $state; } public function close() { $this->setstate($this->state->close()) } ... } interface doorstate { public function close; public function open; public function lock; public function unlock; } class dooraction implements doorstate { public function close() { throw new doorerror(); } ... }
then several classes define appropriate actions in states
class openeddoor extends dooraction { public function close() { return new closeddoor(); } }
so have thing like
$door = new door('1', new openeddoor()); doctrinedoorrepository::save($door); $door->close(); doctrinedoorrepository::save($door);
how implement mapping in doctrine can persist it? i'm hung on $state property. save whole dooraction based object have map dooraction super class or each individual sub class?
i've looked @ implementing using embeddable or supermapping run problems each.
doctrine2 dbal has feature in documentation allows enum
's
http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/cookbook/mysql-enums.html
when take solution 2: defining type base, 1 create own type, instance called doorstatetype
or similar represent open/closed state. instance this:
<?php namespace acme\model\door; use doctrine\dbal\types\type; use doctrine\dbal\platforms\abstractplatform; class doorstatetype extends type { const enum_doorstate = 'enumdoorstate'; const state_open = 'open'; const state_closed = 'closed'; public function getsqldeclaration(array $fielddeclaration, abstractplatform $platform) { return "enum('" . self::state_open . "', '" . self::state_closed . "') comment '(dc2type:" . enum_doorstate . ")'"; } public function converttophpvalue($value, abstractplatform $platform) { return $value; } public function converttodatabasevalue($value, abstractplatform $platform) { if (!in_array($value, array(self::state_open, self::state_closed))) { throw new \invalidargumentexception("invalid state"); } return $value; } public function getname() { return self::enum_doorstate; } }
and use this:
<?php namespace acme\model\door; /** @entity */ class door { /** @column(type="enumdoorstate") */ private $state; public function open() { if (!doorstatetype::state_open === $this->state) { throw new \logicexception('cannot open open door'); } $this->state = doorstatetype::state_open; } public function close() { if (!doorstatetype::state_closed === $this->state) { throw new \logicexception('cannot close closed door'); } $this->state = doorstatetype::state_closed; } }
this allows searching states:
$opendoors = $repository->findby(array('state' => doorstatetype::state_open));
you have converttophpvalue
method create objects of desired states allow logic, checking if open door can locked or similar.
in case state has class contains logic, implement this:
first define normal state can inherit:
<?php namespace acme\model\door; abstract class doorstate { // methods define default behaviour when isn't possible public function open() { throw new \logicexception('cannot open door'); } public function close() { throw new \logicexception('cannot close door'); } abstract public function getstatename(); }
then openstate:
<?php namespace acme\model\door; class openstate extends doorstate { const state = 'open'; public function close() { return new closedstate(); } public function getstatename() { return self::state; } // more logic }
and closedstate:
<?php namespace acme\model\door; class closedstate extends doorstate { const state = 'open'; public function open() { return new openstate(); } public function getstatename() { return self::state; } // more logic }
we can then, persistence, use different convert methods:
<?php namespace acme\model\door; use doctrine\dbal\types\type; use doctrine\dbal\platforms\abstractplatform; class doorstatetype extends type { // sql declarations etc. public function converttophpvalue($value, abstractplatform $platform) { if ($value === openstate::state) { return new openstate(); } if ($value === closedstate::state) { return new closedstate(); } throw new \exception(sprintf('unknown state "%s", expected 1 of "%s"', $value, implode('", "', [openstate::state, closedstate::state]))); } public function converttodatabasevalue($value, abstractplatform $platform) { return $value->getstatename(); } }
Comments
Post a Comment