diff --git a/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/Href.php b/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/Href.php index e0447151..a74d3153 100644 --- a/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/Href.php +++ b/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/Href.php @@ -21,6 +21,7 @@ use Pimcore\Bundle\DataHubBundle\GraphQL\Service; use Pimcore\Bundle\DataHubBundle\GraphQL\Traits\ServiceTrait; use Pimcore\Bundle\DataHubBundle\WorkspaceHelper; +use Pimcore\Bundle\EcommerceFrameworkBundle\Model\DefaultMockup; use Pimcore\Model\DataObject\ClassDefinition; use Pimcore\Model\DataObject\ClassDefinition\Data; use Pimcore\Model\Element\ElementInterface; @@ -74,8 +75,9 @@ public function resolve($value = null, $args = [], $context = [], ResolveInfo $r if ($value instanceof BaseDescriptor) { $relation = \Pimcore\Bundle\DataHubBundle\GraphQL\Service::resolveValue($value, $this->fieldDefinition, $this->attribute, $args); - if ($relation instanceof ElementInterface) { - if (!WorkspaceHelper::checkPermission($relation, 'read')) { + if ($relation instanceof ElementInterface || $relation instanceof DefaultMockup) { + $type = ($relation instanceof DefaultMockup) ? 'object' : ''; + if (!WorkspaceHelper::checkPermission($relation, 'read', $type)) { return null; } diff --git a/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/Multihref.php b/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/Multihref.php index 32aec412..9abb9064 100644 --- a/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/Multihref.php +++ b/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/Multihref.php @@ -21,6 +21,7 @@ use Pimcore\Bundle\DataHubBundle\GraphQL\Service; use Pimcore\Bundle\DataHubBundle\GraphQL\Traits\ServiceTrait; use Pimcore\Bundle\DataHubBundle\WorkspaceHelper; +use Pimcore\Bundle\EcommerceFrameworkBundle\Model\DefaultMockup; use Pimcore\Model\DataObject\ClassDefinition; use Pimcore\Model\Element\AbstractElement; @@ -74,7 +75,8 @@ public function resolve($value = null, $args = [], $context = [], ResolveInfo $r if ($relations) { /** @var AbstractElement $relation */ foreach ($relations as $relation) { - if (!WorkspaceHelper::checkPermission($relation, 'read')) { + $type = ($relation instanceof DefaultMockup) ? 'object' : ''; + if (!WorkspaceHelper::checkPermission($relation, 'read', $type)) { continue; } diff --git a/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/MultihrefMetadata.php b/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/MultihrefMetadata.php index 590a0a54..b74d1aed 100644 --- a/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/MultihrefMetadata.php +++ b/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/MultihrefMetadata.php @@ -21,6 +21,7 @@ use Pimcore\Bundle\DataHubBundle\GraphQL\Service; use Pimcore\Bundle\DataHubBundle\GraphQL\Traits\ServiceTrait; use Pimcore\Bundle\DataHubBundle\WorkspaceHelper; +use Pimcore\Bundle\EcommerceFrameworkBundle\Model\DefaultMockup; use Pimcore\Model\DataObject\ClassDefinition; use Pimcore\Model\DataObject\ClassDefinition\Data; use Pimcore\Model\DataObject\Data\ElementMetadata; @@ -76,7 +77,8 @@ public function resolve($value = null, $args = [], $context = [], ResolveInfo $r /** @var ElementMetadata $relation */ foreach ($relations as $relation) { $element = $relation->getElement(); - if (!WorkspaceHelper::checkPermission($element, 'read')) { + $type = ($element instanceof DefaultMockup) ? 'object' : ''; + if (!WorkspaceHelper::checkPermission($element, 'read', $type)) { continue; } diff --git a/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/Objects.php b/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/Objects.php index fd3415b6..be4ec3d7 100644 --- a/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/Objects.php +++ b/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/Objects.php @@ -82,10 +82,7 @@ public function resolve($value = null, $args = [], $context = [], ResolveInfo $r // Explicitly set the type of the mockup object because // these don't have the matching class instance for // auto-detect. - $type = ''; - if ($relation instanceof DefaultMockup) { - $type = $relation->getType(); - } + $type = ($relation instanceof DefaultMockup) ? 'object' : ''; if (!WorkspaceHelper::checkPermission($relation, 'read', $type)) { continue; } diff --git a/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/ObjectsMetadata.php b/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/ObjectsMetadata.php index 1ac5b36b..fd2e5118 100644 --- a/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/ObjectsMetadata.php +++ b/src/GraphQL/DataObjectQueryFieldConfigGenerator/Helper/ObjectsMetadata.php @@ -21,6 +21,7 @@ use Pimcore\Bundle\DataHubBundle\GraphQL\Service; use Pimcore\Bundle\DataHubBundle\GraphQL\Traits\ServiceTrait; use Pimcore\Bundle\DataHubBundle\WorkspaceHelper; +use Pimcore\Bundle\EcommerceFrameworkBundle\Model\DefaultMockup; use Pimcore\Model\DataObject\ClassDefinition; use Pimcore\Model\DataObject\Data\ObjectMetadata; @@ -75,7 +76,8 @@ public function resolve($value = null, $args = [], $context = [], ResolveInfo $r /** @var ObjectMetadata $relation */ foreach ($relations as $relation) { $element = $relation->getElement(); - if (!WorkspaceHelper::checkPermission($element, 'read')) { + $type = ($element instanceof DefaultMockup) ? 'object' : ''; + if (!WorkspaceHelper::checkPermission($element, 'read', $type)) { continue; } diff --git a/src/GraphQL/FieldHelper/DataObjectFieldHelper.php b/src/GraphQL/FieldHelper/DataObjectFieldHelper.php index 45bb8739..0b34691d 100644 --- a/src/GraphQL/FieldHelper/DataObjectFieldHelper.php +++ b/src/GraphQL/FieldHelper/DataObjectFieldHelper.php @@ -370,59 +370,12 @@ public function doExtractData(FieldNode $ast, &$data, $container, $args, $contex if ($this->skipField($container, $astName)) { return; } - // example for http://webonyx.github.io/graphql-php/error-handling/ // throw new MySafeException("fieldhelper", "TBD customized error message"); $getter = 'get' . ucfirst($astName); - - $isLocalizedField = false; - $containerDefinition = null; - - // This reflection object is used to determine if the getter can be used. - // $container isn't used directly in order to allow specialized handling - // of mock objects and other placeholders which act transparently but - // don't implement the getters themselves. - $methodCheckClass = new \ReflectionClass($container); - $skipMethodCallCheck = false; - // Adjust meta data for data handling on type of the data container. - switch (true) { - case $container instanceof Concrete: - $containerDefinition = $container->getClass(); - break; - - case $container instanceof AbstractData: - case $container instanceof \Pimcore\Model\DataObject\Objectbrick\Data\AbstractData: - $containerDefinition = $container->getDefinition(); - break; - - // All default indexers implement o_classId - access it directly to - // load class definition and with it use the model loader to fetch - // the actual implementing class for further reflection. - case $container instanceof DefaultMockup: - if (($mockClassId = $container->getParam('o_classId'))) { - $containerDefinition = ClassDefinition::getById($mockClassId); - // Unfortunately there's no API for this so we re-implement - // what \Pimcore\Model\DataObject\AbstractObject::getById() - // does. - $baseClassName = 'Pimcore\\Model\\DataObject\\' . ucfirst($containerDefinition->getName()); - $className = $this->modelFactory->getClassNameFor($baseClassName); - $methodCheckClass = new \ReflectionClass($className); - } else { - $skipMethodCallCheck = true; - } - break; - } - - if ($containerDefinition) { - /** @var Data\Localizedfields|null $lfDefs */ - $lfDefs = $containerDefinition->getFieldDefinition('localizedfields'); - if ($lfDefs && $lfDefs->getFieldDefinition($astName)) { - $isLocalizedField = true; - } - } - - if (($methodCheckClass->hasMethod($getter) && $methodCheckClass->getMethod($getter)->isPublic()) || $skipMethodCallCheck) { + if ($this->getGraphQlService()::checkContainerMethodExists($container, $getter)) { + $isLocalizedField = $this->getGraphQlService()::isLocalizedField($container, $astName); if ($isLocalizedField) { // defer it $data[$astName] = function ($source, $args, $context, ResolveInfo $info) use ( diff --git a/src/GraphQL/RelationHelper.php b/src/GraphQL/RelationHelper.php index 9c724ad9..8ba9f898 100644 --- a/src/GraphQL/RelationHelper.php +++ b/src/GraphQL/RelationHelper.php @@ -16,12 +16,13 @@ namespace Pimcore\Bundle\DataHubBundle\GraphQL; use GraphQL\Type\Definition\ResolveInfo; +use Pimcore\Bundle\EcommerceFrameworkBundle\Model\DefaultMockup; use Pimcore\Model\Element\ElementInterface; class RelationHelper { /** - * @param ElementInterface $relation + * @param ElementInterface|DefaultMockup $relation * @param Service $graphQlService * @param array $args * @param array $context @@ -29,7 +30,7 @@ class RelationHelper * * @return ElementDescriptor */ - public static function processRelation(ElementInterface $relation, Service $graphQlService, $args, $context, ResolveInfo $resolveInfo) + public static function processRelation(ElementInterface|DefaultMockup $relation, Service $graphQlService, $args, $context, ResolveInfo $resolveInfo) { $data = new ElementDescriptor($relation); $graphQlService->extractData($data, $relation, $args, $context, $resolveInfo); diff --git a/src/GraphQL/Service.php b/src/GraphQL/Service.php index 3bd6fad5..ef2ca793 100644 --- a/src/GraphQL/Service.php +++ b/src/GraphQL/Service.php @@ -930,7 +930,7 @@ public static function setValue($object, $attribute, $callback) return $result; } - } elseif (method_exists($container, $setter)) { + } elseif (static::checkContainerMethodExists($container, $setter)) { $result = $callback($container, $setter); } @@ -1077,7 +1077,7 @@ public static function resolveValue(BaseDescriptor $descriptor, Data $fieldDefin return $value; } - } elseif (method_exists($container, $getter)) { + } elseif (static::checkContainerMethodExists($container, $getter)) { $isLocalizedField = self::isLocalizedField($container, $fieldDefinition->getName()); if ($isLocalizedField) { $result = $container->$getter($args['language'] ?? null); @@ -1124,15 +1124,9 @@ public static function resolveCachedValue($value, $resolveInfo = null) * * @return bool */ - private static function isLocalizedField($container, $fieldName): bool + public static function isLocalizedField($container, $fieldName): bool { - $containerDefinition = null; - - if ($container instanceof Concrete) { - $containerDefinition = $container->getClass(); - } elseif ($container instanceof AbstractData) { - $containerDefinition = $container->getDefinition(); - } + $containerDefinition = static::getContainerClassDefinition($container); if ($containerDefinition) { /** @var Data\Localizedfields|null $lfDefs */ @@ -1214,4 +1208,89 @@ public function querySchemaEnabled(string $type) return $enabled; } + + /** + * Checks if a container has a given method. + * + * This works with DefaultMockup objects too that's why it's so overly + * complex. DefaultMockup objects will use the class definition of the + * mocked object to determine which model class has to be checked for the + * method. + * + * @param object $container + * @param string $method + * + * @return bool + * @throws \ReflectionException + */ + public static function checkContainerMethodExists(object $container, string $method): bool + { + // This reflection object is used to determine if the getter can be used. + // $container isn't used directly in order to allow specialized handling + // of mock objects and other placeholders which act transparently but + // don't implement the getters themselves. + $methodCheckClass = new \ReflectionClass($container); + $skipMethodCallCheck = false; + + // Adjust meta data for data handling on type of the data container. + $containerDefinition = static::getContainerClassDefinition($container); + if ($container instanceof DefaultMockup) { + if ($containerDefinition) { + // Unfortunately there's no API for this so we re-implement + // what \Pimcore\Model\DataObject\AbstractObject::getById() + // does. + $baseClassName = 'Pimcore\\Model\\DataObject\\' . ucfirst($containerDefinition->getName()); + // @TODO figure out a nicer way to handle this. Really naughty + // to call kernel directly - but static doesn't have DI. + /** @var self $service */ + $service = \Pimcore::getKernel()->getContainer()->get(static::class); + $className = $service->getModelFactory()->getClassNameFor($baseClassName); + $methodCheckClass = new \ReflectionClass($className); + } else { + $skipMethodCallCheck = true; + } + } + if ( + ( + $methodCheckClass->hasMethod($method) + && $methodCheckClass->getMethod($method)->isPublic() + ) + || $skipMethodCallCheck + ) { + return true; + } + return false; + } + + /** + * Returns the ClassDefinition of a container - works with DefaultMockup + * objects too. + * + * @param object $container + * + * @return ClassDefinition|null + * @throws \Exception + */ + public static function getContainerClassDefinition(object $container): ?ClassDefinition + { + // Adjust meta data for data handling on type of the data container. + switch (true) { + case $container instanceof Concrete: + return $container->getClass(); + + case $container instanceof \Pimcore\Model\DataObject\Fieldcollection\Data\AbstractData: + case $container instanceof \Pimcore\Model\DataObject\Objectbrick\Data\AbstractData: + return $container->getDefinition(); + + // All default indexers implement o_classId - access it directly to + // load class definition and with it use the model loader to fetch + // the actual implementing class for further reflection. + case $container instanceof DefaultMockup: + if (($mockClassId = $container->getParam('o_classId'))) { + return ClassDefinition::getById($mockClassId); + } + break; + } + return null; + } }