1+ <?php
2+
3+ namespace Ark4ne \JsonApi \Filters ;
4+
5+ use Closure ;
6+ use Illuminate \Contracts \Auth \Access \Gate ;
7+ use Illuminate \Http \Request ;
8+ use Illuminate \Http \Resources \MissingValue ;
9+ use Illuminate \Support \Collection ;
10+
11+ /**
12+ * @template Resource
13+ */
14+ class Filters
15+ {
16+ /** @var array<FilterRule<Resource>> */
17+ protected array $ rules = [];
18+
19+ /**
20+ * Add a policy-based filter
21+ *
22+ * @param iterable<string>|string $abilities Abilities to check
23+ * @param array<mixed> $arguments Arguments to pass to the policy method, the model is always the first argument
24+ * @param string $gateClass Gate class to use, defaults to the default Gate implementation
25+ * @param string|null $guard Guard to use, defaults to the default guard
26+ * @return static
27+ */
28+ public function can (iterable |string $ abilities , array $ arguments = [], string $ gateClass = Gate::class, ?string $ guard = null ): static
29+ {
30+ $ this ->rules [] = new PolicyFilterRule ($ abilities , $ arguments , $ gateClass , $ guard );
31+ return $ this ;
32+ }
33+
34+ /**
35+ * Add a custom filter rule
36+ *
37+ * @param Closure $callback Callback that receives (Request $request, Model $model) and returns bool
38+ * @return static
39+ */
40+ public function when (Closure $ callback ): static
41+ {
42+ $ this ->rules [] = new CallbackFilterRule ($ callback );
43+ return $ this ;
44+ }
45+
46+ /**
47+ * Apply all filters to the given data
48+ *
49+ * @param mixed $data
50+ * @return mixed
51+ */
52+ public function apply (Request $ request , mixed $ data ): mixed
53+ {
54+ if ($ data instanceof MissingValue || $ data === null ) {
55+ return $ data ;
56+ }
57+
58+ // If it's a collection/array, filter each item
59+ if (is_iterable ($ data )) {
60+ $ filtered = [];
61+ foreach ($ data as $ key => $ item ) {
62+ if ($ this ->shouldInclude ($ request , $ item )) {
63+ $ filtered [$ key ] = $ item ;
64+ }
65+ }
66+
67+ // Preserve the original collection type
68+ if ($ data instanceof Collection) {
69+ return new Collection ($ filtered );
70+ }
71+
72+ return $ filtered ;
73+ }
74+
75+ // Single model - check if it should be included
76+ return $ this ->shouldInclude ($ request , $ data ) ? $ data : new MissingValue ();
77+ }
78+
79+ /**
80+ * Check if a model should be included based on all filter rules
81+ *
82+ * @param mixed $model
83+ * @return bool
84+ */
85+ protected function shouldInclude (Request $ request , mixed $ model ): bool
86+ {
87+ // All rules must pass
88+ foreach ($ this ->rules as $ rule ) {
89+ if (!$ rule ->passes ($ request , $ model )) {
90+ return false ;
91+ }
92+ }
93+
94+ return true ;
95+ }
96+ }
0 commit comments