Skip to content

Commit

Permalink
Merge b2e4de1 into 8d8e36f
Browse files Browse the repository at this point in the history
  • Loading branch information
hrach committed Jul 3, 2015
2 parents 8d8e36f + b2e4de1 commit 01a1707
Show file tree
Hide file tree
Showing 50 changed files with 639 additions and 305 deletions.
2 changes: 1 addition & 1 deletion src/Bridges/NetteDI/OrmExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ protected function setupMetadataStorage(array $repositoryConfig)
$builder->addDefinition($this->prefix('metadataStorage'))
->setClass('Nextras\Orm\Model\MetadataStorage')
->setArguments([
'entityClasses' => array_keys($repositoryConfig[2]),
'entityClassesMap' => $repositoryConfig[2],
'repositoryLoader' => '@' . $this->prefix('repositoryLoader'),
]);
}
Expand Down
14 changes: 11 additions & 3 deletions src/Collection/Helpers/ConditionParserHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,21 @@ class ConditionParserHelper

public static function parseCondition($condition)
{
if (!preg_match('#^(this((?:->\w+)+)|\w+)(!|!=|<=|>=|=|>|<)?$#', $condition, $matches)) {
if (!preg_match('#^([\w\\\]+(?:->\w+)*)(!|!=|<=|>=|=|>|<)?$#', $condition, $matches)) {
throw new InvalidArgumentException('Unsupported condition format.');
}

$source = NULL;
$tokens = explode('->', $matches[1]);
if (count($tokens) > 1) {
$source = array_shift($tokens);
$source = $source === 'this' ? NULL : $source;
}

return [
!empty($matches[2]) ? explode('->', substr($matches[2], 2)) : [$matches[1]],
isset($matches[3]) ? ($matches[3] === '!' ? '!=' : $matches[3]) : '=',
$tokens,
isset($matches[2]) ? ($matches[2] === '!' ? '!=' : $matches[2]) : '=',
$source
];
}

Expand Down
5 changes: 3 additions & 2 deletions src/Collection/ICollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ public function fetchAll();

/**
* Fetches all records like $key => $value pairs.
* @param string $key associative key
* @param string $value value
* @param string $key associative key
* @param string $value value
* @return array
*/
public function fetchPairs($key = NULL, $value = NULL);
Expand Down Expand Up @@ -132,4 +132,5 @@ public function getRelationshipMapper();
*/
public function countStored();


}
161 changes: 100 additions & 61 deletions src/Entity/Reflection/AnnotationParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ class AnnotationParser
/** @var array */
protected $primaryKey = [];

/** @var array */
protected $entityClassesMap;


public function __construct(array $entityClassesMap)
{
$this->entityClassesMap = $entityClassesMap;
}


public function parseMetadata($class, & $fileDependencies)
{
Expand Down Expand Up @@ -189,95 +198,119 @@ protected function processPropertyModifier(PropertyMetadata $property, array $ma

protected function parseOneHasOne(PropertyMetadata $property, array $args)
{
if (count($args) === 0) {
throw new InvalidStateException('Missing repository name for {1:1} relationship.');
}

$property->relationshipType = PropertyMetadata::RELATIONSHIP_ONE_HAS_ONE;
$property->relationshipRepository = $this->makeFQN(array_shift($args));
$property->relationshipProperty = $this->getPropertyNameSingular(array_shift($args));
$property->relationship = new PropertyRelationshipMetadata();
$property->relationship->type = PropertyRelationshipMetadata::ONE_HAS_ONE;
$property->container = 'Nextras\Orm\Relationships\OneHasOne';
$this->processRelationshipEntityProperty($args, TRUE, $property);
}


protected function parseOneHasOneDirected(PropertyMetadata $property, array $args)
{
if (count($args) === 0) {
throw new InvalidStateException('Missing repository name for {1:1d} relationship.');
}

$property->relationshipType = PropertyMetadata::RELATIONSHIP_ONE_HAS_ONE_DIRECTED;
$property->relationshipRepository = $this->makeFQN(array_shift($args));
$property->relationship = new PropertyRelationshipMetadata();
$property->relationship->type = PropertyRelationshipMetadata::ONE_HAS_ONE_DIRECTED;
$property->container = 'Nextras\Orm\Relationships\OneHasOneDirected';

if (count($args) === 2) {
$property->relationshipProperty = $this->getPropertyNameSingular(array_shift($args));
$property->relationshipIsMain = array_shift($args) === 'primary';
} else {
$arg = array_shift($args);
$property->relationshipProperty = $this->getPropertyNameSingular($arg === 'primary' ? NULL : $arg);
$property->relationshipIsMain = $arg === 'primary';
}
$this->processRelationshipEntityProperty($args, TRUE, $property);
$this->processRelationshipPrimary($args, $property);
}


protected function parseOneHasMany(PropertyMetadata $property, array $args)
{
if (count($args) === 0) {
throw new InvalidStateException('Missing repository name for {1:m} relationship.');
}

$arg = array_pop($args);
if (stripos($arg, 'order:') === 0) {
$property->args->relationship = ['order' => explode(',', substr($arg, 6)) + [1 => ICollection::ASC]];
} else {
$args[] = $arg;
}

$property->relationshipType = PropertyMetadata::RELATIONSHIP_ONE_HAS_MANY;
$property->relationshipRepository = $this->makeFQN(array_shift($args));
$property->relationshipProperty = $this->getPropertyNameSingular(array_shift($args));
$property->relationship = new PropertyRelationshipMetadata();
$property->relationship->type = PropertyRelationshipMetadata::ONE_HAS_MANY;
$property->container = 'Nextras\Orm\Relationships\OneHasMany';
$this->processRelationshipEntityProperty($args, TRUE, $property);
$this->processRelationshipOrder($args, $property);
}


protected function parseManyHasOne(PropertyMetadata $property, array $args)
{
if (count($args) === 0) {
throw new InvalidStateException('Missing repository name for {m:1} relationship.');
}

$property->relationshipType = PropertyMetadata::RELATIONSHIP_MANY_HAS_ONE;
$property->relationshipRepository = $this->makeFQN(array_shift($args));
$property->relationshipProperty = $this->getPropertyNamePlural(array_shift($args));
$property->relationship = new PropertyRelationshipMetadata();
$property->relationship->type = PropertyRelationshipMetadata::MANY_HAS_ONE;
$property->container = 'Nextras\Orm\Relationships\ManyHasOne';
$this->processRelationshipEntityProperty($args, FALSE, $property);
}


protected function parseManyHasMany(PropertyMetadata $property, array $args)
{
if (count($args) === 0) {
throw new InvalidStateException('Missing repository name for {m:n} relationship.');
$property->relationship = new PropertyRelationshipMetadata();
$property->relationship->type = PropertyRelationshipMetadata::MANY_HAS_MANY;
$property->container = 'Nextras\Orm\Relationships\ManyHasMany';
$this->processRelationshipEntityProperty($args, FALSE, $property);
$this->processRelationshipPrimary($args, $property);
$this->processRelationshipOrder($args, $property);
}


private function processRelationshipEntityProperty(array & $args, $useSingular, PropertyMetadata $propertyMetadata)
{
$class = array_shift($args);
if ($class === NULL) {
throw new InvalidStateException("Relationship in {$this->reflection->name}::{$propertyMetadata->name} has not defined target entity name.");
}

$property->relationshipType = PropertyMetadata::RELATIONSHIP_MANY_HAS_MANY;
$property->relationshipRepository = $this->makeFQN(array_shift($args));
$property->container = 'Nextras\Orm\Relationships\ManyHasMany';
if ($pos = strpos($class, '::')) {
$entity = $this->makeFQN(substr($class, 0, $pos));
if (!isset($this->entityClassesMap[$entity])) {
throw new InvalidStateException("Relationship in {$this->reflection->name}::{$propertyMetadata->name} points to uknonw entity.");
}
$repository = $this->entityClassesMap[$entity];
$property = substr($class, $pos + 3); // skip ::$

} elseif (!stripos($class, 'repository')) {
$entity = $this->makeFQN($class);
if (!isset($this->entityClassesMap[$entity])) {
throw new InvalidStateException("Relationship in {$this->reflection->name}::{$propertyMetadata->name} points to uknonw entity.");
}
$repository = $this->entityClassesMap[$entity];
$property = $useSingular
? $this->getPropertyNameSingular(NULL)
: $this->getPropertyNamePlural(NULL);

$arg = array_pop($args);
if (stripos($arg, 'order:') === 0) {
$property->args->relationship = ['order' => explode(',', substr($arg, 6)) + [1 => ICollection::ASC]];
} else {
$args[] = $arg;
$repository = $this->makeFQN($class);
if (!class_exists($repository)) {
throw new InvalidStateException("Relationship in {$this->reflection->name}::{$propertyMetadata->name} points to unknown repository.");
}
$entity = $repository::getEntityClassNames()[0];
$property = reset($args)[0] === '$' ? array_shift($args) : NULL;
$property = $useSingular
? $this->getPropertyNameSingular($property)
: $this->getPropertyNamePlural($property);
}

if (count($args) === 2) {
$property->relationshipProperty = $this->getPropertyNamePlural(array_shift($args));
$property->relationshipIsMain = array_shift($args) === 'primary';
$propertyMetadata->relationship->repository = $repository;
$propertyMetadata->relationship->entity = $entity;
$propertyMetadata->relationship->property = $property;
}


private function processRelationshipOrder(array & $args, PropertyMetadata $property)
{
$order = array_shift($args);
if ($order === NULL) {
return;
}

if (stripos($order, 'order:') === FALSE) {
throw new InvalidStateException("Relationship definition in {$this->reflection->name}::{$property->name} is expected to have order expression.");
}

$property->relationship->order = explode(',', substr($order, 6)) + [1 => ICollection::ASC];
}


private function processRelationshipPrimary(array & $args, PropertyMetadata $property)
{
$index = array_search('primary', $args, TRUE);
if ($index !== FALSE) {
$property->relationship->isMain = TRUE;
unset($args[$index]);
} else {
$arg = array_shift($args);
$property->relationshipProperty = $this->getPropertyNamePlural($arg === 'primary' ? NULL : $arg);
$property->relationshipIsMain = $arg === 'primary';
$property->relationship->isMain = FALSE;
}
}

Expand Down Expand Up @@ -308,11 +341,15 @@ protected function parseEnum(PropertyMetadata $property, array $args)
}
}
if ($count === 0) {
throw new InvalidArgumentException("No constant matching {$classReflection->name}::{$const} pattern required by enum macro in {$this->reflection->name}::\${$property->name} found.");
throw new InvalidArgumentException(
"No constant matching {$classReflection->name}::{$const} pattern required by enum macro in {$this->reflection->name}::\${$property->name} found."
);
}
} else {
if (!array_key_exists($const, $constants)) {
throw new InvalidArgumentException("Constant {$classReflection->name}::{$const} required by enum macro in {$this->reflection->name}::\${$property->name} not found.");
throw new InvalidArgumentException(
"Constant {$classReflection->name}::{$const} required by enum macro in {$this->reflection->name}::\${$property->name} not found."
);
}
$value = $classReflection->getConstant($const);
$enumValues[$value] = $value;
Expand Down Expand Up @@ -384,7 +421,9 @@ protected function parseLiteral($literal, PropertyMetadata $property)
$classReflection = new ReflectionClass($className);
$constants = $classReflection->getConstants();
if (!array_key_exists($const, $constants)) {
throw new InvalidArgumentException("Constant {$classReflection->name}::{$const} required by default macro in {$this->reflection->name}::\${$property->name} not found.");
throw new InvalidArgumentException(
"Constant {$classReflection->name}::{$const} required by default macro in {$this->reflection->name}::\${$property->name} not found."
);
}
return $constants[$const];

Expand Down
48 changes: 25 additions & 23 deletions src/Entity/Reflection/MetadataValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,47 +25,49 @@ class MetadataValidator extends Object
public function validate(array $metadata, IRepositoryLoader $repositoryLoader)
{
$pairs = [
PropertyMetadata::RELATIONSHIP_MANY_HAS_MANY => PropertyMetadata::RELATIONSHIP_MANY_HAS_MANY,
PropertyMetadata::RELATIONSHIP_MANY_HAS_ONE => PropertyMetadata::RELATIONSHIP_ONE_HAS_MANY,
PropertyMetadata::RELATIONSHIP_ONE_HAS_MANY => PropertyMetadata::RELATIONSHIP_MANY_HAS_ONE,
PropertyMetadata::RELATIONSHIP_ONE_HAS_ONE => PropertyMetadata::RELATIONSHIP_ONE_HAS_ONE,
PropertyMetadata::RELATIONSHIP_ONE_HAS_ONE_DIRECTED => PropertyMetadata::RELATIONSHIP_ONE_HAS_ONE_DIRECTED,
PropertyRelationshipMetadata::MANY_HAS_MANY => PropertyRelationshipMetadata::MANY_HAS_MANY,
PropertyRelationshipMetadata::MANY_HAS_ONE => PropertyRelationshipMetadata::ONE_HAS_MANY,
PropertyRelationshipMetadata::ONE_HAS_MANY => PropertyRelationshipMetadata::MANY_HAS_ONE,
PropertyRelationshipMetadata::ONE_HAS_ONE => PropertyRelationshipMetadata::ONE_HAS_ONE,
PropertyRelationshipMetadata::ONE_HAS_ONE_DIRECTED => PropertyRelationshipMetadata::ONE_HAS_ONE_DIRECTED,
];

foreach ($metadata as $entityMeta) {
foreach ($entityMeta->getProperties() as $propertyMeta) {
if (!$propertyMeta->relationshipType) continue;
if (!$propertyMeta->relationship) {
continue;
}

$repositoryName = $propertyMeta->relationshipRepository;
$repositoryName = $propertyMeta->relationship->repository;
if (!$repositoryLoader->hasRepository($repositoryName)) {
throw new InvalidStateException("{$entityMeta->className}::\${$propertyMeta->name} points to unknown '{$propertyMeta->relationshipRepository}' repository.");
throw new InvalidStateException("{$entityMeta->className}::\${$propertyMeta->name} points to unknown '{$propertyMeta->relationship->repository}' repository.");
}

$symetricEntityMeta = $metadata[$repositoryName::getEntityClassNames()[0]];
$symetricEntityMeta = $metadata[$propertyMeta->relationship->entity];

if (!$symetricEntityMeta->hasProperty($propertyMeta->relationshipProperty)) {
throw new InvalidStateException("{$entityMeta->className}::\${$propertyMeta->name} has not defined a symetric relationship in {$symetricEntityMeta->className}::\${$propertyMeta->relationshipProperty}.");
if (!$symetricEntityMeta->hasProperty($propertyMeta->relationship->property)) {
throw new InvalidStateException("{$entityMeta->className}::\${$propertyMeta->name} has not defined a symetric relationship in {$symetricEntityMeta->className}::\${$propertyMeta->relationship->property}.");
}

/** @var PropertyMetadata $symetricPropertyMeta */
$symetricPropertyMeta = $symetricEntityMeta->getProperty($propertyMeta->relationshipProperty);
if ($propertyMeta->name !== $symetricPropertyMeta->relationshipProperty) {
throw new InvalidStateException("{$entityMeta->className}::\${$propertyMeta->name} relationship with {$symetricEntityMeta->className}::\${$propertyMeta->relationshipProperty} is not symetric.");
$symetricPropertyMeta = $symetricEntityMeta->getProperty($propertyMeta->relationship->property);
if ($symetricPropertyMeta->relationship === NULL) {
throw new InvalidStateException("{$entityMeta->className}::\${$propertyMeta->name} has not defined a symetric relationship in {$symetricEntityMeta->className}::\${$propertyMeta->relationship->property}.");
}

if ($symetricPropertyMeta->relationshipType === NULL) {
throw new InvalidStateException("{$entityMeta->className}::\${$propertyMeta->name} has not defined a symetric relationship in {$symetricEntityMeta->className}::\${$propertyMeta->relationshipProperty}.");
if ($propertyMeta->name !== $symetricPropertyMeta->relationship->property) {
throw new InvalidStateException("{$entityMeta->className}::\${$propertyMeta->name} relationship with {$symetricEntityMeta->className}::\${$propertyMeta->relationship->property} is not symetric.");
}

if ($symetricPropertyMeta->relationshipType !== $pairs[$propertyMeta->relationshipType]) {
throw new InvalidStateException("{$entityMeta->className}::\${$propertyMeta->name} has not defined a propper reverse relationship type in {$symetricEntityMeta->className}::\${$propertyMeta->relationshipProperty}.");
if ($symetricPropertyMeta->relationship->type !== $pairs[$propertyMeta->relationship->type]) {
throw new InvalidStateException("{$entityMeta->className}::\${$propertyMeta->name} has not defined a propper reverse relationship type in {$symetricEntityMeta->className}::\${$propertyMeta->relationship->property}.");
}

if ($propertyMeta->relationshipType === PropertyMetadata::RELATIONSHIP_MANY_HAS_MANY || $propertyMeta->relationshipType === PropertyMetadata::RELATIONSHIP_ONE_HAS_ONE_DIRECTED) {
if ($propertyMeta->relationshipIsMain && $symetricPropertyMeta->relationshipIsMain) {
throw new InvalidStateException("Only one side of relationship {$entityMeta->className}::\${$propertyMeta->name} × {$symetricEntityMeta->className}::\${$propertyMeta->relationshipProperty} could be defined as a primary.");
} elseif (!$propertyMeta->relationshipIsMain && !$symetricPropertyMeta->relationshipIsMain) {
throw new InvalidStateException("At least one side of relationship {$entityMeta->className}::\${$propertyMeta->name} × {$symetricEntityMeta->className}::\${$propertyMeta->relationshipProperty} has to be defined as a primary.");
if ($propertyMeta->relationship->type === PropertyRelationshipMetadata::MANY_HAS_MANY || $propertyMeta->relationship->type === PropertyRelationshipMetadata::ONE_HAS_ONE_DIRECTED) {
if ($propertyMeta->relationship->isMain && $symetricPropertyMeta->relationship->isMain) {
throw new InvalidStateException("Only one side of relationship {$entityMeta->className}::\${$propertyMeta->name} × {$symetricEntityMeta->className}::\${$propertyMeta->relationship->property} could be defined as a primary.");
} elseif (!$propertyMeta->relationship->isMain && !$symetricPropertyMeta->relationship->isMain) {
throw new InvalidStateException("At least one side of relationship {$entityMeta->className}::\${$propertyMeta->name} × {$symetricEntityMeta->className}::\${$propertyMeta->relationship->property} has to be defined as a primary.");
}
}
}
Expand Down
31 changes: 13 additions & 18 deletions src/Entity/Reflection/PropertyMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ class PropertyMetadata extends Object
const WRITE = 2;
const READWRITE = 3;

/** @const int Relationship types */
const RELATIONSHIP_ONE_HAS_ONE = 1;
const RELATIONSHIP_ONE_HAS_ONE_DIRECTED = 2;
const RELATIONSHIP_ONE_HAS_MANY = 3;
const RELATIONSHIP_MANY_HAS_ONE = 4;
const RELATIONSHIP_MANY_HAS_MANY = 5;
/** @deprecated */
const RELATIONSHIP_ONE_HAS_ONE_DIRECTED = PropertyRelationshipMetadata::ONE_HAS_ONE_DIRECTED;
/** @deprecated */
const RELATIONSHIP_ONE_HAS_MANY = PropertyRelationshipMetadata::ONE_HAS_MANY;
/** @deprecated */
const RELATIONSHIP_MANY_HAS_ONE = PropertyRelationshipMetadata::MANY_HAS_ONE;
/** @deprecated */
const RELATIONSHIP_MANY_HAS_MANY = PropertyRelationshipMetadata::MANY_HAS_MANY;
/** @deprecated */
const RELATIONSHIP_ONE_HAS_ONE = PropertyRelationshipMetadata::ONE_HAS_ONE;

/** @var string property name */
public $name;
Expand Down Expand Up @@ -61,21 +65,12 @@ class PropertyMetadata extends Object
/** @var mixed */
public $defaultValue;

/** @var PropertyRelationshipMetadata|NULL */
public $relationship;

/** @var stdClass */
public $args;

/** @var string */
public $relationshipRepository;

/** @var string */
public $relationshipProperty;

/** @var bool */
public $relationshipIsMain = FALSE;

/** @var int */
public $relationshipType;

/** @var mixed[] */
public $enum;

Expand Down

0 comments on commit 01a1707

Please sign in to comment.