|DateRange> */ private array $defaults = [ 'page' => 1, 'size' => self::DEFAULT_PAGESIZE, 'orderBy' => 'id', 'order' => self::ORDER_ASC, 'searchTerm' => null, ]; private bool $isApiCall = false; private int $page = 1; private int $pageSize = self::DEFAULT_PAGESIZE; private string $orderBy = 'id'; private string $order = self::ORDER_ASC; /** * @var array */ private array $orderGroups = []; private ?User $currentUser = null; /** * @var array */ private array $teams = []; private ?SearchTerm $searchTerm = null; private ?Bookmark $bookmark = null; private ?string $name = null; private bool $bookmarkSearch = false; /** * @param Team[] $teams * @return $this */ public function setTeams(?array $teams): self { $this->teams = []; if (null !== $teams) { foreach ($teams as $team) { $this->addTeam($team); } } return $this; } public function addTeam(Team $team): self { $this->teams[$team->getId()] = $team; return $this; } public function hasTeams(): bool { return \count($this->teams) > 0; } /** * @return Team[] */ public function getTeams(): array { return array_values($this->teams); } public function getCurrentUser(): ?User { return $this->currentUser; } /** * By setting the current user, you activate (team) permission checks. * * @param User $user * @return self */ public function setCurrentUser(?User $user): self { $this->currentUser = $user; return $this; } public function getPage(): int { return $this->page; } public function setPage(?int $page): self { if ($page !== null && $page > 0) { $this->page = $page; } return $this; } public function getPageSize(): int { return $this->pageSize; } public function setPageSize(?int $pageSize): self { if ($pageSize !== null && $pageSize > 0) { $this->pageSize = $pageSize; } return $this; } public function getSize(): int { return $this->getPageSize(); } public function setSize(?int $size): void { $this->setPageSize($size); } public function getOrderBy(): string { return $this->orderBy; } public function setOrderBy(?string $orderBy): self { if ($orderBy === null) { $orderBy = (string) $this->defaults['orderBy']; // @phpstan-ignore-line } $this->orderBy = $orderBy; return $this; } public function getOrder(): string { return $this->order; } public function setOrder(?string $order): self { if ($order === null) { $order = (string) $this->defaults['order']; // @phpstan-ignore-line } if (\in_array($order, [self::ORDER_ASC, self::ORDER_DESC])) { $this->order = $order; } return $this; } public function addOrderGroup(string $orderBy, string $order): void { $this->orderGroups[$orderBy] = $order; } public function getOrderGroups(): array { if (empty($this->orderGroups)) { return [$this->orderBy => $this->order]; } return $this->orderGroups; } public function hasSearchTerm(): bool { return null !== $this->searchTerm; } public function getSearchTerm(): ?SearchTerm { return $this->searchTerm; } public function setSearchTerm(?SearchTerm $searchTerm): self { $this->searchTerm = $searchTerm; return $this; } protected function set(string $name, mixed $value): void { $method = 'set' . ucfirst($name); if (method_exists($this, $method)) { \call_user_func([$this, $method], $value); return; } if (str_ends_with($name, 's')) { $method = 'add' . ucfirst(substr($name, 0, \strlen($name) - 1)); if (method_exists($this, $method) && \is_array($value)) { foreach ($value as $v) { \call_user_func([$this, $method], $v); } return; } } if (property_exists($this, $name)) { $this->{$name} = $value; } } protected function get(string $name): mixed { $methods = ['get' . ucfirst($name), 'is' . ucfirst($name), 'has' . ucfirst($name)]; foreach ($methods as $method) { if (method_exists($this, $method)) { return \call_user_func([$this, $method]); } } if (property_exists($this, $name)) { return $this->{$name}; } return null; } /** * You have to add ALL user facing form fields as default! * * @param array|DateRange> $defaults * @return self */ protected function setDefaults(array $defaults): self { $this->defaults = array_merge($this->defaults, $defaults); foreach ($this->defaults as $key => $value) { $this->set($key, $value); } return $this; } /** * @param FormErrorIterator $errors * @return $this */ public function resetByFormError(FormErrorIterator $errors): self { foreach ($errors as $error) { $key = $error->getOrigin()?->getName(); if ($key !== null && \array_key_exists($key, $this->defaults)) { $this->set($key, $this->defaults[$key]); } } return $this; } public function setBookmark(?Bookmark $bookmark): void { $this->bookmark = $bookmark; } public function getBookmark(): ?Bookmark { return $this->bookmark; } public function hasBookmark(): bool { return null !== $this->bookmark; } public function setName(string $name): void { $this->name = $name; } public function getName(): string { if (null !== $this->name) { return $this->name; } $shortClass = explode('\\', static::class); return array_pop($shortClass); } /** * @template T of BaseQuery * @param T $query * @return T * @internal */ final public function copyTo(BaseQuery $query): BaseQuery { $query->setDefaults($this->defaults); if (null !== $this->getCurrentUser()) { $query->setCurrentUser($this->getCurrentUser()); } $query->setOrder($this->getOrder()); $query->setOrderBy($this->getOrderBy()); $query->setSearchTerm($this->getSearchTerm()); $query->setPage($this->getPage()); $query->setPageSize($this->getPageSize()); foreach ($this->getTeams() as $team) { $query->addTeam($team); } if ($this instanceof VisibilityInterface && $query instanceof VisibilityInterface) { $query->setVisibility($this->getVisibility()); } $query->copyFrom($this); return $query; } protected function copyFrom(BaseQuery $query): void { } public function isDefaultFilter(string $filter): bool { if (!\array_key_exists($filter, $this->defaults)) { return false; } $expectedValue = $this->defaults[$filter]; return $this->matchesFilter($filter, $expectedValue); } public function matchesFilter(string $filter, $expectedValue): bool { $currentValue = $this->get($filter); if (\is_object($currentValue)) { if ($currentValue instanceof EquatableInterface) { return $currentValue->isEqualTo($expectedValue); } // this is a loose comparison by choice, as == compares object type and content // instead of === which only compares if both sides are the same instance if ($currentValue != $expectedValue) { // @phpstan-ignore-line return false; } } else { if ($currentValue !== $expectedValue) { return false; } } return true; } public function countFilter(): int { $filter = 0; foreach (array_keys($this->defaults) as $key) { if ($key === 'page') { continue; } if (!$this->isDefaultFilter($key)) { $filter++; } } return $filter; } public function resetFilter(): void { foreach ($this->defaults as $key => $value) { $this->set($key, $value); } } public function flagAsBookmarkSearch(): void { $this->bookmarkSearch = true; } public function isBookmarkSearch(): bool { return $this->bookmarkSearch; } public function setIsApiCall(bool $isApiCall): void { $this->isApiCall = $isApiCall; } public function isApiCall(): bool { return $this->isApiCall; } }