Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filter query type based on FilterDefinition objects - Work in Progess #149

Open
wants to merge 69 commits into
base: 1.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
add0e0d
PoC for FilterService integration with GraphQL
das-peter Nov 28, 2019
64494cb
Added support for filterDefinition to prime settings and facets.
das-peter Nov 28, 2019
d251576
Added support for passing in facet filters. Refactored filter type co…
das-peter Nov 29, 2019
c3e3af1
Merge remote-tracking branch 'remotes/upstream/master' into filter-qu…
das-peter Dec 10, 2019
4eb7a07
Re-added permission check and service SQL conditions - at least in ca…
das-peter Dec 10, 2019
b6367c4
Fix typo and added doc for return value of method.
das-peter Dec 10, 2019
ccea8bc
prefix type names with classnames because every type in the schema mu…
juckerf Dec 18, 2019
d818da3
Merge remote-tracking branch 'das-peter/filter-query-type' into cando…
mugge6 Apr 15, 2020
7154229
Fix issue with schema violation
mugge6 Apr 15, 2020
2830ae5
Fix other schema violations
mugge6 Apr 15, 2020
cd7d926
Merge remote-tracking branch 'remotes/upstream/master' into filter-qu…
das-peter Apr 27, 2020
2d3378b
Merge branch 'master' into cando-customizations
mugge6 Apr 30, 2020
0b71368
Add thumbnail resolutions to the srcset node
mugge6 Apr 30, 2020
4baca4c
Refactoring and add resolutions to all assets
mugge6 May 1, 2020
1f89238
Fix image extension
mugge6 May 4, 2020
95af8cb
Create thumbnail from asset otherwise medias are not loaded
mugge6 May 4, 2020
c4e2d0b
Refactoring for PR
mugge6 May 7, 2020
9728c30
merge pimcore/master
juckerf May 13, 2020
bdaf0d5
Merge branch 'master' into cando-customizations
mugge6 May 26, 2020
71b94f3
Merge branch 'master' into filter-query-type
weisswurstkanone Jul 3, 2020
d0d7117
merge upstream master
juckerf Jul 8, 2020
d8dc9a4
Merge branch 'filter-query-type' into cando-customizations
mugge6 Sep 15, 2020
f6a7c93
Fix buf, if asset is null
mugge6 Sep 17, 2020
a952916
Merge branch 'filter-query-type' of github.com:das-peter/data-hub int…
juckerf Sep 17, 2020
09cc296
Merge pull request #1 from CandoImage/filter-query-type
juckerf Sep 17, 2020
b2ef27c
translate labels with prefix
juckerf Sep 17, 2020
575f73f
formatting
juckerf Sep 17, 2020
c45bf1d
Merge branch 'filter-query-type' into cando-customizations
juckerf Sep 17, 2020
45633d8
extend filter definition arguments to allow to fetch definition via r…
juckerf Sep 17, 2020
2bc5f53
Merge branch 'filter-query-type' into cando-customizations
juckerf Sep 17, 2020
637eb2b
check derived filter definition for type
juckerf Sep 17, 2020
4dd8f95
Merge branch 'filter-query-type' into cando-customizations
juckerf Sep 17, 2020
84472b2
fallbackFilterDefinition should be used even if there is no id set
mugge6 Sep 18, 2020
d5fbbf6
cherry-pick filter-fallback bugfix from cando-customizations
juckerf Sep 21, 2020
a18681f
fix order handling for filters (compatible with index ProductLists)
juckerf Sep 21, 2020
831ed58
Merge branch 'filter-query-type' into cando-customizations
juckerf Sep 21, 2020
fcf2197
Merge pull request #1 from CandoImage/filter-query-type
das-peter Oct 1, 2020
ceca7db
Merge remote-tracking branch 'remotes/upstream/master' into filter-qu…
das-peter Oct 1, 2020
60ed9d4
Changed API for filters:
zoidbergx Oct 30, 2020
2ec26ed
added HijackAbstractFilterService.php to read protected property "fil…
zoidbergx Oct 30, 2020
824a6cd
missing update from last commit to get only string instead of object …
zoidbergx Oct 30, 2020
b0c6ea2
again missing update to add filterType in Resolver QueryType.php
zoidbergx Oct 30, 2020
1c72dae
changed name of CustomScalarType in Query\QueryType.php to get a prop…
zoidbergx Oct 30, 2020
048fc75
fixed resolve filters only if requested from GraphQL Query
zoidbergx Oct 30, 2020
47de9ab
prefix type names with classnames because every type in the schema mu…
juckerf Dec 18, 2019
0aa2d44
translate labels with prefix
juckerf Sep 17, 2020
a76458f
formatting
juckerf Sep 17, 2020
3e20abe
extend filter definition arguments to allow to fetch definition via r…
juckerf Sep 17, 2020
43a10a0
check derived filter definition for type
juckerf Sep 17, 2020
b19c490
fallbackFilterDefinition should be used even if there is no id set
mugge6 Sep 18, 2020
f0e890d
cherry-pick filter-fallback bugfix from cando-customizations
juckerf Sep 21, 2020
dacf2e5
fix order handling for filters (compatible with index ProductLists)
juckerf Sep 21, 2020
3889071
Changed API for filters:
zoidbergx Oct 30, 2020
d533c12
added HijackAbstractFilterService.php to read protected property "fil…
zoidbergx Oct 30, 2020
587b42f
missing update from last commit to get only string instead of object …
zoidbergx Oct 30, 2020
fb75bc6
again missing update to add filterType in Resolver QueryType.php
zoidbergx Oct 30, 2020
7279ef8
changed name of CustomScalarType in Query\QueryType.php to get a prop…
zoidbergx Oct 30, 2020
4596cde
fixed resolve filters only if requested from GraphQL Query
zoidbergx Oct 30, 2020
34e4ab3
Remove unrelated changes to make review easier.
das-peter Nov 1, 2020
c0d0181
added support for fragment filters
zoidbergx Nov 2, 2020
9e37fe8
added support for fragment and check the fragment type instead of the…
zoidbergx Nov 2, 2020
d0f45b8
added support for fragment filters
zoidbergx Nov 2, 2020
05c1859
added support for fragment and check the fragment type instead of the…
zoidbergx Nov 2, 2020
7f5fc2d
added compatibility to support multiple facets calls to separate diff…
zoidbergx Nov 5, 2020
d4ee4c3
missing copy paste code
zoidbergx Nov 5, 2020
2e03035
fix check for possible fragment names
zoidbergx Nov 5, 2020
dc56f7d
removed translation in "resolveFacet" because the attributes are tran…
zoidbergx Nov 9, 2020
f60950c
changed getting ResultList based on language
zoidbergx Nov 10, 2020
ab80455
Merge remote-tracking branch 'remotes/CandoImage/cando-customizations…
das-peter Nov 11, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
67 changes: 67 additions & 0 deletions src/FilterService/FilterType/HijackAbstractFilterType.php
@@ -0,0 +1,67 @@
<?php

namespace Pimcore\Bundle\DataHubBundle\FilterService\FilterType;

use Pimcore\Bundle\EcommerceFrameworkBundle\FilterService\FilterType\AbstractFilterType;
use Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\ProductList\ProductListInterface;
use Pimcore\Bundle\EcommerceFrameworkBundle\Model\AbstractFilterDefinitionType;

/**
* Class HijackAbstractFilterType
*
* Allows to access protected methods of AbstractFilterType instances in order
* to use these data in the Filter Query Type.
*
* @package Pimcore\Bundle\DataHubBundle\FilterService\FilterType
*/
class HijackAbstractFilterType extends AbstractFilterType {

public function getFilterFrontend(
AbstractFilterDefinitionType $filterDefinition,
ProductListInterface $productList,
$currentFilter
) {
return [];
}

public function addCondition(
AbstractFilterDefinitionType $filterDefinition,
ProductListInterface $productList,
$currentFilter,
$params,
$isPrecondition = FALSE
) {
return [];
}

/**
* Allows to access the protected getField method of a filter.
*
* @param \Pimcore\Bundle\EcommerceFrameworkBundle\FilterService\FilterType\AbstractFilterType $filter
* @param \Pimcore\Bundle\EcommerceFrameworkBundle\Model\AbstractFilterDefinitionType $filterDefinition
* @return string
*/
public static function getFieldFromFilter(AbstractFilterType $filter, AbstractFilterDefinitionType $filterDefinition) {
return $filter->getField($filterDefinition);
}

/**
* Returns if the filter accepts multiple values.
*
* @FIXME There's currently no sane way to determine if a Filter accepts
* multiple values. Needs to be changed in the Filter-API.
*
* @param \Pimcore\Bundle\EcommerceFrameworkBundle\FilterService\FilterType\AbstractFilterType $filter
* @param \Pimcore\Bundle\EcommerceFrameworkBundle\Model\AbstractFilterDefinitionType $filterDefinition
* @return bool
*/
public static function isMultiValueFilter(AbstractFilterType $filter, AbstractFilterDefinitionType $filterDefinition) {
// Speculate that there will be an API for this.
if (method_exists($filter, 'acceptsMultipleValues')) {
return $filter->acceptsMultipleValues();
}
// For now the core filters seem to have the string "Multi" in the class
// name whenever multiple values are accepted.
return strpos(get_class($filter), 'Multi') !== FALSE;
}
}
37 changes: 37 additions & 0 deletions src/FilterService/HijackAbstractFilterService.php
@@ -0,0 +1,37 @@
<?php

namespace Pimcore\Bundle\DataHubBundle\FilterService;

use Pimcore\Bundle\EcommerceFrameworkBundle\FilterService\FilterService;

/**
* Class HijackAbstractFilterService
*
* Allows to access protected property of FilterService instances in order
* to use these data in the Filter Query Type.
*
* @package Pimcore\Bundle\DataHubBundle\FilterService
*/
class HijackAbstractFilterService extends FilterService
{

/**
* Returns the configured filter types as array
*
*
* @param FilterService $instance
* @return array
*/
public static function getFilterTypes(FilterService $instance)
{
$types = $instance->filterTypes;
if (!empty($types)) {
$filterTypes = [];
foreach ($types as $key => $filterType) {
$filterTypes[] = $key;
}
return $filterTypes;
}
return [];
}
}
229 changes: 214 additions & 15 deletions src/GraphQL/Query/QueryType.php
Expand Up @@ -15,11 +15,16 @@

namespace Pimcore\Bundle\DataHubBundle\GraphQL\Query;

use GraphQL\Type\Definition\CustomScalarType;
use GraphQL\Type\Definition\InputObjectField;
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\UnionType;
use Pimcore\Bundle\DataHubBundle\Configuration;
use Pimcore\Bundle\DataHubBundle\Event\GraphQL\Model\QueryTypeEvent;
use Pimcore\Bundle\DataHubBundle\Event\GraphQL\QueryEvents;
use Pimcore\Bundle\DataHubBundle\FilterService\HijackAbstractFilterService;
use Pimcore\Bundle\DataHubBundle\GraphQL\ClassTypeDefinitions;
use Pimcore\Bundle\DataHubBundle\GraphQL\Resolver\AssetListing;
use Pimcore\Bundle\DataHubBundle\GraphQL\Service;
Expand Down Expand Up @@ -92,10 +97,12 @@ public function buildFolderQueries($type, &$config = [], $context = [])

if ($type == "asset") {
$graphQlType = $this->getGraphQlService()->getAssetTypeDefinition("_" . $type . "_folder");
} else if ($type == "document") {
$graphQlType = $this->getGraphQlService()->getDocumentTypeDefinition("_" . $type . "_folder");
} else {
$graphQlType = $this->getGraphQlService()->getDataObjectTypeDefinition("_" . $type . "_folder");
if ($type == "document") {
$graphQlType = $this->getGraphQlService()->getDocumentTypeDefinition("_" . $type . "_folder");
} else {
$graphQlType = $this->getGraphQlService()->getDataObjectTypeDefinition("_" . $type . "_folder");
}
}

// GETTER DEFINITION
Expand Down Expand Up @@ -186,6 +193,37 @@ protected function getResolver($class = null, $configuration = null) {
return $resolver;
}

/**
* @param ClassDefinition $class
* @param array $context
*
* @return \GraphQL\Type\Definition\ObjectType
* @throws \Exception
*/
protected function getEdgeTypeDefinition(ClassDefinition $class, array $context): ObjectType
{
static $instances = [];
$configuration = $context['configuration'];
$resolver = $this->getResolver($class, $configuration);
$ucFirstClassName = ucfirst($class->getName());

if (!isset($instances[$ucFirstClassName])) {
$instances[$ucFirstClassName] = new ObjectType(
[
'name' => $ucFirstClassName . 'Edge',
'fields' => [
'cursor' => Type::string(),
'node' => [
'type' => ClassTypeDefinitions::get($class),
'resolve' => [$resolver, "resolveEdge"]
],
],
]
);
}
return $instances[$ucFirstClassName];
}

/**
* @param array $config
* @param array $context
Expand Down Expand Up @@ -219,18 +257,7 @@ public function buildDataObjectQueries(&$config = [], $context = []): void
];

// LISTING DEFINITION
$edgeType = new ObjectType(
[
'name' => $ucFirstClassName . 'Edge',
'fields' => [
'cursor' => Type::string(),
'node' => [
'type' => ClassTypeDefinitions::get($class),
'resolve' => [$resolver, "resolveEdge"]
],
],
]
);
$edgeType = $this->getEdgeTypeDefinition($class, $context);

$listingType = new ObjectType(
[
Expand Down Expand Up @@ -278,6 +305,175 @@ public function buildDataObjectQueries(&$config = [], $context = []): void
}
}

/**
* @param array &$config
* @param array $context
* @throws \Exception
*/
public function buildFilterQueries(&$config = [], $context = []): void
{
/** @var $configuration Configuration */
$configuration = $context['configuration'];
$entities = $configuration->getQueryEntities();
//Get Filter Service Instance to Hijack configured filter services
$factory = \Pimcore\Bundle\EcommerceFrameworkBundle\Factory::getInstance();
$filterService = $factory->getFilterService();
$filterTypes = HijackAbstractFilterService::getFilterTypes($filterService);

foreach ($entities as $entity) {
$class = ClassDefinition::getByName($entity);
if (!$class) {
Logger::error("class " . $entity . " not found");
continue;
}
if (!is_subclass_of('\\Pimcore\Model\\DataObject\\' . $class->getName(),
\Pimcore\Bundle\EcommerceFrameworkBundle\Model\IndexableInterface::class)) {
Logger::info("class " . $entity . " is not filterable.");
continue;
}

$resolver = $this->getResolver($class, $configuration);
$ucFirstClassName = ucfirst($class->getName());

$edgeType = $this->getEdgeTypeDefinition($class, $context);

//Create ObjectTypes for each configured filter Type in "ecommerce-config.yml"
$filterFields = [];
foreach ($filterTypes as $filterType) {

$entry = new ObjectType([
'name' => $ucFirstClassName . $filterType,
'fields' => [
'filterType' => ['type' => Type::string()],
'field' => ['type' => Type::string()],
'label' => ['type' => Type::string()],
'options' => [
'type' => Type::listOf(new ObjectType([
'name' => $ucFirstClassName . $filterType . 'Option',
'fields' => [
'value' => ['type' => Type::string()],
'label' => ['type' => Type::string()],
'count' => ['type' => Type::int()],
],
]),),
],
]
]);
$filterFields[$filterType] = $entry;
}

$unionType = new UnionType([
'name' => $ucFirstClassName . 'FilterFacet',
'types' => $filterFields,
'resolveType' => function ($value) use ($resolver, $filterFields) {
$type = $value['filter']->getType();
if (isset($filterFields[$type])) {
$filterFields[$type]->resolveFieldFn = [$resolver, "resolveFacet"];
return $filterFields[$type];
}
throw new \Exception("No Filter Type found, only types allowed which are defined in ecommerce-config.yml");
}
]);

$filterType = new ObjectType(
[
'name' => $ucFirstClassName . 'Filter',
'fields' => [
'edges' => [
'type' => Type::listOf($edgeType),
'resolve' => [$resolver, "resolveEdges"]
],
'facets' => [
'type' => UnionType::listOf($unionType),
'resolve' => [$resolver, "resolveFacets"]
],
'totalCount' => [
'description' => 'The total count of all queryable objects for this schema listing',
'resolve' => [$resolver, "resolveFilterTotalCount"],
'type' => Type::int()
]
]
]
);

$defFilter = [
'name' => 'get' . $ucFirstClassName . 'Filter',
'args' => [
'tenant' => ['type' => Type::string()],
'variantMode' => [
'type' => Type::string(),
'description' => 'Define how item variants in the results are handled.. Valid values: ' .
\Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\ProductList\ProductListInterface::VARIANT_MODE_HIDE . ',' .
\Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\ProductList\ProductListInterface::VARIANT_MODE_INCLUDE . ',' .
\Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\ProductList\ProductListInterface::VARIANT_MODE_INCLUDE_PARENT_OBJECT . ',' .
\Pimcore\Bundle\EcommerceFrameworkBundle\IndexService\ProductList\ProductListInterface::VARIANT_MODE_VARIANTS_ONLY,
],
'defaultLanguage' => ['type' => Type::string()],
'fulltext' => [
'type' => Type::string(),
'description' => 'The keys to use for the fulltext search.'
],
'first' => ['type' => Type::int()],
'after' => ['type' => Type::int()],
'sortBy' => ['type' => Type::listOf(Type::string())],
'sortOrder' => [
'type' => Type::listOf(Type::string()),
'description' => "Sort by ASC or DESC, use the same position as the sortBy argument for each column to sort by",
],
'filter' => ['type' => Type::string()],
'filterDefinition' => [
'type' => new InputObjectType([
'name' => $ucFirstClassName . 'FilterDefinitionArg',
'fields' => [
'id' => ['type' => Type::int()],
'relationField' => ['type' => Type::string()],
'fallbackFilterDefinitionId' => ['type' => Type::int()],
],
]),
'description' => "Define the id of a filterDefinition to use to configure the filter.",
],
'published' => ['type' => Type::boolean()],
'category' => [
'type' => Type::id(),
'description' => "ID of the category to filter by.",
],
'priceFrom' => ['type' => Type::float()],
'priceTo' => ['type' => Type::float()],
'facets' => [
'type' => Type::listOf(new InputObjectType([
'name' => $ucFirstClassName . 'FilterFacetArg',
'fields' => [
'field' => ['type' => Type::string()],
'values' => ['type' => CustomScalarType::listOf(new CustomScalarType([
'name' => 'Object', //used for GraphIQL Editor to recognize a Type
'description' => 'The Input can be any kind of array.
For Select Filters a String Array is required e.g.
"values": [
"11",
"12"
]
For Range Filters an Object Array is required e.g.
"values": [
{"from": "10"},
{"to": "100"}
]'
])
)],
],
])),
],
],
'type' => $filterType,
'resolve' => [$resolver, "resolveFilter"],
];

if (!$config['fields']) {
$config['fields'] = [];
}
$config['fields']['get' . $ucFirstClassName . 'Filter'] = $defFilter;
}
}

/**
* @param array $config
* @param array $context
Expand Down Expand Up @@ -368,6 +564,9 @@ public function build(&$config = [], $context = [])
$this->buildAssetQueries($config, $context);
$this->buildDocumentQueries($config, $context);
$this->buildDataObjectQueries($config, $context);
if (interface_exists('\Pimcore\Bundle\EcommerceFrameworkBundle\Model\IndexableInterface')) {
$this->buildFilterQueries($config, $context);
}
$this->buildAssetListingQueries($config, $context);
$this->buildFolderQueries("asset", $config, $context);
$this->buildFolderQueries("document", $config, $context);
Expand Down