Skip to content

Commit

Permalink
Fixed Page Size & Custom Filters (#58)
Browse files Browse the repository at this point in the history
* ~ store page size, so it can be changed via the grid config

* custom filter in the result tab
  • Loading branch information
David Riedl authored and fashxp committed Sep 2, 2019
1 parent 4bd46a5 commit 34d5fe8
Show file tree
Hide file tree
Showing 12 changed files with 627 additions and 44 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ advanced_object_search:
newDataTypeName: SERVICE_ID_OF_FIELD_DEFINITION_ADAPTER
```
## Extend Filters in the Result Tab
If you want custom filters in the result tab directly without having to create a new advanced object search every time
read [here on how to extend the result tab with custom filters.](./doc/01_Extending_Filters.md).
## Running with Pimcore < 5.4
With Pimcore 5.4 the location of static Pimcore files like icons has changed. In order to make this bundle work
Expand Down
164 changes: 164 additions & 0 deletions doc/01_Extending_Filters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
## Extending Filters

The advanced object search enables the definition of custom search filters that can be used to filter the result. This
gives the possibility to filter lists of products by article numbers, types or colors e.g. without having to create
a complete custom advanced object search for it.

In order to create a custom filter for an advanced object search you have to follow these two steps:
- create an event listener that adds the necessary condition(s) to the search
- create the corresponding javascript filter that handles the input and is shown in the toolbar of the result tab

## Event Listeners

To add your custom condition(s) to the search, you have two possibilities
- add the condition(s) to the elastic search directly
- add the condition(s) to the pimcore listing which will load all objects the elastic search found

For convenience reasons you can extend the ``AdvancedObjectSearchBundle\Event\AbstractFilterListener`` and register it
as an event subscriber in your container. This class provides you with two methods
- ``public function onElasticSearch(FilterSearchEvent $event)`` to add condition(s) to the elastic search
- ``public function onListing(FilterListingEvent $event)`` to add the condition(s) to the pimcore listing

If you use the ``AbstractFilterListener`` you have to implement the ``supports(): bool`` method, which defines if the
listener should actually add condition(s) to the current search. For example if you build an event listener to filter
by article numbers, you only want to add the condition if an article number is in the query parameters.

Here is an example for such a ``supports(): bool`` method

```php
class ArticleNumber extends AbstractFilterListener
{
protected function supports(): bool
{
return !empty($this->getParameters()->get("articleNumber"));
}
}
```

### Elastic Search Conditions

Condition(s) can be added to the elastic search directly by creating an event listener that listens to the
``advanced_object_search.elastic_filter`` event (``AdvancedObjectSearchBundle\Event::ELASITIC_FILTER``). As mentioned
before the ``AbstractFilterListener`` already takes care of that for you, if you extend it.

Here is an example for the article number filter again, if your product has a field called ``artno``. As defined in the
previous example, this condition will only be added if the ``articleNumber`` query parameter is present.

```php
class ArticleNumber extends AbstractFilterListener
{
protected function addElasticSearchFilter(FilterSearchEvent $event)
{
$path = "artno." . DefaultAdapter::ES_MAPPING_PROPERTY_STANDARD;

$query = new BoolQuery();
$query->add(
new MatchQuery($path, $this->getParameters()->get("articleNumber"))
);

$event->getSearch()->addPostFilter($query);
}
}
```

### Listing Conditions

In some cases you might have to join your result with another table before you can actually achieve the filter that you
want. For that you can use the listing filter. The elastic search only returns the object ids which were found. These
will be loaded using a basic pimcore listing of that class which enables you to add more mysql queries to that listing.

For that you can use the ``advanced_object_search.listing_filter`` event (``AdvancedObjectSearchBundle\Event::LISTING_FILER``).

Here is an example to filter for the article number as well, but with the listing instead of the elastic search. Keep
in mind that this resolves in more overhead and less performant searches, only use this if you actually have to join
your result to filter. If the data is available in elastic search use Elastic Search Conditions instead.

```php
class ArticleNumber extends AbstractFilterListener
{
protected function addListingFiler(FilterListingEvent $event)
{
$listing = $event->getListing();

$listing->addConditionParam("artno = :artno", ["artno" => $this->getParameters()->get("articleNumber")]);
}
}
```

### Javascript Filter

Now that the result can be filtered by an article number, you have to create an input with javascript to be able to type
in an article number.

For that you have to create a javascript class that extends ``pimcore.bundle.advancedObjectSearch.searchConfig.ResultExtension``
and register it in the extension bag when the result page of an advanced object search is opened.

Here is a basic implementation of such an extension that creates a simple input if the field ``artno`` is present in the result.

```js
pimcore.registerNS("pimcore.bundle.AppBundle.AdvancedObjectSearch.Extension.*");
pimcore.bundle.AppBundle.AdvancedObjectSearch.Extension.ArticleNumber = Class.create(pimcore.bundle.advancedObjectSearch.searchConfig.ResultExtension, {
supports: function(extensionBag) {
// this filter will be displayed in the search if the field "artno" is present in the result tab
return extensionBag.hasField("artno");
},

getLayout: function() {
// create a simple input
if (!this.input) {
this.input = new Ext.create("Ext.form.Text", {
listeners: {
specialkey: function (field, e) {
if (e.getKey() === e.ENTER) {
this.getExtensionBag().update();
}
}.bind(this)
}
});

this.input.setValue(this.getExtensionBag().getPredefinedFilter("articleNumber"));
}

return this.input;
},

getFilterData: function () {
var value = null;

if (this.input) {
value = this.input.getValue();
}

// extract the value of your input, return value has to be an object
// all custom filter return values will be merged
// e.g. return { articleNumber: 'mynumber'} and return { articleType: 'concrete' } will result in
// the query parameters { articleNumber: 'mynumber', articleType: 'concrete' }
return {
articleNumber: value
};
}
});
```

The extension has to be registered in the extension bag of a result panel. This can be done in the ``onAdvancedObjectSearchResult``
of the pimcore plugin of your bundle.

```js
pimcore.registerNS("pimcore.bundle.AppBundle.*");
pimcore.bundle.AppBundle.Bundle = Class.create(pimcore.plugin.admin, {
getClassName: function() {
return "pimcore.bundle.AppBundle.Bundle";
},

initialize: function() {
pimcore.plugin.broker.registerPlugin(this);
},

onAdvancedObjectSearchResult: function (extensionBag) {
// here you can register all the extension that you implement
extensionBag.addExtension(new pimcore.bundle.AppBundle.AdvancedObjectSearch.Extension.ArticleNumber());
}
});

var AppBundle = new pimcore.bundle.AppBundle.Bundle();
```
29 changes: 15 additions & 14 deletions src/AdvancedObjectSearchBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,20 +71,21 @@ public function getJsPaths()
{
return [
'/bundles/advancedobjectsearch/js/startup.js',
'/bundles/advancedobjectsearch/js/selector.js',
'/bundles/advancedobjectsearch/js/helper.js',
'/bundles/advancedobjectsearch/js/searchConfigPanel.js',
'/bundles/advancedobjectsearch/js/searchConfig/conditionPanelContainerBuilder.js',
'/bundles/advancedobjectsearch/js/searchConfig/conditionPanel.js',
'/bundles/advancedobjectsearch/js/searchConfig/resultPanel.js',
'/bundles/advancedobjectsearch/js/searchConfig/conditionAbstractPanel.js',
'/bundles/advancedobjectsearch/js/searchConfig/conditionEntryPanel.js',
'/bundles/advancedobjectsearch/js/searchConfig/conditionGroupPanel.js',
'/bundles/advancedobjectsearch/js/searchConfig/fieldConditionPanel/default.js',
'/bundles/advancedobjectsearch/js/searchConfig/fieldConditionPanel/localizedfields.js',
'/bundles/advancedobjectsearch/js/searchConfig/fieldConditionPanel/numeric.js',
'/bundles/advancedobjectsearch/js/searchConfig/fieldConditionPanel/manyToManyOne.js',
'/bundles/advancedobjectsearch/js/searchConfig/fieldConditionPanel/manyToManyObjectRelation.js',
'/bundles/advancedobjectsearch/js/selector.js',
'/bundles/advancedobjectsearch/js/helper.js',
'/bundles/advancedobjectsearch/js/searchConfigPanel.js',
'/bundles/advancedobjectsearch/js/searchConfig/conditionPanelContainerBuilder.js',
'/bundles/advancedobjectsearch/js/searchConfig/conditionPanel.js',
'/bundles/advancedobjectsearch/js/searchConfig/resultPanel.js',
'/bundles/advancedobjectsearch/js/searchConfig/resultExtension.js',
'/bundles/advancedobjectsearch/js/searchConfig/conditionAbstractPanel.js',
'/bundles/advancedobjectsearch/js/searchConfig/conditionEntryPanel.js',
'/bundles/advancedobjectsearch/js/searchConfig/conditionGroupPanel.js',
'/bundles/advancedobjectsearch/js/searchConfig/fieldConditionPanel/default.js',
'/bundles/advancedobjectsearch/js/searchConfig/fieldConditionPanel/localizedfields.js',
'/bundles/advancedobjectsearch/js/searchConfig/fieldConditionPanel/numeric.js',
'/bundles/advancedobjectsearch/js/searchConfig/fieldConditionPanel/manyToManyOne.js',
'/bundles/advancedobjectsearch/js/searchConfig/fieldConditionPanel/manyToManyObjectRelation.js',
'/bundles/advancedobjectsearch/js/searchConfig/fieldConditionPanel/manyToManyRelation.js',
'/bundles/advancedobjectsearch/js/searchConfig/fieldConditionPanel/fieldcollections.js',
'/bundles/advancedobjectsearch/js/searchConfig/fieldConditionPanel/objectbricks.js',
Expand Down
6 changes: 5 additions & 1 deletion src/Controller/AdminController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@

namespace AdvancedObjectSearchBundle\Controller;

use AdvancedObjectSearchBundle\Event\AdvancedObjectSearchEvents;
use AdvancedObjectSearchBundle\Event\FilterListingEvent;
use AdvancedObjectSearchBundle\Model\SavedSearch;
use AdvancedObjectSearchBundle\Service;
use Pimcore\Bundle\AdminBundle\Helper\QueryParams;
use Pimcore\Model\DataObject;
use Pimcore\Tool;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;
use Symfony\Component\Routing\Annotation\Route;
Expand Down Expand Up @@ -83,7 +86,7 @@ public function getFieldsAction(Request $request, Service $service) {
* @param Request $request
* @Route("/grid-proxy")
*/
public function gridProxyAction(Request $request, Service $service) {
public function gridProxyAction(Request $request, Service $service, EventDispatcherInterface $eventDispatcher) {
$requestedLanguage = $request->get("language");
if ($requestedLanguage) {
if ($requestedLanguage != "default") {
Expand Down Expand Up @@ -154,6 +157,7 @@ public function gridProxyAction(Request $request, Service $service) {
}

$list->setCondition(implode(" AND ", $conditionFilters));
$eventDispatcher->dispatch(AdvancedObjectSearchEvents::LISTING_FILER, new FilterListingEvent($list));

$list->load();

Expand Down
104 changes: 104 additions & 0 deletions src/Event/AbstractFilterListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

namespace AdvancedObjectSearchBundle\Event;

use AdvancedObjectSearchBundle\Service;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\RequestStack;

abstract class AbstractFilterListener implements EventSubscriberInterface
{
/**
* @var ParameterBag
*/
protected $parameters;

/**
* @var Service
*/
protected $service;

public function __construct(RequestStack $requestStack, Service $service)
{
$request = $requestStack->getCurrentRequest();

$this->service = $service;
if($request) {

$this->parameters = new ParameterBag(json_decode($request->get("customFilter"), true) ?: []);
} else {

$this->parameters = new ParameterBag([]);
}
}

/**
* @return ParameterBag
*/
protected function getParameters(): ParameterBag
{
return $this->parameters;
}

/**
* @return Service
*/
protected function getService(): Service
{
return $this->service;
}

/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return [
AdvancedObjectSearchEvents::ELASITIC_FILTER => [
["onElasticSearch", 10],
],

AdvancedObjectSearchEvents::LISTING_FILER => [
["onListing", 10]
]
];
}

public function onElasticSearch(FilterSearchEvent $event)
{
if ($this->supports()) {
$this->addElasticSearchFilter($event);
}
}

public function onListing(FilterListingEvent $event)
{
if ($this->supports()) {
$this->addListingFiler($event);
}
}

/**
* @return bool
*/
protected abstract function supports(): bool;

/**
* @param FilterSearchEvent $event
*
* @return void
*/
protected function addElasticSearchFilter(FilterSearchEvent $event)
{
}

/**
* @param FilterListingEvent $event
*
* @return void
*/
protected function addListingFiler(FilterListingEvent $event)
{
}
}
16 changes: 16 additions & 0 deletions src/Event/AdvancedObjectSearchEvents.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace AdvancedObjectSearchBundle\Event;

final class AdvancedObjectSearchEvents
{
/**
* @Event("AdvancedObjectSearchBundle\Event\SearchEvent")
*/
const ELASITIC_FILTER = "advanced_object_search.elastic_filter";

/**
* @Event("AdvancedObjectSearchBundle\Event\SearchEvent")
*/
const LISTING_FILER = "advanced_object_search.listing_filter";
}
27 changes: 27 additions & 0 deletions src/Event/FilterListingEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace AdvancedObjectSearchBundle\Event;

use Pimcore\Model\DataObject\Listing;
use Symfony\Component\EventDispatcher\Event;

class FilterListingEvent extends Event
{
/**
* @var Listing
*/
protected $listing;

public function __construct(Listing $listing)
{
$this->listing = $listing;
}

/**
* @return Listing
*/
public function getListing(): Listing
{
return $this->listing;
}
}
Loading

0 comments on commit 34d5fe8

Please sign in to comment.