Skip to content

Commit d02f7e4

Browse files
arraypressclaude
andcommitted
Let a relational field send arguments with its search
`data-search-args` was rendered, the script forwarded it, the endpoint cleaned it and the callback took it as a third argument -- and nothing could ever put anything in it. search_args() returned an empty array and read no configuration, so the whole path existed and was unreachable from a field. It reads `search_args` now. The reason to want it is context the search term does not carry: most often which record the picker is sitting in, so the callback can leave that one out. A product's "also suggest" offering the product itself is a choice that looks available, and is thrown away on save. Scalars only, matching what the endpoint reduces them to anyway -- a nested array would reach the callback as the string "Array" and read as a filter that had been applied. Subclasses that build their own arguments still override; a post picker has a post type to send and no use for anything a caller invents. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tk8y5eZa3CHVVSM6nVxsJw
1 parent 1e56b86 commit d02f7e4

2 files changed

Lines changed: 56 additions & 2 deletions

File tree

‎src/Types/AbstractRelationalType.php‎

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,29 @@ abstract protected function resolve_labels( array $ids, Field $field ): array;
7474
/**
7575
* Query arguments forwarded to the endpoint.
7676
*
77+
* Whatever the field declares under `search_args`, so a callback source
78+
* can be told something the search term does not carry -- which record
79+
* the picker is sitting in, most often, so it can leave that one out.
80+
* Without this the attribute existed and nothing could ever fill it.
81+
*
82+
* They reach the callback as its third argument, and the endpoint
83+
* reduces them to scalars before they get there.
84+
*
85+
* Subclasses that build their own arguments override this; a post picker
86+
* has a post type to send and no use for anything a caller invents.
87+
*
7788
* @param Field $field The field.
7889
*
7990
* @return array<string, scalar>
8091
*/
8192
protected function search_args( Field $field ): array {
82-
return [];
93+
$args = $field->get( 'search_args', [] );
94+
95+
if ( ! is_array( $args ) ) {
96+
return [];
97+
}
98+
99+
return array_filter( $args, 'is_scalar' );
83100
}
84101

85102
/**
@@ -284,7 +301,7 @@ public function config_keys(): array {
284301
// it in the list twice.
285302
return array_merge(
286303
parent::config_keys(),
287-
[ 'min_chars', 'search_callback', 'sortable', 'min', 'max' ]
304+
[ 'min_chars', 'search_callback', 'search_args', 'sortable', 'min', 'max' ]
288305
);
289306
}
290307

‎tests/RelationalLimitsTest.php‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,4 +243,41 @@ public function test_config_keys_have_no_duplicates( string $type ): void {
243243
$this->assertSame( array_values( array_unique( $keys ) ), $keys, "{$type} repeats a config key" );
244244
}
245245

246+
/**
247+
* A callback source can be told something the search term does not carry.
248+
*
249+
* `search_args` reaches the endpoint, which passes it to the callback as
250+
* its third argument. The usual reason is which record the picker is
251+
* sitting in, so it can leave that one out of the results -- a product's
252+
* "also suggest" offering the product itself is a choice that looks
253+
* available and is thrown away on save.
254+
*
255+
* The attribute existed before this and nothing could fill it.
256+
*/
257+
public function test_search_args_reach_the_page(): void {
258+
$markup = $this->render( 'ajax', [ 'search_args' => [ 'exclude' => 12 ] ] );
259+
260+
$this->assertStringContainsString( 'data-search-args', $markup );
261+
$this->assertStringContainsString( '&quot;exclude&quot;:12', $markup );
262+
}
263+
264+
/**
265+
* A field that declares none gets no attribute at all.
266+
*/
267+
public function test_no_search_args_means_no_attribute(): void {
268+
$this->assertStringNotContainsString( 'data-search-args', $this->render( 'ajax' ) );
269+
}
270+
271+
/**
272+
* Only scalars, because the endpoint reduces them to scalars anyway.
273+
*
274+
* A nested array would arrive as the string "Array" and read as a filter
275+
* that had been applied.
276+
*/
277+
public function test_search_args_drop_anything_that_is_not_scalar(): void {
278+
$markup = $this->render( 'ajax', [ 'search_args' => [ 'ok' => 1, 'bad' => [ 1, 2 ] ] ] );
279+
280+
$this->assertStringContainsString( '&quot;ok&quot;:1', $markup );
281+
$this->assertStringNotContainsString( 'bad', $markup );
282+
}
246283
}

0 commit comments

Comments
 (0)