Skip to content

Commit a51271f

Browse files
Merge pull request #19 from Guiziweb/docs/initial-documentation
docs: add README pitch + installation, usage, extending guides
2 parents c99cbc9 + f59eab3 commit a51271f

6 files changed

Lines changed: 402 additions & 145 deletions

File tree

README.md

Lines changed: 35 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -8,122 +8,56 @@
88
</a>
99
</p>
1010

11-
<h1 align="center">Plugin Skeleton</h1>
11+
<h1 align="center">Sylius Grid Assistant Plugin</h1>
1212

13-
<p align="center">Skeleton for starting Sylius plugins.</p>
14-
15-
## Documentation
16-
17-
For a comprehensive guide on Sylius Plugins development please go to Sylius documentation,
18-
there you will find the <a href="https://docs.sylius.com/en/latest/plugin-development-guide/index.html">Plugin Development Guide</a>, that is full of examples.
19-
20-
For more information about the **Test Application** included in the skeleton, please refer to the [Sylius documentation](https://docs.sylius.com/sylius-plugins/plugins-development-guide/testapplication).
21-
22-
## Quickstart Installation
23-
24-
Run `composer create-project sylius/plugin-skeleton ProjectName`.
25-
26-
### Traditional
27-
28-
1. From the plugin skeleton root directory, run the following commands:
29-
30-
```bash
31-
(cd vendor/sylius/test-application && yarn install)
32-
(cd vendor/sylius/test-application && yarn build)
33-
vendor/bin/console assets:install
34-
35-
vendor/bin/console doctrine:database:create
36-
vendor/bin/console doctrine:migrations:migrate -n
37-
# Optionally load data fixtures
38-
vendor/bin/console sylius:fixtures:load -n
39-
```
40-
41-
To be able to set up a plugin's database, remember to configure your database credentials in `tests/TestApplication/.env` and `tests/TestApplication/.env.test`.
42-
43-
2. Run your local server:
13+
<p align="center">
14+
<a href="https://github.com/Guiziweb/GuiziwebSyliusGridAssistantPlugin/actions"><img src="https://img.shields.io/github/actions/workflow/status/Guiziweb/GuiziwebSyliusGridAssistantPlugin/build.yaml?branch=main" alt="Build status"></a>
15+
<a href="https://packagist.org/packages/guiziweb/sylius-grid-assistant-plugin"><img src="https://img.shields.io/packagist/v/guiziweb/sylius-grid-assistant-plugin" alt="Latest version"></a>
16+
<a href="https://packagist.org/packages/guiziweb/sylius-grid-assistant-plugin"><img src="https://img.shields.io/packagist/php-v/guiziweb/sylius-grid-assistant-plugin" alt="PHP version"></a>
17+
<img src="https://img.shields.io/badge/phpunit-passing-success" alt="PHPUnit">
18+
<img src="https://img.shields.io/badge/behat-passing-success" alt="Behat">
19+
<img src="https://img.shields.io/badge/phpstan-level%20max-blue" alt="PHPStan max">
20+
<img src="https://img.shields.io/badge/ecs-passing-success" alt="ECS">
21+
<a href="https://packagist.org/packages/guiziweb/sylius-grid-assistant-plugin"><img src="https://img.shields.io/packagist/l/guiziweb/sylius-grid-assistant-plugin" alt="License"></a>
22+
</p>
4423

45-
```bash
46-
symfony server:ca:install
47-
symfony server:start -d
48-
```
24+
> **Natural language → filtered grid.** AI-powered filtering for Sylius admin grids.
4925
50-
3. Open your browser and navigate to `https://localhost:8000`.
26+
## How it works
5127

52-
### Docker
28+
In an admin grid (orders, products, customers...), the assistant adds a search bar at the top. Type your query in plain language:
5329

54-
1. Execute `make init` to initialize the container and install the dependencies.
30+
> *"orders over $100 from john.doe@gmail.com last month"*
5531
56-
2. Execute `make database-init` to create the database and run migrations.
32+
The plugin sends the query to a Large Language Model (OpenAI, Gemini, Anthropic, Mistral...), gets back structured filters and sorting (JSON Schema strict), and redirects to the same grid with the filters applied - exactly as if you had clicked them manually.
5733

58-
3. (Optional) Execute `make load-fixtures` to load the fixtures.
34+
![Demo](docs/demo.gif)
5935

60-
4. Your app is available at `http://localhost`.
36+
## Requirements
6137

62-
## Usage
38+
- PHP ^8.2
39+
- Sylius ^2.0
40+
- An AI provider account (OpenAI, Gemini, Anthropic, Mistral...) and its API key
6341

64-
### Running plugin tests
42+
## Quick install
6543

66-
- PHPUnit
44+
This plugin ships a [Symfony Flex recipe](https://github.com/Guiziweb/SyliusRecipes). With the Guiziweb recipe endpoint configured in your project (see linked repo), the install boils down to:
6745

68-
```bash
69-
vendor/bin/phpunit
70-
```
46+
```bash
47+
composer require guiziweb/sylius-grid-assistant-plugin
48+
composer require symfony/ai-bundle symfony/ai-open-ai-platform # or another bridge
49+
```
7150

72-
- Behat (non-JS scenarios)
51+
Then fill in your API key in `.env.local` and enable the grids you want. The Flex recipes (plugin + bridge) take care of the rest (bundle registration, config files, env var stub).
7352

74-
```bash
75-
vendor/bin/behat --strict --tags="~@javascript&&~@mink:chromedriver"
76-
```
53+
Full step-by-step guide (with and without Flex): [installation](docs/installation.md).
7754

78-
- Behat (JS scenarios)
79-
80-
1. [Install Symfony CLI command](https://symfony.com/download).
81-
82-
2. Start Headless Chrome:
83-
84-
```bash
85-
google-chrome-stable --enable-automation --disable-background-networking --no-default-browser-check --no-first-run --disable-popup-blocking --disable-default-apps --allow-insecure-localhost --disable-translate --disable-extensions --no-sandbox --enable-features=Metal --headless --remote-debugging-port=9222 --window-size=2880,1800 --proxy-server='direct://' --proxy-bypass-list='*' http://127.0.0.1
86-
```
87-
88-
3. Install SSL certificates (only once needed) and run test application's webserver on `127.0.0.1:8080`:
89-
90-
```bash
91-
symfony server:ca:install
92-
APP_ENV=test symfony server:start --port=8080 --daemon
93-
```
94-
95-
4. Run Behat:
96-
97-
```bash
98-
vendor/bin/behat --strict --tags="@javascript,@mink:chromedriver"
99-
```
100-
101-
- Static Analysis
102-
103-
- PHPStan
104-
105-
```bash
106-
vendor/bin/phpstan analyse -c phpstan.neon -l max src/
107-
```
55+
## Documentation
10856

109-
- Coding Standard
110-
111-
```bash
112-
vendor/bin/ecs check
113-
```
57+
- [Installation](docs/installation.md) - full setup steps
58+
- [Usage](docs/usage.md) - admin guide with query examples
59+
- [Extending](docs/extending.md) - add custom filter types
11460

115-
### Opening Sylius with your plugin
61+
## License
11662

117-
- Using `test` environment:
118-
119-
```bash
120-
APP_ENV=test vendor/bin/console vendor/bin/console sylius:fixtures:load -n
121-
APP_ENV=test symfony server:start -d
122-
```
123-
124-
- Using `dev` environment:
125-
126-
```bash
127-
vendor/bin/console vendor/bin/console sylius:fixtures:load -n
128-
symfony server:start -d
129-
```
63+
MIT - see [LICENSE](LICENSE).

docs/configuration.md

Lines changed: 0 additions & 44 deletions
This file was deleted.

docs/demo.gif

1.1 MB
Loading

docs/extending.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Extending
2+
3+
The plugin's 8 built-in schema builders cover 10 native Sylius filter types out of the box: `boolean`, `date`, `entity`, `enum`, `exists`, `money`, `numeric_range`, `select`, `string`, `ux_autocomplete`, `ux_translatable_autocomplete`. If your project defines **a custom filter type** outside that list, you need to teach the assistant about it.
4+
5+
## When you need to extend
6+
7+
Only when you have a **custom Sylius filter type**. If you're using stock Sylius filters with custom options/configurations, the existing builders already handle them.
8+
9+
## Scope: this plugin vs Sylius
10+
11+
This guide covers **the plugin's side** - the SchemaBuilder/ValueFormatter pair that teaches the AI about your filter. Registering the filter type with Sylius itself is **not** part of this plugin and must be done separately:
12+
13+
1. A PHP class implementing `Sylius\Component\Grid\Filtering\FilterInterface`
14+
2. A service tagged `sylius.grid_filter` with both `type` and `form_type` attributes
15+
3. A template registered in `sylius_grid.templates.filter` (otherwise Sylius admin sidebar throws "Missing template for filter type X")
16+
17+
See the [Sylius Grid Bundle documentation](https://docs.sylius.com/en/latest/book/grids/grids.html) for these steps. Once your filter is wired with Sylius, the rest of this guide tells you how to teach the AI about it.
18+
19+
## Two interfaces to implement
20+
21+
For each custom filter type, you need to provide:
22+
23+
1. **A SchemaBuilder** - describes the filter to the LLM (JSON Schema describing the shape of the value the AI should return)
24+
2. **A ValueFormatter** - converts the LLM's response into the format your Sylius filter expects
25+
26+
Both are auto-registered via Symfony service autoconfiguration - just create the classes, no service tag or YAML needed.
27+
28+
## Example: custom `tags` filter
29+
30+
Say your Sylius project has a custom filter named `tags` (matching a Sylius `TagsFilter::NAME`), which expects a comma-separated string and matches products by tag.
31+
32+
### 1. SchemaBuilder
33+
34+
```php
35+
<?php
36+
37+
declare(strict_types=1);
38+
39+
namespace App\GridAssistant\Schema;
40+
41+
use Guiziweb\SyliusGridAssistantPlugin\Schema\Builder\FilterSchemaBuilderInterface;
42+
use Sylius\Component\Grid\Definition\Filter;
43+
44+
final class TagsFilterSchemaBuilder implements FilterSchemaBuilderInterface
45+
{
46+
public static function getType(): string
47+
{
48+
return 'tags';
49+
}
50+
51+
public function build(Filter $filter): array
52+
{
53+
return [
54+
'type' => 'string',
55+
'description' => sprintf(
56+
'%s - comma-separated list of tags. Omit if not mentioned by the user.',
57+
(string) $filter->getLabel(),
58+
),
59+
];
60+
}
61+
}
62+
```
63+
64+
The `build()` method returns a JSON Schema fragment that becomes part of the `criteria.<filter_name>` shape in the LLM `response_format` parameter.
65+
66+
### 2. ValueFormatter
67+
68+
```php
69+
<?php
70+
71+
declare(strict_types=1);
72+
73+
namespace App\GridAssistant\Schema;
74+
75+
use Guiziweb\SyliusGridAssistantPlugin\Schema\Formatter\FilterFormatResult;
76+
use Guiziweb\SyliusGridAssistantPlugin\Schema\Formatter\FilterValueFormatterInterface;
77+
use Sylius\Component\Grid\Definition\Filter;
78+
79+
final class TagsFilterValueFormatter implements FilterValueFormatterInterface
80+
{
81+
public static function getType(): string
82+
{
83+
return 'tags';
84+
}
85+
86+
public function format(mixed $value, Filter $filter): FilterFormatResult
87+
{
88+
if (!is_string($value) || '' === trim($value)) {
89+
return new FilterFormatResult(null); // ignore this criterion
90+
}
91+
92+
return new FilterFormatResult(trim($value));
93+
}
94+
}
95+
```
96+
97+
The `format()` method receives the raw value from the LLM (whatever the schema returned by `build()` allows) and returns a `FilterFormatResult` containing the value in the shape Sylius expects.
98+
99+
Return `new FilterFormatResult(null)` to silently drop the criterion (e.g. empty value, invalid format).
100+
101+
## That's it
102+
103+
No service registration. The plugin auto-discovers any class implementing `FilterSchemaBuilderInterface` or `FilterValueFormatterInterface` and registers them with the proper tags.
104+
105+
## Testing your extension
106+
107+
1. **Check the container**: `bin/console lint:container` should pass.
108+
2. **Test with a query**: in the admin, use a query that should trigger your filter (e.g. `"products tagged limited-edition"` for a `tags` filter).
109+
3. **Inspect the URL after redirect**: it should include `?criteria[tags]=limited-edition`.
110+
4. **Look at the AI log**: by default the plugin logs to the `ai` channel - search for `[GridAssistant]` entries to see what filters/sorting the LLM produced.
111+
112+
## Multi-type builders/formatters
113+
114+
If your class handles several filter types (rare), `getType()` can return an array:
115+
116+
```php
117+
public static function getType(): array
118+
{
119+
return ['tags', 'labels'];
120+
}
121+
```
122+
123+
The same builder/formatter will be used for both types.
124+
125+
## Overriding a built-in type
126+
127+
Overriding a native builder/formatter for one of the stock types is technically possible (the registry stores them by type, so any same-type registration replaces the previous one), **but the registration order isn't guaranteed** by Symfony's autoconfiguration. If you really need to override, the safest path is to disable the plugin's built-in service in your `services.yaml` (turning off autoconfigure removes the tag that would register it as a builder) and replace it explicitly:
128+
129+
```yaml
130+
services:
131+
Guiziweb\SyliusGridAssistantPlugin\Schema\Builder\StringFilterSchemaBuilder:
132+
autoconfigure: false # disables the auto-tag, so it's no longer registered as a builder
133+
134+
App\GridAssistant\Schema\MyStringFilterSchemaBuilder: ~
135+
```
136+
137+
Most users don't need this - prefer extending behavior via filter options (`ai_searchable`, custom `formOptions`) before resorting to overriding.

0 commit comments

Comments
 (0)