parsedParameters as $property => $param) { switch($param->searchMethod) { case SearchMethodEnum::Like: case SearchMethodEnum::LikeLeft: case SearchMethodEnum::LikeRight: $repository->like($param->field, $param->value()); break; case SearchMethodEnum::Where: $repository->where($param->field, $param->value(), $param->operator(), $param->condition()); break; case SearchMethodEnum::OrderBy: $value = $param->value(); $repository->orderBy($param->field, $value instanceof \BackedEnum ? $value->value : $value); break; case SearchMethodEnum::GroupBy: $repository->groupBy($param->field); break; } } return $this; } public function fromArray(array|\ArrayAccess $data) : static { $this->objectReflection = ObjectReflection::fromClass(static::class)->reflectClass(); $this->page = $data['page'] ?? 1; $this->applyValues( fn($propertyName, $attribute) => $data[$propertyName] ?? null ); $this->applyOperators( fn($propertyName) => $data["$propertyName:operator"] ?? null ); $this->applyConditions( fn($propertyName) => $data["$propertyName:condition"] ?? null ); return $this; } public function fromRequest(ServerRequestInterface $request) : static { $this->objectReflection = ObjectReflection::fromClass(static::class)->reflectClass(); if (method_exists($this, 'prepare')) { $this->prepare($request); } $queryParams = new \ArrayObject(array_filter($request->getQueryParams(), function($i) { return ! is_null($i) && $i !== ""; })); $this->page = $queryParams->offsetExists('page') ? $queryParams['page'] : 1; $this->applyValues( fn($propertyName, $attribute) =>$this->getValueFromSource($request, $propertyName, $attribute) ); $operators = $request->getAttribute(SearchRequestInterface::SEARCH_REQUEST_OPERATORS); if ($operators) { $this->applyOperators( fn($propertyName) => $operators[$propertyName] ?? null ); } $conditions = $request->getAttribute(SearchRequestInterface::SEARCH_REQUEST_CONDITIONS); if ($conditions) { $this->applyConditions( fn($propertyName) => $conditions[$propertyName] ?? null ); } return $this; } protected function applyConditions(\Closure $getCondition) : void { foreach($this->objectReflection->getProperties() as $propertyName => $property) { if (! isset($this->parsedParameters[$propertyName])) { continue; } $attribute = $property->getAttribute(Attribute\SearchParameter::class)->object ?? false; if ( $condition = $getCondition($propertyName) ) { match(true) { $attribute instanceof SearchManual => throw new \LogicException("Impossible to add a condition to a manually filtered elements."), $attribute instanceof SearchGroupBy => throw new \LogicException("Impossible to add a condition to a group by field."), $attribute instanceof SearchOrderBy => throw new \LogicException("Impossible to add a condition to a order by field."), default => $this->parsedParameters[$propertyName]->condition = $condition, }; } } } protected function applyOperators(\Closure $getOperator) : void { foreach($this->objectReflection->getProperties() as $propertyName => $property) { if (! isset($this->parsedParameters[$propertyName])) { continue; } $attribute = $property->getAttribute(Attribute\SearchParameter::class)->object ?? false; if ($attribute) { if ($operator = $getOperator($propertyName)) { match (true) { $attribute instanceof SearchManual => throw new \LogicException("Impossible to add an operator to a manually filtered elements."), $attribute instanceof SearchGroupBy => throw new \LogicException("Impossible to add an operator to a group by field."), $attribute instanceof SearchOrderBy => throw new \LogicException("Impossible to add an operator to a order by field."), default => $this->parsedParameters[$propertyName]->operator = $operator, }; } } } } protected function applyValues(\Closure $getValue) : void { foreach($this->objectReflection->getProperties() as $propertyName => $property) { $attribute = $property->getAttribute(Attribute\SearchParameter::class)->object ?? false; if ($attribute) { $field = $this->getEntityField($attribute->field ?? $propertyName); $field = $this->transformField($property->getAttributes(Attribute\FieldValueModifier::class), $field); $value = $getValue($propertyName, $attribute); if ($value !== null) { $value = $this->transformValue($property->getAttributes(Attribute\PropertyValueModifier::class), $value); } switch(true) { case $attribute instanceof SearchGroupBy: $this->parseAttributeGroupBy($attribute, $field, $propertyName); break; case $attribute instanceof SearchOrderBy: if ($value !== null) { $this->$propertyName = $this->parseValue($property, $value); } $this->parseAttributeOrderBy($attribute, $field, $propertyName); break; case $attribute instanceof SearchWhere: case $attribute instanceof SearchLike: case $attribute instanceof SearchManual: if ($attribute->toggle) { $this->$propertyName = !empty($value); } elseif ($value !== null) { $this->$propertyName = $this->parseValue($property, $value); } $this->parseAttributeMethod($attribute, $field, $propertyName); break; } } } } protected function parseValue(\Notes\Common\ReflectedProperty $property, mixed $value) : mixed { foreach ($property->getTypes() as $type) { $enum = $type->type; if (enum_exists($enum)) { try { return $value instanceof \UnitEnum ? $value : $type->type::from($value); } catch (\ValueError $ex) { $cases = implode(', ', array_map(fn($e) => $e->name, $enum::cases())); throw new \ValueError( sprintf("Given value '%s' do not exists within enum '%s'. Try one of those values instead : %s", $value, $enum, $cases) ); } } elseif ($type->builtIn || $value instanceof \Stringable ) { return $value; } } } protected function getEntityField(array|string $fieldName) : string|\Stringable { # Field could be defined for another entity class if (is_array($fieldName)) { $field = \Ulmus\Attribute\Attribute::handleArrayField($fieldName, false); } # Default class using it, if SearchRequestParameter is defined elseif ($classAttributes = $this->objectReflection->getAttributes(SearchRequestParameter::class)) { $searchRequestAttribute = $classAttributes[0]->object; $className = $searchRequestAttribute->class; $field = $className::field($fieldName, $searchRequestAttribute->alias); } return $field ?? $fieldName; } protected function getValueFromSource(RequestInterface $request, string $propertyName, SearchParameter $attribute) : mixed { $classAttributes = $this->objectReflection->getAttributes(SearchRequestParameter::class); $searchRequestAttribute = $classAttributes[0]->object; $className = $searchRequestAttribute->class; if ( $override = $request->getAttribute(sprintf("searchRequest.%s:%s", $className, $propertyName)) ) { return $override; } $queryParamName = $attribute->getParameters() ?: [ $propertyName ]; foreach($attribute->getSources() as $source) { switch($source) { case PropertyValueSource::QueryParams: $queryParams = new \ArrayObject(array_filter($request->getQueryParams(), function($i) { return ! is_null($i) && $i !== ""; })); foreach($queryParamName as $param) { if ($queryParams->offsetExists($param)) { return $queryParams[$param]; } } break; case PropertyValueSource::RequestAttribute: foreach($queryParamName as $param) { $fromAttribute = $request->getAttribute($param, null); if ($fromAttribute !== null) { return $fromAttribute; } } break; } } return null; } protected function transformField(array $fieldModifierAttributes, mixed $field) : mixed { foreach($fieldModifierAttributes as $transform) { $field = $transform->object->run($field); } return $field; } protected function transformValue(array $valueModifierAttributes, mixed $value) : mixed { foreach($valueModifierAttributes as $transform) { $value = $transform->object->run($value); } return $value; } protected function parseAttributeMethod(object $attribute, string|\Stringable $field, string $propertyName) : void { if (! isset($this->$propertyName)) { return; } switch ($attribute->method) { case SearchMethodEnum::Like: case SearchMethodEnum::LikeLeft: case SearchMethodEnum::LikeRight: case SearchMethodEnum::Where: $this->parsedParameters[$propertyName] = new ParsedSearchParameter( searchMethod: $attribute->method, field: $field, value: $this->$propertyName, ); } } protected function parseAttributeOrderBy(object $attribute, string $field, mixed $propertyName) : void { if ( ! empty($this->$propertyName) ) { $this->parsedParameters[$propertyName] = new ParsedSearchParameter( searchMethod: SearchMethodEnum::OrderBy, field: $field, value: $this->$propertyName, ); } } protected function parseAttributeGroupBy(object $attribute, string $field, mixed $propertyName) : void { if (! empty($this->$propertyName)) { $this->parsedParameters[$propertyName] = new ParsedSearchParameter( searchMethod: SearchMethodEnum::GroupBy, field: $field, value: null, ); } } }