44
55namespace Cone \Root \Fields ;
66
7+ use Closure ;
8+ use Cone \Root \Fields \Select as SelectField ;
9+ use Cone \Root \Filters \Select ;
710use Illuminate \Database \Eloquent \Builder ;
811use Illuminate \Database \Eloquent \Model ;
912use Illuminate \Database \Eloquent \Relations \MorphTo as EloquentRelation ;
@@ -20,6 +23,11 @@ class MorphTo extends BelongsTo
2023 */
2124 protected string $ template = 'root::fields.relation ' ;
2225
26+ /**
27+ * Indicates whether the field is async.
28+ */
29+ protected bool $ async = true ;
30+
2331 /**
2432 * The morph types.
2533 */
@@ -36,24 +44,61 @@ public function getRelation(Model $model): EloquentRelation
3644 /**
3745 * {@inheritdoc}
3846 */
39- public function filters ( Request $ request ): array
47+ public function async ( bool $ value = true ): static
4048 {
41- $ filters = parent ::filters ($ request );
49+ return $ this ;
50+ }
51+
52+ /**
53+ * Get the morph types.
54+ */
55+ public function getTypes (): array
56+ {
57+ return $ this ->types ;
58+ }
4259
43- if ($ this ->isAsync ()) {
44- $ typeFilter = Select::make (__ ('Type ' ), 'type ' )
45- ->options (array_map (
60+ /**
61+ * {@inheritdoc}
62+ */
63+ public function filters (Request $ request ): array
64+ {
65+ $ typeFilter = new class ($ this ) extends Select
66+ {
67+ public function __construct (protected MorphTo $ field )
68+ {
69+ parent ::__construct ('type ' );
70+ }
71+
72+ public function getName (): string
73+ {
74+ return __ ('Type ' );
75+ }
76+
77+ public function apply (Request $ request , Builder $ query , mixed $ value ): Builder
78+ {
79+ return $ query ;
80+ }
81+
82+ public function options (Request $ request ): array
83+ {
84+ return array_map (
4685 static function (string $ type ): string {
4786 return __ (Str::of ($ type )->classBasename ()->headline ()->value ());
4887 },
49- array_combine ($ this ->types , $ this ->types )
50- ))
51- ->toFilter ();
52-
53- array_unshift ($ filters , $ typeFilter );
54- }
88+ array_combine ($ this ->field ->getTypes (), $ this ->field ->getTypes ())
89+ );
90+ }
91+
92+ public function toField (): SelectField
93+ {
94+ return parent ::toField ()
95+ ->value (function (Request $ request , Model $ model ): ?string {
96+ return $ model ->getAttribute ($ this ->field ->getRelation ($ model )->getMorphType ());
97+ });
98+ }
99+ };
55100
56- return $ filters ;
101+ return array_merge ([ $ typeFilter ], parent :: filters ( $ request )) ;
57102 }
58103
59104 /**
@@ -73,6 +118,59 @@ public function resolveRelatableQuery(Request $request, Model $model): Builder
73118 return parent ::resolveRelatableQuery ($ request , $ model );
74119 }
75120
121+ /**
122+ * Map the async searchable fields.
123+ */
124+ protected function mapAsyncSearchableFields (Request $ request ): Fields
125+ {
126+ $ fields = new Fields ;
127+
128+ foreach ($ this ->getSearchableColumns () as $ type => $ columns ) {
129+ foreach ($ columns as $ column ) {
130+ $ field = Hidden::make ($ this ->getRelationName (), sprintf ('%s:%s ' , $ type , $ column ))
131+ ->searchable (callback: function (Request $ request , Builder $ query , mixed $ value , string $ attribute ): Builder {
132+ [$ type , $ column ] = explode (': ' , $ attribute );
133+
134+ return match ($ query ->getModel ()::class) {
135+ $ type => $ query ->where ($ query ->qualifyColumn ($ column ), 'like ' , "% {$ value }% " , 'or ' ),
136+ default => $ query ,
137+ };
138+ });
139+
140+ $ fields ->push ($ field );
141+ }
142+ }
143+
144+ return $ fields ;
145+ }
146+
147+ /**
148+ * {@inheritdoc}
149+ */
150+ public function searchable (bool |Closure $ value = true , ?Closure $ callback = null , array $ columns = ['id ' ]): static
151+ {
152+ $ columns = match (true ) {
153+ array_is_list ($ columns ) => array_fill_keys ($ this ->types , $ columns ),
154+ default => $ columns ,
155+ };
156+
157+ return parent ::searchable ($ value , $ callback , $ columns );
158+ }
159+
160+ /**
161+ * Resolve the filter query.
162+ */
163+ public function resolveSearchQuery (Request $ request , Builder $ query , mixed $ value ): Builder
164+ {
165+ if (! $ this ->isSearchable ()) {
166+ return parent ::resolveSearchQuery ($ request , $ query , $ value );
167+ }
168+
169+ return call_user_func_array ($ this ->searchQueryResolver , [
170+ $ request , $ query , $ value , $ this ->getSearchableColumns (),
171+ ]);
172+ }
173+
76174 /**
77175 * {@inheritdoc}
78176 */
@@ -95,7 +193,10 @@ public function resolveHydrate(Request $request, Model $model, mixed $value): vo
95193 */
96194 public function newOption (Model $ related , string $ label ): Option
97195 {
98- return new Option (sprintf ('%s:%s ' , $ related ::class, $ related ->getKey ()), $ label );
196+ return new Option (
197+ sprintf ('%s:%s ' , $ related ::class, $ related ->getKey ()),
198+ sprintf ('%s (%s) ' , $ label , __ (Str::of ($ related ::class)->classBasename ()->headline ()->value ()))
199+ );
99200 }
100201
101202 /**
0 commit comments