collection methods
This commit is contained in:
@@ -37,6 +37,7 @@ use SeekableIterator;
|
||||
use RuntimeException;
|
||||
use OutOfBoundsException;
|
||||
use InvalidArgumentException;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* A standard collection of entities. It allocates a memory for all entities.
|
||||
@@ -399,4 +400,72 @@ class EntityCollection implements Collection, Iterator, Countable, ArrayAccess,
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter.
|
||||
*
|
||||
* @param Closure(TEntity): bool $callback A filter callback.
|
||||
* @return self<TEntity> A filtered collection. A new instance.
|
||||
* @since 9.1.0
|
||||
*/
|
||||
public function filter(Closure $callback): self
|
||||
{
|
||||
$newList = [];
|
||||
|
||||
foreach ($this as $entity) {
|
||||
if ($callback($entity)) {
|
||||
$newList[] = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
return new EntityCollection($newList, $this->entityType, $this->entityFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort.
|
||||
*
|
||||
* @param Closure(TEntity, TEntity): int $callback The comparison function.
|
||||
* @return self<TEntity> A sorted collection. A new instance.
|
||||
* @since 9.1.0
|
||||
*/
|
||||
public function sort(Closure $callback): self
|
||||
{
|
||||
$newList = [...$this];
|
||||
|
||||
usort($newList, $callback);
|
||||
|
||||
return new EntityCollection($newList, $this->entityType, $this->entityFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse.
|
||||
*
|
||||
* @return self<TEntity> A reversed collection.
|
||||
* @since 9.1.0
|
||||
*/
|
||||
public function reverse(): self
|
||||
{
|
||||
$newList = array_reverse([...$this]);
|
||||
|
||||
return new EntityCollection($newList, $this->entityType, $this->entityFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find.
|
||||
*
|
||||
* @param Closure(TEntity): bool $callback A filter callback.
|
||||
* @return ?TEntity
|
||||
* @since 9.1.0
|
||||
* @noinspection PhpDocSignatureInspection
|
||||
*/
|
||||
public function find(Closure $callback): ?Entity
|
||||
{
|
||||
foreach ($this as $entity) {
|
||||
if ($callback($entity)) {
|
||||
return $entity;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
namespace Espo\ORM;
|
||||
|
||||
use Espo\ORM\Query\Select as SelectQuery;
|
||||
|
||||
use IteratorAggregate;
|
||||
use Countable;
|
||||
use Traversable;
|
||||
@@ -38,6 +37,7 @@ use PDO;
|
||||
use PDOStatement;
|
||||
use RuntimeException;
|
||||
use LogicException;
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* Reasonable to use when selecting a large number of records.
|
||||
@@ -144,7 +144,7 @@ class SthCollection implements Collection, IteratorAggregate, Countable
|
||||
$list = [];
|
||||
|
||||
foreach ($this as $entity) {
|
||||
$list[] = $entity->getValueMap();;
|
||||
$list[] = $entity->getValueMap();
|
||||
}
|
||||
|
||||
return $list;
|
||||
@@ -170,6 +170,7 @@ class SthCollection implements Collection, IteratorAggregate, Countable
|
||||
* Create from a query.
|
||||
*
|
||||
* @return self<Entity>
|
||||
* @internal
|
||||
*/
|
||||
public static function fromQuery(SelectQuery $query, EntityManager $entityManager): self
|
||||
{
|
||||
@@ -192,6 +193,7 @@ class SthCollection implements Collection, IteratorAggregate, Countable
|
||||
* Create from an SQL.
|
||||
*
|
||||
* @return self<Entity>
|
||||
* @internal
|
||||
*/
|
||||
public static function fromSql(string $entityType, string $sql, EntityManager $entityManager): self
|
||||
{
|
||||
@@ -203,4 +205,61 @@ class SthCollection implements Collection, IteratorAggregate, Countable
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter.
|
||||
*
|
||||
* @param Closure(TEntity): bool $callback A filter callback.
|
||||
* @return EntityCollection<TEntity> A filtered collection.
|
||||
* @since 9.1.0
|
||||
*/
|
||||
public function filter(Closure $callback): EntityCollection
|
||||
{
|
||||
return $this->toEntityCollection()->filter($callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort.
|
||||
*
|
||||
* @param Closure(TEntity, TEntity): int $callback The comparison function.
|
||||
* @return EntityCollection<TEntity> A sorted collection. A new instance.
|
||||
* @since 9.1.0
|
||||
*/
|
||||
public function sort(Closure $callback): EntityCollection
|
||||
{
|
||||
return $this->toEntityCollection()->sort($callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse.
|
||||
*
|
||||
* @return EntityCollection<TEntity> A reversed collection.
|
||||
* @since 9.1.0
|
||||
*/
|
||||
public function reverse(): EntityCollection
|
||||
{
|
||||
return $this->toEntityCollection()->reverse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find.
|
||||
*
|
||||
* @param Closure(TEntity): bool $callback A filter callback.
|
||||
* @return ?TEntity
|
||||
* @since 9.1.0
|
||||
* @noinspection PhpDocSignatureInspection
|
||||
*/
|
||||
public function find(Closure $callback): ?Entity
|
||||
{
|
||||
return $this->toEntityCollection()->find($callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityCollection<TEntity>
|
||||
*/
|
||||
private function toEntityCollection(): EntityCollection
|
||||
{
|
||||
/** @var EntityCollection<TEntity> */
|
||||
return (new EntityCollection([...$this], $this->entityType));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
namespace tests\integration\Espo\ORM;
|
||||
|
||||
use Espo\Modules\Crm\Entities\Account;
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\ORM\SthCollection;
|
||||
use tests\integration\Core\BaseTestCase;
|
||||
@@ -234,4 +235,25 @@ class SthCollectionTest extends BaseTestCase
|
||||
|
||||
$this->assertEquals(2, $count);
|
||||
}
|
||||
|
||||
public function testMethods(): void
|
||||
{
|
||||
$e1 = $this->getEntityManager()->createEntity(Account::ENTITY_TYPE, ['name' => '1']);
|
||||
$e2 = $this->getEntityManager()->createEntity(Account::ENTITY_TYPE, ['name' => '2']);
|
||||
$e3 = $this->getEntityManager()->createEntity(Account::ENTITY_TYPE, ['name' => '3']);
|
||||
$e4 = $this->getEntityManager()->createEntity(Account::ENTITY_TYPE, ['name' => '4']);
|
||||
|
||||
$collection = $this->getEntityManager()
|
||||
->getRDBRepositoryByClass(Account::class)
|
||||
->sth()
|
||||
->order('name')
|
||||
->find();
|
||||
|
||||
$filtered = $collection->filter(function ($e) use ($e2, $e3) {
|
||||
return $e->getId() !== $e2->getId() && $e->getId() !== $e3->getId();
|
||||
});
|
||||
|
||||
$this->assertEquals([$e1->getId(), $e4->getId()], array_map(fn ($it) => $it->getId(), [...$filtered]));
|
||||
$this->assertEquals($collection->getEntityType(), $filtered->getEntityType());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,12 +32,17 @@ namespace tests\unit\Espo\ORM;
|
||||
use Espo\ORM\BaseEntity;
|
||||
use Espo\ORM\Defs;
|
||||
use Espo\ORM\Defs\DefsData;
|
||||
use Espo\ORM\Entity;
|
||||
use Espo\ORM\EntityCollection;
|
||||
use Espo\ORM\Executor\QueryExecutor;
|
||||
use Espo\ORM\Metadata;
|
||||
use Espo\ORM\MetadataDataProvider;
|
||||
use Espo\Core\ORM\EntityManager;
|
||||
|
||||
use Espo\ORM\SthCollection;
|
||||
use PDOStatement;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use SplObjectStorage;
|
||||
|
||||
require_once 'tests/unit/testData/DB/Entities.php';
|
||||
|
||||
@@ -117,4 +122,78 @@ class CollectionTest extends TestCase
|
||||
|
||||
$this->assertEquals(1, $collection->count());
|
||||
}
|
||||
|
||||
public function testFilter(): void
|
||||
{
|
||||
$e1 = $this->createMock(Entity::class);
|
||||
$e2 = $this->createMock(Entity::class);
|
||||
$e3 = $this->createMock(Entity::class);
|
||||
$e4 = $this->createMock(Entity::class);
|
||||
|
||||
$collection = new EntityCollection([$e1, $e2, $e3, $e4], 'Account');
|
||||
|
||||
$filtered = $collection->filter(function ($e) use ($e2, $e3) {
|
||||
return $e !== $e2 && $e !== $e3;
|
||||
});
|
||||
|
||||
$this->assertEquals([$e1, $e2], [...$filtered]);
|
||||
$this->assertEquals($collection->getEntityType(), $filtered->getEntityType());
|
||||
}
|
||||
|
||||
public function testSort(): void
|
||||
{
|
||||
$e1 = $this->createMock(Entity::class);
|
||||
$e2 = $this->createMock(Entity::class);
|
||||
$e3 = $this->createMock(Entity::class);
|
||||
|
||||
$map = new SplObjectStorage();
|
||||
|
||||
$map[$e1] = 3;
|
||||
$map[$e2] = 2;
|
||||
$map[$e3] = 1;
|
||||
|
||||
$collection = new EntityCollection([$e1, $e2, $e3], 'Account');
|
||||
|
||||
$sorted = $collection->sort(function ($e1, $e2) use ($map) {
|
||||
return $map[$e1] - $map[$e2];
|
||||
});
|
||||
|
||||
$this->assertEquals([$e3, $e2, $e1], [...$sorted]);
|
||||
$this->assertEquals($collection->getEntityType(), $sorted->getEntityType());
|
||||
}
|
||||
|
||||
public function testFind(): void
|
||||
{
|
||||
$e1 = $this->createMock(Entity::class);
|
||||
$e2 = $this->createMock(Entity::class);
|
||||
$e3 = $this->createMock(Entity::class);
|
||||
|
||||
$collection = new EntityCollection([$e1, $e2, $e3]);
|
||||
|
||||
$e = $collection->find(function ($e) use ($e2) {
|
||||
return $e === $e2;
|
||||
});
|
||||
|
||||
$this->assertSame($e2, $e);
|
||||
|
||||
$e = $collection->find(function ($e) use ($e2) {
|
||||
return $e === 0;
|
||||
});
|
||||
|
||||
$this->assertNull($e);
|
||||
}
|
||||
|
||||
public function testReverse(): void
|
||||
{
|
||||
$e1 = $this->createMock(Entity::class);
|
||||
$e2 = $this->createMock(Entity::class);
|
||||
$e3 = $this->createMock(Entity::class);
|
||||
|
||||
$collection = new EntityCollection([$e1, $e2, $e3], 'Account');
|
||||
|
||||
$reversed = $collection->reverse();
|
||||
|
||||
$this->assertEquals([$e3, $e2, $e1], [...$reversed]);
|
||||
$this->assertEquals($collection->getEntityType(), $reversed->getEntityType());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user