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 "\n12345JohnSmithTomSmith\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);
}