From 6b9c7d95f2e7d7e639973f48dee46c0cc7c918dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jan=20Kucha=C5=99?= <honza.kuchar@grifart.cz>
Date: Sat, 21 May 2016 15:16:36 +0200
Subject: [PATCH] Position: Value Object is now immutable (as described in
 Domain Driven Design by Eric Evans)

---
 src/Position.php      | 50 ++++++++++++++-----------------------------
 src/TrackedCursor.php | 45 ++++++++++++++++++--------------------
 2 files changed, 37 insertions(+), 58 deletions(-)

diff --git a/src/Position.php b/src/Position.php
index ff4f203..8a8e9ef 100644
--- a/src/Position.php
+++ b/src/Position.php
@@ -5,10 +5,9 @@
 
 namespace Grifart\Mappi\Cursor;
 
-// todo: make this immutable as VOs should be
-
 /**
- * Value object for TrackedCursor position
+ * Value Object that holds position of cursor
+ * use by {@see TrackedCursor}
  *
  * @link    https://github.com/nicolopignatelli/valueobjects (inspiration)
  * @package Grifart\Mappi\Cursor
@@ -25,9 +24,13 @@ final class Position
 	 * @param PositionOrigin $origin   from left or from right?
 	 * @param int            $position which position
 	 */
-	public function __construct(PositionOrigin $origin, int $position)
+	private function __construct(PositionOrigin $origin, int $position)
 	{
-		$this->setPosition($origin, $position);
+		if($position < 0) {
+			throw CursorException::cursorPosition_invalidIndexValue($position);
+		}
+		$this->origin = $origin;
+		$this->position = $position;
 	}
 
 	public static function fromLeft(int $position) : self
@@ -45,36 +48,15 @@ final class Position
 			$position
 		);
 	}
-
-	public function setPositionFromLeft(int $position)
-	{
-		$this->setPosition(PositionOrigin::get(PositionOrigin::FROM_LEFT), $position);
-	}
-
-	public function setPositionFromRight(int $position)
-	{
-		$this->setPosition(PositionOrigin::get(PositionOrigin::FROM_RIGHT), $position);
-	}
-
-	private function setPosition(PositionOrigin $origin, int $position)
-	{
-		if($position < 0) {
-			throw CursorException::cursorPosition_invalidIndexValue($position);
-		}
-		$this->origin = $origin;
-		$this->position = $position;
-	}
-
-	/**
-	 * @param int $by negative = left; positive = right
-	 */
-	public function movePositionBy(int $by)
+	
+	public static function relativeTo(Position $position, int $offset) : self
 	{
-		$modifier = 1;
-		if ($this->origin->is(PositionOrigin::FROM_RIGHT)) {
-			$modifier = -1;
-		}
-		$this->position += $modifier * $by;
+		$positionDelta = ($position->getOrigin()
+			->is(PositionOrigin::FROM_LEFT) ? 1 : -1) * $offset;
+		return new self(
+			$position->getOrigin(),
+			$position->getPosition() + $positionDelta
+		);
 	}
 
 	/**
diff --git a/src/TrackedCursor.php b/src/TrackedCursor.php
index 2d1ceb2..cf92d01 100644
--- a/src/TrackedCursor.php
+++ b/src/TrackedCursor.php
@@ -47,18 +47,16 @@ class TrackedCursor implements ICursor
 	{
 		$this->cursor->moveTo($index);
 		if ($this->cursor->isOnRecord()) {
-			$this->position->setPositionFromLeft(
-				$index
-			);
+			$this->position = Position::fromLeft($index);
 			return;
 		}
 
 		if ($index === 0) {
-			$this->position->setPositionFromLeft(0); // BEGINNING
+			$this->position = Position::fromLeft(0); // BEGINNING
 			return;
 		}
 		if ($index > 0 ) {
-			$this->position->setPositionFromRight(0); // END
+			$this->position = Position::fromRight(0); // END
 			return;
 		}
 		throw CursorException::cursorPosition_unknownError();
@@ -68,18 +66,16 @@ class TrackedCursor implements ICursor
 	{
 		$this->cursor->moveFromEndTo($index);
 		if ($this->cursor->isOnRecord()) {
-			$this->position->setPositionFromRight(
-				$index
-			);
+			$this->position = Position::fromRight($index);
 			return;
 		}
 
 		if ($index === 0) {
-			$this->position->setPositionFromRight(0); // END
+			$this->position = Position::fromRight(0); // END
 			return;
 		}
 		if ($index > 0) {
-			$this->position->setPositionFromLeft(0); // BEGINNING
+			$this->position = Position::fromLeft(0); // BEGINNING
 			return;
 		}
 		throw CursorException::cursorPosition_unknownError();
@@ -90,15 +86,15 @@ class TrackedCursor implements ICursor
 		// todo: use MOVE FORWARD n IN ... which returns number of rows read
 		$this->cursor->moveBy($offset);
 		if ($this->cursor->isOnRecord()) {
-			$this->position->movePositionBy($offset);
+			$this->position = Position::relativeTo($this->position, $offset);
 			return;
 		}
 		if ($offset < 0) {
-			$this->position->setPositionFromLeft(0); // BEGINNING
+			$this->position = Position::fromLeft(0); // BEGINNING
 			return;
 		}
 		if ($offset > 0) {
-			$this->position->setPositionFromRight(0); // END
+			$this->position = Position::fromRight(0); // END
 			return;
 		}
 		if ($offset === 0) {
@@ -118,17 +114,18 @@ class TrackedCursor implements ICursor
 		$count = count($result);
 		$reachedEnd = $count < abs($offset);
 		if(!$reachedEnd) {
-			$this->position->movePositionBy(
+			$this->position = Position::relativeTo(
+				$this->position,
 				$count * ($forward ? 1 : -1)
 			);
 			return $result;
 		}
 
 		if($forward) {
-			$this->position->setPositionFromRight(0); // right END
+			$this->position = Position::fromRight(0); // right END
 			return $result;
 		} else {
-			$this->position->setPositionFromLeft(0); // left END
+			$this->position = Position::fromLeft(0); // left END
 			return $result;
 		}
 	}
@@ -138,24 +135,24 @@ class TrackedCursor implements ICursor
 		$row = $this->cursor->fetchOneAt($index);
 		if ($row !== NULL) {
 			if($index < 0) {
-				$this->position->setPositionFromRight(abs($index));
+				$this->position = Position::fromRight(abs($index));
 			} else /* >= 0 */ {
-				$this->position->setPositionFromLeft($index);
+				$this->position = Position::fromLeft(abs($index));
 			}
 			return $row;
 		}
 
 		if ($index === 0) {
-			$this->position->setPositionFromLeft(0); // BEGINNING
+			$this->position = Position::fromLeft(0); // BEGINNING
 			return NULL;
 		}
 
 		if ($index < 0) { // went from right to left -> hit beginning
-			$this->position->setPositionFromLeft(0);
+			$this->position = Position::fromLeft(0);
 			return NULL;
 		}
 		if ($index > 0) { // went left->right -> hit end
-			$this->position->setPositionFromRight(0);
+			$this->position = Position::fromRight(0);
 			return NULL;
 		}
 
@@ -166,16 +163,16 @@ class TrackedCursor implements ICursor
 	{ // todo: content tests
 		$row = $this->cursor->fetchOneBy($offset);
 		if ($row !== NULL) {
-			$this->position->movePositionBy($offset);
+			$this->position = $this->position = Position::relativeTo($this->position, $offset);
 			return $row;
 		}
 
 		if ($offset < 0) {
-			$this->position->setPositionFromLeft(0); // BEGINNING
+			$this->position = Position::fromLeft(0); // BEGINNING
 			return NULL;
 		}
 		if ($offset > 0) {
-			$this->position->setPositionFromRight(0); // END
+			$this->position = Position::fromRight(0); // END
 			return NULL;
 		}
 		if ($offset === 0) {
-- 
GitLab