Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
83.87% |
52 / 62 |
|
54.55% |
6 / 11 |
CRAP | |
0.00% |
0 / 1 |
| DependsOnPropertyRepository | |
83.87% |
52 / 62 |
|
54.55% |
6 / 11 |
57.67 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
| withProperties | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| findAll | |
91.18% |
31 / 34 |
|
0.00% |
0 / 1 |
24.40 | |||
| getListUri | |
60.00% |
3 / 5 |
|
0.00% |
0 / 1 |
8.30 | |||
| isSameParentProperty | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
| isRemoteListProperty | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
| isParentProperty | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
3 | |||
| isPropertyNotSupported | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
6 | |||
| getProperties | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| isPropertyWidgetAllowed | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
4 | |||
| isParentPropertyWidgetAllowed | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
2 | |||
| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * This program is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of the GNU General Public License |
| 6 | * as published by the Free Software Foundation; under version 2 |
| 7 | * of the License (non-upgradable). |
| 8 | * |
| 9 | * This program is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | * GNU General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU General Public License |
| 15 | * along with this program; if not, write to the Free Software |
| 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 17 | * |
| 18 | * Copyright (c) 2021 (original work) Open Assessment Technologies SA; |
| 19 | */ |
| 20 | |
| 21 | declare(strict_types=1); |
| 22 | |
| 23 | namespace oat\tao\model\Lists\DataAccess\Repository; |
| 24 | |
| 25 | use InvalidArgumentException; |
| 26 | use core_kernel_classes_Class; |
| 27 | use core_kernel_classes_Property; |
| 28 | use tao_helpers_form_elements_Combobox; |
| 29 | use tao_helpers_form_GenerisFormFactory; |
| 30 | use oat\tao\model\featureFlag\FeatureFlagChecker; |
| 31 | use oat\tao\helpers\form\elements\xhtml\SearchTextBox; |
| 32 | use oat\tao\helpers\form\elements\xhtml\SearchDropdown; |
| 33 | use oat\tao\model\Lists\Business\Domain\DependsOnProperty; |
| 34 | use oat\tao\model\featureFlag\FeatureFlagCheckerInterface; |
| 35 | use oat\tao\model\Specification\PropertySpecificationInterface; |
| 36 | use oat\tao\model\Lists\Business\Domain\DependsOnPropertyCollection; |
| 37 | use oat\tao\model\Lists\Business\Contract\DependsOnPropertyRepositoryInterface; |
| 38 | use oat\tao\model\Lists\Business\Contract\ParentPropertyListRepositoryInterface; |
| 39 | |
| 40 | class DependsOnPropertyRepository implements DependsOnPropertyRepositoryInterface |
| 41 | { |
| 42 | public const DEPENDENT_RESTRICTED_TYPES = [ |
| 43 | tao_helpers_form_elements_Combobox::WIDGET_ID, |
| 44 | SearchDropdown::WIDGET_ID, |
| 45 | SearchTextBox::WIDGET_ID |
| 46 | ]; |
| 47 | |
| 48 | /** @var FeatureFlagCheckerInterface */ |
| 49 | private $featureFlagChecker; |
| 50 | |
| 51 | /** @var PropertySpecificationInterface */ |
| 52 | private $primaryPropertySpecification; |
| 53 | |
| 54 | /** @var PropertySpecificationInterface */ |
| 55 | private $remoteListPropertySpecification; |
| 56 | |
| 57 | /** @var PropertySpecificationInterface */ |
| 58 | private $dependentPropertySpecification; |
| 59 | |
| 60 | /** @var ParentPropertyListRepositoryInterface */ |
| 61 | private $parentPropertyListRepository; |
| 62 | |
| 63 | /** @var core_kernel_classes_Property[] */ |
| 64 | private $properties; |
| 65 | |
| 66 | public function __construct( |
| 67 | FeatureFlagCheckerInterface $featureFlagChecker, |
| 68 | PropertySpecificationInterface $primaryPropertySpecification, |
| 69 | PropertySpecificationInterface $remoteListPropertySpecification, |
| 70 | PropertySpecificationInterface $dependentPropertySpecification, |
| 71 | ParentPropertyListRepositoryInterface $parentPropertyListRepository |
| 72 | ) { |
| 73 | $this->featureFlagChecker = $featureFlagChecker; |
| 74 | $this->primaryPropertySpecification = $primaryPropertySpecification; |
| 75 | $this->remoteListPropertySpecification = $remoteListPropertySpecification; |
| 76 | $this->dependentPropertySpecification = $dependentPropertySpecification; |
| 77 | $this->parentPropertyListRepository = $parentPropertyListRepository; |
| 78 | } |
| 79 | |
| 80 | public function withProperties(array $properties) |
| 81 | { |
| 82 | $this->properties = $properties; |
| 83 | } |
| 84 | |
| 85 | public function findAll(array $filter): DependsOnPropertyCollection |
| 86 | { |
| 87 | $collection = new DependsOnPropertyCollection(); |
| 88 | |
| 89 | if (!$this->featureFlagChecker->isEnabled(FeatureFlagChecker::FEATURE_FLAG_LISTS_DEPENDENCY_ENABLED)) { |
| 90 | return $collection; |
| 91 | } |
| 92 | |
| 93 | if (empty($filter[self::FILTER_PROPERTY]) && empty($filter[self::FILTER_CLASS])) { |
| 94 | throw new InvalidArgumentException('class or property filter need to be provided'); |
| 95 | } |
| 96 | |
| 97 | /** @var core_kernel_classes_Property $property */ |
| 98 | $property = $filter[self::FILTER_PROPERTY] ?? null; |
| 99 | |
| 100 | if ( |
| 101 | ($property && $this->primaryPropertySpecification->isSatisfiedBy($property)) |
| 102 | || !$this->isPropertyWidgetAllowed($filter) |
| 103 | ) { |
| 104 | return $collection; |
| 105 | } |
| 106 | |
| 107 | /** @var core_kernel_classes_Class $class */ |
| 108 | $class = $property |
| 109 | ? ($property->getDomain()->count() > 0 ? $property->getDomain()->get(0) : null) |
| 110 | : $filter[self::FILTER_CLASS] ?? null; |
| 111 | |
| 112 | if ($class === null || (empty($filter[self::FILTER_LIST_URI]) && $property && !$property->getRange())) { |
| 113 | return $collection; |
| 114 | } |
| 115 | |
| 116 | if (isset($filter[self::FILTER_LIST_URI]) && $property && !$property->getRange()) { |
| 117 | $property = null; |
| 118 | } |
| 119 | |
| 120 | $listUri = $this->getListUri($filter, $property); |
| 121 | |
| 122 | if (!$listUri) { |
| 123 | return $collection; |
| 124 | } |
| 125 | |
| 126 | if ($property && !$this->isRemoteListProperty($property)) { |
| 127 | return $collection; |
| 128 | } |
| 129 | |
| 130 | $parentPropertiesUris = $this->parentPropertyListRepository->findAllUris( |
| 131 | [ |
| 132 | 'listUri' => $listUri |
| 133 | ] |
| 134 | ); |
| 135 | |
| 136 | if (empty($parentPropertiesUris)) { |
| 137 | return $collection; |
| 138 | } |
| 139 | |
| 140 | /** @var core_kernel_classes_Property $property */ |
| 141 | foreach ($this->getProperties($class) as $classProperty) { |
| 142 | if ( |
| 143 | ($property && $this->isPropertyNotSupported($property, $classProperty)) |
| 144 | || !$this->isParentProperty($classProperty, $parentPropertiesUris) |
| 145 | ) { |
| 146 | continue; |
| 147 | } |
| 148 | |
| 149 | $collection->append(new DependsOnProperty($classProperty)); |
| 150 | } |
| 151 | |
| 152 | return $collection; |
| 153 | } |
| 154 | |
| 155 | private function getListUri(array $options, core_kernel_classes_Property $property = null): ?string |
| 156 | { |
| 157 | if (empty($options['listUri']) && $property && !$property->getRange()) { |
| 158 | return null; |
| 159 | } |
| 160 | |
| 161 | return empty($options['listUri']) && $property |
| 162 | ? $property->getRange()->getUri() |
| 163 | : ($options['listUri'] ?? null); |
| 164 | } |
| 165 | |
| 166 | private function isSameParentProperty( |
| 167 | core_kernel_classes_Property $property, |
| 168 | core_kernel_classes_Property $classProperty |
| 169 | ): bool { |
| 170 | $parentProperty = $classProperty->getDependsOnPropertyCollection()->current(); |
| 171 | |
| 172 | return $parentProperty && $property->getUri() === $parentProperty->getUri(); |
| 173 | } |
| 174 | |
| 175 | private function isRemoteListProperty(core_kernel_classes_Property $property): bool |
| 176 | { |
| 177 | return $property->getDomain()->count() && $this->remoteListPropertySpecification->isSatisfiedBy($property); |
| 178 | } |
| 179 | |
| 180 | private function isParentProperty(core_kernel_classes_Property $classProperty, array $parentPropertiesUris): bool |
| 181 | { |
| 182 | return !$this->dependentPropertySpecification->isSatisfiedBy($classProperty) |
| 183 | && in_array($classProperty->getUri(), $parentPropertiesUris, true) |
| 184 | && $this->isParentPropertyWidgetAllowed($classProperty); |
| 185 | } |
| 186 | |
| 187 | private function isPropertyNotSupported( |
| 188 | core_kernel_classes_Property $property, |
| 189 | core_kernel_classes_Property $classProperty |
| 190 | ): bool { |
| 191 | return $property->getUri() === $classProperty->getUri() |
| 192 | || !$this->remoteListPropertySpecification->isSatisfiedBy($classProperty); |
| 193 | } |
| 194 | |
| 195 | private function getProperties(core_kernel_classes_Class $class): array |
| 196 | { |
| 197 | return $this->properties ?? tao_helpers_form_GenerisFormFactory::getClassProperties($class); |
| 198 | } |
| 199 | |
| 200 | private function isPropertyWidgetAllowed(array $filter): bool |
| 201 | { |
| 202 | /** @var core_kernel_classes_Property $property */ |
| 203 | $property = $filter[self::FILTER_PROPERTY] ?? null; |
| 204 | |
| 205 | $widgetUri = $filter[self::FILTER_PROPERTY_WIDGET_URI] ?? null; |
| 206 | $widgetUri = $widgetUri ?? ($property && $property->getWidget() ? $property->getWidget()->getUri() : null); |
| 207 | |
| 208 | if ($widgetUri === null) { |
| 209 | return true; |
| 210 | } |
| 211 | |
| 212 | return in_array($widgetUri, self::DEPENDENT_RESTRICTED_TYPES, true); |
| 213 | } |
| 214 | |
| 215 | private function isParentPropertyWidgetAllowed(core_kernel_classes_Property $property): bool |
| 216 | { |
| 217 | return $property->getWidget() |
| 218 | && in_array($property->getWidget()->getUri(), self::DEPENDENT_RESTRICTED_TYPES, true); |
| 219 | } |
| 220 | } |