Skip to content

Commit

Permalink
embeddables: implement support for relationships
Browse files Browse the repository at this point in the history
- embeddables propagate PropertyMetadata with path
- recursively set parent entity to embeddables' properties
- implements path-style fetching of raw values
  • Loading branch information
hrach committed Sep 26, 2020
1 parent fdcd409 commit c7a15b8
Show file tree
Hide file tree
Showing 37 changed files with 420 additions and 131 deletions.
25 changes: 14 additions & 11 deletions src/Collection/EntityIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
use Nette\Utils\Arrays;
use Nextras\Orm\Entity\IEntity;
use Nextras\Orm\Entity\IEntityHasPreloadContainer;
use Nextras\Orm\Entity\Reflection\PropertyMetadata;
use Nextras\Orm\Exception\InvalidStateException;
use function assert;
use function count;
use function spl_object_hash;


/**
Expand Down Expand Up @@ -87,24 +91,23 @@ public function count(): int
}


public function getPreloadValues(string $property): array
public function getPreloadValues(PropertyMetadata $property): array
{
if (isset($this->preloadCache[$property])) {
return $this->preloadCache[$property];
$cacheKey = spl_object_hash($property);
if (isset($this->preloadCache[$cacheKey])) {
return $this->preloadCache[$cacheKey];
}

$values = [];
foreach ($this->iteratable as $entity) {
// property may not exist when using STI
if ($entity->getMetadata()->hasProperty($property)) {
// relationship may be already nulled in removed entity
$value = $entity->getRawValue($property);
if ($value !== null) {
$values[] = $value;
}
// $checkPropertyExistence = false - property may not exist when using STI
$value = $entity->getRawValue($property->path ?? $property->name, false);
// relationship may be already null-ed in removed entity
if ($value !== null) {
$values[] = $value;
}
}

return $this->preloadCache[$property] = $values;
return $this->preloadCache[$cacheKey] = $values;
}
}
7 changes: 5 additions & 2 deletions src/Collection/IEntityPreloadContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
namespace Nextras\Orm\Collection;


use Nextras\Orm\Entity\Reflection\PropertyMetadata;


interface IEntityPreloadContainer
{
/**
* Returns array of $property values for preloading.
* Returns array of values in $propertyMetadata position for preloading.
* @phpstan-return list<mixed>
*/
public function getPreloadValues(string $property): array;
public function getPreloadValues(PropertyMetadata $propertyMetadata): array;
}
25 changes: 14 additions & 11 deletions src/Collection/MultiEntityIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
use Nette\Utils\Arrays;
use Nextras\Orm\Entity\IEntity;
use Nextras\Orm\Entity\IEntityHasPreloadContainer;
use Nextras\Orm\Entity\Reflection\PropertyMetadata;
use Nextras\Orm\Exception\InvalidStateException;
use function assert;
use function count;
use function spl_object_hash;


/**
Expand Down Expand Up @@ -105,26 +109,25 @@ public function count(): int
}


public function getPreloadValues(string $property): array
public function getPreloadValues(PropertyMetadata $property): array
{
if (isset($this->preloadCache[$property])) {
return $this->preloadCache[$property];
$cacheKey = spl_object_hash($property);
if (isset($this->preloadCache[$cacheKey])) {
return $this->preloadCache[$cacheKey];
}

$values = [];
foreach ($this->data as $entities) {
foreach ($entities as $entity) {
// property may not exist when using STI
if ($entity->getMetadata()->hasProperty($property)) {
// relationship may be already nulled in removed entity
$value = $entity->getRawValue($property);
if ($value !== null) {
$values[] = $value;
}
// $checkPropertyExistence = false - property may not exist when using STI
$value = $entity->getRawValue($property->path ?? $property->name, false);
// relationship may be already null-ed in removed entity
if ($value !== null) {
$values[] = $value;
}
}
}

return $this->preloadCache[$property] = $values;
return $this->preloadCache[$cacheKey] = $values;
}
}
37 changes: 27 additions & 10 deletions src/Entity/AbstractEntity.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use Nextras\Orm\Relationships\IRelationshipCollection;
use Nextras\Orm\Relationships\IRelationshipContainer;
use Nextras\Orm\Repository\IRepository;
use function array_shift;
use function assert;
use function get_class;

Expand Down Expand Up @@ -130,23 +131,39 @@ public function setRawValue(string $name, $value): void
}


public function &getRawValue(string $name)
public function &getRawValue($name, bool $checkPropertyExistence = false)
{
$property = $this->metadata->getProperty($name);
$path = (array) $name;
$name = array_shift($path);

if (!$checkPropertyExistence && !$this->metadata->hasProperty($name)) {
$value = null;
return $value;
}

$propertyMetadata = $this->metadata->getProperty($name);

if (!isset($this->validated[$name])) {
$this->initProperty($property, $name);
$this->initProperty($propertyMetadata, $name);
}

$value = $this->data[$name];

if ($value instanceof IProperty) {
if (count($path) > 0) {
if (!$value instanceof IMultiPropertyPropertyContainer) {
throw new InvalidStateException("Path to raw value doesn't go through IMultiPropertyPropertyContainer property.");
}

$value = $value->getRawValueOf($path, $checkPropertyExistence);
return $value;

} elseif ($value instanceof IProperty) {
$value = $value->getRawValue();
return $value;
}

if ($property->isVirtual) {
$value = $this->internalGetValue($property, $name);
if ($propertyMetadata->isVirtual) {
$value = $this->internalGetValue($propertyMetadata, $name);
return $value;
}

Expand Down Expand Up @@ -229,18 +246,18 @@ public function __clone()
$this->data['id'] = null;
$this->persistedId = null;
$this->data[$name] = clone $this->data[$name];
$this->data[$name]->setPropertyEntity($this);
$this->data[$name]->onAttach($this, $metadataProperty);
$this->data[$name]->set($data);
$this->data['id'] = $id;
$this->persistedId = $persistedId;

} elseif ($this->data[$name] instanceof IRelationshipContainer) {
$this->data[$name] = clone $this->data[$name];
$this->data[$name]->setPropertyEntity($this);
$this->data[$name]->onAttach($this, $metadataProperty);

} elseif ($this->data[$name] instanceof EmbeddableContainer) {
$this->data[$name] = clone $this->data[$name];
$this->data[$name]->setPropertyEntity($this);
$this->data[$name]->onAttach($this, $metadataProperty);

} else {
$this->data[$name] = clone $this->data[$name];
Expand Down Expand Up @@ -499,7 +516,7 @@ private function createPropertyWrapper(PropertyMetadata $metadata): IProperty
assert($wrapper instanceof IProperty);

if ($wrapper instanceof IEntityAwareProperty) {
$wrapper->setPropertyEntity($this);
$wrapper->onAttach($this, $metadata);
}

return $wrapper;
Expand Down
58 changes: 51 additions & 7 deletions src/Entity/Embeddable/Embeddable.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Nextras\Orm\Entity\IEntity;
use Nextras\Orm\Entity\IEntityAwareProperty;
use Nextras\Orm\Entity\ImmutableDataTrait;
use Nextras\Orm\Entity\IMultiPropertyPropertyContainer;
use Nextras\Orm\Entity\IProperty;
use Nextras\Orm\Entity\IPropertyContainer;
use Nextras\Orm\Entity\Reflection\PropertyMetadata;
Expand All @@ -27,6 +28,9 @@ abstract class Embeddable implements IEmbeddable
/** @var IEntity|null */
protected $parentEntity;

/** @var PropertyMetadata */
protected $propertyMetadata;


/**
* @param array<string, mixed>|null $data
Expand Down Expand Up @@ -73,9 +77,53 @@ public function getRawValue(): array
}


public function onAttach(IEntity $entity): void
public function &getRawValueOf(array $path, bool $checkPropertyExistence = true)
{
$name = array_shift($path);

if (!$checkPropertyExistence && !$this->metadata->hasProperty($name)) {
$value = null;
return $value;
}

$propertyMetadata = $this->metadata->getProperty($name);

if (!isset($this->validated[$name])) {
$this->initProperty($propertyMetadata, $name);
}

$value = $this->data[$name];

if (count($path) > 0) {
if (!$value instanceof IMultiPropertyPropertyContainer) {
throw new InvalidStateException("Path to raw value doesn't go through IMultiPropertyPropertyContainer property.");
}

$value = $value->getRawValueOf($path, $checkPropertyExistence);
return $value;

} elseif ($value instanceof IProperty) {
$value = $value->getRawValue();
return $value;
}

return $value;
}


public function onAttach(IEntity $entity, PropertyMetadata $propertyMetadata): void
{
$this->parentEntity = $entity;
$this->propertyMetadata = $propertyMetadata;

foreach ($this->data as $key => $property) {
if ($property instanceof IEntityAwareProperty) {
$property->onAttach(
$entity,
$this->metadata->getProperty($key)->withPath($this->propertyMetadata->path)
);
}
}
}


Expand Down Expand Up @@ -140,12 +188,8 @@ private function createPropertyWrapper(PropertyMetadata $metadata): IProperty
$wrapper = new $class($metadata);
assert($wrapper instanceof IProperty);

if ($wrapper instanceof IEntityAwareProperty) {
if ($this->parentEntity === null) {
throw new InvalidStateException("");
} else {
$wrapper->setPropertyEntity($this->parentEntity);
}
if ($wrapper instanceof IEntityAwareProperty && $this->parentEntity !== null) {
$wrapper->onAttach($this->parentEntity, $metadata->withPath($this->propertyMetadata->path));
}

return $wrapper;
Expand Down
22 changes: 19 additions & 3 deletions src/Entity/Embeddable/EmbeddableContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Nette\SmartObject;
use Nextras\Orm\Entity\IEntity;
use Nextras\Orm\Entity\IEntityAwareProperty;
use Nextras\Orm\Entity\IMultiPropertyPropertyContainer;
use Nextras\Orm\Entity\IPropertyContainer;
use Nextras\Orm\Entity\Reflection\PropertyMetadata;
use Nextras\Orm\Exception\InvalidArgumentException;
Expand All @@ -17,7 +18,7 @@
use function count;


class EmbeddableContainer implements IPropertyContainer, IEntityAwareProperty
class EmbeddableContainer implements IPropertyContainer, IMultiPropertyPropertyContainer, IEntityAwareProperty
{
use SmartObject;

Expand Down Expand Up @@ -50,9 +51,14 @@ public function __construct(PropertyMetadata $propertyMetadata)
}


public function setPropertyEntity(IEntity $entity): void
public function onAttach(IEntity $entity, PropertyMetadata $propertyMetadata): void
{
$this->entity = $entity;
$this->metadata = $propertyMetadata->withPath($propertyMetadata->path ?? []); // force creation

if ($this->value !== null) {
$this->value->onAttach($entity, $this->metadata);
}
}


Expand Down Expand Up @@ -106,6 +112,16 @@ public function getRawValue()
}


public function getRawValueOf(array $path, bool $checkPropertyExistence = true)
{
if ($this->value !== null) {
return $this->value->getRawValueOf($path, $checkPropertyExistence);
}

return null;
}


public function setInjectedValue($value): bool
{
assert($this->entity !== null);
Expand All @@ -118,7 +134,7 @@ public function setInjectedValue($value): bool

if ($value !== null) {
assert($value instanceof IEmbeddable);
$value->onAttach($this->entity);
$value->onAttach($this->entity, $this->metadata);
}

$this->value = $value;
Expand Down
12 changes: 11 additions & 1 deletion src/Entity/Embeddable/IEmbeddable.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@


use Nextras\Orm\Entity\IEntity;
use Nextras\Orm\Entity\Reflection\PropertyMetadata;


interface IEmbeddable
Expand Down Expand Up @@ -37,10 +38,19 @@ public function setRawValue(array $data): void;
public function getRawValue(): array;


/**
* Returns raw value for specific property.
* @param string[] $path
* @phpstan-param list<string> $path
* @return mixed
*/
public function getRawValueOf(array $path, bool $checkPropertyExistence = true);


/**
* Attaches entity to embeddable object.
* This is called after injecting embeddable into property wrapper.
* @internal
*/
public function onAttach(IEntity $entity): void;
public function onAttach(IEntity $entity, PropertyMetadata $propertyMetadata): void;
}
6 changes: 4 additions & 2 deletions src/Entity/IEntity.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,12 @@ public function setRawValue(string $name, $value): void;

/**
* Returns raw value.
* Raw value is normalized value which is suitable unique identification and storing.
* Raw value is normalized value to be suitable for storing.
* @param string|string[] $name
* @phpstan-param string|list<string> $name
* @return mixed
*/
public function &getRawValue(string $name);
public function &getRawValue($name, bool $checkPropertyExistence = true);


/**
Expand Down
Loading

0 comments on commit c7a15b8

Please sign in to comment.