diff --git a/lib/Db/CardMapper.php b/lib/Db/CardMapper.php index 51154ac75..169b2aab4 100644 --- a/lib/Db/CardMapper.php +++ b/lib/Db/CardMapper.php @@ -131,7 +131,11 @@ class CardMapper extends QBMapper implements IPermissionMapper { return $card; } - public function findAll($stackId, $limit = null, $offset = null, $since = -1) { + /** + * @return Card[] + * @throws \OCP\DB\Exception + */ + public function findAll($stackId, ?int $limit = null, ?int $offset = null, int $since = -1) { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from('deck_cards') @@ -146,6 +150,32 @@ class CardMapper extends QBMapper implements IPermissionMapper { return $this->findEntities($qb); } + /** + * @param int[] $stackIds + * @return array + * @throws \OCP\DB\Exception + */ + public function findAllForStacks(array $stackIds, ?int $limit = null, ?int $offset = null, int $since = -1): array { + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from('deck_cards') + ->where($qb->expr()->in('stack_id', $qb->createNamedParameter($stackIds, IQueryBuilder::PARAM_INT_ARRAY))) + ->andWhere($qb->expr()->eq('archived', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL))) + ->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT))) + ->andWhere($qb->expr()->gt('last_modified', $qb->createNamedParameter($since, IQueryBuilder::PARAM_INT))) + ->setMaxResults($limit) + ->setFirstResult($offset) + ->orderBy('order') + ->addOrderBy('id'); + + $rawCards = $this->findEntities($qb); + $cards = array_fill_keys($stackIds, null); + foreach ($rawCards as $card) { + $cards[$card->getStackId()][] = $card; + } + return $cards; + } + public function queryCardsByBoard(int $boardId): IQueryBuilder { $qb = $this->db->getQueryBuilder(); $qb->select('c.*') diff --git a/lib/Db/Stack.php b/lib/Db/Stack.php index 000d79c3e..42c21cedd 100644 --- a/lib/Db/Stack.php +++ b/lib/Db/Stack.php @@ -15,6 +15,7 @@ use Sabre\VObject\Component\VCalendar; * @method int getDeletedAt() * @method int getLastModified() * @method int getOrder() + * @method Card[] getCards() */ class Stack extends RelationalEntity { protected $title; diff --git a/lib/Db/StackMapper.php b/lib/Db/StackMapper.php index 5e4a43842..e9e10647f 100644 --- a/lib/Db/StackMapper.php +++ b/lib/Db/StackMapper.php @@ -77,12 +77,10 @@ class StackMapper extends DeckMapper implements IPermissionMapper { /** * @param numeric $boardId - * @param int|null $limit - * @param int|null $offset * @return Stack[] * @throws \OCP\DB\Exception */ - public function findAll($boardId, $limit = null, $offset = null): array { + public function findAll($boardId, ?int $limit = null, ?int $offset = null): array { $qb = $this->db->getQueryBuilder(); $qb->select('*') ->from($this->getTableName()) diff --git a/lib/Service/StackService.php b/lib/Service/StackService.php index 8495d9d92..0a90390a0 100644 --- a/lib/Service/StackService.php +++ b/lib/Service/StackService.php @@ -75,19 +75,22 @@ class StackService { $this->stackServiceValidator = $stackServiceValidator; } - private function enrichStackWithCards($stack, $since = -1) { - $cards = $this->cardMapper->findAll($stack->getId(), null, null, $since); + /** @param Stack[] $stacks */ + private function enrichStacksWithCards(array $stacks, $since = -1): void { + $cardsByStackId = $this->cardMapper->findAllForStacks(array_map(fn (Stack $stack) => $stack->getId(), $stacks), null, null, $since); - if (\count($cards) === 0) { - return; - } + foreach ($cardsByStackId as $stackId => $cards) { + if (!$cards) { + continue; + } - $stack->setCards($this->cardService->enrichCards($cards)); - } - private function enrichStacksWithCards($stacks, $since = -1) { - foreach ($stacks as $stack) { - $this->enrichStackWithCards($stack, $since); + foreach ($stacks as $stack) { + if ($stack->getId() === $stackId) { + $stack->setCards($this->cardService->enrichCards($cards)); + break; + } + } } } @@ -124,9 +127,9 @@ class StackService { } /** - * @param $boardId + * @param mixed $boardId * - * @return array + * @return Stack[] * @throws \OCA\Deck\NoPermissionException * @throws BadRequestException */ @@ -247,7 +250,7 @@ class StackService { ); $this->changeHelper->boardChanged($stack->getBoardId()); $this->eventDispatcher->dispatchTyped(new BoardUpdatedEvent($stack->getBoardId())); - $this->enrichStackWithCards($stack); + $this->enrichStacksWithCards([$stack]); return $stack; } diff --git a/tests/unit/Service/StackServiceTest.php b/tests/unit/Service/StackServiceTest.php index a7fb2515f..2161a591b 100644 --- a/tests/unit/Service/StackServiceTest.php +++ b/tests/unit/Service/StackServiceTest.php @@ -129,12 +129,17 @@ class StackServiceTest extends TestCase { } ) ); - $this->cardMapper->expects($this->any())->method('findAll')->willReturn($this->getCards(222)); - + $this->cardMapper->expects($this->any())->method('findAllForStacks')->willReturnCallback(function (array $stackIds) { + $r = []; + foreach ($stackIds as $stackId) { + $r[$stackId] = $this->getCards(222); + } + return $r; + }); $actual = $this->stackService->findAll(123); for ($stackId = 0; $stackId < 3; $stackId++) { - for ($cardId = 0;$cardId < 10;$cardId++) { + for ($cardId = 0; $cardId < 10; $cardId++) { $this->assertEquals($actual[0]->getCards()[$cardId]->getId(), $cardId); $this->assertEquals($actual[0]->getCards()[$cardId]->getStackId(), 222); $this->assertEquals($actual[0]->getCards()[$cardId]->getLabels(), $this->getLabels()[$cardId]); @@ -211,7 +216,7 @@ class StackServiceTest extends TestCase { $stackToBeDeleted->setBoardId(1); $this->stackMapper->expects($this->once())->method('find')->willReturn($stackToBeDeleted); $this->stackMapper->expects($this->once())->method('update')->willReturn($stackToBeDeleted); - $this->cardMapper->expects($this->once())->method('findAll')->willReturn([]); + $this->cardMapper->expects($this->once())->method('findAllForStacks')->willReturn([]); $this->stackService->delete(123); $this->assertTrue($stackToBeDeleted->getDeletedAt() <= time(), 'deletedAt is in the past'); $this->assertTrue($stackToBeDeleted->getDeletedAt() > 0, 'deletedAt is set');