Skip to content

Commit c129140

Browse files
author
antoine
committed
Merge remote-tracking branch 'origin/poc-autocomplete-closure' into poc-autocomplete-closure
2 parents f71b637 + 3842afc commit c129140

19 files changed

+551
-308
lines changed

demo/app/Sharp/Posts/PostForm.php

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,18 +138,17 @@ public function buildFormFields(FieldsContainer $formFields): void
138138
SharpFormAutocompleteRemoteField::make('author_id')
139139
->setReadOnly(! auth()->user()->isAdmin())
140140
->setLabel('Author')
141-
->setRemoteEndpoint('/api/admin/users')
142-
->queryResultsUsing(function ($search, $data) {
143-
$users = User::orderBy('name');
141+
->setRemoteCallback(function ($search) {
142+
$users = User::orderBy('name')->limit(10);
144143

145144
foreach (explode(' ', trim($search)) as $word) {
146-
$users->where(function ($query) use ($word) {
147-
$query->orWhere('name', 'like', "%$word%")
148-
->orWhere('email', 'like', "%$word%");
149-
});
145+
$users->where(fn ($query) => $query
146+
->where('name', 'like', "%$word%")
147+
->orWhere('email', 'like', "%$word%")
148+
);
150149
}
151150

152-
return $users->limit(10)->get();
151+
return $users->get();
153152
})
154153
->setListItemTemplate('<div>{{ $name }}</div><div><small>{{ $email }}</small></div>')
155154
->setHelpMessage('This field is only editable by admins.'),

demo/app/Sharp/TestForm/TestForm.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,28 +209,24 @@ public function buildFormFields(FieldsContainer $formFields): void
209209
)
210210
->addField(
211211
SharpFormSelectField::make('select_dropdown', $this->options())
212-
->setLocalized()
213212
->setLabel('Select dropdown')
214213
->setMultiple()
215214
->setDisplayAsDropdown(),
216215
)
217216
->addField(
218217
SharpFormSelectField::make('select_list', $this->options())
219-
->setLocalized()
220218
->setLabel('Select list')
221219
->setDisplayAsList(),
222220
)
223221
->addField(
224222
SharpFormSelectField::make('select_list_multiple', $this->options())
225-
->setLocalized()
226223
->setLabel('Select list multiple')
227224
->setMultiple()
228225
->setDisplayAsList()
229226
->setMaxSelected(2),
230227
)
231228
->addField(
232229
SharpFormTagsField::make('tags', $this->options())
233-
->setLocalized()
234230
->setLabel('Tags')
235231
->setCreatable(true)
236232
->setCreateAttribute('label')

docs/guide/form-fields/autocomplete-list.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ You can in fact define the list as this:
1212
SharpFormAutocompleteListField::make('winners')
1313
->setLabel('Winners')
1414
->setItemField(
15-
SharpFormAutocompleteField::make('item', 'remote')
15+
SharpFormAutocompleteRemoteField::make('item')
1616
->setRemoteEndpoint('/players')
1717
// [...]
1818
)
@@ -30,7 +30,7 @@ But why can't we use a classic List for this? Well, the `model->winners` relatio
3030

3131
Configuration is the same as the classic [List](list.md), except for:
3232

33-
### `setItemField(SharpFormAutocompleteField $field)`
33+
### `setItemField(SharpFormAutocompleteRemoteField $field)`
3434

3535
You can use this function instead of `addItemField`, since items of AutocompleteList have only one field.
3636

docs/guide/form-fields/autocomplete.md

Lines changed: 55 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,52 @@ This method is useful to link the dataset of a local autocomplete (aka: the `loc
2121

2222
### `setRemoteEndpoint(string $remoteEndpoint)`
2323

24-
The endpoint to hit with mode=remote.
24+
The remote endpoint which should return JSON-formatted results. Note that you can add the `sharp_auth` middleware to this route to handle authentication and prevent this API endpoint to be called by non-sharp users:
2525

26-
If this endpoint is yours (`remote` mode here is useful to avoid loading a lot of data in the view), you can add the `sharp_auth` middleware to the API route to handle authentication and prevent this API endpoint to be called by non-sharp users:
26+
```php
27+
// in a route file
28+
29+
Route::get('/api/sharp/clients', [MySharpApiClientController::class, 'index'])
30+
->middleware('sharp_auth');
31+
```
32+
33+
::: tip
34+
This endpoint MUST be part of your application. If you need to hit an external endpoint, you should create a custom endpoint in your application that will call the external endpoint (be sure to check the alternative `setRemoteCallback` method).
35+
:::
36+
37+
### `setRemoteCallback(Closure $closure, ?array $linkedFields = null)`
38+
39+
To avoid the pain of writing a new dedicated endpoint, and for simple cases, you can use this method to provide a callback that will be called when the autocomplete field needs to fetch data. The callback will receive the search string as a parameter and should return an array of objects.
40+
41+
Example:
42+
43+
```php
44+
SharpFormAutocompleteRemoteField::make('customer')
45+
->setRemoteCallback(function ($search) {
46+
return Customer::select('id', 'name', 'email')
47+
->where('name', 'like', "%$search%")
48+
->get();
49+
});
50+
```
51+
52+
The second argument, `$linkedFields`, allows you to provide a list of fields that will be sent with their values to the callback, so you can filter the results based on the values of other fields.
53+
54+
Example:
2755

2856
```php
29-
Route::get('/api/sharp/clients')
30-
->middleware('sharp_auth')
31-
->uses('MySharpApiClientController@index')
57+
SharpFormAutocompleteRemoteField::make('customer')
58+
->setRemoteCallback(function ($search, $linkedFields) {
59+
return Customer::select('id', 'name', 'email')
60+
->when(
61+
$linkedFields['country'],
62+
fn ($query) => $query->where('country_id', $linkedFields['country'])
63+
)
64+
->where('name', 'like', "%$search%")
65+
->get();
66+
}, linkedFields: ['country']);
3267
```
3368

69+
3470
### `setSearchMinChars(int $searchMinChars)`
3571

3672
Set a minimum number of character to type before performing the search.
@@ -61,7 +97,7 @@ Set the remote method to GET (default) or POST.
6197
In a remote autocomplete case, you can use this method instead of `setRemoteEndpoint` to handle a dynamic URL, based on another form field. Here's how, for example:
6298

6399
```php
64-
SharpFormAutocompleteField::make('brand', 'remote')
100+
SharpFormAutocompleteRemoteField::make('brand')
65101
->setDynamicRemoteEndpoint('/brands/{{country}}');
66102
```
67103

@@ -71,7 +107,7 @@ You may need to provide a default value for the endpoint, used when `country` (i
71107
fill the second argument:
72108

73109
```php
74-
SharpFormAutocompleteField::make('model', 'remote')
110+
SharpFormAutocompleteRemoteField::make('model')
75111
->setDynamicRemoteEndpoint(''/models/{{country}}/{{brand}}'', [
76112
'country' => 'france',
77113
'brand' => 'renault'
@@ -90,53 +126,22 @@ Set the name of the id attribute for items. This is useful :
90126
- to designate the id attribute in the remote API call return.
91127
Default: `"id"`
92128

93-
### `setListItemInlineTemplate(string $template)`
94-
### `setResultItemInlineTemplate(string $template)`
95-
Just write the template as a string, using placeholders for data like this: `{{var}}`.
129+
### `setListItemTemplate(View|string $template)`
130+
### `setResultItemTemplate(View|string $template)`
96131

97-
Example:
132+
The templates for the list and result items can be set in two ways: either by passing a string, or by passing a Laravel view.
98133

99-
```php
100-
$panel->setInlineTemplate(
101-
'Foreground: <strong>{{color}}</strong>'
102-
)
103-
```
104-
105-
The template will be used, depending on the function, to display either the list item (in the result dropdown) or the result item (meaning the valuated form input).
106-
107-
Be aware that you'll need for this to work to pass a valuated object to the Autocomplete, as data.
108-
109-
### `setListItemTemplatePath(string $listItemTemplatePath)`
110-
### `setResultItemTemplatePath(string $resultItemTemplate)`
111-
112-
Use this if you need more control: give the path of a full template, in its own file.
113-
114-
The template will be [interpreted by Vue.js](https://vuejs.org/v2/guide/syntax.html), meaning you can add data placeholders, DOM structure but also directives, and anything that Vue will parse. For instance:
115-
116-
```vue
117-
<div v-if="show">result is {{value}}</div>
118-
<div v-else>result is unknown</div>
119-
```
120-
121-
The template will be used, depending on the function, to display either the list item (in the result dropdown) or the result item (meaning the valuated form input).
122-
123-
Be aware that you'll need for this to work to pass a valuated object to the Autocomplete, as data.
124-
125-
### `setAdditionalTemplateData(array $data)`
126-
127-
Useful to add some static (or at least not instance-dependant) data to the template. For instance:
134+
Examples:
128135

129136
```php
130-
SharpFormAutocompleteRemoteField::make('brand')
131-
->setAdditionalTemplateData([
132-
'years' => [2020, 1018, 2017]
133-
]);
134-
```
135-
136-
In the template, the provided data can be used as normal:
137-
138-
```vue
139-
<div v-for="year in years"> {{ year }} </div>
137+
SharpFormAutocompleteRemoteField::make('customer')
138+
->setRemoteCallback(function ($search) {
139+
return Customer::select('id', 'name', 'email')
140+
->where('name', 'like', "%$search%")
141+
->get();
142+
})
143+
->setListItemTemplate('<div>{{$name}}</div><div><small>{{$email}}</small></div>')
144+
->setResultItemTemplate(view('my/customer/blade/view'));
140145
```
141146

142147
## Formatter

docs/guide/upgrading/9.0.md

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,11 @@ class PostBlockList extends SharpEntityList
495495

496496
You can of course instead declare a real Filter.
497497

498-
## SharpFormGeolocationField using Google Maps API must now provide a Map ID
498+
## Localization feature was removed for `SharpFormSelectField`, `SharpFormTagsField` and `SharpFormAutocompleteField` fields
499+
500+
Those fields could be localized in 8.x it a weird way: **labels** were localized, but not **values**. This was a really misleading, so we decided to remove entirely this behavior in 9.x. The only real impact should be to remove setLocalized() calls in your code for these fields.
501+
502+
## `SharpFormGeolocationField` using Google Maps API must now provide a Map ID
499503

500504
Sharp 9.x now uses Advanced Markers which requires a [Map ID](https://developers.google.com/maps/documentation/get-map-id), register it with the following method of the field :
501505

@@ -515,15 +519,15 @@ class PostForm extends SharpForm
515519
```
516520

517521

518-
## SharpFormAutocompleteFormField was split in two subclasses for local and remote cases
522+
## `SharpFormAutocompleteFormField` was rewritten and need to be migrated
519523

520-
REWRITE THIS WITH:
521-
- no more `setAdditionalTemplateData`
522-
-
524+
First, the `SharpFormAutocompleteFormField` class was split into two classes: `SharpFormAutocompleteLocalField` and `SharpFormAutocompleteRemoteField`, to clearly separate these two different use cases.
523525

524-
This isn’t a breaking change, since `SharpFormAutocompleteFormField::make($key, $mode)` is still supported, but deprecated. You should migrate to the new `SharpFormAutocompleteLocalField` and `SharpFormAutocompleteRemoteField` classes.
526+
Second, Vue templates must be migrated to Blade templates (similar to the `SharpWidgetPanel` or Page Alerts migrations). You can either pass a view name or a blade template directly to the newly named `setListItemTemplate()` and `setResultItemTemplate()` methods (the old `setListItemInlineTemplate()`, `setListItemTemplatePath()`, `setResultItemInlineTemplate()` and `setResultItemTemplatePath()` were removed).
525527

526-
In 8.x:
528+
The `setAdditionalTemplateData()` method was also removed, in favor of a more straightforward way to pass additional data to the template.
529+
530+
Example in 8.x:
527531

528532
```php
529533
class PostForm extends SharpForm
@@ -565,8 +569,7 @@ class PostForm extends SharpForm
565569
->addField(
566570
SharpFormAutocompleteRemoteField::make('author_id')
567571
->setRemoteEndpoint('/api/admin/users')
568-
->setListItemInlineTemplate('{{name}}')
569-
->setResultItemInlineTemplate('{{name}}')
572+
->setListItemTemplate('{{$name}}')
570573
)
571574
->addField(
572575
SharpFormAutocompleteLocalField::make('category_id')
@@ -585,6 +588,51 @@ class PostForm extends SharpForm
585588
}
586589
```
587590

591+
Finally, there is a big evolution which concerns the remote autocomplete endpoint: your 8.x implementation should still work, but you should note that:
592+
- you can now directly write the autocomplete endpoint as a callback closure in the field (no need to use a dedicated route + controller),
593+
- external endpoint URLs aren’t supported anymore (you must write a wrapper around this external endpoint, either as a route + controller or via the new callback option).
594+
595+
Example in 8.x:
596+
597+
```php
598+
class PostForm extends SharpForm
599+
{
600+
public function buildFormFields(FieldsContainer $formFields): void
601+
{
602+
$formFields
603+
->addField(
604+
SharpFormAutocompleteField::make('author_id', 'remote')
605+
->setRemoteEndpoint('/api/admin/users')
606+
->setListItemInlineTemplate('{{name}}')
607+
->setResultItemInlineTemplate('{{name}}')
608+
);
609+
}
610+
611+
// ...
612+
}
613+
```
614+
615+
In 9.x:
616+
617+
```php
618+
class PostForm extends SharpForm
619+
{
620+
public function buildFormFields(FieldsContainer $formFields): void
621+
{
622+
$formFields
623+
->addField(
624+
SharpFormAutocompleteRemoteField::make('author_id')
625+
->setRemoteCallback(function ($search) {
626+
return User::where('name', 'like', "%$search%")->get();
627+
})
628+
->setListItemTemplate('{{$name}}')
629+
);
630+
}
631+
632+
// ...
633+
}
634+
```
635+
588636
## `SharpFormUploadField`’s image related methods were renamed
589637

590638
This isn't a breaking change, since the old methods are still available, but deprecated. You should migrate to the new methods:

src/Data/Form/Fields/FormAutocompleteRemoteFieldData.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public function __construct(
2525
public string $itemIdAttribute,
2626
public int $searchMinChars,
2727
public int $debounceDelay,
28-
public string $remoteEndpoint,
28+
public ?string $remoteEndpoint = null,
2929
/** @var string[]|null */
3030
public ?array $callbackLinkedFields = null,
3131
public ?string $placeholder = null,

src/Form/Fields/SharpFormAutocompleteLocalField.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,9 @@
55
use Code16\Sharp\Form\Fields\Formatters\AutocompleteLocalFormatter;
66
use Code16\Sharp\Form\Fields\Utils\IsSharpFormAutocompleteField;
77
use Code16\Sharp\Form\Fields\Utils\SharpFormAutocompleteCommonField;
8-
use Code16\Sharp\Utils\Fields\IsSharpFieldWithLocalization;
98
use Illuminate\Support\Collection;
109

11-
class SharpFormAutocompleteLocalField
12-
extends SharpFormField
13-
implements IsSharpFieldWithLocalization, IsSharpFormAutocompleteField
10+
class SharpFormAutocompleteLocalField extends SharpFormField implements IsSharpFormAutocompleteField
1411
{
1512
use SharpFormAutocompleteCommonField;
1613

0 commit comments

Comments
 (0)