Question

I've come across an odd problem using CakePHP 1.3 to find information. Let's use this dbschema as an example:

id is int(11) PRIMARY auto_increment amount is float(10,2) NULL status is ENUM(Completed, Removed, Pending)

id  amount  status
1   100.00  Completed
2   100.00  Removed
3   100.00  Completed
4   100.00  Completed
5   100.00  Pending

When using Cake's find to retrieve data from this table, I use this query:

$this->Testtable->find('all', array(
    'conditions' => array(
        'status LIKE ' => 'Removed',
        'status LIKE ' => 'Pending',
        'status LIKE ' => 'Completed'
    )
)) 

Looking at this query, I would assume that Cake would return all rows that match all of those conditions (which is totally acceptable in SQL), however it only uses the last condition and returns WHERE status LIKE 'Completed'.

I ran a test doing this, which returned all rows correctly (I know it's a more "correct" way to do the query anyway):

'conditions' => array(
    'status' => array('Removed', 'Pending', 'Completed')        
)

Take the opposite for an example, I want to return all rows that aren't Removed or Pending:

'conditions' => array(
    'status !=' => 'Removed',
    'status !=' => 'Pending'
)

This query returns all rows with Completed and Removed, as it only listens to the last statement. I assume that this is happening because instead of concatenating these search conditions into the query, Cake is overwriting the conditions based on the "field" being status !=. I can prove this theory by adding a space after the != in either of those conditions, creating the desired result of only Confirmed records.

Can anybody tell me why Cake would do this? As this is a legitimate thing to do in SQL, I see no reason that Cake wouldn't allow you to do it. Does anybody know if this issue is fixed up in newer versions of Cake?

Was it helpful?

Solution

I suppose that this comes down to the fact that at the end of the day, I am reassigning the array value based on that key, and it's not actually CakePHP's fault at all. I had a look into Cake's model.php and found this:

$query = array_merge(compact('conditions', 'fields', 'order', 'recursive'), array('limit' => 1));

I ran a test:

$array = array(
'conditions' => array(
    'test' => 'yes',
    'test' => 'no'
)
);

$var = 'hello';
$c = compact('array', 'var');
print_r($c);

As mentioned above, compact is only receiving the value no from the test key. I assumed that the use of compact to merge variables/arrays into the query would recursively merge similar keys from the conditions array into the last specified, but it turns out that yes isn't even making it that far as it's being redefined on the spot.

I suppose this is a limitation of PHP rather than Cake, but it is still something that didn't occur to me and should be done differently somehow (if it hasn't already) in future.

Edit

I ran a couple more tests. I wrapped identical conditions in their own arrays, then compared them the way Cake's find functions would. Using compact (which Cake does) the arrays containing identical keys remain intact, however using array_merge, the first key is overwritten by the second. I guess in this case it's a very, very good thing that Cake uses compact instead of array_merge to merge its query criteria.

$array = array(
    array('test' => 'yes'),
    array('test' => 'no')
);

$m = array_merge($array[0], $array[1]);
$c = compact('array');

print_r($c);
print_r($m);

Result:

Array
(
    [array] => Array
        (
            [0] => Array
                (
                    [test] => yes
                )

            [1] => Array
                (
                    [test] => no
                )

        )

)
Array
(
    [test] => no
)

While this is obviously a simple problem in the way you fundamentally write PHP code, it wasn't inherently obvious while writing in Cake syntax that conditions would overwrite each other...

OTHER TIPS

Basic PHP: Don't use the same array key twice

'conditions' => array(
    'status LIKE ' => 'Removed',
    'status LIKE ' => 'Pending',
    'status LIKE ' => 'Completed'
)

should be

'conditions' => array(
    'status LIKE' => array('Removed', 'Pending', 'Completed'),
)

Same for any other array key.

Note that some quick debugging of the array reveals this. Please also see the tons of other stackoverflow questions with the same issue or other areas where basic research could have pointed you in this direction. Taking a look there first can help to resolve the issue in less time.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top