1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144:
<?php
namespace LeanMapperQuery;
use LeanMapper;
use LeanMapper\Filtering;
use LeanMapper\Fluent;
use LeanMapper\Reflection\Property;
use LeanMapper\Relationship;
use LeanMapper\Result;
use LeanMapperQuery\Caller;
use LeanMapperQuery\Exception\InvalidArgumentException;
use LeanMapperQuery\Exception\InvalidMethodCallException;
use LeanMapperQuery\Exception\InvalidRelationshipException;
use LeanMapperQuery\Exception\InvalidStateException;
use LeanMapperQuery\Exception\MemberAccessException;
use LeanMapperQuery\IQuery;
class Entity extends LeanMapper\Entity
{
protected static $magicMethodsPrefixes = [];
protected function queryProperty($field, IQuery $query)
{
return static::queryEntityProperty($this, $field, $query);
}
public static function queryEntityProperty(LeanMapper\Entity $entity, $field, IQuery $query)
{
if ($entity->isDetached()) {
throw new InvalidStateException('Cannot query detached entity.');
}
$property = $entity->getCurrentReflection()->getEntityProperty($field);
if ($property === NULL) {
throw new MemberAccessException("Cannot access undefined property '$field' in entity " . get_called_class() . '.');
}
if (!$property->hasRelationship()) {
throw new InvalidArgumentException("Property '{$property->getName()}' in entity ". get_called_class() . " has no relationship.");
}
$class = $property->getType();
$filters = $entity->createImplicitFilters($class, new Caller($entity, $property))->getFilters();
$mapper = $entity->mapper;
$filters[] = function (Fluent $fluent) use ($mapper, $query) {
$query->applyQuery($fluent, $mapper);
};
$relationship = $property->getRelationship();
if ($relationship instanceof Relationship\BelongsToMany) {
$targetTable = $relationship->getTargetTable();
$referencingColumn = $relationship->getColumnReferencingSourceTable();
$strategy = $relationship->getStrategy();
if ($query->junctionQueryNeeded()) {
$strategy = Result::STRATEGY_UNION;
}
$rows = $entity->row->referencing($targetTable, $referencingColumn, new Filtering($filters), $strategy);
} elseif ($relationship instanceof Relationship\HasMany) {
$relationshipTable = $relationship->getRelationshipTable();
$sourceReferencingColumn = $relationship->getColumnReferencingSourceTable();
$targetReferencingColumn = $relationship->getColumnReferencingTargetTable();
$targetTable = $relationship->getTargetTable();
$targetPrimaryKey = $mapper->getPrimaryKey($targetTable);
$rows = [];
$resultRows = [];
$targetResultProxy = NULL;
$relationshipFiltering = NULL;
$strategy = $relationship->getStrategy();
if ($query->junctionQueryNeeded()) {
$strategy = Result::STRATEGY_UNION;
$relationshipFiltering = new Filtering(function (Fluent $fluent) use ($mapper, $query, $relationship) {
$query->applyJunctionQuery($fluent, $mapper, $relationship);
});
$filters[] = function (Fluent $fluent) {
$fluent->removeClause('LIMIT');
$fluent->removeClause('OFFSET');
};
}
foreach ($entity->row->referencing($relationshipTable, $sourceReferencingColumn, $relationshipFiltering, $strategy) as $relationship) {
$row = $relationship->referenced($targetTable, $targetReferencingColumn, new Filtering($filters));
if ($row !== NULL && $targetResultProxy === NULL) {
$targetResultProxy = $row->getResultProxy();
}
$row !== NULL && $resultRows[$row->{$targetPrimaryKey}] = $row;
}
if ($targetResultProxy) {
foreach ($targetResultProxy as $rowId => $rowData) {
if (isset($resultRows[$rowId])) {
$rows[] = $resultRows[$rowId];
}
}
} else {
$rows = $resultRows;
}
} else {
throw new InvalidRelationshipException('Only BelongsToMany and HasMany relationships are supported when querying entity property. ' . get_class($relationship) . ' given.');
}
$entities = [];
$table = $mapper->getTable($class);
foreach ($rows as $row) {
$newEntity = $entity->entityFactory->createEntity($mapper->getEntityClass($table, $row), $row);
$newEntity->makeAlive($entity->entityFactory);
$entities[] = $newEntity;
}
return $entities;
}
public function __call($name, array $arguments)
{
if (preg_match('#^('.implode('|', static::$magicMethodsPrefixes).')(.+)$#', $name, $matches)) {
if (count($arguments) !== 1) {
throw new InvalidMethodCallException(get_called_class() . "::$name expects exactly 1 argument. " . count($arguments) . ' given.');
}
list($query) = $arguments;
if (!$query instanceof IQuery) {
throw new InvalidArgumentException('Argument 1 passed to ' . get_called_class() . "::$name must implement interface LeanMapperQuery\\IQuery. " . gettype($query) . ' given.');
}
list(, $method, $field) = $matches;
$field = lcfirst($field);
return $this->$method($field, $query);
} else {
return parent::__call($name, $arguments);
}
}
}