Skip to content

Commit

Permalink
Making sure value objects are immutable
Browse files Browse the repository at this point in the history
  • Loading branch information
mkorkmaz committed Nov 27, 2018
1 parent b8e466b commit 4402567
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 60 deletions.
10 changes: 10 additions & 0 deletions src/Entity.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ public function __construct(Model $model, string $id, ?stdClass $data = null)
$this->data->id = $id;
}

public function __set($name, $value) : void
{
$this->data->{$name} = $value;
}

public function __unset($name)
{
unset($this->data->{$name});
}

public static function createFromJsonFile($filePath, string $id) : Entity
{
if (!file_exists($filePath)) {
Expand Down
9 changes: 9 additions & 0 deletions src/Exception/BadMethodCallException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);

namespace Selami\Entity\Exception;

class BadMethodCallException extends \BadMethodCallException
{

}
21 changes: 3 additions & 18 deletions src/ObjectTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,17 @@ public function __get($name)
return $this->data->{$name};
}

public function __set($name, $value) : void
{
$this->data->{$name} = $value;
}

public function __isset($name) : bool
{
return property_exists($this->data, $name);
}

public function __unset($name)
{
unset($this->data->{$name});
}

public function validate() : bool
final public function validate() : bool
{
return $this->validateData($this->data, $this->model->getSchema());
}

public function validatePartially(array $requiredFields) : bool
final public function validatePartially(array $requiredFields) : bool
{
$model = $this->model->getModel();
$model->required = $requiredFields;
Expand Down Expand Up @@ -75,16 +65,11 @@ private function validateData($data, $schema) : bool

public function equals($rightHandedObject) : bool
{
return (string) $this === (string) $rightHandedObject;
return (string) json_encode($this->data) === (string) json_encode($rightHandedObject);
}

public function jsonSerialize() : stdClass
{
return $this->data;
}

public function __toString() : string
{
return (string) json_encode($this);
}
}
25 changes: 17 additions & 8 deletions src/ValueObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,39 @@
use stdClass;
use JsonSerializable;
use Selami\Entity\Exception\UnexpectedValueException;
use Selami\Entity\Exception\BadMethodCallException;

final class ValueObject implements JsonSerializable
{
use ObjectTrait;

public function __construct(Model $model, ?stdClass $data = null)
public function __construct(Model $model, stdClass $data)
{
$this->model = $model;
$this->data = $data;
if ($data === null) {
$this->data = new stdClass();
}
}

public static function createFromJsonFile($filePath) : ValueObject
final public function __set($name, $value) : void
{
throw new BadMethodCallException('Can\'t manipulate Immutable Object');
}

final public function __unset($name)
{
throw new BadMethodCallException('Can\'t manipulate Immutable Object');
}

public static function createFromJsonFile($filePath, stdClass $data) : ValueObject
{
if (!file_exists($filePath)) {
throw new UnexpectedValueException(sprintf('Model definition file (%s) does not exist!', $filePath));
}
$json = file_get_contents($filePath);
return static::createFromJson($json);
return static::createFromJson($json, $data);
}
public static function createFromJson($json) : ValueObject

public static function createFromJson($json, stdClass $data) : ValueObject
{
return new static(new Model($json));
return new static(new Model($json), $data);
}
}
6 changes: 2 additions & 4 deletions tests/unit/EntityTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public function shouldReturnEntityObjectSuccessfully() : void
$item->value = 100;
$entity->skills = [$item];
$this->assertTrue($entity->validate());
$arrayFromJson = json_decode($entity, true);
$arrayFromJson = json_decode(json_encode($entity), true);
$this->assertEquals(31, $arrayFromJson['age']);
$this->assertTrue(isset($entity->name));
unset($entity->name);
Expand All @@ -62,6 +62,7 @@ public function shouldValidatePartiallySuccessfully() : void
$this->expectException(\Selami\Entity\Exception\InvalidArgumentException::class);
$entity->validatePartially($requiredFields);
}

/**
* @test
*/
Expand Down Expand Up @@ -91,9 +92,6 @@ public function shouldCompareTwoEntityObjectSuccessfully() : void
$this->assertFalse($entity1->equals($entity3));
}




/**
* @test
* @expectedException \Selami\Entity\Exception\InvalidArgumentException
Expand Down
108 changes: 78 additions & 30 deletions tests/unit/ValueObjectTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ protected function _after()
*/
public function shouldReturnValueObjectSuccessfully() : void
{
$valueObject = ValueObject::createFromJsonFile(__DIR__.'/../resources/test-schema-value-object.json');
$valueObject = new stdClass();
$valueObject->name = 'John Doe';
$valueObject->age = 31;
$valueObject->email = "[email protected]";
Expand All @@ -37,52 +37,73 @@ public function shouldReturnValueObjectSuccessfully() : void
$item->name = 'PHP';
$item->value = 100;
$valueObject->skills = [$item];
$this->assertTrue($valueObject->validate());
$arrayFromJson = json_decode($valueObject, true);

$valObject = ValueObject::createFromJsonFile(
__DIR__.'/../resources/test-schema-value-object.json',
$valueObject
);

$this->assertTrue($valObject->validate());
$arrayFromJson = json_decode(json_encode($valObject), true);
$this->assertEquals(31, $arrayFromJson['age']);
$this->assertTrue(isset($valueObject->name));
unset($valueObject->name);
$this->assertFalse(isset($valueObject->name));
$this->assertTrue(isset($valObject->name));
}

/**
* @test
*/
public function shouldValidatePartiallySuccessfully() : void
{
$valueObject = ValueObject::createFromJsonFile(__DIR__.'/../resources/test-schema-value-object.json');
$valueObject = new stdClass();
$valueObject->name = 'John Doe';
$valueObject->age = 31;
$requiredFields = ['name', 'age'];
$this->assertTrue($valueObject->validatePartially($requiredFields));
$valObject = ValueObject::createFromJsonFile(
__DIR__.'/../resources/test-schema-value-object.json',
$valueObject
);

$this->assertTrue($valObject->validatePartially($requiredFields));
$requiredFields = ['name', 'age', 'email'];
$this->expectException(\Selami\Entity\Exception\InvalidArgumentException::class);
$valueObject->validatePartially($requiredFields);
$valObject->validatePartially($requiredFields);
}
/**
* @test
*/
public function shouldCompareTwoValueObjectSuccessfully() : void
{
$valueObject1 = ValueObject::createFromJsonFile(__DIR__.'/../resources/test-schema-value-object.json');
$valueObject2 = ValueObject::createFromJsonFile(__DIR__.'/../resources/test-schema-value-object.json');
$valueObject3 = ValueObject::createFromJsonFile(__DIR__.'/../resources/test-schema-value-object.json');
$object1 = new stdClass();
$object2 = new stdClass();
$object3 = new stdClass();

$valueObject1->name = 'Kedibey';
$valueObject1->details = new stdClass();
$valueObject1->details->age = 11;
$valueObject1->details->type = 'Angora';
$object1->name = 'Kedibey';
$object1->details = new stdClass();
$object1->details->age = 11;
$object1->details->type = 'Angora';

$valueObject2->name = 'Kedibey';
$valueObject2->details = new stdClass();
$valueObject2->details->age = 11;
$valueObject2->details->type = 'Angora';
$object2->name = 'Kedibey';
$object2->details = new stdClass();
$object2->details->age = 11;
$object2->details->type = 'Angora';

$valueObject3->name = 'Kedibey';
$valueObject3->details = new stdClass();
$valueObject3->details->age = 11;
$valueObject3->details->type = 'Van';
$object3->name = 'Kedibey';
$object3->details = new stdClass();
$object3->details->age = 11;
$object3->details->type = 'Van';

$valueObject1 = ValueObject::createFromJsonFile(
__DIR__.'/../resources/test-schema-value-object.json',
$object1
);
$valueObject2 = ValueObject::createFromJsonFile(
__DIR__.'/../resources/test-schema-value-object.json',
$object2
);
$valueObject3 = ValueObject::createFromJsonFile(
__DIR__.'/../resources/test-schema-value-object.json',
$object3
);
$this->assertTrue($valueObject1->equals($valueObject2));
$this->assertFalse($valueObject1->equals($valueObject3));
}
Expand All @@ -96,12 +117,14 @@ public function shouldCompareTwoValueObjectSuccessfully() : void
*/
public function shouldFailForRequiredInput() : void
{
$object = new stdClass();
$object->name = 'John Doe';
$object->age = 31;
$object->email = "[email protected]";
$object->website = null;

$model = Model::createFromJsonFile(__DIR__.'/../resources/test-schema.json');
$valueObject = new ValueObject($model);
$valueObject->name = 'John Doe';
$valueObject->age = 31;
$valueObject->email = "[email protected]";
$valueObject->website = null;
$valueObject = new ValueObject($model, $object);
$valueObject->validate();
}

Expand All @@ -111,6 +134,31 @@ public function shouldFailForRequiredInput() : void
*/
public function shouldFailForAModelFileDoesNotExist() : void
{
ValueObject::createFromJsonFile(__DIR__.'/../resources/test-schema-no-file.json');
ValueObject::createFromJsonFile(__DIR__.'/../resources/test-schema-no-file.json', new stdClass());
}

/**
* @test
* @expectedException \Selami\Entity\Exception\BadMethodCallException
*/
public function shouldFailForSettingNewValue() : void
{
$valueObject = ValueObject::createFromJsonFile(__DIR__.'/../resources/test-schema-value-object.json', new stdClass());
$valueObject->name = 'Kedibey';
}
/**
* @test
* @expectedException \Selami\Entity\Exception\BadMethodCallException
*/
public function shouldFailForUnsettingAValue() : void
{
$object = new stdClass();
$object->name='Mırmır';

$valueObject = ValueObject::createFromJsonFile(
__DIR__.'/../resources/test-schema-value-object.json',
$object
);
unset($valueObject->name);
}
}

0 comments on commit 4402567

Please sign in to comment.