From fcc15901c90e5b28f69ea717612f3bed2d723257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kruli=C5=A1?= Date: Sat, 7 Mar 2026 12:59:03 +0100 Subject: [PATCH 1/3] Patching bool query values so they have proper type for injection into action and check methods. --- .../presenters/base/BasePresenter.php | 16 +++++++++++ .../MetaFormats/Validators/BaseValidator.php | 12 +++++++++ app/helpers/MetaFormats/Validators/VArray.php | 9 +++++++ app/helpers/MetaFormats/Validators/VBool.php | 27 ++++++++++++++----- 4 files changed, 58 insertions(+), 6 deletions(-) diff --git a/app/V1Module/presenters/base/BasePresenter.php b/app/V1Module/presenters/base/BasePresenter.php index 544f29ef2..c37d55233 100644 --- a/app/V1Module/presenters/base/BasePresenter.php +++ b/app/V1Module/presenters/base/BasePresenter.php @@ -130,6 +130,11 @@ public function startup() Validators::init(); $this->processParams($actionReflection); + // TODO -- coerce bools in params + if (!empty($this->params['archived'])) { + $this->params['archived'] = filter_var($this->params['archived'], FILTER_VALIDATE_BOOLEAN); + } + // ACL-checking method $this->tryCall($this->formatPermissionCheckMethod($this->getAction()), $this->params); } @@ -257,6 +262,17 @@ private function processParamsLoose(array $paramData) } else { $param->conformsToDefinition($paramValue); } + + // Path and query parameter might need patching, so they have correct type for + // being injected into the presenter method. + if ( + $param->type === Type::Path || $param->type === Type::Query + && array_key_exists($param->name, $this->params) + ) { + foreach ($param->validators as $validator) { + $validator->patchQueryParameter($this->params[$param->name]); + } + } } } diff --git a/app/helpers/MetaFormats/Validators/BaseValidator.php b/app/helpers/MetaFormats/Validators/BaseValidator.php index c1c09f9fd..1b5552933 100644 --- a/app/helpers/MetaFormats/Validators/BaseValidator.php +++ b/app/helpers/MetaFormats/Validators/BaseValidator.php @@ -65,4 +65,16 @@ public function validate(mixed $value): bool // return false by default to enforce overriding in derived types return false; } + + /** + * Patches the query/path parameter value after successful validation. + * This is useful for special data types that needs to be converted to a different type before being used + * in the presenter method (e.g., convert strings like "true"/"false" to booleans). + * Note: this method is not called for values that did not pass validation. + * @param mixed $value The value to be patched. Passed by reference, so it can be modified by the method. + */ + public function patchQueryParameter(mixed &$value): void + { + // no-op by default, can be overridden by validators that need to change the value before validation + } } diff --git a/app/helpers/MetaFormats/Validators/VArray.php b/app/helpers/MetaFormats/Validators/VArray.php index 706800641..9a8fe2f28 100644 --- a/app/helpers/MetaFormats/Validators/VArray.php +++ b/app/helpers/MetaFormats/Validators/VArray.php @@ -102,4 +102,13 @@ public function validate(mixed $value): bool } return true; } + + public function patchQueryParameter(mixed &$value): void + { + if (is_array($value) && $this->nestedValidator !== null) { + foreach ($value as &$element) { + $this->nestedValidator->patchQueryParameter($element); + } + } + } } diff --git a/app/helpers/MetaFormats/Validators/VBool.php b/app/helpers/MetaFormats/Validators/VBool.php index 3f55e63c2..d3913740b 100644 --- a/app/helpers/MetaFormats/Validators/VBool.php +++ b/app/helpers/MetaFormats/Validators/VBool.php @@ -22,14 +22,29 @@ public function validate(mixed $value): bool if (!$this->strict) { // FILTER_VALIDATE_BOOL is not used because it additionally allows "on", "yes", "off", "no" and "" - return $value === 0 - || $value === 1 - || $value === "0" - || $value === "1" - || $value === "false" - || $value === "true"; + + if ($value === 0 || $value === 1) { + return true; + } + + if (is_string($value)) { + $lower = strtolower(trim($value)); + return $lower === "0" + || $lower === "1" + || $lower === "false" + || $lower === "true"; + } } return false; } + + public function patchQueryParameter(mixed &$value): void + { + if (is_string($value)) { + $value = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE); + } else { + $value = (bool)$value; + } + } } From 5d47e51fd421372ce3618e61b86e1f0a8a3c8b80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kruli=C5=A1?= Date: Sun, 15 Mar 2026 17:45:15 +0100 Subject: [PATCH 2/3] Fixing a bug. --- app/model/repository/PlagiarismDetectionBatches.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/model/repository/PlagiarismDetectionBatches.php b/app/model/repository/PlagiarismDetectionBatches.php index 06e21bec4..14c85b27e 100644 --- a/app/model/repository/PlagiarismDetectionBatches.php +++ b/app/model/repository/PlagiarismDetectionBatches.php @@ -34,8 +34,9 @@ public function findByToolAndSolution(?string $detectionTool, ?AssignmentSolutio $sub = $qb->getEntityManager()->createQueryBuilder() ->select("ds")->from(PlagiarismDetectedSimilarity::class, "ds"); $sub->andWhere("ds.batch = b.id") - ->andWhere("ds.testedSolution = :sid")->setParameter("sid", $solution->getId()); - $qb->andWhere($qb->expr()->exists($sub->getDQL())); + ->andWhere("ds.testedSolution = :sid"); + $qb->andWhere($qb->expr()->exists($sub->getDQL())) + ->setParameter("sid", $solution->getId()); } $qb->orderBy("b.createdAt", "DESC"); return $qb->getQuery()->getResult(); From 7cf84a0cc16206854e26726fae13504ebc04f012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kruli=C5=A1?= Date: Sun, 15 Mar 2026 17:51:36 +0100 Subject: [PATCH 3/3] Removing lingering code. --- app/V1Module/presenters/base/BasePresenter.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/V1Module/presenters/base/BasePresenter.php b/app/V1Module/presenters/base/BasePresenter.php index c37d55233..cefd811b4 100644 --- a/app/V1Module/presenters/base/BasePresenter.php +++ b/app/V1Module/presenters/base/BasePresenter.php @@ -130,11 +130,6 @@ public function startup() Validators::init(); $this->processParams($actionReflection); - // TODO -- coerce bools in params - if (!empty($this->params['archived'])) { - $this->params['archived'] = filter_var($this->params['archived'], FILTER_VALIDATE_BOOLEAN); - } - // ACL-checking method $this->tryCall($this->formatPermissionCheckMethod($this->getAction()), $this->params); }