Merge branch 'fix'
This commit is contained in:
@@ -61,6 +61,12 @@ class RelationConverter
|
||||
RelationParam::INDEXES,
|
||||
];
|
||||
|
||||
/** @var string[] */
|
||||
private $manyMergeParams = [
|
||||
RelationParam::ORDER_BY,
|
||||
RelationParam::ORDER,
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
private Metadata $metadata,
|
||||
private InjectableFactory $injectableFactory,
|
||||
@@ -112,7 +118,7 @@ class RelationConverter
|
||||
$raw = $convertedEntityDefs->toAssoc();
|
||||
|
||||
if (isset($raw[EntityParam::RELATIONS][$name])) {
|
||||
$this->mergeParams($raw[EntityParam::RELATIONS][$name], $params, $foreignParams ?? []);
|
||||
$this->mergeParams($raw[EntityParam::RELATIONS][$name], $params, $foreignParams ?? [], $linkType);
|
||||
$this->correct($raw[EntityParam::RELATIONS][$name]);
|
||||
}
|
||||
|
||||
@@ -172,9 +178,18 @@ class RelationConverter
|
||||
* @param array<string, mixed> $params
|
||||
* @param array<string, mixed> $foreignParams
|
||||
*/
|
||||
private function mergeParams(array &$relationDefs, array $params, array $foreignParams): void
|
||||
private function mergeParams(array &$relationDefs, array $params, array $foreignParams, string $linkType): void
|
||||
{
|
||||
foreach ($this->mergeParams as $name) {
|
||||
$mergeParams = $this->mergeParams;
|
||||
|
||||
if (
|
||||
$linkType === RelationType::HAS_MANY ||
|
||||
$linkType === RelationType::HAS_CHILDREN
|
||||
) {
|
||||
$mergeParams = array_merge($mergeParams, $this->manyMergeParams);
|
||||
}
|
||||
|
||||
foreach ($mergeParams as $name) {
|
||||
$additionalParam = $this->getMergedParam($name, $params, $foreignParams);
|
||||
|
||||
if ($additionalParam === null) {
|
||||
@@ -227,6 +242,13 @@ class RelationConverter
|
||||
*/
|
||||
private function correct(array &$relationDefs): void
|
||||
{
|
||||
if (
|
||||
isset($relationDefs[RelationParam::ORDER]) &&
|
||||
is_string($relationDefs[RelationParam::ORDER])
|
||||
) {
|
||||
$relationDefs[RelationParam::ORDER] = strtoupper($relationDefs[RelationParam::ORDER]);
|
||||
}
|
||||
|
||||
if (!isset($relationDefs[RelationParam::ADDITIONAL_COLUMNS])) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -93,4 +93,18 @@ class RelationParam
|
||||
* Deferred load.
|
||||
*/
|
||||
public const DEFERRED_LOAD = 'deferredLoad';
|
||||
|
||||
/**
|
||||
* Default order by. Applied on the entity level.
|
||||
*
|
||||
* @since 9.2.5
|
||||
*/
|
||||
public const ORDER_BY = 'orderBy';
|
||||
|
||||
/**
|
||||
* Default order. Applied on the entity level.
|
||||
*
|
||||
* @since 9.2.5
|
||||
*/
|
||||
public const ORDER = 'order';
|
||||
}
|
||||
|
||||
@@ -32,12 +32,14 @@ namespace Espo\ORM\Relation;
|
||||
use Espo\ORM\BaseEntity;
|
||||
use Espo\ORM\Defs\Defs;
|
||||
use Espo\ORM\Defs\Params\RelationParam;
|
||||
use Espo\ORM\Defs\RelationDefs;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\ORM\EntityCollection;
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\ORM\Mapper\RDBMapper;
|
||||
use Espo\ORM\Name\Attribute;
|
||||
use Espo\ORM\Query\Part\Order;
|
||||
use Espo\ORM\Repository\RDBRelationSelectBuilder;
|
||||
use Espo\ORM\Type\RelationType;
|
||||
use LogicException;
|
||||
use RuntimeException;
|
||||
@@ -293,22 +295,11 @@ class RDBRelations implements Relations
|
||||
->getEntity($this->getEntity()->getEntityType())
|
||||
->getRelation($relation);
|
||||
|
||||
$orderBy = null;
|
||||
$order = null;
|
||||
$builder = $this->entityManager
|
||||
->getRelation($this->getEntity(), $relation)
|
||||
->createBuilder();
|
||||
|
||||
if ($relationDefs->getParam('orderBy')) {
|
||||
$orderBy = $relationDefs->getParam('orderBy');
|
||||
|
||||
if ($relationDefs->getParam('order')) {
|
||||
$order = strtoupper($relationDefs->getParam('order')) === Order::DESC ? Order::DESC : Order::ASC;
|
||||
}
|
||||
}
|
||||
|
||||
$builder = $this->entityManager->getRelation($this->getEntity(), $relation);
|
||||
|
||||
if ($orderBy) {
|
||||
$builder->order($orderBy, $order);
|
||||
}
|
||||
$this->applyOrder($relationDefs, $builder);
|
||||
|
||||
$collection = $builder->find();
|
||||
|
||||
@@ -398,4 +389,21 @@ class RDBRelations implements Relations
|
||||
|
||||
return $foreignEntity;
|
||||
}
|
||||
|
||||
private function applyOrder(RelationDefs $relationDefs, RDBRelationSelectBuilder $builder): void
|
||||
{
|
||||
$orderBy = $relationDefs->getParam(RelationParam::ORDER_BY);
|
||||
|
||||
if (!$orderBy) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order = $relationDefs->getParam(RelationParam::ORDER);
|
||||
|
||||
if ($order !== null) {
|
||||
$order = strtoupper($order) === Order::DESC ? Order::DESC : Order::ASC;
|
||||
}
|
||||
|
||||
$builder->order($orderBy, $order);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ use RuntimeException;
|
||||
/**
|
||||
* An access point for a specific relation of a record.
|
||||
*
|
||||
* @template TEntity of Entity
|
||||
* @template TEntity of Entity = Entity
|
||||
*/
|
||||
class RDBRelation
|
||||
{
|
||||
@@ -95,6 +95,18 @@ class RDBRelation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a select builder.
|
||||
*
|
||||
* @return Builder<TEntity>
|
||||
*
|
||||
* @since 9.2.5
|
||||
*/
|
||||
public function createBuilder(): Builder
|
||||
{
|
||||
return $this->createSelectBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a select builder.
|
||||
*
|
||||
|
||||
@@ -53,7 +53,7 @@ use InvalidArgumentException;
|
||||
/**
|
||||
* Builds select parameters for related records for RDB repository.
|
||||
*
|
||||
* @template TEntity of Entity
|
||||
* @template TEntity of Entity = Entity
|
||||
*/
|
||||
class RDBRelationSelectBuilder
|
||||
{
|
||||
|
||||
@@ -234,7 +234,7 @@ class RDBRepository implements Repository
|
||||
*/
|
||||
public function find(): EntityCollection|SthCollection
|
||||
{
|
||||
return $this->createSelectBuilder()->find();
|
||||
return $this->createBuilder()->find();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,7 +273,7 @@ class RDBRepository implements Repository
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return $this->createSelectBuilder()->count();
|
||||
return $this->createBuilder()->count();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,7 +283,7 @@ class RDBRepository implements Repository
|
||||
*/
|
||||
public function max(string $attribute)
|
||||
{
|
||||
return $this->createSelectBuilder()->max($attribute);
|
||||
return $this->createBuilder()->max($attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,7 +293,7 @@ class RDBRepository implements Repository
|
||||
*/
|
||||
public function min(string $attribute)
|
||||
{
|
||||
return $this->createSelectBuilder()->min($attribute);
|
||||
return $this->createBuilder()->min($attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,7 +303,7 @@ class RDBRepository implements Repository
|
||||
*/
|
||||
public function sum(string $attribute)
|
||||
{
|
||||
return $this->createSelectBuilder()->sum($attribute);
|
||||
return $this->createBuilder()->sum($attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -334,7 +334,7 @@ class RDBRepository implements Repository
|
||||
*/
|
||||
public function join($target, ?string $alias = null, $conditions = null): RDBSelectBuilder
|
||||
{
|
||||
return $this->createSelectBuilder()->join($target, $alias, $conditions);
|
||||
return $this->createBuilder()->join($target, $alias, $conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -348,7 +348,7 @@ class RDBRepository implements Repository
|
||||
*/
|
||||
public function leftJoin($target, ?string $alias = null, $conditions = null): RDBSelectBuilder
|
||||
{
|
||||
return $this->createSelectBuilder()->leftJoin($target, $alias, $conditions);
|
||||
return $this->createBuilder()->leftJoin($target, $alias, $conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,7 +358,7 @@ class RDBRepository implements Repository
|
||||
*/
|
||||
public function distinct(): RDBSelectBuilder
|
||||
{
|
||||
return $this->createSelectBuilder()->distinct();
|
||||
return $this->createBuilder()->distinct();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -368,7 +368,7 @@ class RDBRepository implements Repository
|
||||
*/
|
||||
public function forUpdate(): RDBSelectBuilder
|
||||
{
|
||||
return $this->createSelectBuilder()->forUpdate();
|
||||
return $this->createBuilder()->forUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,7 +378,7 @@ class RDBRepository implements Repository
|
||||
*/
|
||||
public function sth(): RDBSelectBuilder
|
||||
{
|
||||
return $this->createSelectBuilder()->sth();
|
||||
return $this->createBuilder()->sth();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -395,7 +395,7 @@ class RDBRepository implements Repository
|
||||
*/
|
||||
public function where($clause = [], $value = null): RDBSelectBuilder
|
||||
{
|
||||
return $this->createSelectBuilder()->where($clause, $value);
|
||||
return $this->createBuilder()->where($clause, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,7 +412,7 @@ class RDBRepository implements Repository
|
||||
*/
|
||||
public function having($clause = [], $value = null): RDBSelectBuilder
|
||||
{
|
||||
return $this->createSelectBuilder()->having($clause, $value);
|
||||
return $this->createBuilder()->having($clause, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -432,7 +432,7 @@ class RDBRepository implements Repository
|
||||
*/
|
||||
public function order($orderBy = Attribute::ID, $direction = null): RDBSelectBuilder
|
||||
{
|
||||
return $this->createSelectBuilder()->order($orderBy, $direction);
|
||||
return $this->createBuilder()->order($orderBy, $direction);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -442,7 +442,7 @@ class RDBRepository implements Repository
|
||||
*/
|
||||
public function limit(?int $offset = null, ?int $limit = null): RDBSelectBuilder
|
||||
{
|
||||
return $this->createSelectBuilder()->limit($offset, $limit);
|
||||
return $this->createBuilder()->limit($offset, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -462,7 +462,7 @@ class RDBRepository implements Repository
|
||||
*/
|
||||
public function select($select = [], ?string $alias = null): RDBSelectBuilder
|
||||
{
|
||||
return $this->createSelectBuilder()->select($select, $alias);
|
||||
return $this->createBuilder()->select($select, $alias);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -479,15 +479,17 @@ class RDBRepository implements Repository
|
||||
*/
|
||||
public function group($groupBy): RDBSelectBuilder
|
||||
{
|
||||
return $this->createSelectBuilder()->group($groupBy);
|
||||
return $this->createBuilder()->group($groupBy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a select builder.
|
||||
*
|
||||
* @return RDBSelectBuilder<TEntity>
|
||||
*
|
||||
* @since 9.2.5
|
||||
*/
|
||||
protected function createSelectBuilder(): RDBSelectBuilder
|
||||
public function createBuilder(): RDBSelectBuilder
|
||||
{
|
||||
/** @var RDBSelectBuilder<TEntity> $builder */
|
||||
$builder = new RDBSelectBuilder($this->entityManager, $this->entityType);
|
||||
@@ -495,6 +497,19 @@ class RDBRepository implements Repository
|
||||
return $builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a select builder.
|
||||
*
|
||||
* @return RDBSelectBuilder<TEntity>
|
||||
*
|
||||
* @deprecated Since v9.2.5. Use `createBuilder` instead.
|
||||
* @todo Remove in v10.0.
|
||||
*/
|
||||
protected function createSelectBuilder(): RDBSelectBuilder
|
||||
{
|
||||
return $this->createBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use hooks instead.
|
||||
*
|
||||
|
||||
@@ -29,8 +29,10 @@
|
||||
|
||||
namespace tests\integration\Espo\ORM;
|
||||
|
||||
use Espo\Modules\Crm\Entities\Contact;
|
||||
use Espo\Modules\Crm\Entities\Lead;
|
||||
use Espo\Modules\Crm\Entities\Task;
|
||||
use Espo\ORM\Defs\Params\RelationParam;
|
||||
use Espo\ORM\EntityCollection;
|
||||
use Espo\Modules\Crm\Entities\Account;
|
||||
use Espo\Modules\Crm\Entities\Opportunity;
|
||||
@@ -232,4 +234,71 @@ class RelationsTest extends BaseTestCase
|
||||
|
||||
$this->assertEquals(null, $lead2->get('createdAccountId'));
|
||||
}
|
||||
|
||||
public function testOrder(): void
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
|
||||
$account1 = $em->createEntity(Account::ENTITY_TYPE, ['name' => 'a1']);
|
||||
$account2 = $em->createEntity(Account::ENTITY_TYPE, ['name' => 'a2']);
|
||||
|
||||
$contact = $em->createEntity(Contact::ENTITY_TYPE);
|
||||
|
||||
$this->assertInstanceOf(Contact::class, $contact);
|
||||
|
||||
$em->getRelation($contact, 'accounts')->relate($account1);
|
||||
$em->getRelation($contact, 'accounts')->relate($account2);
|
||||
|
||||
//
|
||||
|
||||
$metadata = $this->getMetadata();
|
||||
$metadata->set('entityDefs', Contact::ENTITY_TYPE, [
|
||||
'links' => [
|
||||
'accounts' => [
|
||||
RelationParam::ORDER_BY => 'name',
|
||||
RelationParam::ORDER => 'desc',
|
||||
]
|
||||
]
|
||||
]);
|
||||
$metadata->save();
|
||||
|
||||
$this->reCreateApplication();
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
|
||||
$contact = $em->getRDBRepositoryByClass(Contact::class)->getById($contact->getId());
|
||||
$this->assertNotNull($contact);
|
||||
|
||||
$accounts = $contact->getAccounts();
|
||||
|
||||
$this->assertCount(2, $accounts);
|
||||
$this->assertEquals('a2', $accounts[0]->getName());
|
||||
$this->assertEquals('a1', $accounts[1]->getName());
|
||||
|
||||
//
|
||||
|
||||
$metadata = $this->getMetadata();
|
||||
$metadata->set('entityDefs', Contact::ENTITY_TYPE, [
|
||||
'links' => [
|
||||
'accounts' => [
|
||||
RelationParam::ORDER_BY => 'name',
|
||||
RelationParam::ORDER => 'asc',
|
||||
]
|
||||
]
|
||||
]);
|
||||
$metadata->save();
|
||||
|
||||
$this->reCreateApplication();
|
||||
|
||||
$em = $this->getEntityManager();
|
||||
|
||||
$contact = $em->getRDBRepositoryByClass(Contact::class)->getById($contact->getId());
|
||||
$this->assertNotNull($contact);
|
||||
|
||||
$accounts = $contact->getAccounts();
|
||||
|
||||
$this->assertCount(2, $accounts);
|
||||
$this->assertEquals('a1', $accounts[0]->getName());
|
||||
$this->assertEquals('a2', $accounts[1]->getName());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user