CakePHP: add/substract with save()? - database

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));

Related

CakePHP set redirect parameters using an array

I am writing some simple redirects in CakePHP 2.4.2 and came across one that stumped me a bit.
How can I redirect to another view with the same passedArgs? It seems like it should be simple, but I must be overlooking something. I tried this:
$this->redirect(array_merge(array('action' => 'index',$this->params['named'])));
The debug output seems correct:
array(
'action' => 'index',
(int) 0 => array(
'this' => 'url',
'and' => 'stuff'
)
)
My desired outcome is that
view/this:url/and:stuff
redirects to
index/this:url/and:stuff
but now it just sends me to
index/
Not sure what I am missing here, perhaps I have deeper configuration issues - although it's not a terribly complicated app.
For passed params (numeric keys) use array_merge.
But since you use named params, which have a string based key, you can leverage PHP basics:
$this->redirect(array('action' => 'index') + $this->request->params['named']));
This adds all array values from the named params except for action, which is already in use here (and hence should not be overwritten anyway). So the priority is clear, as well.
Cake expects a flat array of parameters, so you need to use array_merge to add in extra arrays to it on the sides. Try this:
$this->redirect(array_merge(array('action' => 'index'), $this->params['named']));
... or using your original variable $passedArgs:
$this->redirect(array_merge(array('action' => 'index'), $this->passedArgs));
Maybe better solution will be used persist attribute in Router::connect()?

Cakephp : add condition in this->Model->save()

i am working on a cakephp 2.x .want to add a condition into my save query .. for example i want to implement this query
INSERT INTO 'this' where condition is 'this'
right now i am doing this
$count = $this->Message->find('count', array(
'conditions' => array('Message.mobileNo' => $mobileNo,
'Message.body'=>$body
)));
if($count>0){
echo "already have a message";
}else
{
$this->Message->create();
$this->Message->save($this->request->data);
}
at times now i am first checking through count and then saving into the database ... can i add condition into my save so i dont have to query two times into database just to accomplish one task
This is not really CakePHP question rather than MySQL. But you can't do this since the INSERT query doesn't have conditional query.
There are 2 ways of doing it:
As Mark in the comment said use validation. The validation although apply to single field, so it will be quite tricky to do it.
Use beforeValidate() or beforeSave() callbacks in your model to check this and if they return false the save operation wont be executed.
You can put UNIQUE index to your table so it won't allow insertion of phone+message together.
I would go with the method 2.
try the beforeSave methode in your Model
You can use the following code in a better way:
$conditions = array('Message.mobileNo' => $mobileNo,
'Message.body'=>$body);
if ($this->Message->hasAny($conditions)){
echo "already have a message";
}
else{
$this->Message->create();
$this->Message->save($this->request->data);
}

CakePHP Fulltext search MySQL with rating

I've read that, to be able to rank search results you may query MySQL like this:
SELECT * ,
MATCH (title, body) AGAINST ('$search') AS rating
FROM posts
WHERE MATCH (title, body) AGAINST ('$search')
ORDER BY rating DESC
Is there a way to do this in CakePHP 2.X?
Also, I need to do this while paginating at the same time. So I think I would need to write condition for the paginator, not a direct 'query'.
Thanks for your help!
Use like this it will prevent mysql injection too
array("MATCH(User.current_position) AGAINST(? IN BOOLEAN MODE)" => $srch_arr['text'])
Ok, it took me some time... Since, the key issue was to get a rating on the resulting matches, the complicated part in this query was the specific field:
MATCH (title, body) AGAINST ('$search') AS rating
I figured that I should just write that field in the "field" option, in the pagination array.
The resulting code was the following:
$this->paginate = array(
'limit' => 15,
'fields' => array('*', "MATCH (data) AGAINST ('$q') AS rating"),
'conditions' => "MATCH(SearchIndex.data) AGAINST('$q' IN BOOLEAN MODE)",
'order' => array(
'rating' => 'desc',
),
);
$paginatedResults = $this->paginate('SearchIndex');
And that worked seamlessly!
I think this is the best way to achieve real search results using Cake. Unless someone has a better alternative :)
Searching phrases in between double quotes will give you the results you should expect!
I have used the above database call by Thomas (thank you) and it does work seamlessly.
However the code:
'conditions' => "MATCH(SearchIndex.data) AGAINST('$q' IN BOOLEAN MODE)",
removes the Data Abstraction Layer and opens up your site to SQL injection.
It's probably not quite as good (haven't fully tested it) but try:
'SearchIndex.data LIKE'=>'%'.$search.'%'
I hope this is helpful in someway.

AutoMagic select box not populating in CakePHP

I've got the following relationship set-up between two models
Story belongsTo StoryType
StoryType hasMany Story
I've set-up a form to select the StoryType for each story using the following code:
echo $this->Form->input('Story.story_type_id', array('tabindex' => 2));
with this code in the controller to populate the list
$this->set('story_types', $this->Story->StoryType->find('list', array ('order' => 'title')));
But it's not populating the select box with anything. I know that the find() option is working because doing a debug within the controller produces this:
Array
(
[1] => First Person
[3] => Third Person
)
The weird thing is that it's exactly the same code, just querying other models, to populate select lists for things like users and genres, it's just the story types that isn't working.
Any ideas? Cheers.
You don't mention which version of CakePHP you're using, but try setting storyTypes rather than story_types:
$this->set( 'storyTypes', $this->Story->StoryType->find( 'list', array( 'order' => 'title' ) ) );
Older versions of CakePHP (pre-1.3) modified set variable names to headlessCamelCase and, even if you're using 1.3.x, there may be a little bit of that infrastructure lingering. It's a bit of a reach, but it's easy enough to test and it seems plausible that this could be the root of your problem.
I'll be curious to see what you find out.
This is a little hacky, but I think it will work:
echo $this->Form->input('Story.story_type_id', array('tabindex' => 2, 'options' => $story_types));
here's what you should really do .. (esp, for version 2.x) - in case if some people are facing the same problem.
[inside your constroller action]
$oneOfTheColumns = 'title'; //just for sake of making it clear - if you have to order the results
$storyTypes = $this->Story->StoryType('find', array('order'=>$oneOfTheColumns));
$this->set(compact('storyTypes'));
[inside your view]
echo $this->Form->input('StoryType');

Select from a table where associated table fieldname = 'x' in Cake PHP

In CakePHP, I have two tables, Countries & Networks. They have a HABTM relationship and are joined by countries_networks.
I'm trying to get all countries from the countries table where the 'name' field in Networks = 'o2'
I've realised I can't do this using a basic find(), so I've been experimenting with the containable behaviour. I have managed to restrict the returned data, but it looks as though 'containable' doesn't exactly work as I want. Heres my code:
$countries = $this->Country->find('all', array('contain' => array(
'Network' => array(
'conditions' => array('Network.name =' => "o2"),
)
)));
This query however returns ALL countries, and the Network.name if its 'o2'. What I really need to do is return ONLY the countries that have a Network.name of 'o2', and no others.
Can anyone help? Thanks.
"=' =>"
What is it? there is no needed to use "=" symbol after "Network.name"
Your query returns you exactly what your ask.
Try to select from Network.
$countries = $this->Country->Network->find('first', array(
'conditions' => array('Network.name' => "o2"),
'contain' => array('Country')
));
$countries = $countries['Country'];
You should be able to do something like this:
$this->Country->hasAndBelongsToMany['Network']['conditions'] = array('Network.name'=>'o2');
$myCountries = $this->Country->find('all');
I haven't tested this, but it should get you pretty close to where you need to be.
Also, bear in mind that changing the hasAndBelongsToMany array like this will affect all subsequent queries against that model during the current page load (but it will be reset the next time the model is instantiated).
I'm on my way out the door, so sorry for the brief explanation.
I think what you want is a HABTM relationship, but to be able to filter based on the associated model's data. To do this, check out the "Containable" behavior in Cake's manual. Pretty sure that's what you're after.

Resources