*/
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;
}
/**
return $this->data instanceof Expr;
}
+ /**
+ * Check whether the cell has been modified
+ * @return bool
+ */
+ function isDirty() {
+ return $this->dirty;
+ }
+
/**
* Get value
* @return mixed
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) {
$data = $this->row->getTable()->getConnection()->quote($data);
$this->data->add(new Expr("%s %s"), isset($op) ? $op : "||", $data);
}
+
+ $this->dirty = true;
+
return $this;
}
*/
function set($data) {
$this->data = $data;
+ $this->dirty = true;
return $this;
}
}
/**
* @var array
*/
- protected $mods = array();
+ protected $cell = array();
/**
* @param \pq\Gateway\Table $table
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;
}
* @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];
}
/**
*/
function create() {
$this->data = $this->table->create($this->changes())->current()->data;
- $this->mods = array();
+ $this->cell = array();
return $this;
}
*/
function update() {
$this->data = $this->table->update($this->criteria(), $this->changes())->current()->data;
- $this->mods = array();
+ $this->cell = array();
return $this;
}
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);
}
* @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;
}
class Table
{
+ /**
+ * @var \pq\Connection
+ */
+ public static $defaultConnection;
+
/**
* @var \pq\Connection
*/
/**
* @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;
}
/**
* @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);
}
* @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)) {
* @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;
*/
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);
* @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);
}
}
--- /dev/null
+<?php
+
+namespace pq\Gateway;
+
+include __DIR__."/../../../setup.inc";
+
+/**
+ * Generated by PHPUnit_SkeletonGenerator 1.2.0 on 2013-03-05 at 16:08:03.
+ */
+class TableTest extends \PHPUnit_Framework_TestCase {
+
+ /**
+ * @var \pq\Connection
+ */
+ protected $conn;
+
+ /**
+ * @var Table
+ */
+ protected $object;
+
+ /**
+ * Sets up the fixture, for example, opens a network connection.
+ * This method is called before a test is executed.
+ */
+ protected function setUp() {
+ $this->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());
+ }
+
+}
--- /dev/null
+<?php
+
+define("PQ_DSN", "");
+define("PQ_TEST_TABLE_NAME", "test");
+define("PQ_TEST_CREATE_TABLE", sprintf(
+<<<SQL
+ create table %s (
+ id serial primary key,
+ created timestamp,
+ counter int,
+ number decimal,
+ data text
+ )
+SQL
+ , PQ_TEST_TABLE_NAME));
+define("PQ_TEST_DROP_TABLE", sprintf("drop table if exists %s", PQ_TEST_TABLE_NAME));
+define("PQ_TEST_CREATE_DATA", sprintf(
+<<<SQL
+ insert into %1\$s values (default, 'yesterday', -1, -1.1, 'yesterday');
+ insert into %1\$s values (default, 'today', 0, 0, 'today');
+ insert into %1\$s values (default, 'tomorrow', 1, 1.1, 'tomorrow');
+SQL
+ , PQ_TEST_TABLE_NAME
+));
+
+spl_autoload_register(function($c) {
+ if (substr($c,0,3) == "pq\\") return require_once sprintf("%s/../lib/%s.php", __DIR__, strtr($c, "\\", "/"));
+});