Skip to content

Commit 82721bf

Browse files
authored
Merge pull request #105 from tarfin-labs/WB-180-event-machine-interacts-with-input-trait
Event Machine InteractsWithInput trait
2 parents d09d7e6 + b701585 commit 82721bf

File tree

3 files changed

+255
-17
lines changed

3 files changed

+255
-17
lines changed

src/Behavior/EventBehavior.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,25 @@
44

55
namespace Tarfinlabs\EventMachine\Behavior;
66

7+
use Illuminate\Support\Arr;
78
use Spatie\LaravelData\Data;
89
use Spatie\LaravelData\Optional;
10+
use Illuminate\Support\Collection;
11+
use Illuminate\Support\Enumerable;
12+
use Illuminate\Support\LazyCollection;
13+
use Spatie\LaravelData\DataCollection;
914
use Tarfinlabs\EventMachine\ContextManager;
15+
use Illuminate\Pagination\AbstractPaginator;
1016
use Tarfinlabs\EventMachine\Enums\SourceType;
1117
use Illuminate\Validation\ValidationException;
18+
use Spatie\LaravelData\PaginatedDataCollection;
19+
use Illuminate\Support\Traits\InteractsWithData;
20+
use Illuminate\Pagination\AbstractCursorPaginator;
1221
use Spatie\LaravelData\Attributes\WithoutValidation;
22+
use Spatie\LaravelData\CursorPaginatedDataCollection;
23+
use Illuminate\Contracts\Pagination\Paginator as PaginatorContract;
1324
use Tarfinlabs\EventMachine\Exceptions\MachineEventValidationException;
25+
use Illuminate\Contracts\Pagination\CursorPaginator as CursorPaginatorContract;
1426

1527
/**
1628
* Class EventBehavior.
@@ -19,6 +31,17 @@
1931
*/
2032
abstract class EventBehavior extends Data
2133
{
34+
/**
35+
* Use InteractsWithData trait with aliases to avoid conflicts between:
36+
* - Spatie Data's static collect() vs Laravel trait's non-static collect()
37+
* - Different return types between parent class and trait methods
38+
*/
39+
use InteractsWithData {
40+
InteractsWithData::collect as collection;
41+
InteractsWithData::only as onlyItems;
42+
InteractsWithData::except as exceptItems;
43+
}
44+
2245
/** Actor performing the event. */
2346
private mixed $actor = null;
2447

@@ -103,4 +126,64 @@ public static function stopOnFirstFailure(): bool
103126
{
104127
return true;
105128
}
129+
130+
/**
131+
* Delegate to parent's static collect() from Spatie Data class.
132+
* The trait's non-static collect() is aliased as 'collection' to avoid conflict.
133+
*/
134+
public static function collect(...$args): array|DataCollection|PaginatedDataCollection|CursorPaginatedDataCollection|Enumerable|AbstractPaginator|PaginatorContract|AbstractCursorPaginator|CursorPaginatorContract|LazyCollection|Collection
135+
{
136+
return parent::collect(...$args);
137+
}
138+
139+
/**
140+
* Override only() to return static type for fluent interface.
141+
* Uses parent implementation which correctly returns EventBehavior instance.
142+
*/
143+
public function only(...$args): static
144+
{
145+
return parent::only(...$args);
146+
}
147+
148+
/**
149+
* Override except() to return static type for fluent interface.
150+
* Uses parent implementation which correctly returns EventBehavior instance.
151+
*/
152+
public function except(...$args): static
153+
{
154+
return parent::except(...$args);
155+
}
156+
157+
/**
158+
* Get all of the input and files for the request.
159+
*
160+
* @param array|mixed|null $keys
161+
*/
162+
public function all($keys = null): array
163+
{
164+
$input = $this->payload ?? [];
165+
166+
if (!$keys) {
167+
return $input;
168+
}
169+
170+
$results = [];
171+
172+
foreach (is_array($keys) ? $keys : func_get_args() as $key) {
173+
Arr::set($results, $key, Arr::get($input, $key));
174+
}
175+
176+
return $results;
177+
}
178+
179+
/**
180+
* Retrieve data from the instance.
181+
*
182+
* @param string $key
183+
* @param mixed $default
184+
*/
185+
public function data($key = null, $default = null): mixed
186+
{
187+
return data_get($this->all(), $key, $default);
188+
}
106189
}

tests/Behavior/EventTest.php

Lines changed: 157 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,163 @@
22

33
declare(strict_types=1);
44

5-
use Tarfinlabs\EventMachine\Behavior\EventBehavior;
5+
use Tarfinlabs\EventMachine\Tests\Stubs\Events\SimpleEvent;
66

77
test('an event has a version', function (): void {
8-
$eventWithoutExplicitVersionDefinition = new class() extends EventBehavior {
9-
public static function getType(): string
10-
{
11-
return 'TEST_EVENT';
12-
}
13-
};
14-
15-
$eventWithVersionDefinition = new class(version: 13) extends EventBehavior {
16-
public static function getType(): string
17-
{
18-
return 'TEST_EVENT';
19-
}
20-
};
21-
22-
expect($eventWithoutExplicitVersionDefinition->version)->toBe(1);
23-
expect($eventWithVersionDefinition->version)->toBe(13);
8+
$eventWithoutExplicitVersionDefinition = new SimpleEvent();
9+
$eventWithVersionDefinition = new SimpleEvent(version: 13);
10+
11+
expect($eventWithoutExplicitVersionDefinition->version)->toBe(1)
12+
->and($eventWithVersionDefinition->version)->toBe(13);
13+
});
14+
15+
test('an event has a type', function (): void {
16+
$event = new SimpleEvent();
17+
18+
expect($event->type)->toBe('SIMPLE_EVENT')
19+
->and(SimpleEvent::getType())->toBe('SIMPLE_EVENT');
20+
});
21+
22+
test('all method returns entire payload', function (): void {
23+
$payload = [
24+
'name' => 'John',
25+
'email' => 'john@example.com',
26+
'age' => 30,
27+
];
28+
$event = new SimpleEvent(payload: $payload);
29+
30+
expect($event->all())->toBe($payload);
31+
});
32+
33+
test('all method returns empty array when payload is null', function (): void {
34+
$event = new SimpleEvent();
35+
36+
expect($event->all())->toBe([]);
37+
});
38+
39+
test('all method returns specific keys from payload', function (): void {
40+
$payload = [
41+
'name' => 'John',
42+
'email' => 'john@example.com',
43+
'age' => 30,
44+
'city' => 'New York',
45+
];
46+
$event = new SimpleEvent(payload: $payload);
47+
$result = $event->all(['name', 'email']);
48+
49+
expect($result)->toBe([
50+
'name' => 'John',
51+
'email' => 'john@example.com',
52+
]);
53+
});
54+
55+
test('all method handles nested keys', function (): void {
56+
$payload = [
57+
'user' => [
58+
'name' => 'John',
59+
'email' => 'john@example.com',
60+
],
61+
'settings' => [
62+
'theme' => 'dark',
63+
],
64+
];
65+
$event = new SimpleEvent(payload: $payload);
66+
$result = $event->all(['user.name', 'settings.theme']);
67+
68+
expect($result)->toBe([
69+
'user' => ['name' => 'John'],
70+
'settings' => ['theme' => 'dark'],
71+
]);
72+
});
73+
74+
test('all method returns null for non-existent keys', function (): void {
75+
$payload = [
76+
'name' => 'John',
77+
];
78+
$event = new SimpleEvent(payload: $payload);
79+
$result = $event->all(['name', 'nonexistent']);
80+
81+
expect($result)->toBe([
82+
'name' => 'John',
83+
'nonexistent' => null,
84+
]);
85+
});
86+
87+
test('data method retrieves value by key', function (): void {
88+
$payload = [
89+
'name' => 'John',
90+
'email' => 'john@example.com',
91+
];
92+
$event = new SimpleEvent(payload: $payload);
93+
94+
expect($event->data('name'))->toBe('John')
95+
->and($event->data('email'))->toBe('john@example.com');
96+
});
97+
98+
test('data method retrieves nested value with dot notation', function (): void {
99+
$payload = [
100+
'user' => [
101+
'name' => 'John',
102+
'address' => [
103+
'city' => 'New York',
104+
'zip' => '10001',
105+
],
106+
],
107+
];
108+
$event = new SimpleEvent(payload: $payload);
109+
110+
expect($event->data('user.name'))->toBe('John')
111+
->and($event->data('user.address.city'))->toBe('New York')
112+
->and($event->data('user.address.zip'))->toBe('10001');
113+
});
114+
115+
test('data method returns default value for non-existent key', function (): void {
116+
$payload = [
117+
'name' => 'John',
118+
];
119+
$event = new SimpleEvent(payload: $payload);
120+
121+
expect($event->data('email', 'default@example.com'))->toBe('default@example.com')
122+
->and($event->data('nonexistent', 'default'))->toBe('default');
123+
});
124+
125+
test('data method returns null for non-existent key without default', function (): void {
126+
$payload = [
127+
'name' => 'John',
128+
];
129+
$event = new SimpleEvent(payload: $payload);
130+
131+
expect($event->data('nonexistent'))->toBeNull();
132+
});
133+
134+
test('data method returns all data when key is null', function (): void {
135+
$payload = [
136+
'name' => 'John',
137+
'email' => 'john@example.com',
138+
];
139+
$event = new SimpleEvent(payload: $payload);
140+
141+
expect($event->data())->toBe($payload);
142+
});
143+
144+
test('trait methods work with event payload', function (): void {
145+
$event = new SimpleEvent(payload: [
146+
'name' => 'John Doe',
147+
'age' => '30',
148+
'price' => '19.99',
149+
'active' => true,
150+
'birthdate' => '1994-01-01',
151+
'tags' => ['php', 'laravel'],
152+
]);
153+
154+
expect($event->has('name'))->toBeTrue()
155+
->and($event->missing('email'))->toBeTrue()
156+
->and($event->integer('age'))->toBe(30)
157+
->and($event->float('price'))->toBe(19.99)
158+
->and($event->boolean('active'))->toBeTrue()
159+
->and($event->str('name'))->toEqual(str('John Doe'))
160+
->and($event->string('name')->upper()->toString())->toBe('JOHN DOE')
161+
->and($event->date('birthdate'))->toEqual(Date::parse('1994-01-01'))
162+
->and($event->array('tags'))->toEqual(['php', 'laravel'])
163+
->and($event->collection('tags'))->toHaveCount(2);
24164
});

tests/Stubs/Events/SimpleEvent.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tarfinlabs\EventMachine\Tests\Stubs\Events;
6+
7+
use Tarfinlabs\EventMachine\Behavior\EventBehavior;
8+
9+
class SimpleEvent extends EventBehavior
10+
{
11+
public static function getType(): string
12+
{
13+
return 'SIMPLE_EVENT';
14+
}
15+
}

0 commit comments

Comments
 (0)