From dc40f7c62fa1ad67f2084218c0a4e913425348b3 Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 10 Jun 2026 10:05:37 -0700 Subject: [PATCH 01/13] wip --- src/Entries.php | 29 +++++++++++++++++++ src/Events.php | 68 ++++++++++++++++++++++++++------------------- src/Tags/Events.php | 2 +- 3 files changed, 69 insertions(+), 30 deletions(-) create mode 100644 src/Entries.php diff --git a/src/Entries.php b/src/Entries.php new file mode 100644 index 0000000..c0a2d99 --- /dev/null +++ b/src/Entries.php @@ -0,0 +1,29 @@ +whereIn('collection', $this->collections->map->handle()->all()); + + $this->querySite($query); + $this->queryPublished($query); + $this->queryTaxonomies($query); + $this->queryConditions($query); + $this->queryScopes($query); + + return $query; + } +} diff --git a/src/Events.php b/src/Events.php index deda2fe..c5524e6 100644 --- a/src/Events.php +++ b/src/Events.php @@ -4,24 +4,20 @@ use Carbon\CarbonInterface; use Exception; -use Illuminate\Support\Traits\Conditionable; +use Illuminate\Pagination\Paginator; +use Illuminate\Support\Collection; use Statamic\Entries\Entry; use Statamic\Entries\EntryCollection; use Statamic\Extensions\Pagination\LengthAwarePaginator; use Statamic\Facades\Addon; use Statamic\Facades\Cascade; -use Statamic\Facades\Entry as EntryFacade; -use Statamic\Facades\Site; use Statamic\Fields\Values; use Statamic\Support\Arr; -use Statamic\Tags\Concerns\QueriesConditions; +use Statamic\Tags\Parameters; use TransformStudios\Events\Types\MultiDayEvent; class Events { - use Conditionable; - use QueriesConditions; - private bool $collapseMultiDays = false; private ?string $collection = null; @@ -30,19 +26,21 @@ class Events private ?string $event = null; - private array $filters = []; + // private array $filters = []; private ?int $offset = null; private ?int $page = null; + private Collection $params; + private ?int $perPage = null; private ?string $site = null; private string $sort = 'asc'; - private array $terms = []; + // private array $terms = []; private ?string $timezone = null; @@ -53,12 +51,30 @@ public static function defaultTimezone(): string public static function fromCollection(string $handle): self { - return tap(new static)->collection($handle); + return tap(new static(collect()))->collection($handle); } public static function fromEntry(string $id): self { - return tap(new static)->event($id); + return tap(new static(collect()))->event($id); + } + + public function __construct(Collection $params) + { + if ($params->has('event')) { + $this->event($params->get('event')); + } + + $this->params = $params; + + $this + ->collection($params->get('collection', 'events')) + ->collapseMultiDays(boolval($params->get('collapse_multi_days'))) + ->offset(offset: intval($params->get('offset'))) + ->pagination(page: Paginator::resolveCurrentPage(), perPage: intval($params->get('paginate'))) + ->params($params) + ->sort($params->get('sort', 'asc')) + ->timezone(timezone: $params->get('timezone', static::defaultTimezone())); } public static function setting(string $key, $default = null): mixed @@ -66,8 +82,6 @@ public static function setting(string $key, $default = null): mixed return Addon::get('transformstudios/events')->settings()->get($key, $default); } - private function __construct() {} - public function collapseMultiDays(?bool $collapseMultiDays = true): self { $this->collapseMultiDays = $collapseMultiDays; @@ -124,6 +138,13 @@ public function pagination(int $page = 1, int $perPage = 10): self return $this; } + public function params(Collection $params): self + { + $this->params = $params; + + return $this; + } + public function site(?string $handle = null): self { $this->site = $handle; @@ -191,22 +212,11 @@ private function output(callable $type): EntryCollection|LengthAwarePaginator private function entries(): self { - $query = EntryFacade::query() - ->when( - $this->event, - fn ($query, $id) => $query->where('id', $id), - fn ($query) => $query->where('collection', $this->collection) - )->where('site', $this->site ?? Site::current()->handle()) - ->whereStatus('published') - ->when($this->terms, fn ($query, $terms) => $query->whereTaxonomyIn($terms)); - - collect($this->filters)->each(function ($value, $fieldCondition) use ($query) { - [$field, $condition] = explode(':', $fieldCondition); - - $this->queryCondition(query: $query, field: $field, condition: $condition, value: $value); - }); - - $this->entries = $query->get(); + $params = $this->params->all(); + if ($this->collection) { + $params['collection'] = $this->collection; + } + $this->entries = (new Entries(new Parameters($params)))->get(); return $this; } diff --git a/src/Tags/Events.php b/src/Tags/Events.php index 3b8fa62..05e1d91 100755 --- a/src/Tags/Events.php +++ b/src/Tags/Events.php @@ -175,6 +175,7 @@ private function explodeTerms(array|Builder|string $terms): array private function generator(): Generator { + return new Generator($this->params); $generator = $this->params->has('event') ? Generator::fromEntry($this->params->get('event')) : Generator::fromCollection($this->params->get('collection', 'events')); @@ -183,7 +184,6 @@ private function generator(): Generator ->collapseMultiDays($this->params->bool('collapse_multi_days')) ->offset(offset: $this->params->int('offset')) ->pagination(page: Paginator::resolveCurrentPage(), perPage: $this->params->int('paginate')) - ->site($this->params->get('site')) ->sort($this->params->get('sort', 'asc')) ->timezone(timezone: $this->params->get('timezone', Generator::defaultTimezone())) ->when( From 7d6a49ed4b0c5f364112ce8aa296a27a4a3e4d89 Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 10 Jun 2026 10:16:07 -0700 Subject: [PATCH 02/13] nicer this way --- src/Events.php | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Events.php b/src/Events.php index c5524e6..eaf4ec5 100644 --- a/src/Events.php +++ b/src/Events.php @@ -32,7 +32,7 @@ class Events private ?int $page = null; - private Collection $params; + private Parameters $params; private ?int $perPage = null; @@ -51,15 +51,15 @@ public static function defaultTimezone(): string public static function fromCollection(string $handle): self { - return tap(new static(collect()))->collection($handle); + return new static(new Parameters(['collection' => $handle])); } public static function fromEntry(string $id): self { - return tap(new static(collect()))->event($id); + return new static(new Parameters(['event' => $id])); } - public function __construct(Collection $params) + public function __construct(Parameters $params) { if ($params->has('event')) { $this->event($params->get('event')); @@ -69,9 +69,9 @@ public function __construct(Collection $params) $this ->collection($params->get('collection', 'events')) - ->collapseMultiDays(boolval($params->get('collapse_multi_days'))) - ->offset(offset: intval($params->get('offset'))) - ->pagination(page: Paginator::resolveCurrentPage(), perPage: intval($params->get('paginate'))) + ->collapseMultiDays($params->bool('collapse_multi_days')) + ->offset(offset: $params->int('offset')) + ->pagination(page: Paginator::resolveCurrentPage(), perPage: $params->int('paginate')) ->params($params) ->sort($params->get('sort', 'asc')) ->timezone(timezone: $params->get('timezone', static::defaultTimezone())); @@ -213,9 +213,6 @@ private function output(callable $type): EntryCollection|LengthAwarePaginator private function entries(): self { $params = $this->params->all(); - if ($this->collection) { - $params['collection'] = $this->collection; - } $this->entries = (new Entries(new Parameters($params)))->get(); return $this; From 70127bfe2a0fe2d94e9a73c3abf5f520f0101dfb Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 10 Jun 2026 11:01:04 -0700 Subject: [PATCH 03/13] fix filters --- src/Events.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Events.php b/src/Events.php index eaf4ec5..29d3f03 100644 --- a/src/Events.php +++ b/src/Events.php @@ -26,8 +26,6 @@ class Events private ?string $event = null; - // private array $filters = []; - private ?int $offset = null; private ?int $page = null; @@ -114,7 +112,7 @@ public function filters(array $filters): self public function filter(string $fieldCondition, $value): self { - $this->filters[$fieldCondition] = $value; + $this->params->put($fieldCondition, $value); return $this; } From ffdf711e75c9da132318d99a8be0fef0ae04be86 Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 10 Jun 2026 11:35:49 -0700 Subject: [PATCH 04/13] not used anymore --- src/Tags/Events.php | 55 --------------------------------------------- 1 file changed, 55 deletions(-) diff --git a/src/Tags/Events.php b/src/Tags/Events.php index 05e1d91..1a18b7a 100755 --- a/src/Tags/Events.php +++ b/src/Tags/Events.php @@ -8,16 +8,13 @@ use Carbon\CarbonPeriod; use Carbon\CarbonPeriodImmutable; use Closure; -use Illuminate\Pagination\Paginator; use Illuminate\Support\Collection; use Statamic\Contracts\Query\Builder; -use Statamic\Contracts\Taxonomies\Term; use Statamic\Entries\Entry; use Statamic\Entries\EntryCollection; use Statamic\Facades\Compare; use Statamic\Facades\Site; use Statamic\Support\Arr; -use Statamic\Support\Str; use Statamic\Tags\Concerns\OutputsItems; use Statamic\Tags\Tags; use TransformStudios\Events\Events as Generator; @@ -176,28 +173,6 @@ private function explodeTerms(array|Builder|string $terms): array private function generator(): Generator { return new Generator($this->params); - $generator = $this->params->has('event') ? - Generator::fromEntry($this->params->get('event')) : - Generator::fromCollection($this->params->get('collection', 'events')); - - return $generator - ->collapseMultiDays($this->params->bool('collapse_multi_days')) - ->offset(offset: $this->params->int('offset')) - ->pagination(page: Paginator::resolveCurrentPage(), perPage: $this->params->int('paginate')) - ->sort($this->params->get('sort', 'asc')) - ->timezone(timezone: $this->params->get('timezone', Generator::defaultTimezone())) - ->when( - value: $this->parseTerms(), - callback: fn (Generator $generator, array $terms) => $generator->terms(terms: $terms) - )->when( - value: $this->parseFilters(), - callback: fn (Generator $generator, array $filters) => $generator->filters(filters: $filters) - ); - } - - private function getTermId(string $handle, Term|string $term): string - { - return $term instanceof Term ? $term->id() : Str::of($handle)->append('::', $term); } private function makeEmptyDates(CarbonInterface $from, CarbonInterface $to): Collection @@ -219,36 +194,6 @@ private function makeEmptyDates(CarbonInterface $from, CarbonInterface $to): Col return $dates; } - private function parseFilters(): array - { - return collect($this->params) - ->filter(fn ($value, $key) => Str::contains($key, ':') && ! Str::startsWith($key, 'taxonomy:')) - ->all(); - } - - private function parseTerms(): array - { - $taxonomyParams = collect($this->params) - ->filter(fn ($value, $key) => Str::startsWith($key, 'taxonomy:')); - - if ($taxonomyParams->filter()->isEmpty()) { - return []; - } - - return $taxonomyParams - ->flatMap(fn ($terms, $key) => $this->parseTermIds($key, $terms)) - ->all(); - } - - private function parseTermIds(string $key, array|Builder|string $terms): array - { - [$ignore, $handle] = explode(':', $key); - - return collect($this->explodeTerms($terms)) - ->map(fn (Term|string $term) => $this->getTermId(handle: $handle, term: $term)) - ->all(); - } - private function spanningDays(): Closure { return function (Entry $occurrence) { From 9ce62ad96b0943642db5754225fc19313b357c8e Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 10 Jun 2026 11:36:21 -0700 Subject: [PATCH 05/13] need to do this so this is non-breaking --- src/Events.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Events.php b/src/Events.php index 29d3f03..e432723 100644 --- a/src/Events.php +++ b/src/Events.php @@ -159,7 +159,13 @@ public function sort(string $direction): self public function terms(string|array $terms): self { - $this->terms = Arr::wrap($terms); + // these will be term ids, `taxonomy-handle::term` + // need to be added to the parameters like `[taxonomy:taxonomy-handle => term]` + foreach (Arr::wrap($terms) as $termId) { + [$taxonomy, $term] = explode('::', $termId); + + $this->params->put('taxonomy:'.$taxonomy, $term); + } return $this; } From 40de216e04f313b380fca02d8badc04daea10438 Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 10 Jun 2026 11:36:32 -0700 Subject: [PATCH 06/13] from is used for dates --- src/Entries.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entries.php b/src/Entries.php index c0a2d99..362ca2c 100644 --- a/src/Entries.php +++ b/src/Entries.php @@ -8,7 +8,7 @@ class Entries extends BaseEntries { // we don't support these - protected $ignoredParams = ['as', 'order_by', 'since', 'sort', 'until']; + protected $ignoredParams = ['as', 'from', 'order_by', 'since', 'sort', 'until']; /* Same as the parent but removed the queries we don't support From ce7bd864af2d73fdb94cae2dfe4a636b042292ba Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 10 Jun 2026 11:57:13 -0700 Subject: [PATCH 07/13] handle these on our own --- src/Entries.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entries.php b/src/Entries.php index 362ca2c..907c1d8 100644 --- a/src/Entries.php +++ b/src/Entries.php @@ -8,7 +8,7 @@ class Entries extends BaseEntries { // we don't support these - protected $ignoredParams = ['as', 'from', 'order_by', 'since', 'sort', 'until']; + protected $ignoredParams = ['as', 'from', 'offset', 'order_by', 'paginate', 'limit', 'since', 'sort', 'until']; /* Same as the parent but removed the queries we don't support From 965f4bef58a122d1ec1e5da8c7318e34f1bc6fda Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 10 Jun 2026 11:57:25 -0700 Subject: [PATCH 08/13] add so Entries can do the filtering --- src/Events.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Events.php b/src/Events.php index e432723..b0a1d6e 100644 --- a/src/Events.php +++ b/src/Events.php @@ -90,6 +90,7 @@ public function collapseMultiDays(?bool $collapseMultiDays = true): self public function collection(string $handle): self { $this->collection = $handle; + $this->params->put('collection', $handle); return $this; } From b3367497455cd7119e10fb0ee9817904972ca2c9 Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 10 Jun 2026 11:57:32 -0700 Subject: [PATCH 09/13] formatting --- tests/Tags/EventsTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Tags/EventsTest.php b/tests/Tags/EventsTest.php index 0dc46dd..b1566a5 100755 --- a/tests/Tags/EventsTest.php +++ b/tests/Tags/EventsTest.php @@ -483,7 +483,7 @@ ->spanning_start->toBeTrue() ->spanning_end->toBeFalse(); - expect($secondSpanningOccurrence) - ->spanning_start->toBeFalse() - ->spanning_end->toBeTrue(); + expect($secondSpanningOccurrence) + ->spanning_start->toBeFalse() + ->spanning_end->toBeTrue(); }); From 13fd7537ff2a996bd0a66b1f97c45c717b8ae394 Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 10 Jun 2026 13:17:11 -0700 Subject: [PATCH 10/13] small tidy --- src/Events.php | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/Events.php b/src/Events.php index b0a1d6e..3602c77 100644 --- a/src/Events.php +++ b/src/Events.php @@ -59,18 +59,13 @@ public static function fromEntry(string $id): self public function __construct(Parameters $params) { - if ($params->has('event')) { - $this->event($params->get('event')); - } - - $this->params = $params; - $this + ->params($params) // gotta be first cuz some of the later one push to it ->collection($params->get('collection', 'events')) ->collapseMultiDays($params->bool('collapse_multi_days')) + ->event($params->get('event')) ->offset(offset: $params->int('offset')) ->pagination(page: Paginator::resolveCurrentPage(), perPage: $params->int('paginate')) - ->params($params) ->sort($params->get('sort', 'asc')) ->timezone(timezone: $params->get('timezone', static::defaultTimezone())); } @@ -95,9 +90,11 @@ public function collection(string $handle): self return $this; } - public function event($id): self + public function event(?string $id = null): self { - $this->event = $id; + if (! is_null($id)) { + $this->event = $id; + } return $this; } From 8c485009652c2c8b63506eccf4871c670db114d3 Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 10 Jun 2026 14:02:12 -0700 Subject: [PATCH 11/13] add test for taxonomy:not filtering --- tests/Tags/EventsTest.php | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/tests/Tags/EventsTest.php b/tests/Tags/EventsTest.php index b1566a5..49533e9 100755 --- a/tests/Tags/EventsTest.php +++ b/tests/Tags/EventsTest.php @@ -239,7 +239,39 @@ $occurrences = $this->tag->between(); - expect($occurrences)->toHaveCount(1); + expect($occurrences) + ->toHaveCount(1) + ->first()->title->toBe('Recurring Event'); +}); + +test('can generate upcoming occurrences without taxonomy terms', function () { + Carbon::setTestNow(now()->setTimeFromTimeString('10:00')); + + Entry::make() + ->collection('events') + ->slug('single-event') + ->id('single-event') + ->data([ + 'title' => 'Single Event', + 'start_date' => Carbon::now()->toDateString(), + 'start_time' => '17:00', + 'end_time' => '19:00', + ])->save(); + + $this->tag + ->setContext([]) + ->setParameters([ + 'collection' => 'events', + 'from' => Carbon::now()->toDateString(), + 'to' => Carbon::now()->addDay()->toDateString(), + 'taxonomy:categories:not' => 'one', + ]); + + $occurrences = $this->tag->between(); + + expect($occurrences) + ->toHaveCount(1) + ->first()->title->toBe('Single Event'); }); test('can generate upcoming occurrences with filter', function () { From 7044036f126c7df344f2cbbc635a9dd2c30e872a Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 10 Jun 2026 14:03:53 -0700 Subject: [PATCH 12/13] not used --- src/Events.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Events.php b/src/Events.php index 3602c77..b1068f9 100644 --- a/src/Events.php +++ b/src/Events.php @@ -38,8 +38,6 @@ class Events private string $sort = 'asc'; - // private array $terms = []; - private ?string $timezone = null; public static function defaultTimezone(): string From b68c656efd7270dbacd66227759d2eac93c360fd Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 10 Jun 2026 14:06:28 -0700 Subject: [PATCH 13/13] tidy --- src/Events.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Events.php b/src/Events.php index b1068f9..33e74b3 100644 --- a/src/Events.php +++ b/src/Events.php @@ -212,8 +212,7 @@ private function output(callable $type): EntryCollection|LengthAwarePaginator private function entries(): self { - $params = $this->params->all(); - $this->entries = (new Entries(new Parameters($params)))->get(); + $this->entries = (new Entries($this->params))->get(); return $this; }