Add participants parent and child in request filter

This commit is contained in:
philipp lang 2024-03-08 02:19:07 +01:00
parent a0842afd47
commit 78d6c6d864
12 changed files with 147 additions and 21 deletions

View File

@ -2,7 +2,7 @@
namespace App\Form\Actions;
use App\Form\FilterScope;
use App\Form\Scopes\FormFilterScope;
use App\Form\Models\Form;
use App\Form\Resources\FormApiResource;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
@ -20,7 +20,7 @@ class FormApiListAction
*/
public function handle(string $filter, int $perPage): LengthAwarePaginator
{
return FilterScope::fromRequest($filter)->getQuery()->paginate($perPage);
return FormFilterScope::fromRequest($filter)->getQuery()->paginate($perPage);
}
public function asController(ActionRequest $request): AnonymousResourceCollection

View File

@ -2,7 +2,7 @@
namespace App\Form\Actions;
use App\Form\FilterScope;
use App\Form\Scopes\FormFilterScope;
use App\Form\Models\Form;
use App\Form\Resources\FormResource;
use Illuminate\Pagination\LengthAwarePaginator;
@ -20,7 +20,7 @@ class FormIndexAction
*/
public function handle(string $filter): LengthAwarePaginator
{
return FilterScope::fromRequest($filter)->getQuery()->query(fn ($query) => $query->withCount('participants'))->paginate(15);
return FormFilterScope::fromRequest($filter)->getQuery()->query(fn ($query) => $query->withCount('participants'))->paginate(15);
}
public function asController(ActionRequest $request): Response

View File

@ -5,6 +5,7 @@ namespace App\Form\Actions;
use App\Form\Models\Form;
use App\Form\Models\Participant;
use App\Form\Resources\ParticipantResource;
use App\Form\Scopes\ParticipantFilterScope;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Pagination\LengthAwarePaginator;
use Lorisleiva\Actions\Concerns\AsAction;
@ -16,14 +17,15 @@ class ParticipantIndexAction
/**
* @return LengthAwarePaginator<Participant>
*/
public function handle(Form $form): LengthAwarePaginator
public function handle(Form $form, ParticipantFilterScope $filter): LengthAwarePaginator
{
return $form->participants()->with('form')->paginate(15);
return $form->participants()->withFilter($filter)->with('form')->paginate(15);
}
public function asController(Form $form): AnonymousResourceCollection
{
return ParticipantResource::collection($this->handle($form))
$filter = ParticipantFilterScope::fromRequest(request()->input('filter'));
return ParticipantResource::collection($this->handle($form, $filter))
->additional(['meta' => ParticipantResource::meta($form)]);
}
}

View File

@ -2,9 +2,12 @@
namespace App\Form\Models;
use App\Form\Scopes\ParticipantFilterScope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Participant extends Model
{
@ -23,4 +26,21 @@ class Participant extends Model
{
return $this->belongsTo(Form::class);
}
/**
* @return HasMany<self>
*/
public function children(): HasMany
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* @param Builder<self> $query
* @return Builder<self>
*/
public function scopeWithFilter(Builder $query, ParticipantFilterScope $filter): Builder
{
return $filter->apply($query);
}
}

View File

@ -4,7 +4,7 @@ namespace App\Form\Resources;
use App\Form\Enums\NamiType;
use App\Form\Fields\Field;
use App\Form\FilterScope;
use App\Form\Scopes\FormFilterScope;
use App\Form\Models\Form;
use App\Form\Models\Formtemplate;
use App\Group;
@ -59,7 +59,7 @@ class FormResource extends JsonResource
'base_url' => url(''),
'groups' => Group::forSelect(),
'fields' => Field::asMeta(),
'filter' => FilterScope::fromRequest(request()->input('filter', '')),
'filter' => FormFilterScope::fromRequest(request()->input('filter', '')),
'links' => [
'store' => route('form.store'),
'formtemplate_index' => route('formtemplate.index'),

View File

@ -1,20 +1,20 @@
<?php
namespace App\Form;
namespace App\Form\Scopes;
use Laravel\Scout\Builder;
use App\Form\Models\Form;
use App\Lib\Filter;
use App\Lib\ScoutFilter;
use Spatie\LaravelData\Attributes\MapInputName;
use Spatie\LaravelData\Attributes\MapOutputName;
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
/**
* @extends Filter<Form>
* @extends ScoutFilter<Form>
*/
#[MapInputName(SnakeCaseMapper::class)]
#[MapOutputName(SnakeCaseMapper::class)]
class FilterScope extends Filter
class FormFilterScope extends ScoutFilter
{
public function __construct(
public ?string $search = '',

View File

@ -0,0 +1,39 @@
<?php
namespace App\Form\Scopes;
use App\Form\Models\Participant;
use App\Lib\Filter;
use Illuminate\Database\Eloquent\Builder;
use Spatie\LaravelData\Attributes\MapInputName;
use Spatie\LaravelData\Attributes\MapOutputName;
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
/**
* @extends Filter<Participant>
*/
#[MapInputName(SnakeCaseMapper::class)]
#[MapOutputName(SnakeCaseMapper::class)]
class ParticipantFilterScope extends Filter
{
public function __construct(
public ?int $parent = null,
) {
}
/**
* @inheritdoc
*/
public function apply(Builder $query): Builder
{
if ($this->parent === -1) {
$query = $query->whereNull('parent_id');
}
if (!is_null($this->parent) && $this->parent > 0) {
$query = $query->where('parent_id', $this->parent);
}
return $query;
}
}

View File

@ -2,8 +2,8 @@
namespace App\Lib;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Builder;
use Spatie\LaravelData\Data;
/**
@ -14,10 +14,10 @@ abstract class Filter extends Data
{
/**
* @return Builder
* @param Builder<T> $query
* @return Builder<T>
*/
abstract public function getQuery(): Builder;
protected Builder $query;
abstract public function apply(Builder $query): Builder;
/**
* @param array<string, mixed>|string|null $request

41
app/Lib/ScoutFilter.php Normal file
View File

@ -0,0 +1,41 @@
<?php
namespace App\Lib;
use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Builder;
use Spatie\LaravelData\Data;
/**
* @template T of Model
* @property Builder $query
*/
abstract class ScoutFilter extends Data
{
/**
* @return Builder
*/
abstract public function getQuery(): Builder;
protected Builder $query;
/**
* @param array<string, mixed>|string|null $request
*/
public static function fromRequest(array|string|null $request = null): static
{
$payload = is_string($request)
? json_decode(rawurldecode(base64_decode($request)), true)
: $request;
return static::fromPost($payload);
}
/**
* @param array<string, mixed> $post
*/
public static function fromPost(?array $post = null): static
{
return static::withoutMagicalCreationFrom($post ?: []);
}
}

View File

@ -5,8 +5,8 @@ namespace App\Member;
use App\Activity;
use App\Group;
use App\Invoice\BillKind;
use App\Lib\Filter;
use App\Subactivity;
use App\Lib\ScoutFilter;
use Illuminate\Support\Collection;
use Laravel\Scout\Builder;
use Spatie\LaravelData\Attributes\MapInputName;
@ -14,11 +14,11 @@ use Spatie\LaravelData\Attributes\MapOutputName;
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
/**
* @extends Filter<Member>
* @extends ScoutFilter<Member>
*/
#[MapInputName(SnakeCaseMapper::class)]
#[MapOutputName(SnakeCaseMapper::class)]
class FilterScope extends Filter
class FilterScope extends ScoutFilter
{
/**
* @param array<int, int> $activityIds

View File

@ -17,7 +17,7 @@ return new class extends Migration
$table->id();
$table->json('data');
$table->foreignId('form_id');
$table->foreignId('parent_id', 'participants')->nullable();
$table->foreignId('parent_id')->nullable();
$table->string('mitgliedsnr')->nullable();
$table->timestamps();
});

View File

@ -54,6 +54,30 @@ class ParticipantIndexActionTest extends FormTestCase
->assertJsonPath('meta.form_meta.sorting', ['vorname', 'asc']);
}
public function testItShowsOnlyRootMembers(): void
{
$this->login()->loginNami()->withoutExceptionHandling();
$form = Form::factory()->create();
Participant::factory()->for($form)->count(2)
->has(Participant::factory()->count(3)->for($form), 'children')
->create();
$this->callFilter('form.participant.index', ['parent' => -1], ['form' => $form])->assertJsonCount(2, 'data');
$this->callFilter('form.participant.index', ['parent' => null], ['form' => $form])->assertJsonCount(8, 'data');
}
public function testItShowsChildrenOfParent(): void
{
$this->login()->loginNami()->withoutExceptionHandling();
$form = Form::factory()->create();
$parents = Participant::factory()->for($form)->count(2)
->has(Participant::factory()->count(3)->for($form), 'children')
->create();
$this->callFilter('form.participant.index', ['parent' => $parents->get(0)->id], ['form' => $form])->assertJsonCount(3, 'data');
$this->callFilter('form.participant.index', ['parent' => $parents->get(1)->id], ['form' => $form])->assertJsonCount(3, 'data');
}
public function testItPresentsNamiField(): void
{
$this->login()->loginNami()->withoutExceptionHandling();