File: //home/timetracker.panomity.com/tm.old/Repository/TaskRepository.php
<?php
/*
* This file is part of the TaskManagementBundle for Kimai 2.
* All rights reserved by Kevin Papst (www.kevinpapst.de).
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace KimaiPlugin\TaskManagementBundle\Repository;
use App\Entity\Team;
use App\Entity\Timesheet;
use App\Entity\User;
use App\Repository\Paginator\LoaderPaginator;
use App\Repository\Paginator\PaginatorInterface;
use DateTime;
use DateTimeZone;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\QueryBuilder;
use KimaiPlugin\TaskManagementBundle\Entity\Task;
use KimaiPlugin\TaskManagementBundle\Entity\TaskComment;
use KimaiPlugin\TaskManagementBundle\Repository\Loader\TaskLoader;
use KimaiPlugin\TaskManagementBundle\Repository\Query\TaskQuery;
use Pagerfanta\Pagerfanta;
/**
* @extends \Doctrine\ORM\EntityRepository<Task>
*/
final class TaskRepository extends EntityRepository
{
public function getUserTasksQuery(User $user, int $pageSize = 10, int $page = 1): TaskQuery
{
$query = new TaskQuery();
$query->setUser($user);
$query->setPageSize($pageSize);
$query->setPage($page);
$query->setStatus([Task::STATUS_PENDING, Task::STATUS_IN_PROGRESS]);
$query->setBegin(new DateTime('now', new DateTimeZone($user->getTimezone())));
return $query;
}
public function getPendingTasksQuery(User $user, int $pageSize = 10, int $page = 1): TaskQuery
{
$query = new TaskQuery();
$query->setCurrentUser($user);
$query->setUnassigned(true);
$query->setPageSize($pageSize);
$query->setPage($page);
$query->setBegin(new DateTime('now', new DateTimeZone($user->getTimezone())));
return $query;
}
public function findByTimesheet(Timesheet $timesheet): ?Task
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->select('t')
->from(Task::class, 't')
->where($qb->expr()->isMemberOf(':timesheet', 't.timesheets'))
->setParameter('timesheet', [$timesheet])
;
return $qb->getQuery()->getOneOrNullResult();
}
public function saveTask(Task $task): void
{
$entityManager = $this->getEntityManager();
$entityManager->persist($task);
$entityManager->flush();
}
public function deleteTask(Task $task): void
{
$entityManager = $this->getEntityManager();
$entityManager->remove($task);
$entityManager->flush();
}
public function getPagerfantaForQuery(TaskQuery $query): Pagerfanta
{
$paginator = new Pagerfanta($this->getPaginatorForQuery($query));
$paginator->setMaxPerPage($query->getPageSize());
$paginator->setCurrentPage($query->getPage());
return $paginator;
}
private function getPaginatorForQuery(TaskQuery $query): PaginatorInterface
{
$qb = $this->getQueryBuilderForQuery($query);
$qb1 = clone $qb;
$qb
->resetDQLPart('select')
->resetDQLPart('orderBy')
->resetDQLPart('groupBy')
->select($qb->expr()->countDistinct('t.id'))
;
$counter = (int) $qb->getQuery()->getSingleScalarResult();
return new LoaderPaginator(new TaskLoader($qb1->getEntityManager()), $qb1, $counter);
}
/**
* @param TaskQuery $query
* @return Task[]
*/
public function getTasksForQuery(TaskQuery $query): iterable
{
// this is using the paginator internally, as it will load all joined entities into the working unit
// do not "optimize" to use the query directly, as it would results in hundreds of additional lazy queries
$paginator = $this->getPaginatorForQuery($query);
return $paginator->getAll();
}
private function getQueryBuilderForQuery(TaskQuery $query): QueryBuilder
{
if ($query->isUnassigned()) {
return $this->getQueryBuilderForPendingTasks($query);
}
return $this->getQueryBuilderForCollection($query);
}
private function getQueryBuilderForPendingTasks(TaskQuery $query): QueryBuilder
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->select('t')
->from(Task::class, 't')
->leftJoin('t.project', 'p')
->leftJoin('p.customer', 'c')
;
$currentUser = $query->getCurrentUser();
$unassigned = $qb->expr()->andX($qb->expr()->isNull('t.user'));
if (null === $currentUser) {
$unassigned->add($qb->expr()->isNull('t.team'));
} elseif (!$currentUser->canSeeAllData()) {
if ($currentUser->hasTeamAssignment()) {
$unassigned->add(
$qb->expr()->orX(
$qb->expr()->isNull('t.team'),
$qb->expr()->in('t.team', ':userTeams')
)
);
$qb->setParameter('userTeams', $currentUser->getTeams());
} else {
$unassigned->add($qb->expr()->isNull('t.team'));
}
}
$qb->andWhere($unassigned);
// select "in progress" as well, because unassigned tasks can be started as well
$qb->andWhere(
$qb->expr()->in('t.status', [Task::STATUS_PENDING, Task::STATUS_IN_PROGRESS])
);
$qb->setMaxResults($query->getPageSize());
$orderBy = 'endSort';
if ($query->getOrder() === TaskQuery::ORDER_ASC) {
$qb->addSelect('COALESCE(t.end, \'9999-12-31 23:59:59\') AS HIDDEN endSort');
} else {
$qb->addSelect('COALESCE(t.end, \'0000-12-31 23:59:59\') AS HIDDEN endSort');
}
$qb->addOrderBy($orderBy, $query->getOrder());
return $qb;
}
private function getQueryBuilderForCollection(TaskQuery $query): QueryBuilder
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->select('t')
->from(Task::class, 't')
->leftJoin('t.project', 'p')
->leftJoin('p.customer', 'c')
;
$currentUser = $query->getCurrentUser();
$user = [];
if (null !== $query->getUser()) {
$user[] = $query->getUser();
}
$user = array_merge($user, $query->getUsers());
$permission = $qb->expr()->orX();
if (null !== $currentUser && !$currentUser->canSeeAllData()) {
// make sure that the user is in the list of users, if he is part of a team.
// if teams are used and the user is not a teamlead, the list of users would be
// empty and then leading to NOT limit the select by user IDs
if (\count($user) === 0) {
$user[] = $currentUser;
/** @var Team $team */
foreach ($currentUser->getTeams() as $team) {
if ($currentUser->isTeamleadOf($team)) {
foreach ($team->getUsers() as $tmpUser) {
$user[] = $tmpUser;
}
}
}
}
// see tasks of team member in listing page
if ($query->isWithTeamTasks() && $currentUser->hasTeamAssignment()) {
$permission->add($qb->expr()->in('t.team', ':userTeams'));
$qb->setParameter('userTeams', $currentUser->getTeams());
}
if ($query->isWithUnassigned()) {
$orTeams = $qb->expr()->orX();
if ($currentUser->hasTeamAssignment()) {
$orTeams->add($qb->expr()->isNull('t.team'));
$orTeams->add($qb->expr()->in('t.team', ':userTeams'));
$qb->setParameter('userTeams', $currentUser->getTeams());
}
if ($orTeams->count() > 0) {
$permission->add(
$qb->expr()->andX(
$qb->expr()->isNull('t.user'),
$orTeams
)
);
}
$permission->add(
$qb->expr()->andX(
$qb->expr()->isNull('t.user'),
$qb->expr()->isNull('t.team')
)
);
}
}
if (\count($user) > 0) {
$user = array_map(function (User $user) {
return $user->getId();
}, $user);
$user = array_unique($user);
$permission->add($qb->expr()->in('t.user', $user));
}
if ($permission->count() > 0) {
$qb->andWhere($permission);
}
if (null !== $query->getEnd()) {
$orEnd = $qb->expr()->orX(
$qb->expr()->lte('t.end', ':end'),
$qb->expr()->isNull('t.end')
);
$qb->andWhere($orEnd)->setParameter('end', $query->getEnd());
}
if (!empty($query->getStatus())) {
$qb->andWhere(
$qb->expr()->in('t.status', $query->getStatus())
);
}
if ($query->hasActivities()) {
$qb->andWhere($qb->expr()->in('t.activity', ':activity'))
->setParameter('activity', $query->getActivities());
}
if (!empty($query->getTeams())) {
$qb->andWhere($qb->expr()->in('t.team', ':team'))
->setParameter('team', $query->getTeams());
}
if ($query->hasProjects()) {
$qb->andWhere($qb->expr()->in('t.project', ':project'))
->setParameter('project', $query->getProjects());
} elseif ($query->hasCustomers()) {
$qb->andWhere($qb->expr()->in('p.customer', ':customer'))
->setParameter('customer', $query->getCustomers());
}
$tags = $query->getTags();
if (!empty($tags)) {
$qb->andWhere($qb->expr()->isMemberOf(':tags', 't.tags'))
->setParameter('tags', $query->getTags());
}
$orderBy = $query->getOrderBy();
switch ($orderBy) {
case 'project':
$orderBy = 'p.name';
break;
case 'customer':
$orderBy = 'c.name';
break;
case 'activity':
$qb->leftJoin('t.activity', 'a');
$orderBy = 'a.name';
break;
case 'user':
$qb->leftJoin('t.user', 'u');
$orderBy = 'u.username';
break;
case 'team':
$qb->leftJoin('t.team', 'team');
$orderBy = 'team.name';
break;
case 'end':
$orderBy = 'endSort';
if ($query->getOrder() === TaskQuery::ORDER_ASC) {
$qb->addSelect('COALESCE(t.end, \'9999-12-31 23:59:59\') AS HIDDEN endSort');
} else {
$qb->addSelect('COALESCE(t.end, \'0000-12-31 23:59:59\') AS HIDDEN endSort');
}
break;
default:
$orderBy = 't.' . $orderBy;
break;
}
$qb->addOrderBy($orderBy, $query->getOrder());
if ($query->hasSearchTerm()) {
$searchAnd = $qb->expr()->andX();
$searchTerm = $query->getSearchTerm();
if ($searchTerm->hasSearchTerm()) {
$searchAnd->add(
$qb->expr()->orX(
$qb->expr()->like('t.title', ':searchTerm'),
$qb->expr()->like('t.description', ':searchTerm')
)
);
$qb->setParameter('searchTerm', '%' . $searchTerm->getSearchTerm() . '%');
}
if ($searchAnd->count() > 0) {
$qb->andWhere($searchAnd);
}
}
$qb->setMaxResults($query->getPageSize());
return $qb;
}
public function getComments(Task $task): array
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb
->select('comments')
->from(TaskComment::class, 'comments')
->andWhere($qb->expr()->eq('comments.task', ':task'))
->addOrderBy('comments.pinned', 'DESC')
->addOrderBy('comments.createdAt', 'DESC')
->setParameter('task', $task)
;
return $qb->getQuery()->getResult();
}
public function saveComment(TaskComment $comment): void
{
$entityManager = $this->getEntityManager();
$entityManager->persist($comment);
$entityManager->flush();
}
public function deleteComment(TaskComment $comment): void
{
$entityManager = $this->getEntityManager();
$entityManager->remove($comment);
$entityManager->flush();
}
}