Merge pull request #3720 from nextcloud/bugfix/deck_mapper/use_qb_mapper

Update `DeckMapper` to extend `QBMapper`
This commit is contained in:
Julius Härtl
2022-04-20 16:21:18 +02:00
committed by GitHub
10 changed files with 292 additions and 147 deletions

View File

@@ -33,18 +33,45 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
parent::__construct($db, 'deck_board_acl', Acl::class);
}
/**
* @param numeric $boardId
* @param int|null $limit
* @param int|null $offset
* @return Acl[]
* @throws \OCP\DB\Exception
*/
public function findAll($boardId, $limit = null, $offset = null) {
$sql = 'SELECT id, board_id, type, participant, permission_edit, permission_share, permission_manage FROM `*PREFIX*deck_board_acl` WHERE `board_id` = ? ';
return $this->findEntities($sql, [$boardId], $limit, $offset);
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'board_id', 'type', 'participant', 'permission_edit', 'permission_share', 'permission_manage')
->from('deck_board_acl')
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
->setMaxResults($limit)
->setFirstResult($offset);
return $this->findEntities($qb);
}
/**
* @param numeric $userId
* @param numeric $aclId
* @return bool
* @throws \OCP\DB\Exception
*/
public function isOwner($userId, $aclId): bool {
$sql = 'SELECT owner FROM `*PREFIX*deck_boards` WHERE `id` IN (SELECT board_id FROM `*PREFIX*deck_board_acl` WHERE id = ?)';
$stmt = $this->execute($sql, [$aclId]);
$row = $stmt->fetch();
return ($row['owner'] === $userId);
$qb = $this->db->getQueryBuilder();
$qb->select('acl.id')
->from($this->getTableName(), 'acl')
->innerJoin('acl', 'deck_boards', 'b', 'acl.board_id = b.id')
->where($qb->expr()->eq('owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)))
->andWhere($qb->expr()->eq('acl.id', $qb->createNamedParameter($aclId, IQueryBuilder::PARAM_INT)));
return count($qb->executeQuery()->fetchAll()) > 0;
}
/**
* @param numeric $id
* @return int|null
*/
public function findBoardId($id): ?int {
try {
$entity = $this->find($id);
@@ -54,9 +81,21 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
return null;
}
/**
* @param int $type
* @param string $participant
* @return Acl[]
* @throws \OCP\DB\Exception
*/
public function findByParticipant($type, $participant): array {
$sql = 'SELECT * from *PREFIX*deck_board_acl WHERE type = ? AND participant = ?';
return $this->findEntities($sql, [$type, $participant]);
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('type', $qb->createNamedParameter($type, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('participant', $qb->createNamedParameter($participant, IQueryBuilder::PARAM_STR)));
return $this->findEntities($qb);
}
/**

View File

@@ -55,9 +55,6 @@ class AssignmentMapper extends QBMapper implements IPermissionMapper {
$this->circleService = $circleService;
}
/**
* @return Assignment[]
*/
public function findAll(int $cardId): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')

View File

@@ -30,7 +30,6 @@ use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\IUserManager;
use PDO;
class AttachmentMapper extends DeckMapper implements IPermissionMapper {
private $cardMapper;
@@ -52,70 +51,53 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param $id
* @return Entity|Attachment
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @param int $id
* @return Attachment
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
* @throws \OCP\DB\Exception
*/
public function find($id) {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from('deck_attachment')
->from($this->getTableName())
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
$cursor = $qb->execute();
$row = $cursor->fetch(PDO::FETCH_ASSOC);
if ($row === false) {
$cursor->closeCursor();
throw new DoesNotExistException('Did expect one result but found none when executing' . $qb);
}
$row2 = $cursor->fetch();
$cursor->closeCursor();
if ($row2 !== false) {
throw new MultipleObjectsReturnedException('Did not expect more than one result when executing' . $query);
}
return $this->mapRowToEntity($row);
}
public function findByData($cardId, $data) {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from('deck_attachment')
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('data', $qb->createNamedParameter($data, IQueryBuilder::PARAM_STR)));
$cursor = $qb->execute();
$row = $cursor->fetch(PDO::FETCH_ASSOC);
if ($row === false) {
$cursor->closeCursor();
throw new DoesNotExistException('Did expect one result but found none when executing' . $qb);
}
$cursor->closeCursor();
return $this->mapRowToEntity($row);
return $this->findEntity($qb);
}
/**
* @param int $cardId
* @param string $data
* @return Attachment
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
* @throws \OCP\DB\Exception
*/
public function findByData($cardId, $data) {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('data', $qb->createNamedParameter($data, IQueryBuilder::PARAM_STR)));
return $this->findEntity($qb);
}
/**
* Find all attachments for a card
*
* @param $cardId
* @return array
* @return Entity[]
* @throws \OCP\DB\Exception
*/
public function findAll($cardId) {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from('deck_attachment')
->from($this->getTableName())
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
$entities = [];
$cursor = $qb->execute();
while ($row = $cursor->fetch()) {
$entities[] = $this->mapRowToEntity($row);
}
$cursor->closeCursor();
return $entities;
return $this->findEntities($qb);
}
/**
@@ -128,7 +110,7 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
$timeLimit = time() - (60 * 5);
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from('deck_attachment')
->from($this->getTableName())
->where($qb->expr()->gt('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
if ($withOffset) {
$qb
@@ -139,13 +121,7 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
->andWhere($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)));
}
$entities = [];
$cursor = $qb->execute();
while ($row = $cursor->fetch()) {
$entities[] = $this->mapRowToEntity($row);
}
$cursor->closeCursor();
return $entities;
return $this->findEntities($qb);
}

View File

@@ -23,17 +23,15 @@
namespace OCA\Deck\Db;
use OCP\AppFramework\Db\Mapper;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
/**
* Class DeckMapper
*
* @package OCA\Deck\Db
* @deprecated use QBMapper
*
* TODO: Move to QBMapper once Nextcloud 14 is a minimum requirement
*/
class DeckMapper extends Mapper {
class DeckMapper extends QBMapper {
/**
* @param $id
@@ -42,11 +40,11 @@ class DeckMapper extends Mapper {
* @throws \OCP\AppFramework\Db\DoesNotExistException
*/
public function find($id) {
$sql = 'SELECT * FROM `' . $this->tableName . '` ' . 'WHERE `id` = ?';
return $this->findEntity($sql, [$id]);
}
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
protected function execute($sql, array $params = [], $limit = null, $offset = null) {
return parent::execute($sql, $params, $limit, $offset);
return $this->findEntity($qb);
}
}

View File

@@ -26,6 +26,7 @@ namespace OCA\Deck\Db;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
class LabelMapper extends DeckMapper implements IPermissionMapper {
@@ -33,41 +34,105 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
parent::__construct($db, 'deck_labels', Label::class);
}
public function findAll($boardId, $limit = null, $offset = null) {
$sql = 'SELECT * FROM `*PREFIX*deck_labels` WHERE `board_id` = ? ORDER BY `id`';
return $this->findEntities($sql, [$boardId], $limit, $offset);
/**
* @param numeric $boardId
* @param int|null $limit
* @param int|null $offset
* @return Label[]
* @throws \OCP\DB\Exception
*/
public function findAll($boardId, $limit = null, $offset = null): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
->setMaxResults($limit)
->setFirstResult($offset);
return $this->findEntities($qb);
}
public function delete(\OCP\AppFramework\Db\Entity $entity) {
/**
* @param Entity $entity
* @return Entity
* @throws \OCP\DB\Exception
*/
public function delete(Entity $entity): Entity {
// delete assigned labels
$this->deleteLabelAssignments($entity->getId());
// delete label
return parent::delete($entity);
}
public function findAssignedLabelsForCard($cardId, $limit = null, $offset = null) {
$sql = 'SELECT l.*,card_id FROM `*PREFIX*deck_assigned_labels` as al INNER JOIN *PREFIX*deck_labels as l ON l.id = al.label_id WHERE `card_id` = ? ORDER BY l.id';
return $this->findEntities($sql, [$cardId], $limit, $offset);
}
public function findAssignedLabelsForBoard($boardId, $limit = null, $offset = null) {
$sql = 'SELECT c.id as card_id, l.id as id, l.title as title, l.color as color FROM `*PREFIX*deck_cards` as c ' .
' INNER JOIN `*PREFIX*deck_assigned_labels` as al ON al.card_id = c.id INNER JOIN `*PREFIX*deck_labels` as l ON al.label_id = l.id WHERE board_id=? ORDER BY l.id';
return $this->findEntities($sql, [$boardId], $limit, $offset);
/**
* @param numeric $cardId
* @param int|null $limit
* @param int|null $offset
* @return Label[]
* @throws \OCP\DB\Exception
*/
public function findAssignedLabelsForCard($cardId, $limit = null, $offset = null): array {
$qb = $this->db->getQueryBuilder();
$qb->select('l.*', 'card_id')
->from($this->getTableName(), 'l')
->innerJoin('l', 'deck_assigned_labels', 'al', 'l.id = al.label_id')
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)))
->orderBy('l.id')
->setMaxResults($limit)
->setFirstResult($offset);
return $this->findEntities($qb);
}
public function insert(Entity $entity) {
/**
* @param numeric $boardId
* @param int|null $limit
* @param int|null $offset
* @return Label[]
* @throws \OCP\DB\Exception
*/
public function findAssignedLabelsForBoard($boardId, $limit = null, $offset = null): array {
$qb = $this->db->getQueryBuilder();
$qb->select('l.id as id', 'l.title as title', 'l.color as color')
->selectAlias('c.id', 'card_id')
->from($this->getTableName(), 'l')
->innerJoin('l', 'deck_assigned_labels', 'al', 'al.label_id = l.id')
->innerJoin('l', 'deck_cards', 'c', 'al.card_id = c.id')
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
->orderBy('l.id')
->setMaxResults($limit)
->setFirstResult($offset);
return $this->findEntities($qb);
}
/**
* @param Entity $entity
* @return Entity
* @throws \OCP\DB\Exception
*/
public function insert(Entity $entity): Entity {
$entity->setLastModified(time());
return parent::insert($entity);
}
public function update(Entity $entity, $updateModified = true) {
/**
* @param Entity $entity
* @param bool $updateModified
* @return Entity
* @throws \OCP\DB\Exception
*/
public function update(Entity $entity, $updateModified = true): Entity {
if ($updateModified) {
$entity->setLastModified(time());
}
return parent::update($entity);
}
/**
* @param numeric $boardId
* @return array
* @throws \OCP\DB\Exception
*/
public function getAssignedLabelsForBoard($boardId) {
$labels = $this->findAssignedLabelsForBoard($boardId);
$result = [];
@@ -80,27 +145,51 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
return $result;
}
/**
* @param numeric $labelId
* @return void
* @throws \OCP\DB\Exception
*/
public function deleteLabelAssignments($labelId) {
$sql = 'DELETE FROM `*PREFIX*deck_assigned_labels` WHERE label_id = ?';
$stmt = $this->db->prepare($sql);
$stmt->bindParam(1, $labelId, \PDO::PARAM_INT, 0);
$stmt->execute();
$qb = $this->db->getQueryBuilder();
$qb->delete('deck_assigned_labels')
->where($qb->expr()->eq('label_id', $qb->createNamedParameter($labelId, IQueryBuilder::PARAM_INT)));
$qb->executeStatement();
}
/**
* @param numeric $cardId
* @return void
* @throws \OCP\DB\Exception
*/
public function deleteLabelAssignmentsForCard($cardId) {
$sql = 'DELETE FROM `*PREFIX*deck_assigned_labels` WHERE card_id = ?';
$stmt = $this->db->prepare($sql);
$stmt->bindParam(1, $cardId, \PDO::PARAM_INT, 0);
$stmt->execute();
$qb = $this->db->getQueryBuilder();
$qb->delete('deck_assigned_labels')
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)));
$qb->executeStatement();
}
/**
* @param string $userId
* @param numeric $labelId
* @return bool
* @throws \OCP\DB\Exception
*/
public function isOwner($userId, $labelId): bool {
$sql = 'SELECT owner FROM `*PREFIX*deck_boards` WHERE `id` IN (SELECT board_id FROM `*PREFIX*deck_labels` WHERE id = ?)';
$stmt = $this->execute($sql, [$labelId]);
$row = $stmt->fetch();
return ($row['owner'] === $userId);
$qb = $this->db->getQueryBuilder();
$qb->select('l.id')
->from($this->getTableName(), 'l')
->innerJoin('l', 'deck_boards', 'b', 'l.board_id = b.id')
->where($qb->expr()->eq('l.id', $qb->createNamedParameter($labelId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('b.owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
return count($qb->executeQuery()->fetchAll()) > 0;
}
/**
* @param numeric $id
* @return int|null
*/
public function findBoardId($id): ?int {
try {
$entity = $this->find($id);

View File

@@ -26,6 +26,7 @@ namespace OCA\Deck\Db;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
class StackMapper extends DeckMapper implements IPermissionMapper {
@@ -38,62 +39,112 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
/**
* @param $id
* @throws MultipleObjectsReturnedException
* @param numeric $id
* @return Stack
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
* @throws \OCP\DB\Exception
*/
public function find($id): Stack {
$sql = 'SELECT * FROM `*PREFIX*deck_stacks` ' .
'WHERE `id` = ?';
return $this->findEntity($sql, [$id]);
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
return $this->findEntity($qb);
}
/**
* @param $cardId
* @return Stack|null
* @throws \OCP\DB\Exception
*/
public function findStackFromCardId($cardId): ?Stack {
$sql = <<<SQL
SELECT s.*
FROM `*PREFIX*deck_stacks` as `s`
INNER JOIN `*PREFIX*deck_cards` as `c` ON s.id = c.stack_id
WHERE c.id = ?
SQL;
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName(), 's')
->innerJoin('s', 'deck_cards', 'c', 's.id = c.stack_id')
->where($qb->expr()->eq('c.id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)));
try {
return $this->findEntity($sql, [$cardId]);
return $this->findEntity($qb);
} catch (MultipleObjectsReturnedException|DoesNotExistException $e) {
}
return null;
}
/**
* @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 {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
->setFirstResult($offset)
->setMaxResults($limit);
public function findAll($boardId, $limit = null, $offset = null) {
$sql = 'SELECT * FROM `*PREFIX*deck_stacks` WHERE `board_id` = ? AND deleted_at = 0 ORDER BY `order`, `id`';
return $this->findEntities($sql, [$boardId], $limit, $offset);
return $this->findEntities($qb);
}
/**
* @param numeric $boardId
* @param int|null $limit
* @param int|null $offset
* @return Stack[]
* @throws \OCP\DB\Exception
*/
public function findDeleted($boardId, $limit = null, $offset = null) {
$sql = 'SELECT * FROM `*PREFIX*deck_stacks` s
WHERE `s`.`board_id` = ? AND NOT s.deleted_at = 0';
return $this->findEntities($sql, [$boardId], $limit, $offset);
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->neq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
->setFirstResult($offset)
->setMaxResults($limit);
return $this->findEntities($qb);
}
public function delete(Entity $entity) {
/**
* @param Entity $entity
* @return Entity
* @throws \OCP\DB\Exception
*/
public function delete(Entity $entity): Entity {
// delete cards on stack
$this->cardMapper->deleteByStack($entity->getId());
return parent::delete($entity);
}
/**
* @param numeric $userId
* @param numeric $stackId
* @return bool
* @throws \OCP\DB\Exception
*/
public function isOwner($userId, $stackId): bool {
$sql = 'SELECT owner FROM `*PREFIX*deck_boards` WHERE `id` IN (SELECT board_id FROM `*PREFIX*deck_stacks` WHERE id = ?)';
$stmt = $this->execute($sql, [$stackId]);
$row = $stmt->fetch();
return ($row['owner'] === $userId);
$qb = $this->db->getQueryBuilder();
$qb->select('s.id')
->from($this->getTableName(), 's')
->innerJoin('s', 'deck_boards', 'b', 'b.id = s.board_id')
->where($qb->expr()->eq('s.id', $qb->createNamedParameter($stackId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
return count($qb->executeQuery()->fetchAll()) > 0;
}
/**
* @param numeric $id
* @return int|null
* @throws \OCP\DB\Exception
*/
public function findBoardId($id): ?int {
try {
$entity = $this->find($id);

View File

@@ -625,11 +625,9 @@ class BoardService {
} catch (\Exception $e) {
}
}
$delete = $this->aclMapper->delete($acl);
$this->eventDispatcher->dispatchTyped(new AclDeletedEvent($acl));
return $delete;
return (bool) $this->aclMapper->delete($acl);
}
/**

View File

@@ -91,12 +91,10 @@ class LabelService {
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_MANAGE);
$boardLabels = $this->labelMapper->findAll($boardId);
if (\is_array($boardLabels)) {
foreach ($boardLabels as $boardLabel) {
if ($boardLabel->getTitle() === $title) {
throw new BadRequestException('title must be unique');
break;
}
foreach ($boardLabels as $boardLabel) {
if ($boardLabel->getTitle() === $title) {
throw new BadRequestException('title must be unique');
break;
}
}
@@ -163,15 +161,13 @@ class LabelService {
$label = $this->find($id);
$boardLabels = $this->labelMapper->findAll($label->getBoardId());
if (\is_array($boardLabels)) {
foreach ($boardLabels as $boardLabel) {
if ($boardLabel->getId() === $label->getId()) {
continue;
}
if ($boardLabel->getTitle() === $title) {
throw new BadRequestException('title must be unique');
break;
}
foreach ($boardLabels as $boardLabel) {
if ($boardLabel->getId() === $label->getId()) {
continue;
}
if ($boardLabel->getTitle() === $title) {
throw new BadRequestException('title must be unique');
break;
}
}

View File

@@ -420,7 +420,7 @@ class BoardServiceTest extends TestCase {
$this->aclMapper->expects($this->once())
->method('delete')
->with($acl)
->willReturn(true);
->willReturn($acl);
$this->assertTrue($this->service->deleteAcl(123));
}
}

View File

@@ -160,6 +160,7 @@ class CardServiceTest extends TestCase {
$cardExpected->setAssignedUsers(['user1', 'user2']);
$cardExpected->setRelatedBoard($boardMock);
$cardExpected->setRelatedStack($stackMock);
$cardExpected->setLabels([]);
$this->assertEquals($cardExpected, $this->cardService->find(123));
}