use pq\Gateway\Row;
use pq\Gateway\Rowset;
use pq\Gateway\Table;
+use pq\Gateway\Table\Reference;
use pq\Query\Expr;
class Map implements MapInterface
{
+ /**
+ * @var string
+ */
private $class;
+
+ /**
+ * @var Table
+ */
private $gateway;
+
+ /**
+ * @var ObjectManager
+ */
private $objects;
+
+ /**
+ * @var PropertyInterface[]
+ */
private $properties;
+ /**
+ * Create a new object map definition
+ * @param string $class
+ * @param Table $gateway
+ * @param ...PropertyInterface $properties
+ */
function __construct($class, Table $gateway, PropertyInterface ...$properties) {
$this->class = $class;
$this->gateway = $gateway;
$this->objects = new ObjectManager($this);
}
+ /**
+ * Get the name of the mapped class
+ * @return string
+ */
function getClass() {
return $this->class;
}
+ /**
+ * Get the object manager
+ * @return ObjectManager
+ */
function getObjects() {
return $this->objects;
}
/**
+ * Get the underlying table gateway
* @return Table
*/
function getGateway() {
return $this->gateway;
}
+ /**
+ * Get the defined properties to map
+ * @return PropertyInterface[]
+ */
function getProperties() {
return $this->properties;
}
+ /**
+ * Add a property to map
+ * @param PropertyInterface $property
+ * @return Map
+ */
function addProperty(PropertyInterface $property) {
$property->setContainer($this);
$this->properties[] = $property;
return $this;
}
+ /**
+ * Get all child rows by foreign key
+ * @param Row $row
+ * @param string $refName
+ * @param array $objects
+ * @return Rowset
+ */
function allOf(Row $row, $refName, &$objects = null) {
/* apply objectOf to populate the object cache */
return $this->gateway->of($row, $refName)->apply(function($row) use(&$objects) {
});
}
+ /**
+ * Get the parent row by foreign key
+ * @param Row $row
+ * @param string $refName
+ * @param array $objects
+ * @return Rowset
+ */
function refOf(Row $row, $refName, &$objects = null) {
$rid = [];
$rel = $row->getTable()->getRelation($this->gateway->getName(), $refName);
});
}
+ /**
+ * Get the table relation reference
+ * @param MapInterface $map
+ * @param string $refName
+ * @return Reference
+ */
function relOf(MapInterface $map, $refName) {
return $map->getGateway()->getRelation(
$this->gateway->getName(), $refName);
}
+ /**
+ * Drain the deferred callback queue
+ * @param callable[] $deferred
+ * @param callable $exec
+ */
private function drain(array $deferred, callable $exec) {
while ($deferred) {
$cb = array_shift($deferred);
}
}
+ /**
+ * Map a row to an object
+ * @param Row $row
+ * @return object
+ */
function map(Row $row) {
$deferred = [];
$object = $this->objects->asObject($row);
return $object;
}
+ /**
+ * Map a rowset to an array of objects
+ * @param Rowset $rows
+ * @return object[]
+ */
function mapAll(Rowset $rows) {
$objects = [];
foreach ($rows as $row) {
return $objects;
}
+ /**
+ * Unmap on object
+ * @param object $object
+ */
function unmap($object) {
$deferred = [];
/* @var $row Row */
$row->update();
}
}
-
-}
\ No newline at end of file
+}
interface MapInterface
{
/**
+ * Get the mapped class' name
* @return string
*/
function getClass();
/**
+ * The the underlying table gateway
* @return Table
*/
function getGateway();
/**
- * @return array of PropertyInterface instances
+ * Get the mapped properties
+ * @return PropertyInterface[]
*/
function getProperties();
/**
+ * Add a property to map
* @param PropertyInterface $property
*/
function addProperty(PropertyInterface $property);
/**
+ * Get all child rows by foreign key
* @param Row $row
* @param string $refName
* @param array $objects
function allOf(Row $row, $refName, &$objects = null);
/**
+ * Get the parent row by foreign key
* @param Row $row
* @param string $refName
* @param array $objects
function refOf(Row $row, $refName, &$objects = null);
/**
+ * Get the table relation reference
* @param MapInterface $map origin
* @param string $refName relations reference name
- * @return array relation reference
+ * @return Table\Reference
*/
function relOf(MapInterface $map, $refName);
/**
+ * Map a row to an object
* @param Row $row
* @return object
*/
function map(Row $row);
/**
+ * Map a rowset to an array of objects
* @param Rowset $rows
- * @return array
+ * @return object[]
*/
function mapAll(Rowset $rows);
/**
+ * Unmap on object
* @param object $object
* @return Row
*/
namespace pq\Mapper;
+use pq\Mapper\Property\All;
+use pq\Mapper\Property\Field;
+use pq\Mapper\Property\Ref;
+use ReflectionProperty;
use UnexpectedValueException;
class Mapper
{
+ /**
+ * @var MapInterface[]
+ */
private $maps;
+
+ /**
+ * @var ReflectionProperty[]
+ */
private $refp;
/**
- * @param \pq\Mapper\MapInterface $map
- * @return \pq\Mapper\Mapper
+ * Register a mapping
+ * @param MapInterface $map
+ * @return Mapper
*/
function register(MapInterface $map) {
$this->maps[$map->getClass()] = $map;
return $this;
}
+ /**
+ * Get a property reflector
+ * @param string $class
+ * @param string $prop
+ * @return ReflectionProperty
+ */
function getReflector($class, $prop) {
if (is_object($class)) {
$class = get_class($class);
}
$hash = "$class::$prop";
if (!isset($this->refp[$hash])) {
- $this->refp[$hash] = new \ReflectionProperty($class, $prop);
+ $this->refp[$hash] = new ReflectionProperty($class, $prop);
$this->refp[$hash]->setAccessible(true);
}
return $this->refp[$hash];
}
/**
+ * Get the mapping of $class
* @param string $class
- * @return \pq\Mapper\MapInterface
+ * @return MapInterface
* @throws UnexpectedValueException
*/
function mapOf($class) {
}
/**
+ * Create a storage for $class
* @param string $class
- * @return \pq\Mapper\Storage
+ * @return Storage
*/
function createStorage($class) {
return new Storage($this->mapOf($class));
}
/**
+ * Create a simple field mapping
* @param string $property
* @param string $field
- * @return \pq\Mapper\Property\Field
+ * @return Field
*/
function mapField($property, $field = null) {
- return new Property\Field($this, $property, $field);
+ return new Field($this, $property, $field);
}
/**
+ * Create a child rows mapping by foreign key
* @param string $property
- * @return \pq\Mapper\Property\All
+ * @return All
*/
function mapAll($property) {
- return new Property\All($this, $property);
+ return new All($this, $property);
}
/**
+ * Create a parent row mapping by foreign key
* @param string $property
- * @return \pq\Mapper\Property\Ref
+ * @return Ref
*/
function mapRef($property) {
- return new Property\Ref($this, $property);
+ return new Ref($this, $property);
}
}
class ObjectManager
{
+ /**
+ * @var MapInterface
+ */
private $map;
+
+ /**
+ * @var object[]
+ */
private $obj = [];
+
+ /**
+ * @var Row[]
+ */
private $row = [];
+ /**
+ * Create a new ObjectManager for a mapping
+ * @param MapInterface $map
+ */
function __construct(MapInterface $map) {
$this->map = $map;
}
+ /**
+ * Reset all managed objects
+ */
function reset() {
$this->obj = [];
$this->row = [];
}
+ /**
+ * Get the serialized row identity
+ *
+ * When $check is true, the identity will only be serialized if all columns
+ * of the primary key are set.
+ *
+ * @param Row $row
+ * @param bool $check
+ * @return string|false serialized row id or false on failure
+ */
function rowId(Row $row, $check = false) {
try {
$identity = $row->getIdentity();
return $this->serializeRowId($identity, $check);
}
+ /**
+ * Get an object's identity
+ * @param object $object
+ * @return string
+ */
function objectId($object) {
return spl_object_hash($object);
}
+ /**
+ * Extract a row's identity from a mapped object
+ * @param object $object
+ * @return string serialized row identity
+ */
function extractRowId($object) {
$id = [];
foreach ($this->map->getGateway()->getIdentity() as $col) {
return $this->serializeRowId($id, true);
}
+ /**
+ * Serialize a row's identity
+ * @param mixed $identity
+ * @param bool $check
+ * @return string|false the serialized row identity or false on failure
+ */
function serializeRowId($identity, $check = false) {
if (is_scalar($identity)) {
return $identity;
return json_encode($identity);
}
+ /**
+ * Check whether a mapped object is already cached in the manager
+ * @param string $row_id
+ * @return bool
+ */
function hasObject($row_id) {
return isset($this->obj[$row_id]);
}
+ /**
+ * Create a mapped object from $row
+ * @param Row $row
+ * @return object
+ */
function createObject(Row $row) {
$rid = $this->rowId($row);
$cls = $this->map->getClass();
return $obj;
}
+ /**
+ * Forget the mapped object of $row
+ * @param Row $row
+ */
function resetObject(Row $row) {
unset($this->obj[$this->rowId($row)]);
}
+ /**
+ * Get the mapped object of $row
+ * @param Row $row
+ * @return object
+ */
function getObject(Row $row) {
$id = $this->rowId($row);
return $this->getObjectById($id);
}
+ /**
+ * Get the mapped object of $row
+ * @param string $row_id
+ * @return object
+ * @throws BadMethodCallException
+ */
function getObjectById($row_id) {
if (!$this->hasObject($row_id)) {
throw new BadMethodCallException("Object of row with id $row_id does not exist");
return $this->obj[$row_id];
}
+ /**
+ * Check for a mapped object of $row, and create if necessary
+ * @param Row $row
+ * @return object
+ */
function asObject(Row $row){
return $this->hasObject($this->rowId($row))
? $this->getObject($row)
: $this->createObject($row);
}
+ /**
+ * Check whether a row for a mapped object exists
+ * @param string $obj_id
+ * @return Row
+ */
function hasRow($obj_id) {
return isset($this->row[$obj_id]);
}
+ /**
+ * Initialize a Row from a mapped object
+ * @param object $object
+ * @return Row
+ */
function createRow($object) {
$oid = $this->objectId($object);
$row = new Row($this->map->getGateway());
return $row;
}
+ /**
+ * Forget about a row of a mapped object
+ * @param object $object
+ */
function resetRow($object) {
unset($this->row [$this->objectId($object)]);
}
-
+
+ /**
+ * Get the row of a mapped object
+ * @param object $object
+ * @return Row
+ * @throws BadMethodCallException
+ */
function getRow($object) {
$id = $this->objectId($object);
return $this->row[$id];
}
+ /**
+ * Check for a row of a mapped object, create from object if neccessary
+ * @param object $object
+ * @return Row
+ */
function asRow($object) {
return $this->hasRow($this->objectId($object))
? $this->getRow($object)
: $this->createRow($object);
}
-}
\ No newline at end of file
+}
trait Property
{
+ /**
+ *
+ * @var Mapper
+ */
private $mapper;
+
+ /**
+ * @var string
+ */
private $field;
+
+ /**
+ * @var string
+ */
private $property;
+ /**
+ * Set the containing map
+ * @param MapInterface $container
+ * @return Property
+ */
function setContainer(MapInterface $container) {
$this->container = $container;
+ return $this;
}
+ /**
+ * Get the containing map
+ * @return MapInterface
+ */
function getContainer() {
return $this->container;
}
+ /**
+ * Get the property name
+ * @return string
+ */
function getProperty() {
return $this->property;
}
+ /**
+ * Check whether this Property defines $property
+ * @param string $property
+ * @return bool
+ */
function defines($property) {
return $this->property === $property;
}
+ /**
+ * Check whether this property exposes $field
+ * @param string $field
+ * @return bool
+ */
function exposes($field) {
return $this->field === $field;
}
+ /**
+ * Set the value of the mapped property
+ * @param object $object
+ * @param mixed $value
+ */
function assign($object, $value) {
$this->mapper
->getReflector($object, $this->property)
->setValue($object, $value);
}
+ /**
+ * Get the value of the mapped property
+ * @param object $object
+ * @return mixed
+ */
function extract($object) {
return $this->mapper
->getReflector($object, $this->property)
->getValue($object);
}
+ /**
+ * @ignore
+ */
function __toString() {
return sprintf("%s: %s(%s)", get_class($this), $this->property, $this->field?:"NULL");
}
-}
\ No newline at end of file
+}
class All implements RefPropertyInterface
{
use RefProperty;
-
+
+ /**
+ * Create a child rows mapping
+ * @param Mapper $mapper
+ * @param string $property
+ */
function __construct(Mapper $mapper, $property) {
$this->mapper = $mapper;
$this->property = $property;
}
-
+
+ /**
+ * Read the child objects
+ * @param Row $row
+ * @param object $objectToUpdate
+ */
function read(Row $row, $objectToUpdate) {
$val = $this->extract($objectToUpdate);
if (!isset($val)) {
}
}
+ /**
+ * Write the child rows
+ * @param object $object
+ * @param Row $rowToUpdate
+ * @return callable deferred callback
+ */
function write($object, Row $rowToUpdate) {
$property = $this->findRefProperty($object);
$map = $this->mapper->mapOf($this->refClass);
};
}
+ /**
+ * Find the referring property that references $object on our foreign key
+ * @param object $object
+ * @return RefPropertyInterface[]
+ * @throws UnexpectedValueException
+ */
private function findRefProperty($object) {
$map = $this->mapper->mapOf($this->refClass);
$property = array_filter($map->getProperties(), function($property) use($object) {
}
return current($property);
}
-}
\ No newline at end of file
+}
namespace pq\Mapper\Property;
+use pq\Gateway\Cell;
use pq\Gateway\Row;
-
use pq\Mapper\Mapper;
use pq\Mapper\Property;
use pq\Mapper\PropertyInterface;
{
use Property;
+ /**
+ * Create a simple field mapping
+ * @param Mapper $mapper
+ * @param string $property
+ * @param string $field
+ */
function __construct(Mapper $mapper, $property, $field = null) {
$this->mapper = $mapper;
$this->property = $property;
$this->field = $field ?: $property;
}
+ /**
+ * Read property value
+ * @param Row $row
+ * @param object $objectToUpdate
+ */
function read(Row $row, $objectToUpdate) {
- /* @var $val \pq\Gateway\Cell */
+ /* @var $val Cell */
$val = $row->{$this->field};
$this->assign($objectToUpdate, $val->get());
}
+ /**
+ * Write property value
+ * @param object $object
+ * @param Row $rowToUpdate
+ */
function write($object, Row $rowToUpdate) {
$val = $this->extract($object);
$rowToUpdate->{$this->field} = $val;
use pq\Gateway\Row;
use pq\Mapper\Mapper;
+use pq\Mapper\PropertyInterface;
use pq\Mapper\RefProperty;
use pq\Mapper\RefPropertyInterface;
use UnexpectedValueException;
class Ref implements RefPropertyInterface
{
use RefProperty;
-
+
+ /**
+ * Create a parent row mapping
+ * @param Mapper $mapper
+ * @param string $property
+ */
function __construct(Mapper $mapper, $property) {
$this->mapper = $mapper;
$this->property = $property;
}
+ /**
+ * Read the parent object
+ * @param Row $row
+ * @param object $objectToUpdate
+ */
function read(Row $row, $objectToUpdate) {
$val = $this->extract($objectToUpdate);
if (!isset($val)) {
}
}
+ /**
+ * Write the parent row's foreign key
+ * @param object $object
+ * @param Row $rowToUpdate
+ * @throws UnexpectedValueException
+ */
function write($object, Row $rowToUpdate) {
$map = $this->mapper->mapOf($this->refClass);
$ref = $this->extract($object);
}
}
+ /**
+ * Find the property exposing $col
+ * @param string $col
+ * @return PropertyInterface[]
+ */
private function findFieldProperty($col) {
$map = $this->mapper->mapOf($this->refClass);
return array_filter($map->getProperties(), function($property) use($col) {
interface PropertyInterface
{
+ /**
+ * Write the value for the property from $object into the row to update
+ * @param object $object
+ * @param Row $rowToUpdate
+ * @return null|callable eventual deferred callback
+ */
function write($object, Row $rowToUpdate);
+
+ /**
+ * Read the value for the property from $row into the mapped object
+ * @param Row $row
+ * @param object $objectToUpdate
+ * @return null|callable eventual deferred callback
+ */
function read(Row $row, $objectToUpdate);
+ /**
+ * Set the value of the mapped property
+ * @param object $object
+ * @param mixed $value
+ */
function assign($object, $value);
+
+ /**
+ * Get the value of the mapped property
+ * @param object $object
+ * @return mixed
+ */
function extract($object);
+ /**
+ * Get the property name
+ * @return string
+ */
function getProperty();
+ /**
+ * Get the containing map
+ * @return MapInterface
+ */
function getContainer();
+
+ /**
+ * Set the containing map
+ * @param MapInterface $container
+ * @return Property
+ */
function setContainer(MapInterface $container);
+ /**
+ * Check whether this Property defines $property
+ * @param string $property
+ * @return bool
+ */
function defines($property);
- function exposes($field);
+ /**
+ * Check whether this property exposes $field
+ * @param string $field
+ * @return bool
+ */
+ function exposes($field);
}
{
use Property;
+ /**
+ * The referred class
+ * @var string
+ */
private $refClass;
+
+ /**
+ * The foreign key name
+ * @var string
+ */
private $refName;
+ /**
+ * Define the referred class
+ * @param string $class
+ * @return RefPropertyInterface
+ */
function to($class) {
$this->refClass = $class;
return $this;
}
+ /**
+ * Check whether this mapping refers to $class
+ * @param string $class
+ * @return bool
+ */
function references($class) {
return $this->refClass === (is_object($class) ? get_class($class) : $class);
}
+ /**
+ * Define the foreign key name as defined by pq\Gateway\Table\Reference
+ * @param string $ref
+ * @return RefPropertyInterface
+ */
function by($ref) {
$this->refName = $ref;
return $this;
}
+ /**
+ * Check whether this mapping referes to a foreign key
+ * @param string $ref
+ * @return bool
+ */
function on($ref) {
return $this->refName === $ref;
}
-
}
interface RefPropertyInterface extends PropertyInterface
{
/**
+ * Define the referred class
* @param string $class
+ * @return RefPropertyInterface
*/
function to($class);
/**
+ * Check whether this mapping refers to $class
* @param string $class
* @return bool
*/
function references($class);
/**
+ * Define the foreign key name as defined by pq\Gateway\Table\Reference
* @param string $ref
+ * @return RefPropertyInterface
*/
function by($ref);
/**
+ * Check whether this mapping referes to a foreign key
* @param string $ref
* @return bool
*/
namespace pq\Mapper;
+use pq\Gateway\Table;
+
class Storage implements StorageInterface
{
/**
- *
- * @var pq\Mapper\MapInterface
+ * The mapping of this storage
+ * @var MapInterface
*/
- private $map;
+ var $map;
/**
- * @var \pq\Gateway\Table
+ * The underlying table gateway
+ * @var Table
*/
private $gateway;
-
+
+ /**
+ * Create a storage for $map
+ * @param MapInterface $map
+ */
function __construct(MapInterface $map) {
$this->map = $map;
$this->gateway = $map->getGateway();
}
-
+
+ /**
+ * Find
+ * @param array $where
+ * @param string $order
+ * @param int $limit
+ * @param int $offset
+ * @return object[]
+ */
function find($where = [], $order = null, $limit = null, $offset = null) {
/* @var pq\Gateway\Rowset $rowset */
$rowset = $this->gateway->find($where, $order, $limit, $offset);
return $this->map->mapAll($rowset);
}
-
+
+ /**
+ * Delete
+ * @param object $object
+ */
function delete($object) {
$cache = $this->map->getObjects();
$row = $cache->asRow($object)->delete();
$cache->resetObject($row);
$cache->resetRow($object);
}
-
+
+ /**
+ * Save
+ * @param object $object
+ */
function save($object) {
$this->map->unmap($object);
}
-
-}
\ No newline at end of file
+}
interface StorageInterface
{
- function find($where, $order = null, $limit = null, $offset = null);
+ /**
+ * Find
+ * @param array $where
+ * @param string $order
+ * @param int $limit
+ * @param int $offset
+ * @return object[]
+ */
+ function find($where = [], $order = null, $limit = null, $offset = null);
+
+ /**
+ * Delete
+ * @param object $object
+ */
function delete($object);
+
+ /**
+ * Save
+ * @param object $object
+ */
function save($object);
}