relations setting

This commit is contained in:
Yuri Kuznetsov
2024-08-06 20:01:43 +03:00
parent 1b34ede299
commit 120092de43
6 changed files with 179 additions and 24 deletions
+29 -2
View File
@@ -31,6 +31,7 @@ namespace Espo\ORM;
use Espo\ORM\Relation\EmptyRelations;
use Espo\ORM\Relation\Relations;
use Espo\ORM\Type\AttributeType;
use Espo\ORM\Type\RelationType;
use Espo\ORM\Value\ValueAccessorFactory;
use Espo\ORM\Value\ValueAccessor;
@@ -139,6 +140,8 @@ class BaseEntity implements Entity
public function reset(): void
{
$this->valuesContainer = [];
$this->relations->resetAll();
}
/**
@@ -458,6 +461,29 @@ class BaseEntity implements Entity
}
$this->setInContainer($attribute, $preparedValue);
$type = $this->getAttributeType($attribute);
if (
$type === AttributeType::FOREIGN_ID ||
$type === AttributeType::FOREIGN &&
$this->isAttributeHasOneForeignId($attribute)
// @todo Move the logic for hasOne to Espo\Core\ORM\Entity ?
) {
$this->relations->reset(substr($attribute, 0, -2));
} else if ($type === AttributeType::FOREIGN_TYPE) {
$this->relations->reset(substr($attribute, 0, -4));
}
}
private function isAttributeHasOneForeignId(string $attribute): bool
{
$type = $this->getAttributeType($attribute);
return $type === AttributeType::FOREIGN &&
$this->getAttributeParam($attribute, 'relation') === substr($attribute, 0, -2) &&
$this->getAttributeParam($attribute, 'foreign') === 'id' &&
str_ends_with($attribute, 'Id');
}
protected function prepareAttributeValue(string $attribute, mixed $value): mixed
@@ -945,7 +971,7 @@ class BaseEntity implements Entity
}
/**
* Copy all current values to fetched values. All current attribute values will beset as those
* Copy all current values to fetched values. All current attribute values will be set as those
* that are fetched from DB.
*/
public function updateFetchedValues(): void
@@ -1117,12 +1143,13 @@ class BaseEntity implements Entity
continue;
}
if ($attribute == 'id') {
if ($attribute === 'id') {
$this->id = $data[$attribute];
continue;
}
// @todo Revise. To remove?
if ($onlyAccessible && $this->getAttributeParam($attribute, 'notAccessible')) {
continue;
}
+1 -1
View File
@@ -281,7 +281,7 @@ class EntityManager
throw new RuntimeException("Can't refresh a non-existent entity.");
}
$this->relationsMap->get($entity)?->reset();
$this->relationsMap->get($entity)?->resetAll();
$entity->set($fetchedEntity->getValueMap());
$entity->setAsFetched();
@@ -31,17 +31,60 @@ namespace Espo\ORM\Relation;
use Espo\ORM\Entity;
use Espo\ORM\EntityCollection;
use LogicException;
use RuntimeException as RuntimeExceptionAlias;
class EmptyRelations implements Relations
{
/** @var array<string, Entity|EntityCollection<Entity>|null> */
private array $setData = [];
public function __construct() {}
public function reset(): void
{}
public function resetAll(): void
{
$this->setData = [];
}
public function reset(string $relation): void
{
unset($this->setData[$relation]);
}
/**
* @param Entity|EntityCollection<Entity>|null $related
*/
public function set(string $relation, Entity|EntityCollection|null $related): void
{
$this->setData[$relation] = $related;
}
public function isSet(string $relation): bool
{
return array_key_exists($relation, $this->setData);
}
/**
* @return Entity|EntityCollection<Entity>|null
*/
public function getSet(string $relation): Entity|EntityCollection|null
{
if (!array_key_exists($relation, $this->setData)) {
throw new RuntimeExceptionAlias("Relation '$relation' is not set.");
}
return $this->setData[$relation];
}
public function getOne(string $relation): ?Entity
{
return null;
$entity = $this->setData[$relation] ?? null;
if ($entity instanceof EntityCollection) {
throw new LogicException("Not an entity.");
}
return $entity;
}
/***
@@ -49,7 +92,13 @@ class EmptyRelations implements Relations
*/
public function getMany(string $relation): EntityCollection
{
$collection = $this->setData[$relation] ?? new EntityCollection();
if (!$collection instanceof EntityCollection) {
throw new LogicException("Not a collection.");
}
/** @var EntityCollection<Entity> */
return new EntityCollection();
return $collection;
}
}
+75 -13
View File
@@ -29,23 +29,33 @@
namespace Espo\ORM\Relation;
use Espo\ORM\Collection;
use Espo\ORM\Entity;
use Espo\ORM\EntityCollection;
use Espo\ORM\EntityManager;
use Espo\ORM\Query\Part\Order;
use Espo\ORM\Type\RelationType;
use InvalidArgumentException;
use LogicException;
use RuntimeException;
/**
* @internal
*/
class RDBRelations implements Relations
{
/** @var array<string, Entity|Collection<Entity>|null> */
/** @var array<string, Entity|EntityCollection<Entity>|null> */
private array $data = [];
/** @var array<string, Entity|EntityCollection<Entity>|null> */
private array $setData = [];
private ?Entity $entity = null;
/** @var string[] */
private array $manyTypeList = [
RelationType::MANY_MANY,
RelationType::HAS_MANY,
RelationType::HAS_CHILDREN,
];
public function __construct(
private EntityManager $entityManager,
) {}
@@ -59,16 +69,68 @@ class RDBRelations implements Relations
$this->entity = $entity;
}
public function reset(): void
public function reset(string $relation): void
{
unset($this->data[$relation]);
unset($this->setData[$relation]);
}
public function resetAll(): void
{
$this->data = [];
$this->setData = [];
}
public function isSet(string $relation): bool
{
return array_key_exists($relation, $this->setData);
}
/**
* @return Entity|EntityCollection<Entity>|null
*/
public function getSet(string $relation): Entity|EntityCollection|null
{
if (!array_key_exists($relation, $this->setData)) {
throw new RuntimeException("Relation '$relation' is not set.");
}
return $this->setData[$relation];
}
/**
* @param Entity|EntityCollection<Entity>|null $related
*/
public function set(string $relation, Entity|EntityCollection|null $related): void
{
$type = $this->getRelationType($relation);
if (!$type) {
throw new LogicException("Relation '$relation' does not exist.");
}
if (in_array($type, $this->manyTypeList) && !$related instanceof EntityCollection) {
throw new InvalidArgumentException("Non-collection passed for relation '$relation'.");
}
if (
!in_array($type, [
RelationType::BELONGS_TO,
RelationType::BELONGS_TO_PARENT,
RelationType::HAS_ONE,
])
) {
throw new LogicException("Relation type '$type' is not supported for setting.");
}
$this->setData[$relation] = $related;
}
public function getOne(string $relation): ?Entity
{
$entity = $this->get($relation);
if ($entity instanceof Collection) {
if ($entity instanceof EntityCollection) {
throw new LogicException("Not an entity. Use `getMany` instead.");
}
@@ -96,16 +158,16 @@ class RDBRelations implements Relations
*/
private function get(string $relation): Entity|EntityCollection|null
{
if (array_key_exists($relation, $this->setData)) {
return $this->setData[$relation];
}
if (!array_key_exists($relation, $this->data)) {
if (!$this->entity) {
throw new LogicException("No entity set.");
}
$isMany = in_array($this->getRelationType($relation), [
RelationType::MANY_MANY,
RelationType::HAS_MANY,
RelationType::HAS_CHILDREN,
]);
$isMany = in_array($this->getRelationType($relation), $this->manyTypeList);
$this->data[$relation] = $isMany ?
$this->findMany($relation) :
@@ -114,7 +176,7 @@ class RDBRelations implements Relations
$object = $this->data[$relation];
if ($object instanceof Collection) {
if ($object instanceof EntityCollection) {
/** @var EntityCollection<Entity> $object */
$object = new EntityCollection(iterator_to_array($object));
}
@@ -183,7 +245,7 @@ class RDBRelations implements Relations
return $collection;
}
private function getRelationType(string $relation): string
private function getRelationType(string $relation): ?string
{
if (!$this->entity) {
throw new LogicException();
@@ -192,7 +254,7 @@ class RDBRelations implements Relations
return $this->entityManager
->getDefs()
->getEntity($this->entity->getEntityType())
->getRelation($relation)
->getType();
->tryGetRelation($relation)
?->getType();
}
}
+20 -3
View File
@@ -33,14 +33,31 @@ use Espo\ORM\Entity;
use Espo\ORM\EntityCollection;
/**
* @internal
* @internal Not ready for production.
*/
interface Relations
{
/**
* Reset.
* Reset a specific relation.
*/
public function reset(): void;
public function reset(string $relation): void;
/**
* Reset all.
*/
public function resetAll(): void;
/**
* @param Entity|EntityCollection<Entity>|null $related
*/
public function set(string $relation, Entity|EntityCollection|null $related): void;
public function isSet(string $relation): bool;
/**
* @return Entity|EntityCollection<Entity>|null
*/
public function getSet(string $relation): Entity|EntityCollection|null;
/**
* Get one related record. For has-one, belongs-to.
@@ -182,7 +182,7 @@ class RDBRepository implements Repository
$entity->setAsNotBeingSaved();
}
$this->relationsMap?->get($entity)?->reset();
$this->relationsMap?->get($entity)?->resetAll();
}
/**