CakePHP - how to handle double quote in conditions array - cakephp

See the following example code:
$conditions = array("Post.title" => 'This is a "Book"');
// Example usage with a model:
$this->Post->find('first', array('conditions' => $conditions));
Because find() actually looks for title = 'This is a \"Book\"', no result returned. I am wondering how to prevent find() from adding backslashes. Or is there any other solution?
==fixed==
*Actually the error occurred when I used updateAll($field, $conditions), not find(). I did not put the quote around literal values. For example, $field = array('title' => $some_title) should be $field = array('title' => "'" . Sanitize::escape($some_title) . "'") . Don't like the way CakePHP handles this though.*

You must be mistaken. The error must be somewhere else.
The resulting SQL query does contain
LIKE 'foo \"bar\"'
But that escaping is actually intentional.
I will still turn up the DB entry with foo "bar" - I just tried it myself with cake2.3/2.4.
So CakePHP is working correctly.

Just checked with cakePhp version 2.3.5, The double quotes are working fine, Please check below code for a Profile controller.
$data = $this->Profile->find('all',array('conditions'=>array('Profile.type'=>'user "one"')));
pr($data);

Related

How to retrieve data from model using findByType in cakephp?

I am trying to find records from CallForwardingCondition model using following line of code:
$this->loadModel('CallForwardingCondition');
$this->set('callForwardingCondition', $this->CallForwardingCondition->findByType('list'));
In SQL Dump following query is done when page is reloaded:
SELECT `CallForwardingCondition`.`type`, `CallForwardingCondition`.`description` FROM `vpbx`.`call_forwarding_condition` AS `CallForwardingCondition` WHERE `CallForwardingCondition`.`type` = 'list' LIMIT 1
How can I direct Cakephp to findByType which will result in following query?
SELECT `CallForwardingCondition`.`type`, `CallForwardingCondition`.`description` FROM `vpbx`.`call_forwarding_condition` AS `CallForwardingCondition` WHERE `CallForwardingCondition`.`type` LIKE '%' LIMIT 10
For CakePHP 2.x you need to use find('all') and pass it the required conditions and limit:-
$result = $this->CallForwardingCondition->find('all',[
'conditions' => ['CallForwardingCondition.type Like' => '%'],
'limit' => 10
);
findByType is a special find method that will only return the first record matching the type passed as the find method's parameter which is why it isn't returning what you want. You can read more about the findBy magic functions in the official docs.
try this:
$result = $this-> CallForwardingCondition ->find('all',['limit'=>10,'conditions'=>['CallForwardingCondition.type Like'=>'%'])->toArray();
I don't think you can't use findBy with limit. If you want to use limit, you must use findAllBy<fieldName>. Even findAllBy<fieldName>, you can't use LIKE.
This is findAllBy from cakephp
findAllBy<fieldName>(string $value, array $fields, array $order, int $limit, int $page, int $recursive)
So if you want to covert it, you must do the following way;
$this->CallForwardingCondition->findAllByType('something',['CallForwardingCondition.*'],['CallForwardingCondition.id'=>'desc'],'10');
This is findBy from cakephp doc.
findBy<fieldName>(string $value[, mixed $fields[, mixed $order]]);
Hope this help for you.

How to strip HTML from a field in drupal views

I am trying to add a function to that strips the html from a field in drupal views. I found a function for sql server called "udf_StripHTML" that does that.
http://blog.sqlauthority.com/2007/06/16/sql-server-udf-user-defined-function-to-strip-html-parse-html-no-regular-expression/
I am using the following code:
/**
* Implements hook_views_query_alter().
*/
function cviews_views_query_alter(&$view, &$query) {
// Add a strip html tags from content.
$fields = array('field_data_body.body_value');
foreach ($query->where as $key1 => $value) {
foreach ($value['conditions'] as $key2 => $coditions) {
if (in_array($coditions['field'], $fields)) {
$query->where[$key1]['conditions'][$key2]['field'] = 'dbo.udf_StripHTML(' . $coditions['field'] . ')';
}
}
}
}
When views module converts the query object to a string the field become
from:
'dbo.udf_StripHTML(field_data_body.body_value)';
to:
[dbo].[udf_StripHTMLfield_data_body.body_value]
My question is, how can I add a function there?
Thank you,
You are going way too deep here friend. I'm going to assume that you're using Drupal 7, but for Drupal 8 this should be similar (since views is in core for both).
A few things about your approach:
That function is a user defined function which means that it needs to be defined at a much lower-level (in the SQL database) before you can use it in your query.
This is a red-herring approach, however, because you don't need to even touch the SQL to accomplish what you want (you can do this with PHP with strip_tags!)
You don't need a query alter hook here (we don't need to go to the database to do this). You could do this with one of the preprocess or field hooks from the field API or the views API using the function linked in my previous point.
Even better, you don't even have to touch the code to accomplish this. You can do it right in the Drupal UI.
Under the field settings for the view, select rewrite results and then Strip HTML tags. Presto, no more HTML tags in that field.
Image source: https://www.drupal.org/node/750172
Here is the solution that worked for me:
// Traverse through the 'where' part of the query.
foreach ($query->where as &$condition_group) {
foreach ($condition_group['conditions'] as &$condition) {
if (in_array($condition['field'], $fields)) {
$value = $condition['value'];
$field = $condition['field'];
$condition = array(
'value' => array(),
'field' => t('dbo.udf_StripHTML(!field) like \'#value\'', array(
'!field' => $field,
'#value' => $value)),
'operator' => 'formula',);
}
}
}

Cakephp 3 dynamically build contain

How can I dynamically build the contain in the new cakephp 3 query builder. This is what I have now:
$query = $dbTable->find()
->select($contain['select']['fields'])
->contain(function($q) use($array,$contain){
$new = [];
foreach($array as $v){
if(isset($contain['contains'][$v])){
$fields = $contain['contains'][$v];
$new[$v] = $q->select($fields);
}
}
return $new;
});
But I am getting several errors with this:
Warning (2): Illegal offset type in isset or empty [CORE\src\ORM\EagerLoader.php, line 198]
Warning (2): strpos() expects parameter 1 to be string, object given [CORE\src\ORM\EagerLoader.php, line 203]
Warning (2): Illegal offset type [CORE\src\ORM\EagerLoader.php, line 223]
Warning (2): Illegal offset type [CORE\src\ORM\EagerLoader.php, line 224]
As already mentioned by Lorenzo, that's not how it works, contain() doesn't accept callables, just look at the docs:
http://api.cakephp.org/3.0/class-Cake.ORM.Query.html#_contain
Also your code would invoke select() multiple times on one and the same query, that wouldn't work anyways.
However, looking at your code it seems that you could simply make use of the fields option, ie build a simple array to pass to contain(). This is shown in the docs, the example however will trigger an error as it seems to be necessary to explicitly set the foreign key field too:
$query->contain([
'Articles' => [
'fields' => ['foreign_key_column_name', 'title']
]
]);
You cannot use contain with a closure inside. If you believe this is a good idea (I think it could be) then open a enhancement request on github.
To get all fields in the table Articles do this in your controller.
$query = $this->modelName->find('all')
->contain('Articles');
If you want a expecify field use this:
$query = $this->modelName->find('list', [
'keyField' => 'id',
'valueField' => 'author.name'
])->contain(['Articles']);
To more informations about: https://book.cakephp.org/3.0/en/orm/retrieving-data-and-resultsets.html

CakePHP: add/substract with save()?

I'm trying to simply perform the following via Cake's save() function.
UPDATE user SET value = value-1
However, it seems it can only set. It will not understand anything I pass to it to increment or subtract, and no one on the internet seems to be having this issue. :P Even when going through a full piece of software someone built on CakePHP 2.0, I'm finding $this->query() used for updating by increments! Is this really how I'll update if I don't already have the value to be setting?
(code appears as follows)
$data = array('id' => uid, 'value' => "Users.value = Users.value - 1");
$this->User->save($data);
The code for producing an increment or decrement in CakePHP database is as follows:
$this->User->updateAll(array('value' => 'value - 1'), array('id' => uid));
Arun's answer was not correct; you must put the - 1 within quotes to get Cake to recognize it is part of the query. Else it will try to set all User.value to -1. Note that you must put the information (identifiers) of the columns that you want to update on in the second condition.
basically you just have to use updateAll for atomic queries like this
$this->User->updateAll($fields, $conditions);
http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-updateall-array-fields-array-conditions
You can do so using following query:
$this->User->updateAll(array('User.value' => 'User.value' - 1));
//or
//$this->User->updateAll(array('User.value' => 'User.value' - 1), array('User.id' => $uid));

Dynamically add virtual field in cakephp

I am using CakePHP 2.1.3; I want to create a virtual field dynamically in a controller. Is it possible?
The problem is when I am trying to find max value in a table it gives me another array from the model array. Please ask if you need more information.
When I am trying to execute the following query,
$find_max_case_count = $this->CaseMaster->find('first', array(
'conditions' => array(
'CaseMaster.CLIENT_ID' => $client_id,
'CaseMaster.CASE_NO LIKE' => '%-%'
),
'fields' => array('max(CaseMaster.CASE_NO) AS MAX_NO')
));
It is giving me an array like:
[0]=> array([MAX_NO]=> 52)
However I want it to be like as:
[CaseMaster] => array([MAX_NO] => 52)
I found a solution. I can make the virtual field at runtime. The code should looks like:
$this->CaseMaster->virtualFields['MAX_NO'] = 0;
Write it just above the find query and the query will remain same as it was written.
This link was helpful to find out the solution.
There is no way (as far as I am knowledgeable) to create virtual fields "on the fly". What virtual fields are is "arbitrary SQL expressions" that will be executed when a find runs through the Model and "will be indexed under the Model's key alongside other Model fields".
What do you need to do with "dynamically created virtual fields"? If you explain what exactly you need to accomplish maybe we can provide a different (even more suitable? :) ) solution? I'd personally be happy to help you.
After you editing your question I can say that what you're getting is the way the array should be returned, this is because of the fields parameter. If you want to get a different structure out of it I suggest applying a callback to format it.
Firstly move the method inside the CaseMaster Model:
public function getMaxCaseCount($client_id){
$data = $this->find('first', array(
'conditions' => array(
'CaseMaster.CLIENT_ID' => $client_id,
'CaseMaster.CASE_NO LIKE' => '%-%'),
'fields' => array('max(CaseMaster.CASE_NO) AS MAX_NO')));
return array_map(array('CaseMaster', '__filterMaxCaseCount'), $data);
}
private function __filterMaxCaseCount($input){
//This is just an example formatting
//You can do whatever you would like here.
$output['CaseMaster'] = $input[0];
return $output;
}
The array_map function will apply the __filterMaxCaseCount callback method so that when you call:
$this->CaseMaster->getMaxCaseCount($client_id);
from your controller you will get the data in the way you need it. The array_map function could also look like this:
return array_map(array($this, '__filterMaxCaseCount'), $data);
because you're in the same Class.
Just adding your model alias to field definition also works for this purpose
'fields' => array('max(CaseMaster.CASE_NO) AS CaseMaster__MAX_NO')

Resources