Skip to content

Commit

Permalink
Introduce search with SearchFilterType (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kreyu authored Apr 3, 2023
1 parent 4a6f4f6 commit db9def4
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 18 deletions.
15 changes: 11 additions & 4 deletions docs/philosophy/understanding-views-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,15 @@ The `HeaderRowView` represents a row of headers. It contains two additional prop
: Holds collection of `ColumnHeaderView` instances, so you can use it to render row columns.
Instead of accessing the `children` property, you can iterate on the `HeaderRowView` directly:

{% raw %}
```twig
{% for row in header_rows %}
{% for column in row %}
{# ... #}
{% endfor %}
{% endfor %}
```

{% endraw %}

## `ValueRowView`

Expand All @@ -56,34 +57,40 @@ The `ValueRowView` represents a row of data. It contains five additional propert

: Holds collection of `ColumnValueView` instances, so you can use it to render row columns.
Instead of accessing the `children` property, you can iterate on the `ValueRowView` directly:


{% raw %}
```twig
{% for row in value_rows %}
{% for column in row %}
{# ... #}
{% endfor %}
{% endfor %}
```
{% endraw %}

`index`

: Holds the index of a current row, so you can use it to render a row number:


{% raw %}
```twig
{% for row in value_rows %}
{{ row.index }}
{% endfor %}
```
{% endraw %}

`data`

: Holds the data of a current row, so you can use it to access the data of a row:


{% raw %}
```twig
{% for row in value_rows %}
{{ row.data.id }}
{% endfor %}
```
{% endraw %}

`origin`

Expand Down
5 changes: 5 additions & 0 deletions src/Bridge/Doctrine/Orm/Query/DoctrineOrmProxyQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ public function __clone(): void
$this->queryBuilder = clone $this->queryBuilder;
}

public function getQueryBuilder(): QueryBuilder
{
return $this->queryBuilder;
}

public function sort(SortingData $sortingData): void
{
$rootAlias = current($this->queryBuilder->getRootAliases());
Expand Down
33 changes: 32 additions & 1 deletion src/Filter/Form/Type/FiltrationDataType.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Kreyu\Bundle\DataTableBundle\Filter\FilterData;
use Kreyu\Bundle\DataTableBundle\Filter\FilterInterface;
use Kreyu\Bundle\DataTableBundle\Filter\FiltrationData;
use Kreyu\Bundle\DataTableBundle\Filter\Type\SearchFilterTypeInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
Expand Down Expand Up @@ -39,13 +40,22 @@ public function buildForm(FormBuilderInterface $builder, array $options)

public function finishView(FormView $view, FormInterface $form, array $options): void
{
/**
* @var DataTableInterface $dataTable
*/
$dataTable = $options['data_table'];

/**
* @var DataTableView $dataTableView
*/
if (null === $dataTableView = $options['data_table_view']) {
$dataTableView = $options['data_table_view'];

if (null === $dataTableView) {
throw new \LogicException('Unable to create filtration form view without the data table view.');
}

$view->vars['attr']['id'] = $view->vars['id'];

foreach ($view as $name => $filterFormView) {
$filterView = $dataTableView->filters[$name];

Expand All @@ -54,6 +64,27 @@ public function finishView(FormView $view, FormInterface $form, array $options):
'translation_domain' => $filterView->vars['translation_domain'],
]);
}

$searchFields = [];

foreach ($form as $child) {
try {
$filter = $dataTable->getConfig()->getFilter($child->getName());
} catch (\InvalidArgumentException) {
continue;
}

if ($filter->getType()->getInnerType() instanceof SearchFilterTypeInterface) {
$searchField = $view[$child->getName()];
$searchField->vars['attr']['form'] = $view->vars['id'];

$searchFields[] = $searchField;

unset($view[$child->getName()]);
}
}

$view->vars['search_fields'] = $searchFields;
}

public function configureOptions(OptionsResolver $resolver): void
Expand Down
27 changes: 14 additions & 13 deletions src/Filter/Type/FilterType.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,28 @@ public function apply(ProxyQueryInterface $query, FilterData $data, FilterInterf

public function buildView(FilterView $view, FilterInterface $filter, FilterData $data, array $options): void
{
$resolver = clone $filter->getType()->getOptionsResolver();

$value = $data;

if ($value->hasValue() && $formatter = $options['active_filter_formatter']) {
$value = $formatter($data, $filter, $options);
}

$resolver->setDefaults([
'name' => $filter->getName(),
'form_name' => $filter->getFormName(),
'label' => StringUtil::camelToSentence($filter->getName()),
'translation_domain' => $view->parent->vars['translation_domain'],
'query_path' => $filter->getName(),
$view->vars = array_replace($view->vars, [
'name' => $options['name'] ?? $filter->getName(),
'form_name' => $options['form_name'] ?? $filter->getFormName(),
'label' => $options['label'] ?? StringUtil::camelToSentence($filter->getName()),
'label_translation_parameters' => $options['label_translation_parameters'],
'translation_domain' => $options['translation_domain'] ?? $view->parent->vars['translation_domain'],
'query_path' => $options['query_path'] ?? $filter->getName(),
'field_Type' => $options['field_type'],
'field_options' => $options['field_options'],
'operator_type' => $options['operator_type'],
'operator_options' => $options['operator_options'],
'auto_alias_resolving' => $options['auto_alias_resolving'],
'active_filter_formatter' => $options['active_filter_formatter'],
'data' => $data,
'value' => $value,
]);

$options = $resolver->resolve(array_filter($options));

$view->vars = $options;
}

public function configureOptions(OptionsResolver $resolver): void
Expand All @@ -66,7 +67,7 @@ public function configureOptions(OptionsResolver $resolver): void
return $data->getValue();
},
])
->setAllowedTypes('label', ['null', 'string', TranslatableMessage::class])
->setAllowedTypes('label', ['null', 'bool', 'string', TranslatableMessage::class])
->setAllowedTypes('query_path', ['null', 'string'])
->setAllowedTypes('field_type', ['string'])
->setAllowedTypes('field_options', ['array'])
Expand Down
34 changes: 34 additions & 0 deletions src/Filter/Type/SearchFilterType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Kreyu\Bundle\DataTableBundle\Filter\Type;

use Kreyu\Bundle\DataTableBundle\Filter\FilterData;
use Kreyu\Bundle\DataTableBundle\Filter\FilterInterface;
use Kreyu\Bundle\DataTableBundle\Query\ProxyQueryInterface;
use Symfony\Component\Form\Extension\Core\Type\SearchType;
use Symfony\Component\OptionsResolver\OptionsResolver;

class SearchFilterType extends AbstractFilterType implements SearchFilterTypeInterface
{
public function apply(ProxyQueryInterface $query, FilterData $data, FilterInterface $filter, array $options): void
{
$options['handler']($query, (string) $data->getValue(), $filter);
}

public function configureOptions(OptionsResolver $resolver): void
{
$resolver
->setDefaults([
'field_type' => SearchType::class,
'operator_options' => [
'visible' => false,
'choices' => [],
]
])
->setRequired('handler')
->setAllowedTypes('handler', 'callable')
;
}
}
9 changes: 9 additions & 0 deletions src/Filter/Type/SearchFilterTypeInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace Kreyu\Bundle\DataTableBundle\Filter\Type;

interface SearchFilterTypeInterface extends FilterTypeInterface
{
}
6 changes: 6 additions & 0 deletions src/Resources/config/filtration.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Kreyu\Bundle\DataTableBundle\Filter\Type\FilterType;
use Kreyu\Bundle\DataTableBundle\Filter\Type\ResolvedFilterTypeFactory;
use Kreyu\Bundle\DataTableBundle\Filter\Type\ResolvedFilterTypeFactoryInterface;
use Kreyu\Bundle\DataTableBundle\Filter\Type\SearchFilterType;
use Kreyu\Bundle\DataTableBundle\Persistence\CachePersistenceAdapter;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

Expand Down Expand Up @@ -65,6 +66,11 @@
->tag('kreyu_data_table.filter.type')
;

$services
->set('kreyu_data_table.filter.type.search', SearchFilterType::class)
->tag('kreyu_data_table.filter.type')
;

$services
->set('kreyu_data_table.filter.type.doctrine_orm_string', StringFilterType::class)
->tag('kreyu_data_table.filter.type')
Expand Down
14 changes: 14 additions & 0 deletions src/Resources/views/themes/bootstrap_5.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
{% endblock %}

{% block action_bar_end %}
{% if filtration_enabled and filtration_form and filtration_form.vars.search_fields|length > 0 %}
{{ block('action_search') }}
{% endif %}

{% for action in actions %}
{{ data_table_action(action) }}
{% endfor %}
Expand All @@ -44,6 +48,16 @@
{% endif %}
{% endblock %}

{% block action_search %}
{% form_theme filtration_form with [_self, 'bootstrap_5_layout.html.twig'] %}

<div class="d-inline-block">
{% for child in filtration_form.vars.search_fields %}
{{ form_widget(child.value, { attr: { form: filtration_form.vars.id } }) }}
{% endfor %}
</div>
{% endblock %}

{% block action_filter %}
{% set attr = {
'class': 'btn btn-primary',
Expand Down

0 comments on commit db9def4

Please sign in to comment.