Skip to content

Commit

Permalink
Added Fluent::<get|set>RelatedKeys (closes #30)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tharos committed Apr 16, 2014
1 parent afc7f9e commit 567f760
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 51 deletions.
6 changes: 3 additions & 3 deletions LeanMapper/Entity.php
Expand Up @@ -151,7 +151,7 @@ public function __get($name /*, array $filterArgs*/)
try {
$value = $this->row->$column;
} catch (LeanMapperException $e) {
throw new LeanMapperException("Cannot get value of property '{$property->getName()}' in entity " . get_called_class() . ' due to low-level failure: ' . lcfirst($e->getMessage()));
throw new LeanMapperException("Cannot get value of property '{$property->getName()}' in entity " . get_called_class() . ' due to low-level failure: ' . $e->getMessage());
}
if ($pass !== null) {
$value = $this->$pass($value);
Expand Down Expand Up @@ -208,7 +208,7 @@ public function __get($name /*, array $filterArgs*/)
try {
$value = $this->row->$column;
} catch (LeanMapperException $e) {
throw new LeanMapperException("Cannot get value of property '{$property->getName()}' in entity " . get_called_class() . ' due to low-level failure: ' . lcfirst($e->getMessage()));
throw new LeanMapperException("Cannot get value of property '{$property->getName()}' in entity " . get_called_class() . ' due to low-level failure: ' . $e->getMessage());
}
if ($pass !== null) {
$value = $this->$pass($value);
Expand Down Expand Up @@ -623,7 +623,7 @@ protected function getValueByPropertyWithRelationship($property, Filtering $targ
try {
return $this->$method($property, $relationship, $targetTableFiltering, $relationshipTableFiltering);
} catch (Exception $e) {
throw new LeanMapperException("Cannot get value of property '{$property->getName()}' in entity " . get_called_class() . ' due to low-level failure: ' . lcfirst($e->getMessage()));
throw new LeanMapperException("Cannot get value of property '{$property->getName()}' in entity " . get_called_class() . ' due to low-level failure: ' . $e->getMessage());
}
}

Expand Down
27 changes: 27 additions & 0 deletions LeanMapper/Fluent.php
Expand Up @@ -13,6 +13,7 @@

use Closure;
use DibiFluent;
use LeanMapper\Exception\InvalidArgumentException;

/**
* DibiFluent with filter support
Expand All @@ -22,6 +23,10 @@
class Fluent extends DibiFluent
{

/** @var array */
private $relatedKeys;


/**
* Applies given filter to current statement
*
Expand Down Expand Up @@ -61,4 +66,26 @@ public function _export($clause = null, $args = null)
return parent::_export($clause, $args);
}

/**
* @return array|null
*/
public function getRelatedKeys()
{
return $this->relatedKeys;
}

/**
* @param array|null $keys
* @return self
* @throws InvalidArgumentException
*/
public function setRelatedKeys($keys)
{
if (!is_array($keys) and $keys !== null) {
throw new InvalidArgumentException('Invalid related keys given. Expected array or null, ' . gettype($keys) . ' given.');
}
$this->relatedKeys = $keys;
return $this;
}

}
101 changes: 55 additions & 46 deletions LeanMapper/Result.php
Expand Up @@ -628,20 +628,23 @@ private function getReferencedResult($table, $viaColumn, Filtering $filtering =
if (!isset($this->referenced[$key])) {
$data = array();
if ($ids = $this->extractIds($viaColumn)) {
$data = $this->createTableSelection($table)->where('%n.%n IN %in', $table, $primaryKey, $ids)
$data = $this->createTableSelection($table, $ids)->where('%n.%n IN %in', $table, $primaryKey, $ids)
->fetchAll();
}
$this->referenced[$key] = self::createInstance($data, $table, $this->connection, $this->mapper, $key);
}
} else {
$statement = $this->createTableSelection($table)->where('%n.%n IN %in', $table, $primaryKey, $this->extractIds($viaColumn));
$this->applyFiltering($statement, $filtering);
$args = $statement->_export();
$key .= '#' . $this->calculateArgumentsHash($args);
return $this->referenced[$key];
}

if (!isset($this->referenced[$key])) {
$this->referenced[$key] = self::createInstance($this->connection->query($args)->fetchAll(), $table, $this->connection, $this->mapper, $key);
}
// $filtering !== null
$ids = $this->extractIds($viaColumn);
$statement = $this->createTableSelection($table, $ids)->where('%n.%n IN %in', $table, $primaryKey, $ids);
$this->applyFiltering($statement, $filtering);
$args = $statement->_export();
$key .= '#' . $this->calculateArgumentsHash($args);

if (!isset($this->referenced[$key])) {
$this->referenced[$key] = self::createInstance($this->connection->query($args)->fetchAll(), $table, $this->connection, $this->mapper, $key);
}
return $this->referenced[$key];
}
Expand Down Expand Up @@ -685,20 +688,22 @@ private function getReferencingResult($table, $viaColumn = null, Filtering $filt
if ($strategy === self::STRATEGY_IN) {
if ($filtering === null) {
if (!isset($this->referencing[$key])) {
$statement = $this->createTableSelection($table);
$ids = $this->extractIds($primaryKey);
$statement = $this->createTableSelection($table, $ids);
if ($this->isAlias($viaColumn)) {
$statement->where('%n IN %in', $this->trimAlias($viaColumn), $this->extractIds($primaryKey));
$statement->where('%n IN %in', $this->trimAlias($viaColumn), $ids);
} else {
$statement->where('%n.%n IN %in', $table, $viaColumn, $this->extractIds($primaryKey));
$statement->where('%n.%n IN %in', $table, $viaColumn, $ids);
}
$this->referencing[$key] = self::createInstance($statement->fetchAll(), $table, $this->connection, $this->mapper, $key);
}
} else {
$statement = $this->createTableSelection($table);
$ids = $this->extractIds($primaryKey);
$statement = $this->createTableSelection($table, $ids);
if ($this->isAlias($viaColumn)) {
$statement->where('%n IN %in', $this->trimAlias($viaColumn), $this->extractIds($primaryKey));
$statement->where('%n IN %in', $this->trimAlias($viaColumn), $ids);
} else {
$statement->where('%n.%n IN %in', $table, $viaColumn, $this->extractIds($primaryKey));
$statement->where('%n.%n IN %in', $table, $viaColumn, $ids);
}
$this->applyFiltering($statement, $filtering);
$args = $statement->_export();
Expand All @@ -708,38 +713,40 @@ private function getReferencingResult($table, $viaColumn = null, Filtering $filt
$this->referencing[$key] = self::createInstance($this->connection->query($args)->fetchAll(), $table, $this->connection, $this->mapper, $key);
}
}
} else { // self::STRATEGY_UNION
if ($filtering === null) {
if (!isset($this->referencing[$key])) {
$ids = $this->extractIds($primaryKey);
if (count($ids) === 0) {
$data = array();
} else {
$data = $this->connection->query(
$this->buildUnionStrategySql($ids, $table, $viaColumn)
)->fetchAll();
}
$this->referencing[$key] = self::createInstance($data, $table, $this->connection, $this->mapper, $key);
}
} else {
return $this->referencing[$key];
}

// $strategy === self::STRATEGY_UNION
if ($filtering === null) {
if (!isset($this->referencing[$key])) {
$ids = $this->extractIds($primaryKey);
if (count($ids) === 0) {
$this->referencing[$key] = self::createInstance(array(), $table, $this->connection, $this->mapper, $key);
$data = array();
} else {
$firstStatement = $this->createTableSelection($table);
if ($this->isAlias($viaColumn)) {
$firstStatement->where('%n = ?', $this->trimAlias($viaColumn), reset($ids));
} else {
$firstStatement->where('%n.%n = ?', $table, $viaColumn, reset($ids));
}
$this->applyFiltering($firstStatement, $filtering);
$args = $firstStatement->_export();
$key .= '#' . $this->calculateArgumentsHash($args);
$data = $this->connection->query(
$this->buildUnionStrategySql($ids, $table, $viaColumn)
)->fetchAll();
}
$this->referencing[$key] = self::createInstance($data, $table, $this->connection, $this->mapper, $key);
}
} else {
$ids = $this->extractIds($primaryKey);
if (count($ids) === 0) {
$this->referencing[$key] = self::createInstance(array(), $table, $this->connection, $this->mapper, $key);
} else {
$firstStatement = $this->createTableSelection($table, array(reset($ids)));
if ($this->isAlias($viaColumn)) {
$firstStatement->where('%n = ?', $this->trimAlias($viaColumn), reset($ids));
} else {
$firstStatement->where('%n.%n = ?', $table, $viaColumn, reset($ids));
}
$this->applyFiltering($firstStatement, $filtering);
$args = $firstStatement->_export();
$key .= '#' . $this->calculateArgumentsHash($args);

if (!isset($this->referencing[$key])) {
$sql = $this->buildUnionStrategySql($ids, $table, $viaColumn, $filtering);
$this->referencing[$key] = self::createInstance($this->connection->query($sql)->fetchAll(), $table, $this->connection, $this->mapper, $key);
}
if (!isset($this->referencing[$key])) {
$sql = $this->buildUnionStrategySql($ids, $table, $viaColumn, $filtering);
$this->referencing[$key] = self::createInstance($this->connection->query($sql)->fetchAll(), $table, $this->connection, $this->mapper, $key);
}
}
}
Expand Down Expand Up @@ -793,7 +800,7 @@ private function buildUnionStrategySql(array $ids, $table, $viaColumn, Filtering
$viaColumn = $this->trimAlias($viaColumn);
}
foreach ($ids as $id) {
$statement = $this->createTableSelection($table);
$statement = $this->createTableSelection($table, array($id));
if ($isAlias) {
$statement->where('%n = ?', $viaColumn, $id);
} else {
Expand Down Expand Up @@ -822,11 +829,13 @@ private function buildUnionStrategySql(array $ids, $table, $viaColumn, Filtering

/**
* @param string $table
* @param array $relatedKeys
* @return Fluent
*/
private function createTableSelection($table)
private function createTableSelection($table, $relatedKeys = null)
{
return $this->connection->select('%n.*', $table)->from('%n', $table);
$selection = $this->connection->select('%n.*', $table)->from('%n', $table);
return $relatedKeys !== null ? $selection->setRelatedKeys($relatedKeys) : $selection;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions tests/LeanMapper/Entity.construct.defaults.phpt
Expand Up @@ -47,8 +47,8 @@ Assert::equal('Default name', $book->name);

Assert::exception(function () use ($book) {
$book->id;
}, 'LeanMapper\Exception\Exception', "Cannot get value of property 'id' in entity Book due to low-level failure: missing 'id' column in row with id -1.");
}, 'LeanMapper\Exception\Exception', "Cannot get value of property 'id' in entity Book due to low-level failure: Missing 'id' column in row with id -1.");

Assert::exception(function () use ($book) {
$book->getData();
}, 'LeanMapper\Exception\Exception', "Cannot get value of property 'id' in entity Book due to low-level failure: missing 'id' column in row with id -1.");
}, 'LeanMapper\Exception\Exception', "Cannot get value of property 'id' in entity Book due to low-level failure: Missing 'id' column in row with id -1.");
71 changes: 71 additions & 0 deletions tests/LeanMapper/Entity.relatedKeys.phpt
@@ -0,0 +1,71 @@
<?php


use LeanMapper\Connection;
use LeanMapper\Entity;
use LeanMapper\Fluent;
use LeanMapper\Reflection\Property;
use Tester\Assert;

require_once __DIR__ . '/../bootstrap.php';

$connection->onEvent[] = function ($event) use (&$queries, &$i) {
$queries[] = $event->sql;
};

//////////

/**
* @property int $id
* @property Book[] $books m:belongsToMany m:filter(test)
* @property Book[] $unionBooks m:belongsToMany(#union) m:filter(test)
*/
class Author extends Entity
{
}

/**
* @property int $id
*/
class Book extends Entity
{
}

class AuthorRepository extends LeanMapper\Repository
{

public function findAll()
{
return $this->createEntities(
$this->createFluent()->fetchAll()
);
}

}

////////////////////
////////////////////

$connection->registerFilter('test', function (Fluent $statement, Property $property) {
$ids = $statement->getRelatedKeys();
$statement->removeClause('where');
if ($property->getName() === 'books') {
$statement->where('%n.%n IN %in', 'book', 'author_id', $ids);
} else {
$statement->where('%n.%n = %i', 'book', 'author_id', reset($ids));
}
}, Connection::WIRE_PROPERTY);

$authorRepository = new AuthorRepository($connection, $mapper, $entityFactory);

$authors = $authorRepository->findAll();
$author = reset($authors);

$author->books;
$author->unionBooks;

Assert::equal(array(
'SELECT [author].* FROM [author]',
'SELECT [book].* FROM [book] WHERE [book].[author_id] IN (1, 2, 3, 4, 5)',
'SELECT [book].* FROM [book] WHERE [book].[author_id] = 1 UNION SELECT [book].* FROM [book] WHERE [book].[author_id] = 2 UNION SELECT [book].* FROM [book] WHERE [book].[author_id] = 3 UNION SELECT [book].* FROM [book] WHERE [book].[author_id] = 4 UNION SELECT [book].* FROM [book] WHERE [book].[author_id] = 5'
), $queries);

0 comments on commit 567f760

Please sign in to comment.