2023-03-14 23:27:15 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace App\Member;
|
|
|
|
|
2024-03-08 22:26:16 +01:00
|
|
|
use App\Activity;
|
|
|
|
use App\Group;
|
2023-04-18 22:08:45 +02:00
|
|
|
use App\Invoice\BillKind;
|
2024-03-08 22:26:16 +01:00
|
|
|
use App\Subactivity;
|
2024-03-08 02:19:07 +01:00
|
|
|
use App\Lib\ScoutFilter;
|
2024-01-28 11:42:32 +01:00
|
|
|
use Illuminate\Support\Collection;
|
|
|
|
use Laravel\Scout\Builder;
|
2023-03-14 23:27:15 +01:00
|
|
|
use Spatie\LaravelData\Attributes\MapInputName;
|
|
|
|
use Spatie\LaravelData\Attributes\MapOutputName;
|
|
|
|
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
|
|
|
|
|
|
|
|
/**
|
2024-03-08 02:19:07 +01:00
|
|
|
* @extends ScoutFilter<Member>
|
2023-03-14 23:27:15 +01:00
|
|
|
*/
|
|
|
|
#[MapInputName(SnakeCaseMapper::class)]
|
|
|
|
#[MapOutputName(SnakeCaseMapper::class)]
|
2024-03-08 02:19:07 +01:00
|
|
|
class FilterScope extends ScoutFilter
|
2023-03-14 23:27:15 +01:00
|
|
|
{
|
2024-05-09 02:07:31 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @var array<string, mixed>
|
|
|
|
*/
|
|
|
|
public array $options = [];
|
|
|
|
|
2023-06-14 17:11:42 +02:00
|
|
|
/**
|
|
|
|
* @param array<int, int> $activityIds
|
|
|
|
* @param array<int, int> $subactivityIds
|
2023-06-14 17:29:22 +02:00
|
|
|
* @param array<int, int> $groupIds
|
2023-07-10 11:36:58 +02:00
|
|
|
* @param array<int, int> $include
|
|
|
|
* @param array<int, int> $exclude
|
2024-01-29 00:13:03 +01:00
|
|
|
* @param array<int, array{group_ids: array<int, int>, subactivity_ids: array<int, int>, activity_ids: array<int, int>}> $memberships
|
2023-06-14 17:11:42 +02:00
|
|
|
*/
|
2023-03-14 23:27:15 +01:00
|
|
|
public function __construct(
|
|
|
|
public bool $ausstand = false,
|
|
|
|
public ?string $billKind = null,
|
2024-01-29 00:13:03 +01:00
|
|
|
public array $memberships = [],
|
2023-06-14 16:20:18 +02:00
|
|
|
public array $activityIds = [],
|
|
|
|
public array $subactivityIds = [],
|
2023-06-15 00:08:01 +02:00
|
|
|
public ?string $search = '',
|
2023-06-14 17:29:22 +02:00
|
|
|
public array $groupIds = [],
|
2023-07-10 11:36:58 +02:00
|
|
|
public array $include = [],
|
|
|
|
public array $exclude = [],
|
2023-07-24 16:55:07 +02:00
|
|
|
public ?bool $hasFullAddress = null,
|
|
|
|
public ?bool $hasBirthday = null,
|
2023-03-14 23:27:15 +01:00
|
|
|
) {
|
|
|
|
}
|
|
|
|
|
2024-05-09 02:07:31 +02:00
|
|
|
/**
|
|
|
|
* @param array<string, mixed> $options
|
|
|
|
*/
|
|
|
|
public function withOptions(array $options): self
|
|
|
|
{
|
|
|
|
$this->options = $options;
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2024-05-09 02:23:55 +02:00
|
|
|
public function noPageLimit(): self
|
|
|
|
{
|
|
|
|
return $this->withOptions([
|
|
|
|
'hitsPerPage' => config('scout.meilisearch.index-settings.' . Member::class . '.pagination.maxTotalHits')
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
2024-01-28 11:42:32 +01:00
|
|
|
public function getQuery(): Builder
|
|
|
|
{
|
|
|
|
$this->search = $this->search ?: '';
|
|
|
|
|
|
|
|
return Member::search($this->search, function ($engine, string $query, array $options) {
|
|
|
|
$filter = collect([]);
|
|
|
|
|
|
|
|
if ($this->hasFullAddress === true) {
|
|
|
|
$filter->push('address IS NOT EMPTY');
|
|
|
|
}
|
|
|
|
if ($this->hasFullAddress === false) {
|
|
|
|
$filter->push('address IS EMPTY');
|
|
|
|
}
|
|
|
|
if ($this->hasBirthday === false) {
|
|
|
|
$filter->push('birthday IS NULL');
|
|
|
|
}
|
|
|
|
if ($this->hasBirthday === true) {
|
|
|
|
$filter->push('birthday IS NOT NULL');
|
|
|
|
}
|
|
|
|
if ($this->ausstand === true) {
|
|
|
|
$filter->push('ausstand > 0');
|
|
|
|
}
|
|
|
|
if ($this->billKind) {
|
|
|
|
$filter->push('bill_kind = ' . BillKind::fromValue($this->billKind)->value);
|
|
|
|
}
|
|
|
|
if (count($this->groupIds)) {
|
|
|
|
$filter->push($this->inExpression('group_id', $this->groupIds));
|
|
|
|
}
|
|
|
|
if (!$this->subactivityIds && $this->activityIds) {
|
|
|
|
$filter->push($this->inExpression('memberships.activity_id', $this->activityIds));
|
|
|
|
}
|
|
|
|
if ($this->subactivityIds && !$this->activityIds) {
|
|
|
|
$filter->push($this->inExpression('memberships.subactivity_id', $this->subactivityIds));
|
|
|
|
}
|
|
|
|
if ($this->subactivityIds && $this->activityIds) {
|
2024-01-29 00:13:03 +01:00
|
|
|
$combinations = $this->combinations($this->activityIds, $this->subactivityIds)
|
|
|
|
->map(fn ($combination) => implode('|', $combination))
|
2024-01-28 11:42:32 +01:00
|
|
|
->map(fn ($combination) => str($combination)->wrap('"'));
|
|
|
|
$filter->push($this->inExpression('memberships.both', $combinations));
|
|
|
|
}
|
|
|
|
|
2024-01-29 00:13:03 +01:00
|
|
|
foreach ($this->memberships as $membership) {
|
|
|
|
$filter->push($this->inExpression('memberships.with_group', $this->possibleValuesForMembership($membership)->map(fn ($value) => str($value)->wrap('"'))));
|
|
|
|
}
|
|
|
|
|
2024-01-28 11:42:32 +01:00
|
|
|
if (count($this->exclude)) {
|
|
|
|
$filter->push($this->notInExpression('id', $this->exclude));
|
|
|
|
}
|
|
|
|
|
|
|
|
$andFilter = $filter->map(fn ($expression) => "($expression)")->implode(' AND ');
|
|
|
|
|
|
|
|
$options['filter'] = $this->implode(collect([$andFilter])->push($this->inExpression('id', $this->include)), 'OR');
|
|
|
|
$options['sort'] = ['lastname:asc', 'firstname:asc'];
|
|
|
|
|
2024-05-09 02:07:31 +02:00
|
|
|
return $engine->search($query, [...$this->options, ...$options]);
|
2024-01-28 11:42:32 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-03-14 23:27:15 +01:00
|
|
|
/**
|
2024-01-28 11:42:32 +01:00
|
|
|
* @param Collection<int, mixed> $values
|
2023-03-14 23:27:15 +01:00
|
|
|
*/
|
2024-01-28 11:42:32 +01:00
|
|
|
protected function implode(Collection $values, string $between): string
|
2023-03-14 23:27:15 +01:00
|
|
|
{
|
2024-01-28 11:42:32 +01:00
|
|
|
return $values->filter(fn ($expression) => $expression)->implode(" {$between} ");
|
2023-03-14 23:27:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2024-01-28 11:42:32 +01:00
|
|
|
* @param array<int, mixed>|Collection<int, mixed> $values
|
2023-03-14 23:27:15 +01:00
|
|
|
*/
|
2024-01-28 11:42:32 +01:00
|
|
|
private function inExpression(string $key, array|Collection $values): ?string
|
2023-03-14 23:27:15 +01:00
|
|
|
{
|
2024-01-28 11:42:32 +01:00
|
|
|
if (!count($values)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
$valueString = Collection::wrap($values)->implode(',');
|
|
|
|
|
|
|
|
return "$key IN [{$valueString}]";
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array<int, mixed>|Collection<int, mixed> $values
|
|
|
|
*/
|
|
|
|
private function notInExpression(string $key, array|Collection $values): ?string
|
|
|
|
{
|
|
|
|
if (!count($values)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$valueString = Collection::wrap($values)->implode(',');
|
|
|
|
|
|
|
|
return "$key NOT IN [{$valueString}]";
|
2023-03-14 23:27:15 +01:00
|
|
|
}
|
2024-01-29 00:13:03 +01:00
|
|
|
|
2024-01-29 02:12:54 +01:00
|
|
|
/**
|
|
|
|
* @param array{group_ids: array<int, int>, subactivity_ids: array<int, int>, activity_ids: array<int, int>} $membership
|
|
|
|
* @return Collection<int, string>
|
|
|
|
*/
|
2024-01-29 00:13:03 +01:00
|
|
|
protected function possibleValuesForMembership(array $membership): Collection
|
|
|
|
{
|
2024-03-08 22:26:16 +01:00
|
|
|
$membership['group_ids'] = count($membership['group_ids']) === 0 ? Group::pluck('id')->toArray() : $membership['group_ids'];
|
|
|
|
$membership['activity_ids'] = count($membership['activity_ids']) === 0 ? Activity::pluck('id')->toArray() : $membership['activity_ids'];
|
|
|
|
$membership['subactivity_ids'] = count($membership['subactivity_ids']) === 0 ? Subactivity::pluck('id')->toArray() : $membership['subactivity_ids'];
|
2024-01-29 00:13:03 +01:00
|
|
|
return $this->combinations($membership['group_ids'], $membership['activity_ids'], $membership['subactivity_ids'])
|
|
|
|
->map(fn ($combination) => collect($combination)->implode('|'));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2024-01-29 02:12:54 +01:00
|
|
|
* @param array<int, array<int, int>> $parts
|
|
|
|
* @return Collection<int, array<int, int>>
|
2024-01-29 00:13:03 +01:00
|
|
|
*/
|
|
|
|
protected function combinations(...$parts): Collection
|
|
|
|
{
|
|
|
|
$firstPart = array_shift($parts);
|
|
|
|
$otherParts = $parts;
|
|
|
|
|
|
|
|
if (!count($otherParts)) {
|
2024-01-29 02:12:54 +01:00
|
|
|
/** @var Collection<int, Collection<int, int>> */
|
|
|
|
return collect($firstPart)->map(fn ($p) => [$p]);
|
2024-01-29 00:13:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** @var Collection<int, mixed> */
|
|
|
|
$results = collect([]);
|
|
|
|
foreach ($firstPart as $firstPartSegment) {
|
|
|
|
foreach ($this->combinations(...$otherParts) as $combination) {
|
|
|
|
$results->push([$firstPartSegment, ...$combination]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $results;
|
|
|
|
}
|
2023-03-14 23:27:15 +01:00
|
|
|
}
|