From 20d2b6bcce8f1c7a1aaa375b86ffb5be30674956 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Tue, 5 Mar 2013 18:28:01 +0100 Subject: [PATCH 1/1] add test --- lib/pq/Gateway/Cell.php | 30 ++++--- lib/pq/Gateway/Row.php | 44 +++++++--- lib/pq/Gateway/Rowset.php | 42 ++++++---- lib/pq/Gateway/Table.php | 52 ++++++++---- lib/pq/Query/Writer.php | 10 +-- tests/lib/pq/Gateway/TableTest.php | 124 +++++++++++++++++++++++++++++ tests/setup.inc | 28 +++++++ 7 files changed, 273 insertions(+), 57 deletions(-) create mode 100644 tests/lib/pq/Gateway/TableTest.php create mode 100644 tests/setup.inc diff --git a/lib/pq/Gateway/Cell.php b/lib/pq/Gateway/Cell.php index ba58404..f29fd1f 100644 --- a/lib/pq/Gateway/Cell.php +++ b/lib/pq/Gateway/Cell.php @@ -21,15 +21,22 @@ class Cell */ protected $data; + /** + * @var bool + */ + protected $dirty; + /** * @param \pq\Gateway\Row $row * @param string $name * @param mixed $data + * @param bool $dirty */ - function __construct(Row $row, $name, $data) { + function __construct(Row $row, $name, $data, $dirty = false) { $this->row = $row; $this->name = $name; $this->data = $data; + $this->dirty = $dirty; } /** @@ -48,6 +55,14 @@ class Cell return $this->data instanceof Expr; } + /** + * Check whether the cell has been modified + * @return bool + */ + function isDirty() { + return $this->dirty; + } + /** * Get value * @return mixed @@ -65,15 +80,6 @@ class Cell function mod($data, $op = null) { if (!($this->data instanceof Expr)) { $this->data = new Expr($this->name); - /* - if (!isset($this->data)) { - $this->data = new Expr($this->name); - } elseif (is_numeric($this->data)) { - $this->data = new Expr($this->data); - } else { - $this->data = new Expr("%s", $this->row->getTable()->getConnection()->quote($this->data)); - } - */ } if ($data instanceof Expr) { @@ -84,6 +90,9 @@ class Cell $data = $this->row->getTable()->getConnection()->quote($data); $this->data->add(new Expr("%s %s"), isset($op) ? $op : "||", $data); } + + $this->dirty = true; + return $this; } @@ -94,6 +103,7 @@ class Cell */ function set($data) { $this->data = $data; + $this->dirty = true; return $this; } } diff --git a/lib/pq/Gateway/Row.php b/lib/pq/Gateway/Row.php index dc07e3f..5c0efa4 100644 --- a/lib/pq/Gateway/Row.php +++ b/lib/pq/Gateway/Row.php @@ -17,7 +17,7 @@ class Row implements \JsonSerializable /** * @var array */ - protected $mods = array(); + protected $cell = array(); /** * @param \pq\Gateway\Table $table @@ -66,35 +66,53 @@ class Row implements \JsonSerializable return $this->data; } + /** + * Check whether the row contains modifications + * @return boolean + */ + function isDirty() { + foreach ($this->cell as $cell) { + if ($cell->isDirty()) { + return true; + } + } + return false; + } + /** * Fill modified cells * @return \pq\Gateway\Row */ protected function prime() { - $this->mods = array(); + $this->cell = array(); foreach ($this->data as $key => $val) { - $this->mods[$key] = new Cell($this, $key, $val); + $this->cell[$key] = new Cell($this, $key, $val, true); } return $this; } /** * Transform data array to where criteria - * @param array $data * @return array */ protected function criteria() { $where = array(); - array_walk($this->data, function($v, $k) use (&$where) { + foreach($this->data as $k => $v) { $where["$k="] = $v; - }); + } return $where; } + /** + * Get an array of changed properties + * @return array + */ protected function changes() { $changes = array(); - foreach ($this->mods as $name => $cell) { - $changes[$name] = $cell->get(); + foreach ($this->cell as $name => $cell) { + if ($cell->isDirty()) { + $changes[$name] = $cell->get(); + } } return $changes; } @@ -105,10 +123,10 @@ class Row implements \JsonSerializable * @return \pq\Gateway\Cell */ function __get($p) { - if (!isset($this->mods[$p])) { - $this->mods[$p] = new Cell($this, $p, $this->data[$p]); + if (!isset($this->cell[$p])) { + $this->cell[$p] = new Cell($this, $p, $this->data[$p]); } - return $this->mods[$p]; + return $this->cell[$p]; } /** @@ -126,7 +144,7 @@ class Row implements \JsonSerializable */ function create() { $this->data = $this->table->create($this->changes())->current()->data; - $this->mods = array(); + $this->cell = array(); return $this; } @@ -136,7 +154,7 @@ class Row implements \JsonSerializable */ function update() { $this->data = $this->table->update($this->criteria(), $this->changes())->current()->data; - $this->mods = array(); + $this->cell = array(); return $this; } diff --git a/lib/pq/Gateway/Rowset.php b/lib/pq/Gateway/Rowset.php index 2527b02..2b03993 100644 --- a/lib/pq/Gateway/Rowset.php +++ b/lib/pq/Gateway/Rowset.php @@ -20,18 +20,16 @@ class Rowset implements \SeekableIterator, \Countable, \JsonSerializable protected $rows; /** - * @var string + * @var mixed */ - protected $row; + protected $row = "\\pq\\Gateway\\Row"; /** * @param \pq\Gateway\Table $table * @param \pq\Result $result */ - function __construct(Table $table, \pq\Result $result, $row = "\\pq\\Gateway\\Row") { + function __construct(Table $table, \pq\Result $result = null) { $this->table = $table; - $this->row = $row; - $this->hydrate($result); } @@ -51,20 +49,36 @@ class Rowset implements \SeekableIterator, \Countable, \JsonSerializable * @param \pq\Result $result * @return array */ - protected function hydrate(\pq\Result $result) { + protected function hydrate(\pq\Result $result = null) { $this->index = 0; $this->rows = array(); - $row = $this->row; - if (is_callable($row)) { - while (($data = $result->fetchRow(\pq\Result::FETCH_ASSOC))) { - $this->rows[] = $row($data); - } - } else { - while (($data = $result->fetchRow(\pq\Result::FETCH_ASSOC))) { - $this->rows[] = new $row($this->table, $data); + if ($result) { + $row = $this->row; + + if (is_callable($row)) { + while (($data = $result->fetchRow(\pq\Result::FETCH_ASSOC))) { + $this->rows[] = $row($data); + } + } elseif ($row) { + while (($data = $result->fetchRow(\pq\Result::FETCH_ASSOC))) { + $this->rows[] = new $row($this->table, $data); + } + } else { + $this->rows = $result->fetchAll(\pq\Result::FETCH_OBJECT); } } + + return $this; + } + + /** + * Set the row prototype + * @param mixed $row + * @return \pq\Gateway\Table + */ + function setRowPrototype($row) { + $this->row = $row; return $this; } diff --git a/lib/pq/Gateway/Table.php b/lib/pq/Gateway/Table.php index 0807626..805ed79 100644 --- a/lib/pq/Gateway/Table.php +++ b/lib/pq/Gateway/Table.php @@ -6,6 +6,11 @@ use \pq\Query\Writer as QueryWriter; class Table { + /** + * @var \pq\Connection + */ + public static $defaultConnection; + /** * @var \pq\Connection */ @@ -19,16 +24,25 @@ class Table /** * @var string */ - protected $rowset; + protected $rowset = "\\pq\\Gateway\\Rowset"; /** - * @param \pq\Connection $conn * @param string $name + * @param \pq\Connection $conn + */ + function __construct($name, \pq\Connection $conn = null) { + $this->name = $name; + $this->conn = $conn ?: static::$defaultConnection ?: new \pq\Connection; + } + + /** + * Set the rowset prototype + * @param mixed $rowset + * @return \pq\Gateway\Table */ - function __construct(\pq\Connection $conn, $name, $rowset = "\\pq\\Gateway\\Rowset") { - $this->conn = $conn; - $this->name = $name; + function setRowsetPrototype($rowset) { $this->rowset = $rowset; + return $this; } /** @@ -98,14 +112,21 @@ class Table * @param string $returning * @return \pq\Result */ - function create(array $data, $returning = "*") { - $params = array(); - $query = new QueryWriter("INSERT INTO ".$this->conn->quoteName($this->name)." ("); - foreach ($data as $key => $val) { - $query->write($key); - $params[] = $query->param($val); + function create(array $data = null, $returning = "*") { + $query = new QueryWriter("INSERT INTO ".$this->conn->quoteName($this->name)); + if ($data) { + $first = true; + $params = array(); + foreach ($data as $key => $val) { + $query->write($first ? "(" : ",", $key); + $params[] = $query->param($val); + $first and $first = false; + } + $query->write(") VALUES (", $params, ")"); + } else { + $query->write("DEFAULT VALUES"); } - $query->write(") VALUES (", $params, ")"); + if (strlen($returning)) { $query->write("RETURNING", $returning); } @@ -120,9 +141,12 @@ class Table * @retunr \pq\Result */ function update(array $where, array $data, $returning = "*") { - $query = new QueryWriter("UPDATE ".$this->conn->quoteName($this->name)." SET"); + $query = new QueryWriter("UPDATE ".$this->conn->quoteName($this->name)); + $first = true; + $params = array(); foreach ($data as $key => $val) { - $query->write($key, "=", $query->param($val)); + $query->write($first ? "SET" : ",", $key, "=", $query->param($val)); + $first and $first = false; } $query->write("WHERE")->criteria($where); if (strlen($returning)) { diff --git a/lib/pq/Query/Writer.php b/lib/pq/Query/Writer.php index bfd7094..55cb5a2 100644 --- a/lib/pq/Query/Writer.php +++ b/lib/pq/Query/Writer.php @@ -94,10 +94,11 @@ class Writer * @return string */ function param($param, $type = null) { + if ($param instanceof \pq\Gateway\Cell) { + $param = $param->get(); + } if ($param instanceof Expr) { return (string) $param; - } else { - var_dump($param); } $this->params[] = $param; @@ -113,14 +114,13 @@ class Writer */ function criteria(array $criteria) { if ((list($left, $right) = each($criteria))) { - array_shift($criteria); $this->write("("); if (is_array($right)) { $this->criteria($right); } else { $this->write("(", $left, $this->param($right), ")"); } - foreach ($criteria as $left => $right) { + while ((list($left, $right) = each($criteria))) { $this->write(is_int($left) && is_array($right) ? "OR" : "AND"); if (is_array($right)) { $this->criteria($right); @@ -139,8 +139,6 @@ class Writer * @return \pq\Result */ function exec(\pq\Connection $c) { - fprintf(STDERR, "Q: %s\n", $this); - fprintf(STDERR, "P: %s\n", implode(", ", $this->params)); return $c->execParams($this, $this->params, $this->types); } } diff --git a/tests/lib/pq/Gateway/TableTest.php b/tests/lib/pq/Gateway/TableTest.php new file mode 100644 index 0000000..bee7bdd --- /dev/null +++ b/tests/lib/pq/Gateway/TableTest.php @@ -0,0 +1,124 @@ +conn = new \pq\Connection(PQ_DSN); + $this->conn->exec(PQ_TEST_DROP_TABLE); + $this->conn->exec(PQ_TEST_CREATE_TABLE); + Table::$defaultConnection = $this->conn; + $this->object = new Table(PQ_TEST_TABLE_NAME); + } + + /** + * Tears down the fixture, for example, closes a network connection. + * This method is called after a test is executed. + */ + protected function tearDown() { + $this->conn->exec(PQ_TEST_DROP_TABLE); + } + + /** + * Creates test data in the test table + */ + protected function createTestData() { + $this->conn->exec(PQ_TEST_CREATE_DATA); + } + + /** + * @covers pq\Gateway\Table::setRowsetPrototype + */ + public function testSetRowsetPrototype() { + $prop = new \ReflectionProperty("\\pq\\Gateway\\Table", "rowset"); + $prop->setAccessible(true); + $this->assertEquals("\\pq\\Gateway\\Rowset", $prop->getValue($this->object)); + $this->object->setRowsetPrototype(null); + $this->assertNull($prop->getValue($this->object)); + $rowset = new \pq\Gateway\Rowset($this->object); + $this->object->setRowsetPrototype($rowset); + $this->assertSame($rowset, $prop->getValue($this->object)); + } + + /** + * @covers pq\Gateway\Table::getConnection + */ + public function testGetConnection() { + $this->assertSame($this->conn, $this->object->getConnection()); + } + + /** + * @covers pq\Gateway\Table::getName + */ + public function testGetName() { + $this->assertSame(PQ_TEST_TABLE_NAME, $this->object->getName()); + } + + /** + * @covers pq\Gateway\Table::find + */ + public function testFind() { + $rowset = $this->object->find(); + $this->assertInstanceOf("\\pq\\Gateway\\Rowset", $rowset); + $rowset = $this->object->find(array("id = " => 1)); + $this->assertInstanceOf("\\pq\\Gateway\\Rowset", $rowset); + $rowset = $this->object->find(array("id = " => 0)); + $this->assertInstanceOf("\\pq\\Gateway\\Rowset", $rowset); + $rowset = $this->object->find(array(array("id<" => 2), array("id>" => 2))); + $this->assertInstanceOf("\\pq\\Gateway\\Rowset", $rowset); + } + + /** + * @covers pq\Gateway\Table::create + */ + public function testCreate() { + $rowset = $this->object->create(array("id" => new \pq\Query\Expr("DEFAULT"))); + $this->assertInstanceOf("\\pq\\Gateway\\Rowset", $rowset); + $this->assertCount(1, $rowset); + } + + /** + * @covers pq\Gateway\Table::update + */ + public function testUpdate() { + $row = $this->object->create(array())->current(); + $data = array( + "created" => "2013-03-03 03:03:03", + "counter" => 2, + "number" => 2.2, + "data" => "this is a test", + ); + $row = $this->object->update(array("id = " => $row->id), $data)->current(); + $data = array("id" => $row->id->get()) + $data; + $this->assertSame(array_map(function($v){return strval($v);}, $data), $row->getData()); + } + + /** + * @covers pq\Gateway\Table::delete + */ + public function testDelete() { + $this->object->delete(array("id!=" => 0)); + $this->assertCount(0, $this->object->find()); + } + +} diff --git a/tests/setup.inc b/tests/setup.inc new file mode 100644 index 0000000..798d4a4 --- /dev/null +++ b/tests/setup.inc @@ -0,0 +1,28 @@ +