diff --git a/src/Builder/BuilderAbstract.php b/src/Builder/BuilderAbstract.php index 6ec23a6..eff9686 100644 --- a/src/Builder/BuilderAbstract.php +++ b/src/Builder/BuilderAbstract.php @@ -3,20 +3,29 @@ namespace ABC\Builder; use \ABC\Builder\BuilderAction; +use \Exception; abstract class BuilderAbstract { /** @var array(\ABC\Builder\BuilderAction) */ public $builderActions = array(); + /** @var bool */ + public $executed = false; + + /** @var array(callable) */ + public $validations = array(); + /** * @return null */ - public function executeActions() + public function execute() { foreach ($this->builderActions as $action) { call_user_func_array($action->action, $action->arguments); } + $this->validate(); + $this->executed = true; return $this; } @@ -28,4 +37,60 @@ public function addAction($action) $this->builderActions[] = $action; return $this; } + + /** + * @throws \Exception + */ + public function checkStatus() + { + if (!$this->executed) { + throw new Exception('Builder actions not executed'); + } + } + + /** + * @throws \Exception + */ + protected function validate() + { + $actions = $this->compileActionCounts(); + foreach ($this->validations as $validation) { + $result = call_user_func_array($validation['callback'], array($actions)); + if (!$result) { + $class = $validation['exceptionType']; + throw new $class($validation['exceptionMessage']); + } + } + } + + /** + * @return array + */ + protected function compileActionCounts() + { + $counts = array(); + + foreach ($this->builderActions as $action) { + $counts[$action->name] = isset($counts[$action->name]) ? $counts[$action->name]+1 : 1; + } + + return $counts; + } + + /** + * @param callable $callback + * @param string $exceptionType + * @param string $exceptionMessage + * + * @return \ABC\Builder\BuilderAbstract + */ + protected function addValidation($callback, $exceptionType, $exceptionMessage = '') + { + $this->validations[] = array( + 'callback' => $callback, + 'exceptionType' => $exceptionType, + 'exceptionMessage' => $exceptionMessage, + ); + return $this; + } } diff --git a/src/Builder/CompanyBuilder.php b/src/Builder/CompanyBuilder.php index d9306f0..14f595d 100644 --- a/src/Builder/CompanyBuilder.php +++ b/src/Builder/CompanyBuilder.php @@ -10,17 +10,20 @@ class CompanyBuilder extends BuilderAbstract { + /** @var \ABC\Builder\Company */ public $company = null; - public function __construct($company = null) + public function __construct() { - if ($company != null) { - $this->company = $company; - } else { - $this->company = new Company(); - } + $this->company = new Company(); + $this->setUpValidations(); } + /** + * @param \ABC\Builder\Address $address + * + * @return \ABC\Builder\Company + */ public function withAddress(Address $address) { $action = new CompanyBuilderAction('withAddress', array($this->company, 'appendAddress')); @@ -29,6 +32,11 @@ public function withAddress(Address $address) return $this; } + /** + * @param \ABC\Builder\Person $person + * + * @return \ABC\Builder\Company + */ public function withPerson(Person $person) { $action = new CompanyBuilderAction('withPerson', array($this->company, 'appendPerson')); @@ -37,13 +45,34 @@ public function withPerson(Person $person) return $this; } + /** + * Returns executed object as JSON. + * Throws an exception when builder actions + * have not been executed. + * + * @throws \Exception + * + * @return string + */ public function asJson() { + $this->checkStatus(); return json_encode($this->company); } + /** + * Returns executed object as XML. + * Throws an exception when builder actions + * have not been executed. + * + * @throws \Exception + * + * @return string + */ public function asXml() { + $this->checkStatus(); + $xml = new DOMDocument('1.0', 'utf-8'); $company = $xml->createElement('Company'); @@ -75,4 +104,40 @@ public function asXml() return $xml->saveXml(); } + + /** + * Ensures there is at least one address. + * + * @param arraay $actionCounts + * + * @return bool + */ + public function atLeastOneAddress($actionCounts) + { + return isset($actionCounts['withAddress']) && $actionCounts['withAddress'] > 0; + } + + /** + * Ensures there is at least one person. + * + * @param arraay $actionCounts + * + * @return bool + */ + public function atLeastOnePerson($actionCounts) + { + return isset($actionCounts['withPerson']) && $actionCounts['withPerson'] > 0; + } + + /** + * Setups up validations for building Company objects. + * + * @return null + */ + private function setUpValidations() + { + $this + ->addValidation(array($this, 'atLeastOneAddress'), 'Exception', 'Company needs at least one address') + ->addValidation(array($this, 'atLeastOnePerson'), 'Exception', 'Company needs at least one person'); + } } diff --git a/tests/Builder/AddressBuilderTest.php b/tests/Builder/AddressBuilderTest.php index 8b47187..bf88c66 100644 --- a/tests/Builder/AddressBuilderTest.php +++ b/tests/Builder/AddressBuilderTest.php @@ -14,7 +14,7 @@ public function testBuilderWithInitialValueNoActions() $this->assertEquals($this->expectedAddress(), $builder->address); - $builder = $builder->executeActions(); + $builder = $builder->execute(); $this->assertEquals($this->expectedAddress(), $builder->address); } @@ -23,7 +23,7 @@ public function testBuilderWithoutInitialValueNoActions() { $builder = new AddressBuilder(); - $builder = $builder->executeActions(); + $builder = $builder->execute(); $this->assertEquals(new Address(), $builder->address); } diff --git a/tests/Builder/CompanyBuilderTest.php b/tests/Builder/CompanyBuilderTest.php index e5e058f..7ced8e2 100644 --- a/tests/Builder/CompanyBuilderTest.php +++ b/tests/Builder/CompanyBuilderTest.php @@ -20,54 +20,68 @@ public function testBuilderExecutionAddsValuesToProperties() $this->assertEquals(new Company(), $builder->company); - $builder = $builder->executeActions(); + $builder = $builder->execute(); $this->assertEquals($this->expectedCompany(), $builder->company); } - public function testBuilderWithInitialValueNoActions() + /** + * @expectedException \Exception + * @expectedExceptionMessage Builder actions not executed + */ + public function testNonExecutedBuilderAsJson() { - $builder = new CompanyBuilder($this->expectedCompany()); - - $this->assertEquals($this->expectedCompany(), $builder->company); - - $builder = $builder->executeActions(); - - $this->assertEquals($this->expectedCompany(), $builder->company); + $builder = new CompanyBuilder(); + $json = $builder->asJson(); } - public function testBuilderWithInitialValueSomeActions() + /** + * @expectedException \Exception + * @expectedExceptionMessage Builder actions not executed + */ + public function testNonExecutedBuilderAsXml() { - $company = new Company(); - $company->addresses = array($this->address()); - - $builder = new CompanyBuilder($company); - $builder = $builder - ->withPerson($this->person1()) - ->withPerson($this->person2()); - - $builder = $builder->executeActions(); + $builder = new CompanyBuilder(); + $xml = $builder->asXml(); + } - $this->assertEquals($this->expectedCompany(), $builder->company); + /** + * @expectedException \Exception + * @expectedExceptionMessage Company needs at least one address + */ + public function testNonValidBuilderThrowsException() + { + $builder = new CompanyBuilder(); + $builder = $builder->execute(); } public function testBuilderAsJson() { - $builder = new CompanyBuilder($this->expectedCompany()); - $json = $builder->asJson(); + $builder = new CompanyBuilder(); + $json = $builder + ->withAddress($this->address()) + ->withPerson($this->person1()) + ->withPerson($this->person2()) + ->execute() + ->asJson(); - $this->assertEquals($this->expectedJson(), $json); + $this->assertJsonStringEqualsJsonString($this->expectedJson(), $json); } public function testBuilderAsXml() { - $builder = new CompanyBuilder($this->expectedCompany()); - $xml = $builder->asXml(); + $builder = new CompanyBuilder(); + $xml = $builder + ->withAddress($this->address()) + ->withPerson($this->person1()) + ->withPerson($this->person2()) + ->execute() + ->asXml(); $this->assertXmlStringEqualsXmlString($this->expectedXml(), $xml); } - /** @var \ABC\Builder\Company */ + /** @return \ABC\Builder\Company */ protected function expectedCompany() { $company = new Company(); @@ -76,19 +90,65 @@ protected function expectedCompany() return $company; } - /** @var string */ + /** @return string */ protected function expectedJson() { - return '{"name":null,"addresses":[{"address1":null,"address2":null,"city":null,"province":null,"country":null,"postalCode":"12345"}],"people":[{"firstName":"John","lastName":"Smith","address":null},{"firstName":"Tom","lastName":"Smith","address":null}]}'; + return '{ + "name":null, + "addresses":[ + { + "address1":null, + "address2":null, + "city":null, + "province":null, + "country":null, + "postalCode":"12345" + } + ], + "people":[ + { + "firstName":"John", + "lastName":"Smith", + "address":null + }, + { + "firstName":"Tom", + "lastName":"Smith", + "address":null + } + ] + }'; } - /** @var string */ + /** @return string */ protected function expectedXml() { - return "\n
12345
JohnSmithTomSmith
\n"; + return " + + +
+ + + + + 12345 + +
+
+ + + John + Smith + + + Tom + Smith + + +
"; } - /** @var \ABC\Builder\Address */ + /** @return \ABC\Builder\Address */ protected function address() { $address = new Address(); @@ -96,7 +156,7 @@ protected function address() return $address; } - /** @var \ABC\Builder\Person */ + /** @return \ABC\Builder\Person */ protected function person1() { $person = new Person(); @@ -105,7 +165,7 @@ protected function person1() return $person; } - /** @var \ABC\Builder\Person */ + /** @return \ABC\Builder\Person */ protected function person2() { $person = new Person(); diff --git a/tests/Builder/PersonBuilderTest.php b/tests/Builder/PersonBuilderTest.php index 7aae9d6..787e805 100644 --- a/tests/Builder/PersonBuilderTest.php +++ b/tests/Builder/PersonBuilderTest.php @@ -14,7 +14,7 @@ public function testBuilderWithInitialValueNoActions() $this->assertEquals($this->expectedPerson(), $builder->person); - $builder = $builder->executeActions(); + $builder = $builder->execute(); $this->assertEquals($this->expectedPerson(), $builder->person); } @@ -23,7 +23,7 @@ public function testBuilderWithoutInitialValueNoActions() { $builder = new PersonBuilder(); - $builder = $builder->executeActions(); + $builder = $builder->execute(); $this->assertEquals(new Person(), $builder->person); }