From 1a0fab0615312312c694866f58f9cc8d1a180b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Tue, 9 Sep 2025 23:05:03 +0200 Subject: [PATCH] Use a deterministic method to generate parameter names for filters, to allow for proper caching of queries --- src/DataTables/Filters/AttachmentFilter.php | 5 +++++ .../Filters/Constraints/AbstractConstraint.php | 5 +---- .../Filters/Constraints/FilterTrait.php | 15 +++++++++++++-- .../Filters/Constraints/Part/TagsConstraint.php | 2 +- src/DataTables/Filters/LogFilter.php | 4 ++++ src/DataTables/Filters/PartFilter.php | 4 ++++ src/DataTables/Filters/PartSearchFilter.php | 1 + 7 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/DataTables/Filters/AttachmentFilter.php b/src/DataTables/Filters/AttachmentFilter.php index d41bbe39..69d2aeac 100644 --- a/src/DataTables/Filters/AttachmentFilter.php +++ b/src/DataTables/Filters/AttachmentFilter.php @@ -22,6 +22,7 @@ declare(strict_types=1); */ namespace App\DataTables\Filters; +use App\DataTables\Filters\Constraints\AbstractConstraint; use App\DataTables\Filters\Constraints\BooleanConstraint; use App\DataTables\Filters\Constraints\DateTimeConstraint; use App\DataTables\Filters\Constraints\EntityConstraint; @@ -32,6 +33,7 @@ use App\DataTables\Filters\Constraints\TextConstraint; use App\Entity\Attachments\AttachmentType; use App\Services\Trees\NodesListBuilder; use Doctrine\ORM\QueryBuilder; +use Omines\DataTablesBundle\Filter\AbstractFilter; class AttachmentFilter implements FilterInterface { @@ -51,6 +53,9 @@ class AttachmentFilter implements FilterInterface public function __construct(NodesListBuilder $nodesListBuilder) { + //Must be done for every new set of attachment filters, to ensure deterministic parameter names. + AbstractConstraint::resetParameterCounter(); + $this->dbId = new IntConstraint('attachment.id'); $this->name = new TextConstraint('attachment.name'); $this->targetType = new InstanceOfConstraint('attachment'); diff --git a/src/DataTables/Filters/Constraints/AbstractConstraint.php b/src/DataTables/Filters/Constraints/AbstractConstraint.php index 7f16511e..c632b2a4 100644 --- a/src/DataTables/Filters/Constraints/AbstractConstraint.php +++ b/src/DataTables/Filters/Constraints/AbstractConstraint.php @@ -28,10 +28,7 @@ abstract class AbstractConstraint implements FilterInterface { use FilterTrait; - /** - * @var string - */ - protected string $identifier; + protected ?string $identifier; /** diff --git a/src/DataTables/Filters/Constraints/FilterTrait.php b/src/DataTables/Filters/Constraints/FilterTrait.php index 3260e4e3..2932914a 100644 --- a/src/DataTables/Filters/Constraints/FilterTrait.php +++ b/src/DataTables/Filters/Constraints/FilterTrait.php @@ -28,6 +28,7 @@ trait FilterTrait { protected bool $useHaving = false; + protected static int $parameterCounter = 0; public function useHaving($value = true): static { @@ -50,8 +51,18 @@ trait FilterTrait { //Replace all special characters with underscores $property = preg_replace('/\W/', '_', $property); - //Add a random number to the end of the property name for uniqueness - return $property . '_' . uniqid("", false); + return $property . '_' . (self::$parameterCounter++) . '_'; + } + + /** + * Resets the parameter counter, so the next call to generateParameterIdentifier will start from 0 again. + * This should be done before initializing a new set of filters to a fresh query builder, to ensure that the parameter + * identifiers are deterministic so that they are cacheable. + * @return void + */ + public static function resetParameterCounter(): void + { + self::$parameterCounter = 0; } /** diff --git a/src/DataTables/Filters/Constraints/Part/TagsConstraint.php b/src/DataTables/Filters/Constraints/Part/TagsConstraint.php index 02eab7a1..2b28e6b4 100644 --- a/src/DataTables/Filters/Constraints/Part/TagsConstraint.php +++ b/src/DataTables/Filters/Constraints/Part/TagsConstraint.php @@ -88,7 +88,7 @@ class TagsConstraint extends AbstractConstraint //Escape any %, _ or \ in the tag $tag = addcslashes($tag, '%_\\'); - $tag_identifier_prefix = uniqid($this->identifier . '_', false); + $tag_identifier_prefix = $this->generateParameterIdentifier('tag'); $expr = $queryBuilder->expr(); diff --git a/src/DataTables/Filters/LogFilter.php b/src/DataTables/Filters/LogFilter.php index 35d32e74..38dc2191 100644 --- a/src/DataTables/Filters/LogFilter.php +++ b/src/DataTables/Filters/LogFilter.php @@ -22,6 +22,7 @@ declare(strict_types=1); */ namespace App\DataTables\Filters; +use App\DataTables\Filters\Constraints\AbstractConstraint; use App\DataTables\Filters\Constraints\ChoiceConstraint; use App\DataTables\Filters\Constraints\DateTimeConstraint; use App\DataTables\Filters\Constraints\EntityConstraint; @@ -44,6 +45,9 @@ class LogFilter implements FilterInterface public function __construct() { + //Must be done for every new set of attachment filters, to ensure deterministic parameter names. + AbstractConstraint::resetParameterCounter(); + $this->timestamp = new DateTimeConstraint('log.timestamp'); $this->dbId = new IntConstraint('log.id'); $this->level = new ChoiceConstraint('log.level'); diff --git a/src/DataTables/Filters/PartFilter.php b/src/DataTables/Filters/PartFilter.php index ff98c76f..8dcbd6b3 100644 --- a/src/DataTables/Filters/PartFilter.php +++ b/src/DataTables/Filters/PartFilter.php @@ -22,6 +22,7 @@ declare(strict_types=1); */ namespace App\DataTables\Filters; +use App\DataTables\Filters\Constraints\AbstractConstraint; use App\DataTables\Filters\Constraints\BooleanConstraint; use App\DataTables\Filters\Constraints\ChoiceConstraint; use App\DataTables\Filters\Constraints\DateTimeConstraint; @@ -103,6 +104,9 @@ class PartFilter implements FilterInterface public function __construct(NodesListBuilder $nodesListBuilder) { + //Must be done for every new set of attachment filters, to ensure deterministic parameter names. + AbstractConstraint::resetParameterCounter(); + $this->name = new TextConstraint('part.name'); $this->description = new TextConstraint('part.description'); $this->comment = new TextConstraint('part.comment'); diff --git a/src/DataTables/Filters/PartSearchFilter.php b/src/DataTables/Filters/PartSearchFilter.php index 6e2e5894..60832b26 100644 --- a/src/DataTables/Filters/PartSearchFilter.php +++ b/src/DataTables/Filters/PartSearchFilter.php @@ -21,6 +21,7 @@ declare(strict_types=1); * along with this program. If not, see . */ namespace App\DataTables\Filters; +use App\DataTables\Filters\Constraints\AbstractConstraint; use Doctrine\ORM\QueryBuilder; class PartSearchFilter implements FilterInterface