added multi-select for customer, project and activity (#1557)

This commit is contained in:
Kevin Papst
2020-03-16 14:06:26 +01:00
committed by GitHub
parent 6eeb01ad48
commit 4b1cfa5840
74 changed files with 1262 additions and 754 deletions

View File

@@ -154,13 +154,13 @@ class ActivityRepository extends EntityRepository
}
/**
* @deprecated since 1.1
* @deprecated since 1.1 - use getQueryBuilderForFormType() instead - will be removed with 2.0
*/
public function builderForEntityType($activity, $project)
{
$query = new ActivityFormTypeQuery();
$query->setActivity($activity);
$query->setProject($project);
$query->addActivity($activity);
$query->addProject($project);
return $this->getQueryBuilderForFormType($query);
}
@@ -212,14 +212,14 @@ class ActivityRepository extends EntityRepository
if ($query->isGlobalsOnly()) {
$where->add($qb->expr()->isNull('a.project'));
} elseif (null !== $query->getProject()) {
} elseif ($query->hasProjects()) {
$where->add(
$qb->expr()->orX(
$qb->expr()->eq('a.project', ':project'),
$qb->expr()->in('a.project', ':project'),
$qb->expr()->isNull('a.project')
)
);
$qb->setParameter('project', $query->getProject());
$qb->setParameter('project', $query->getProjects());
}
if (null !== $query->getActivityToIgnore()) {
@@ -233,10 +233,9 @@ class ActivityRepository extends EntityRepository
$or->add($where);
// this must always be the last part of the query
/* @var Activity $entity */
if (null !== $query->getActivity()) {
$or->add($qb->expr()->eq('a.id', ':activity'));
$qb->setParameter('activity', $query->getActivity());
if ($query->hasActivities()) {
$or->add($qb->expr()->in('a.id', ':activity'));
$qb->setParameter('activity', $query->getActivities());
}
if ($or->count() > 0) {
@@ -303,9 +302,9 @@ class ActivityRepository extends EntityRepository
if ($query->isGlobalsOnly()) {
$where->add($qb->expr()->isNull('a.project'));
} elseif (null !== $query->getProject()) {
} elseif ($query->hasProjects()) {
$orX = $qb->expr()->orX(
$qb->expr()->eq('a.project', ':project')
$qb->expr()->in('a.project', ':project')
);
if (!$query->isExcludeGlobals()) {
@@ -313,7 +312,7 @@ class ActivityRepository extends EntityRepository
}
$where->add($orX);
$qb->setParameter('project', $query->getProject());
$qb->setParameter('project', $query->getProjects());
} elseif (null !== $query->getCustomer()) {
$where->add('p.customer = :customer');
$qb->setParameter('customer', $query->getCustomer());

View File

@@ -163,12 +163,12 @@ class CustomerRepository extends EntityRepository
}
/**
* @deprecated since 1.1 - don't use this method, it ignores team permission checks
* @deprecated since 1.1 - use getQueryBuilderForFormType() istead - will be removed with 2.0
*/
public function builderForEntityType($customer)
{
$query = new CustomerFormTypeQuery();
$query->setCustomer($customer);
$query->addCustomer($customer);
return $this->getQueryBuilderForFormType($query);
}
@@ -190,9 +190,9 @@ class CustomerRepository extends EntityRepository
$qb->andWhere($qb->expr()->eq('c.visible', ':visible'));
$qb->setParameter('visible', true, \PDO::PARAM_BOOL);
$customer = $query->getCustomer();
if (null !== $customer) {
$qb->orWhere('c.id = :customer')->setParameter('customer', $customer);
if ($query->hasCustomers()) {
$qb->orWhere($qb->expr()->in('c.id', ':customer'))
->setParameter('customer', $query->getCustomers());
}
if (null !== $query->getCustomerToIgnore()) {

View File

@@ -152,13 +152,13 @@ class ProjectRepository extends EntityRepository
}
/**
* @deprecated since 1.1 - don't use this method, it ignores team permission checks
* @deprecated since 1.1 - use getQueryBuilderForFormType() istead - will be removed with 2.0
*/
public function builderForEntityType($project, $customer)
{
$query = new ProjectFormTypeQuery();
$query->setProject($project);
$query->setCustomer($customer);
$query->addProject($project);
$query->addCustomer($customer);
return $this->getQueryBuilderForFormType($query);
}
@@ -216,13 +216,14 @@ class ProjectRepository extends EntityRepository
$qb->setParameter('visible', true, \PDO::PARAM_BOOL);
$qb->setParameter('customer_visible', true, \PDO::PARAM_BOOL);
if (null !== $query->getProject()) {
$qb->orWhere('p.id = :project')->setParameter('project', $query->getProject());
if ($query->hasProjects()) {
$qb->orWhere($qb->expr()->in('p.id', ':project'))
->setParameter('project', $query->getProjects());
}
if (null !== $query->getCustomer()) {
$qb->andWhere('p.customer = :customer')
->setParameter('customer', $query->getCustomer());
if ($query->hasCustomers()) {
$qb->andWhere($qb->expr()->in('p.customer', ':customer'))
->setParameter('customer', $query->getCustomers());
}
if (null !== $query->getProjectToIgnore()) {
@@ -272,9 +273,9 @@ class ProjectRepository extends EntityRepository
$qb->setParameter('customer_visible', true, \PDO::PARAM_BOOL);
}
if (null !== $query->getCustomer()) {
$qb->andWhere('p.customer = :customer')
->setParameter('customer', $query->getCustomer());
if ($query->hasCustomers()) {
$qb->andWhere($qb->expr()->in('p.customer', ':customer'))
->setParameter('customer', $query->getCustomers());
}
// this is far from being perfect, possible enhancements:

View File

@@ -12,67 +12,32 @@ namespace App\Repository\Query;
use App\Entity\Activity;
use App\Entity\Project;
final class ActivityFormTypeQuery
final class ActivityFormTypeQuery extends BaseFormTypeQuery
{
/**
* @var Activity|int|null
*/
private $activity;
/**
* @var Project|int|null
*/
private $project;
/**
* @var Activity|null
*/
private $activityToIgnore;
/**
* @param Activity|int|null $activity
* @param Project|int|null $project
* @param Activity|int|array|null $activity
* @param Project|int|array|null $project
*/
public function __construct($activity = null, $project = null)
{
$this->activity = $activity;
$this->project = $project;
}
if (null !== $activity) {
if (!is_array($activity)) {
$activity = [$activity];
}
$this->setActivities($activity);
}
/**
* @return Activity|int|null
*/
public function getActivity()
{
return $this->activity;
}
/**
* @param Activity|int|null $activity
* @return ActivityFormTypeQuery
*/
public function setActivity($activity): ActivityFormTypeQuery
{
$this->activity = $activity;
return $this;
}
/**
* @return Project|int|null
*/
public function getProject()
{
return $this->project;
}
/**
* @param Project|int|null $project
* @return ActivityFormTypeQuery
*/
public function setProject($project): ActivityFormTypeQuery
{
$this->project = $project;
return $this;
if (null !== $project) {
if (!is_array($project)) {
$project = [$project];
}
$this->setProjects($project);
}
}
/**
@@ -92,12 +57,21 @@ final class ActivityFormTypeQuery
public function isGlobalsOnly(): bool
{
return
(
null === $this->activity ||
($this->activity instanceof Activity && null === $this->activity->getProject())
)
&&
null === $this->project;
if ($this->hasProjects()) {
return false;
}
if (!$this->hasActivities()) {
return true;
}
foreach ($this->getActivities() as $activity) {
// this is a potential problem, if only IDs were set
if ($activity instanceof Activity && !$activity->isGlobal()) {
return false;
}
}
return true;
}
}

View File

@@ -19,9 +19,9 @@ class ActivityQuery extends ProjectQuery
public const ACTIVITY_ORDER_ALLOWED = ['id', 'name', 'comment', 'customer', 'project'];
/**
* @var Project|int|null
* @var Project[]|int[]
*/
private $project;
private $projects = [];
/**
* @var bool
*/
@@ -72,20 +72,58 @@ class ActivityQuery extends ProjectQuery
/**
* @return Project|int|null
* @deprecated since 1.9 - use getProjects() instead - will be removed with 2.0
*/
public function getProject()
{
return $this->project;
if (count($this->projects) > 0) {
return $this->projects[0];
}
return null;
}
/**
* @param Project|int|null $project
* @return self
* @deprecated since 1.9 - use setProjects() or addProject() instead - will be removed with 2.0
*/
public function setProject($project = null): self
{
$this->project = $project;
if (null === $project) {
$this->projects = [];
} else {
$this->projects = [$project];
}
return $this;
}
/**
* @param Project|int $project
* @return self
*/
public function addProject($project): self
{
$this->projects[] = $project;
return $this;
}
public function setProjects(array $projects): self
{
$this->projects = $projects;
return $this;
}
public function getProjects(): array
{
return $this->projects;
}
public function hasProjects(): bool
{
return !empty($this->projects);
}
}

View File

@@ -0,0 +1,257 @@
<?php
/*
* This file is part of the Kimai time-tracking app.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Repository\Query;
use App\Entity\Activity;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\Team;
use App\Entity\User;
abstract class BaseFormTypeQuery
{
/**
* @var array
*/
private $activities = [];
/**
* @var array
*/
private $projects = [];
/**
* @var array
*/
private $customers = [];
/**
* @var User
*/
private $user;
/**
* @var array<Team>
*/
private $teams = [];
/**
* @return Activity|int|null
* @deprecated since 1.9 - use getActivities() instead - will be removed with 2.0
*/
public function getActivity()
{
if (count($this->activities) > 0) {
return $this->activities[0];
}
return null;
}
/**
* @param Activity|int|null $activity
* @return self
* @deprecated since 1.9 - use setActivities() or addActivity() instead - will be removed with 2.0
*/
public function setActivity($activity): self
{
if (null === $activity) {
$this->activities = [];
} else {
$this->activities = [$activity];
}
return $this;
}
/**
* @param Activity|int $activity
* @return self
*/
public function addActivity($activity): self
{
if (null !== $activity) {
$this->activities[] = $activity;
}
return $this;
}
/**
* @param Activity[]|int[] $activities
* @return self
*/
public function setActivities(array $activities): self
{
$this->activities = $activities;
return $this;
}
public function getActivities(): array
{
return $this->activities;
}
public function hasActivities(): bool
{
return !empty($this->activities);
}
/**
* @return Project|int|null
* @deprecated since 1.9 - use getProjects() instead - will be removed with 2.0
*/
public function getProject()
{
if (count($this->projects) > 0) {
return $this->projects[0];
}
return null;
}
/**
* @param Project|int|null $project
* @return self
* @deprecated since 1.9 - use addProject() instead - will be removed with 2.0
*/
public function setProject($project): self
{
if (null === $project) {
$this->projects = [];
} else {
$this->projects = [$project];
}
return $this;
}
/**
* @param Project|int $project
* @return self
*/
public function addProject($project): self
{
if (null !== $project) {
$this->projects[] = $project;
}
return $this;
}
/**
* @param Project[]|int[] $projects
* @return self
*/
public function setProjects(array $projects): self
{
$this->projects = $projects;
return $this;
}
/**
* @return array
*/
public function getProjects(): array
{
return $this->projects;
}
public function hasProjects(): bool
{
return !empty($this->projects);
}
/**
* @return Customer|int|null
* @deprecated since 1.9 - use getCustomers() instead - will be removed with 2.0
*/
public function getCustomer()
{
if (count($this->customers) > 0) {
return $this->customers[0];
}
return null;
}
/**
* @param Customer|int|null $customer
* @return self
* @deprecated since 1.9 - use addCustomer() instead - will be removed with 2.0
*/
public function setCustomer($customer): self
{
if (null === $customer) {
$this->customers = [];
} else {
$this->customers = [$customer];
}
return $this;
}
/**
* @param Customer[]|int[] $customers
* @return self
*/
public function setCustomers(array $customers): self
{
$this->customers = $customers;
return $this;
}
/**
* @param Customer|int $customer
* @return self
*/
public function addCustomer($customer): self
{
$this->customers[] = $customer;
return $this;
}
public function getCustomers(): array
{
return $this->customers;
}
public function hasCustomers(): bool
{
return !empty($this->customers);
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(User $user): self
{
$this->user = $user;
return $this;
}
public function addTeam(Team $team): self
{
$this->teams[$team->getId()] = $team;
return $this;
}
/**
* @return Team[]
*/
public function getTeams(): array
{
return array_values($this->teams);
}
}

View File

@@ -10,83 +10,28 @@
namespace App\Repository\Query;
use App\Entity\Customer;
use App\Entity\Team;
use App\Entity\User;
/**
* Can be used for advanced queries with the: CustomerRepository
*/
final class CustomerFormTypeQuery
final class CustomerFormTypeQuery extends BaseFormTypeQuery
{
/**
* @var Customer|int|null
*/
private $customer;
/**
* @var Customer|null
*/
private $customerToIgnore;
/**
* @var User
*/
private $user;
/**
* @var array<Team>
*/
private $teams = [];
/**
* @param Customer|int|null $customer
*/
public function __construct($customer = null)
{
$this->customer = $customer;
}
public function addTeam(Team $team): CustomerFormTypeQuery
{
$this->teams[$team->getId()] = $team;
return $this;
}
/**
* @return Team[]
*/
public function getTeams(): array
{
return array_values($this->teams);
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(User $user): CustomerFormTypeQuery
{
$this->user = $user;
return $this;
}
/**
* @return Customer|int|null
*/
public function getCustomer()
{
return $this->customer;
}
/**
* @param Customer|int|null $customer
* @return $this
*/
public function setCustomer($customer): CustomerFormTypeQuery
{
$this->customer = $customer;
return $this;
if (null !== $customer) {
if (!is_array($customer)) {
$customer = [$customer];
}
$this->setCustomers($customer);
}
}
/**

View File

@@ -11,31 +11,13 @@ namespace App\Repository\Query;
use App\Entity\Customer;
use App\Entity\Project;
use App\Entity\Team;
use App\Entity\User;
final class ProjectFormTypeQuery
final class ProjectFormTypeQuery extends BaseFormTypeQuery
{
/**
* @var Customer|int|null
*/
private $customer;
/**
* @var Project|int|null
*/
private $project;
/**
* @var Project|null
*/
private $projectToIgnore;
/**
* @var User
*/
private $user;
/**
* @var array<Team>
*/
private $teams = [];
/**
* @var bool
*/
@@ -47,73 +29,19 @@ final class ProjectFormTypeQuery
*/
public function __construct($project = null, $customer = null)
{
$this->project = $project;
$this->customer = $customer;
}
if (null !== $project) {
if (!is_array($project)) {
$project = [$project];
}
$this->setProjects($project);
}
public function addTeam(Team $team): ProjectFormTypeQuery
{
$this->teams[$team->getId()] = $team;
return $this;
}
/**
* @return Team[]
*/
public function getTeams(): array
{
return array_values($this->teams);
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(User $user): ProjectFormTypeQuery
{
$this->user = $user;
return $this;
}
/**
* @return Customer|int|null
*/
public function getCustomer()
{
return $this->customer;
}
/**
* @param Customer|int|null $customer
* @return $this
*/
public function setCustomer($customer): ProjectFormTypeQuery
{
$this->customer = $customer;
return $this;
}
/**
* @return Project|int|null
*/
public function getProject()
{
return $this->project;
}
/**
* @param Project|int|null $project
* @return ProjectFormTypeQuery
*/
public function setProject($project): ProjectFormTypeQuery
{
$this->project = $project;
return $this;
if (null !== $customer) {
if (!is_array($customer)) {
$customer = [$customer];
}
$this->setCustomers($customer);
}
}
/**

View File

@@ -21,9 +21,9 @@ class ProjectQuery extends BaseQuery implements VisibilityInterface
public const PROJECT_ORDER_ALLOWED = ['id', 'name', 'comment', 'customer', 'orderNumber', 'projectStart', 'projectEnd'];
/**
* @var Customer|int|null
* @var array
*/
private $customer;
private $customers = [];
/**
* @var \DateTime
*/
@@ -42,23 +42,61 @@ class ProjectQuery extends BaseQuery implements VisibilityInterface
/**
* @return Customer|int|null
* @deprecated since 1.9 - use getCustomers() instead - will be removed with 2.0
*/
public function getCustomer()
{
return $this->customer;
if (count($this->customers) > 0) {
return $this->customers[0];
}
return null;
}
/**
* @param Customer|int|null $customer
* @return $this
* @deprecated since 1.9 - use setCustomers() or addCustomer() instead - will be removed with 2.0
*/
public function setCustomer($customer = null)
{
$this->customer = $customer;
if (null === $customer) {
$this->customers = [];
} else {
$this->customers = [$customer];
}
return $this;
}
/**
* @param Customer|int $customer
* @return $this
*/
public function addCustomer($customer)
{
$this->customers[] = $customer;
return $this;
}
public function setCustomers(array $customers): self
{
$this->customers = $customers;
return $this;
}
public function getCustomers(): array
{
return $this->customers;
}
public function hasCustomers(): bool
{
return !empty($this->customers);
}
public function getProjectStart(): ?\DateTime
{
return $this->projectStart;

View File

@@ -9,24 +9,6 @@
namespace App\Repository\Query;
use App\Entity\User;
final class TagFormTypeQuery
final class TagFormTypeQuery extends BaseFormTypeQuery
{
/**
* @var User
*/
private $user;
public function getUser(): ?User
{
return $this->user;
}
public function setUser(User $user): TagFormTypeQuery
{
$this->user = $user;
return $this;
}
}

View File

@@ -9,14 +9,50 @@
namespace App\Repository\Query;
use App\Entity\User;
class TeamQuery extends BaseQuery
{
public const TEAM_ORDER_ALLOWED = ['id', 'name', 'teamlead'];
/**
* @var User[]
*/
private $users = [];
public function __construct()
{
$this->setDefaults([
'orderBy' => 'name',
]);
}
public function hasUsers(): bool
{
return !empty($this->users);
}
public function addUser(User $user): self
{
$this->users[$user->getId()] = $user;
return $this;
}
public function removeUser(User $user): self
{
if (isset($this->users[$user->getId()])) {
unset($this->users[$user->getId()]);
}
return $this;
}
/**
* @return User[]
*/
public function getUsers(): array
{
return array_values($this->users);
}
}

View File

@@ -32,9 +32,9 @@ class TimesheetQuery extends ActivityQuery
*/
protected $timesheetUser;
/**
* @var Activity|null
* @var array
*/
protected $activity;
private $activities = [];
/**
* @var int
*/
@@ -114,26 +114,66 @@ class TimesheetQuery extends ActivityQuery
}
/**
* Activity overwrites: setProject() and setCustomer()
*
* @return Activity|null
* @return Activity|int|null
* @deprecated since 1.9 - use getProjects() instead - will be removed with 2.0
*/
public function getActivity()
{
return $this->activity;
if (count($this->activities) > 0) {
return $this->activities[0];
}
return null;
}
public function getActivities(): array
{
return $this->activities;
}
/**
* @param Activity|int|null $activity
* @return TimesheetQuery
* @return $this
* @deprecated since 1.9 - use setActivities() or addActivity() instead - will be removed with 2.0
*/
public function setActivity($activity = null)
public function setActivity($activity)
{
$this->activity = $activity;
if (null === $activity) {
$this->activities = [];
} else {
$this->activities = [$activity];
}
return $this;
}
/**
* @param Activity|int $activity
* @return $this
*/
public function addActivity($activity)
{
$this->activities[] = $activity;
return $this;
}
/**
* @param Activity[]|int[] $activities
* @return $this
*/
public function setActivities(array $activities)
{
$this->activities = $activities;
return $this;
}
public function hasActivities(): bool
{
return !empty($this->activities);
}
/**
* @return int
*/

View File

@@ -9,47 +9,9 @@
namespace App\Repository\Query;
use App\Entity\Team;
use App\Entity\User;
/**
* Can be used for pre-filling form types with the: UserRepository
*/
final class UserFormTypeQuery
final class UserFormTypeQuery extends BaseFormTypeQuery
{
/**
* @var User
*/
private $user;
/**
* @var array<Team>
*/
private $teams = [];
public function addTeam(Team $team): UserFormTypeQuery
{
$this->teams[$team->getId()] = $team;
return $this;
}
/**
* @return Team[]
*/
public function getTeams(): array
{
return array_values($this->teams);
}
public function getUser(): ?User
{
return $this->user;
}
public function setUser(User $user): UserFormTypeQuery
{
$this->user = $user;
return $this;
}
}

View File

@@ -137,6 +137,14 @@ class TeamRepository extends EntityRepository
break;
}
if ($query->hasUsers()) {
$qb->orWhere(
$qb->expr()->in('t.teamlead', ':user'),
$qb->expr()->isMemberOf(':user', 't.users')
)
->setParameter('user', $query->getUsers());
}
$qb->addOrderBy($orderBy, $query->getOrder());
if (!empty($query->getSearchTerm())) {

View File

@@ -9,7 +9,6 @@
namespace App\Repository;
use App\Entity\Activity;
use App\Entity\ActivityRate;
use App\Entity\CustomerRate;
use App\Entity\ProjectRate;
@@ -712,19 +711,17 @@ class TimesheetRepository extends EntityRepository
$qb->andWhere('t.exported = :exported')->setParameter('exported', false, \PDO::PARAM_BOOL);
}
if (null !== $query->getActivity()) {
$qb->andWhere('t.activity = :activity')
->setParameter('activity', $query->getActivity());
if ($query->hasActivities()) {
$qb->andWhere($qb->expr()->in('t.activity', ':activity'))
->setParameter('activity', $query->getActivities());
}
if (null === $query->getActivity() || ($query->getActivity() instanceof Activity && null === $query->getActivity()->getProject())) {
if (null !== $query->getProject()) {
$qb->andWhere('t.project = :project')
->setParameter('project', $query->getProject());
} elseif (null !== $query->getCustomer()) {
$qb->andWhere('p.customer = :customer')
->setParameter('customer', $query->getCustomer());
}
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();