Repository: cakephp/cakephp (PHP)

Issue:
Query::bind() does not make the count query dirty although it should
This is a (multiple allowed):

* [x] bug
* [ ] enhancement
* [ ] feature-discussion (RFC)

* CakePHP Version: 3.8.12
* Platform and Target: Apache, MySQL

### What you did
In a table class:
```
        $Query = $this->find()
            ->where([
                'created BETWEEN :start AND :end',
            ])
            ->bind(':start', $today)
            ->bind(':end', $tomorrow);
        $dailyCount = $Query->count();
        ...
        $Query
            ->bind(':start', $monday)
            ->bind(':end', $sunday);
        $weeklyCount = $Query->count();
```

### What happened
This executed the query only one time when the first call to `->count()` was done.

### What you expected to happen
I would expect two statements to be executed.



Discussion: Worth mentioning that marking an already executed query dirty will reset all existing bindings, so if `bind()` would mark the query dirty, each `bind()` call would remove any previously set bindings, so that only the last `bind()` call would have any effect.

In your specific situation you could call `clearResult()` (only available for ORM queries) before binding, that would mark the query dirty once.
@ndm2 Thanks but I can't find `clearResult()` method. Maybe `count()` should also make the query dirty? Otherwise `count()` (and any method that executes the query) could set a boolean flag indicating that a query was executed and that flag could be used by the first calls to `bind()` (or any other method altering the query) to make the query dirty and reset that flag. 

Calling `disableBufferedResults()` before the second group of `bind()` calls makes the query dirty in my use case but that may not be suitable in other scenarios.
`clearResults()` was added in 3.9 - https://book.cakephp.org/3/en/appendices/3-9-migration-guide.html#orm
Oops, I thought I've seen a `@since 4.0.1` tag, but I must have mixed that up. Still a valid suggestion I'd say, further changes would end up in 3.9 or 3.10 anyways.

Not sure if anyone wants to take up the task of introducing more state into the query builder for this, it would also need to apply to the query being executed (counting will operate on a cloned query), otherwise there would be a weird discrepancy in how `bind()` behaves.
> Oops, I thought I've seen a `@since 4.0.1` tag, but I must have mixed that up. Still a valid suggestion I'd say, further changes would end up in 3.9 or 3.10 anyways.

It was added in 4.1 and 3.9.

Another way to reset the query bindings is to use `$query->getValueBinder()->reset()`. Given there are a few existing ways to do this with either the ValueBinder or Query::clearResult() do we still need new methods or would an update to the documentation around manual bindings suffice?
In my opinion updating documentation is not enough. The present behavior is not intuitive and it is too easy to introduce a bug in the application code because of that.
Both `ORM\Query::all()` and `ORM\Query::count()` cache the results.

The docblock of `Query::count()` explicitly specifies this:
> If the query has not been modified, and the count has already been performed the cached value is returned

So unless you call a method which clears the results you will get the cached results without any actual query execution.

The results can't be cleared when calling `bind()` as that would prevent multiple calls to `bind()` for the same query. So calling `clearResults()` is the right thing to do for your use case if you want the same query instance to be re-executed.
> If the query has not been modified...

Exactly. The query has been modified by calling `bind()` after `count()`.
> The query has been modified by calling bind() after count().

In ndm2's words: "Not sure if anyone wants to take up the task of introducing more state into the query builder for this".
This issue is stale because it has been open for 120 days with no activity. Remove the `stale` label or comment or this will be closed in 15 days
This seems to be a documentation issue only.
So the solution here is to clone before the count(), right?
@markstory Do we close this with a docs note, or is there any value in fixing this up in 5.next or 6.x?
> Do we close this with a docs note, or is there any value in fixing this up in 5.next or 6.x?

I don't think documentation is a solution here. Ideally `bind()` clears the cached results and the query executes again when `count()` is called the second time. The complexity in making that work is tricky part here.
If my fix works, this must have been the most simple 5min fix ever that no one dared to do for 5 years almost :)



Fix (patch):
diff --git a/src/Database/Query.php b/src/Database/Query.php
index d639049747a..b4d18f6a12e 100644
--- a/src/Database/Query.php
+++ b/src/Database/Query.php
@@ -1691,6 +1691,7 @@ protected function _expressionsVisitor(mixed $expression, Closure $callback): vo
      */
     public function bind(string|int $param, mixed $value, string|int|null $type = null)
     {
+        $this->_dirty();
         $this->getValueBinder()->bind($param, $value, $type);
 
         return $this;