Skip to content

Commit 962b163

Browse files
committed
Feature: Attendee table improvements
1 parent 0c105b2 commit 962b163

File tree

40 files changed

+3117
-827
lines changed

40 files changed

+3117
-827
lines changed

backend/app/DomainObjects/AttendeeDomainObject.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
class AttendeeDomainObject extends Generated\AttendeeDomainObjectAbstract implements IsSortable, IsFilterable
1111
{
12+
public const TICKET_NAME_SORT_KEY = 'ticket_name';
13+
1214
private ?OrderDomainObject $order = null;
1315

1416
private ?ProductDomainObject $product = null;
@@ -30,6 +32,10 @@ public static function getAllowedSorts(): AllowedSorts
3032
{
3133
return new AllowedSorts(
3234
[
35+
self::TICKET_NAME_SORT_KEY => [
36+
'asc' => __('Ticket Name A-Z'),
37+
'desc' => __('Ticket Name Z-A'),
38+
],
3339
self::CREATED_AT => [
3440
'asc' => __('Older First'),
3541
'desc' => __('Newest First'),
@@ -64,6 +70,7 @@ public static function getAllowedFilterFields(): array
6470
return [
6571
self::STATUS,
6672
self::PRODUCT_ID,
73+
self::PRODUCT_PRICE_ID,
6774
];
6875
}
6976

backend/app/Http/Actions/Attendees/GetAttendeesAction.php

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,29 @@
44

55
use HiEvents\DomainObjects\AttendeeDomainObject;
66
use HiEvents\DomainObjects\EventDomainObject;
7-
use HiEvents\DomainObjects\OrderDomainObject;
87
use HiEvents\Http\Actions\BaseAction;
98
use HiEvents\Http\DTO\QueryParamsDTO;
10-
use HiEvents\Repository\Eloquent\Value\Relationship;
11-
use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface;
129
use HiEvents\Resources\Attendee\AttendeeResource;
10+
use HiEvents\Services\Application\Handlers\Attendee\GetAttendeesHandler;
1311
use Illuminate\Http\JsonResponse;
1412
use Illuminate\Http\Request;
1513

16-
/**
17-
* @todo move to handler
18-
* @todo - add validation for filter fields
19-
*/
2014
class GetAttendeesAction extends BaseAction
2115
{
22-
private AttendeeRepositoryInterface $attendeeRepository;
23-
24-
public function __construct(AttendeeRepositoryInterface $attendeeRepository)
16+
public function __construct(
17+
private readonly GetAttendeesHandler $getAttendeesHandler,
18+
)
2519
{
26-
$this->attendeeRepository = $attendeeRepository;
2720
}
2821

2922
public function __invoke(int $eventId, Request $request): JsonResponse
3023
{
3124
$this->isActionAuthorized($eventId, EventDomainObject::class);
3225

33-
$attendees = $this->attendeeRepository
34-
->loadRelation(new Relationship(
35-
domainObject: OrderDomainObject::class,
36-
name: 'order'
37-
))
38-
->findByEventId($eventId, QueryParamsDTO::fromArray($request->query->all()));
26+
$attendees = $this->getAttendeesHandler->handle(
27+
eventId: $eventId,
28+
queryParams: QueryParamsDTO::fromArray($request->query->all())
29+
);
3930

4031
return $this->filterableResourceResponse(
4132
resource: AttendeeResource::class,

backend/app/Repository/Eloquent/AttendeeRepository.php

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use HiEvents\DomainObjects\Generated\AttendeeDomainObjectAbstract;
88
use HiEvents\DomainObjects\Status\AttendeeStatus;
99
use HiEvents\DomainObjects\Status\OrderStatus;
10+
use HiEvents\Http\DTO\FilterFieldDTO;
1011
use HiEvents\Http\DTO\QueryParamsDTO;
1112
use HiEvents\Models\Attendee;
1213
use HiEvents\Repository\Eloquent\Value\Relationship;
@@ -76,11 +77,22 @@ public function findByEventId(int $eventId, QueryParamsDTO $params): LengthAware
7677

7778
$this->model = $this->model->select('attendees.*')
7879
->join('orders', 'orders.id', '=', 'attendees.order_id')
79-
->whereIn('orders.status', [OrderStatus::COMPLETED->name, OrderStatus::CANCELLED->name, OrderStatus::AWAITING_OFFLINE_PAYMENT->name])
80-
->orderBy(
81-
'attendees.' . ($params->sort_by ?? AttendeeDomainObject::getDefaultSort()),
82-
$params->sort_direction ?? 'desc',
83-
);
80+
->whereIn('orders.status', [OrderStatus::COMPLETED->name, OrderStatus::CANCELLED->name, OrderStatus::AWAITING_OFFLINE_PAYMENT->name]);
81+
82+
if ($params->filter_fields && $params->filter_fields->isNotEmpty()) {
83+
$this->applyFilterFields($params, AttendeeDomainObject::getAllowedFilterFields(), prefix: 'attendees');
84+
}
85+
86+
$sortBy = $params->sort_by ?? AttendeeDomainObject::getDefaultSort();
87+
$sortDirection = $params->sort_direction ?? AttendeeDomainObject::getDefaultSortDirection();
88+
89+
if ($sortBy === AttendeeDomainObject::TICKET_NAME_SORT_KEY) {
90+
$this->model = $this->model
91+
->leftJoin('products', 'products.id', '=', 'attendees.product_id')
92+
->orderBy('products.title', $sortDirection);
93+
} else {
94+
$this->model = $this->model->orderBy('attendees.' . $sortBy, $sortDirection);
95+
}
8496

8597
return $this->paginateWhere(
8698
where: $where,

backend/app/Repository/Eloquent/BaseRepository.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -387,10 +387,14 @@ protected function handleSingleResult(
387387
return $this->hydrateDomainObjectFromModel($model, $domainObjectOverride);
388388
}
389389

390-
protected function applyFilterFields(QueryParamsDTO $params, array $allowedFilterFields = []): void
390+
protected function applyFilterFields(
391+
QueryParamsDTO $params,
392+
array $allowedFilterFields = [],
393+
?string $prefix = null,
394+
): void
391395
{
392396
if ($params->filter_fields && $params->filter_fields->isNotEmpty()) {
393-
$params->filter_fields->each(function ($filterField) use ($allowedFilterFields) {
397+
$params->filter_fields->each(function ($filterField) use ($prefix, $allowedFilterFields) {
394398
if (!in_array($filterField->field, $allowedFilterFields, true)) {
395399
return;
396400
}
@@ -412,6 +416,8 @@ protected function applyFilterFields(QueryParamsDTO $params, array $allowedFilte
412416
sprintf('Operator %s is not supported', $filterField->operator)
413417
);
414418

419+
$field = $prefix ? $prefix . '.' . $filterField->field : $filterField->field;
420+
415421
// Special handling for IN operator
416422
if ($operator === 'IN') {
417423
// Ensure value is array or convert comma-separated string to array
@@ -420,12 +426,12 @@ protected function applyFilterFields(QueryParamsDTO $params, array $allowedFilte
420426
: explode(',', $filterField->value);
421427

422428
$this->model = $this->model->whereIn(
423-
column: $filterField->field,
429+
column: $field,
424430
values: $value
425431
);
426432
} else {
427433
$this->model = $this->model->where(
428-
column: $filterField->field,
434+
column: $field,
429435
operator: $operator,
430436
value: $isNull ? null : $filterField->value,
431437
);
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace HiEvents\Services\Application\Handlers\Attendee;
4+
5+
use HiEvents\DomainObjects\AttendeeCheckInDomainObject;
6+
use HiEvents\DomainObjects\OrderDomainObject;
7+
use HiEvents\Http\DTO\QueryParamsDTO;
8+
use HiEvents\Repository\Eloquent\Value\Relationship;
9+
use HiEvents\Repository\Interfaces\AttendeeRepositoryInterface;
10+
use Illuminate\Pagination\LengthAwarePaginator;
11+
12+
class GetAttendeesHandler
13+
{
14+
public function __construct(
15+
private readonly AttendeeRepositoryInterface $attendeeRepository,
16+
)
17+
{
18+
}
19+
20+
public function handle(int $eventId, QueryParamsDTO $queryParams): LengthAwarePaginator
21+
{
22+
return $this->attendeeRepository
23+
->loadRelation(new Relationship(
24+
domainObject: OrderDomainObject::class,
25+
name: 'order'
26+
))
27+
->loadRelation(new Relationship(
28+
domainObject: AttendeeCheckInDomainObject::class,
29+
name: 'check_ins'
30+
))
31+
->findByEventId($eventId, $queryParams);
32+
}
33+
}

0 commit comments

Comments
 (0)