relations setting
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user