diff --git a/application/Espo/ORM/BaseEntity.php b/application/Espo/ORM/BaseEntity.php index f724dbc706..c0f538b1d8 100644 --- a/application/Espo/ORM/BaseEntity.php +++ b/application/Espo/ORM/BaseEntity.php @@ -255,8 +255,14 @@ class BaseEntity implements Entity ); } - // @todo Remove $this->id check when relation setting is implemented. + // @todo Remove support in v10.0. if ($this->hasRelation($attribute) && $this->id && !$params) { + trigger_error( + "Accessing related records with Entity::get is deprecated. " . + "Use `\$entityManager->getRelation(...)->find()`.", + E_USER_DEPRECATED + ); + $isMany = in_array($this->getRelationType($attribute), [ RelationType::MANY_MANY, RelationType::HAS_MANY, @@ -268,9 +274,14 @@ class BaseEntity implements Entity $this->relations->getOne($attribute); } - // @todo Revise. Remove? // @todo Remove support in v10.0. if ($this->hasRelation($attribute) && $this->id && $this->entityManager) { + trigger_error( + "Accessing related records with Entity::get is deprecated. " . + "Use `\$entityManager->getRelation(...)->find()`.", + E_USER_DEPRECATED + ); + /** @phpstan-ignore-next-line */ return $this->entityManager ->getRepository($this->getEntityType()) diff --git a/application/Espo/ORM/Relation/RDBRelations.php b/application/Espo/ORM/Relation/RDBRelations.php index d2453e2a4c..5a4d810f31 100644 --- a/application/Espo/ORM/Relation/RDBRelations.php +++ b/application/Espo/ORM/Relation/RDBRelations.php @@ -101,8 +101,6 @@ class RDBRelations implements Relations throw new LogicException("No entity set."); } - $relationRepository = $this->entityManager->getRelation($this->entity, $relation); - $isMany = in_array($this->getRelationType($relation), [ RelationType::MANY_MANY, RelationType::HAS_MANY, @@ -111,7 +109,7 @@ class RDBRelations implements Relations $this->data[$relation] = $isMany ? $this->findMany($relation) : - $relationRepository->findOne(); + $this->findOne($relation); } $object = $this->data[$relation]; @@ -124,6 +122,21 @@ class RDBRelations implements Relations return $object; } + private function findOne(string $relation): ?Entity + { + if (!$this->entity) { + throw new LogicException(); + } + + if (!$this->entity->hasId()) { + return null; + } + + return $this->entityManager + ->getRelation($this->entity, $relation) + ->findOne(); + } + /** * @return EntityCollection */ @@ -133,6 +146,11 @@ class RDBRelations implements Relations throw new LogicException(); } + if (!$this->entity->hasId()) { + /** @var EntityCollection */ + return new EntityCollection(); + } + $relationDefs = $this->entityManager ->getDefs() ->getEntity($this->entity->getEntityType()) diff --git a/tests/integration/Espo/ORM/RelationsTest.php b/tests/integration/Espo/ORM/RelationsTest.php index 543a623473..cb92d3207f 100644 --- a/tests/integration/Espo/ORM/RelationsTest.php +++ b/tests/integration/Espo/ORM/RelationsTest.php @@ -33,26 +33,42 @@ use Espo\ORM\EntityCollection; use Espo\Modules\Crm\Entities\Account; use Espo\Modules\Crm\Entities\Opportunity; use tests\integration\Core\BaseTestCase; +use tests\integration\testClasses\Entities\Account as AccountExtended; +use tests\integration\testClasses\Entities\Opportunity as OpportunityExtended; class RelationsTest extends BaseTestCase { public function testGetOne(): void { + $metadata = $this->getMetadata(); + $metadata->set('entityDefs', Opportunity::ENTITY_TYPE, [ + 'entityClassName' => OpportunityExtended::class, + ]); + $metadata->save(); + + $this->reCreateApplication(); + $em = $this->getEntityManager(); + $opp = $em->getEntity(Opportunity::ENTITY_TYPE); + $this->assertInstanceOf(OpportunityExtended::class, $opp); + $this->assertNull($opp->getRelatedAccount()); + $account = $em->createEntity(Account::ENTITY_TYPE); $opp = $em->createEntity(Opportunity::ENTITY_TYPE, [ 'accountId' => $account->getId(), ]); - $account1 = $opp->get('account'); + $this->assertInstanceOf(OpportunityExtended::class, $opp); + + $account1 = $opp->getRelatedAccount(); $this->assertInstanceOf(Account::class, $account1); $this->assertEquals($account->getId(), $account1->getId()); $em->refreshEntity($opp); - $account2 = $opp->get('account'); + $account2 = $opp->getRelatedAccount(); $this->assertInstanceOf(Account::class, $account2); $this->assertNotSame($account1, $account2); @@ -60,7 +76,7 @@ class RelationsTest extends BaseTestCase $em->saveEntity($opp); - $account3 = $opp->get('account'); + $account3 = $opp->getRelatedAccount(); $this->assertInstanceOf(Account::class, $account3); $this->assertNotSame($account1, $account3); @@ -69,14 +85,28 @@ class RelationsTest extends BaseTestCase public function testGetMany(): void { + $metadata = $this->getMetadata(); + $metadata->set('entityDefs', Account::ENTITY_TYPE, [ + 'entityClassName' => AccountExtended::class, + ]); + $metadata->save(); + + $this->reCreateApplication(); + $em = $this->getEntityManager(); + $account = $em->getEntity(Account::ENTITY_TYPE); + $this->assertInstanceOf(AccountExtended::class, $account); + $this->assertInstanceOf(EntityCollection::class, $account->getRelatedOpportunities()); + $account = $em->createEntity(Account::ENTITY_TYPE); + $this->assertInstanceOf(AccountExtended::class, $account); + $em->createEntity(Opportunity::ENTITY_TYPE, ['accountId' => $account->getId()]); $em->createEntity(Opportunity::ENTITY_TYPE, ['accountId' => $account->getId()]); $em->createEntity(Opportunity::ENTITY_TYPE); - $collection = $account->get('opportunities'); + $collection = $account->getRelatedOpportunities(); $this->assertInstanceOf(EntityCollection::class, $collection); $items = iterator_to_array($collection); diff --git a/tests/integration/testClasses/Entities/Account.php b/tests/integration/testClasses/Entities/Account.php new file mode 100644 index 0000000000..16b7561f4c --- /dev/null +++ b/tests/integration/testClasses/Entities/Account.php @@ -0,0 +1,40 @@ +. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU Affero General Public License version 3. + * + * In accordance with Section 7(b) of the GNU Affero General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +namespace tests\integration\testClasses\Entities; + +use Espo\ORM\EntityCollection; + +class Account extends \Espo\Modules\Crm\Entities\Account +{ + public function getRelatedOpportunities(): EntityCollection + { + return $this->relations->getMany('opportunities'); + } +} diff --git a/tests/integration/testClasses/Entities/Opportunity.php b/tests/integration/testClasses/Entities/Opportunity.php new file mode 100644 index 0000000000..e38ad35c8e --- /dev/null +++ b/tests/integration/testClasses/Entities/Opportunity.php @@ -0,0 +1,39 @@ +. + * + * The interactive user interfaces in modified source and object code versions + * of this program must display Appropriate Legal Notices, as required under + * Section 5 of the GNU Affero General Public License version 3. + * + * In accordance with Section 7(b) of the GNU Affero General Public License version 3, + * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. + ************************************************************************/ + +namespace tests\integration\testClasses\Entities; + +class Opportunity extends \Espo\Modules\Crm\Entities\Opportunity +{ + public function getRelatedAccount(): ?\Espo\Modules\Crm\Entities\Account + { + /** @var ?\Espo\Modules\Crm\Entities\Account */ + return $this->relations->getOne('account'); + } +}