I have a table called user_tasks. I do not have a model for that table and in my totally unrelated controller I have a need to update a single field in the user_tasks table.
This is what I have tried so far
$user = ClassRegistry::init('user_tasks');
$conditions = "user_id = ". $user_id;
$userRows = $user->find('first', array('conditions'=>$conditions));
$user->set(array(
'task' => $level
));
$user->save();
However it does not like $user->save(); and my table never gets updated. What can I do to make it work.
thanks
UPDATE:
In my try / catch block,
I get this
{"errorInfo":["HY000",1364,"Field 'user_id' doesn't have a default value"],"queryString":"INSERT INTO `mytable`.`user_tasks` (`subscription`, `modified`, `created`) VALUES ('Latest', '2015-04-09 15:08:51', '2015-04-09 15:08:51')"}
ok I figured it out. I have to send user id as well
so
$user = ClassRegistry::init('user_tasks');
$conditions = "user_id = ". $user_id;
$userRows = $user->find('first', array('conditions'=>$conditions));
$user->set(array(
'task' => $level,
'user_id' => $user_id
));
$user->save();
The above will create a new record. If you are updating a record then just pass a primary key. And if you are passing the primary key, then you do not need user_id etc. You just need the fields in the set that need to be updated.
Related
I have a tansaction table and have a field called balance. Here I am trying to update this field with new data but insert in new transaction. So I need balance field in controller. So, I have tried below code.
$client_id = $this->request->data['Transaction']['user_id'];
$new = $this->Transaction->find('first', array(
'conditions'=>array('Transaction.user_id'=> $client_id),
'fields'=>array('Transaction.balance')
));
$this->request->data['Transaction']['balance'] =$this->request->data['Transaction']['debit_money']-$this->request->data['Transaction']['credit_money']+new;
But here I am not getting old balance data.
$new is an associative array. You are adding an array to a number.
Try the following:
$client_id = $this->request->data['Transaction']['user_id'];
$new = $this->Transaction->find('first', array(
'conditions'=>array('Transaction.user_id'=> $client_id),
'fields'=>array('Transaction.balance')
));
$this->request->data['Transaction']['balance'] =
$this->request->data['Transaction']['debit_money']
- $this->request->data['Transaction']['credit_money']
+ $new['Transaction']['balance'];
Alternatively, this should also work:
$client_id = $this->request->data['Transaction']['user_id'];
$this->request->data['Transaction']['balance'] =
$this->request->data['Transaction']['debit_money']
- $this->request->data['Transaction']['credit_money']
+ $this->Transaction->field('balance',array('Transaction.user_id'=> $client_id),
));
I have problem,when I tried to update except row of null in CakePHP.
Model -> Spot
column -> name,address
[MySQL]
id / name / address
1 / Shibuya Bakery / Shibuya street 123
For example,there is database like the one above.
Then CakePHP got a name-value(Shibuya Cake Shop) from Android,and address-value is null.
Therefore I wanna update just name-column.
id = 1(post from Android)
public function update()
{
if( isset($this->request->data["id"]) )
{
$id = intval($this->request->data["id"]);
$fields = array( 'Spot.id' => $id );
$data = array( 'Spot.name' => "'".$this->request->data["name"]."'",
"Spot.address" => "'".$this->request->data["address"]."'");
$this->Spot->updateAll( $data,$fields );
}
}
If you just want to update just one field, just use saveField.
$this->Spot->id = $this->request->data['id'];
$this->Spot->saveField('name', $this->request->data['name']);
Don't use updateAll, as that is meant for updating multiple records, though technically you could add conditions to prevent it from doing so. You're also passing incorrect arguments into updateAll. (See http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-updateall-array-fields-mixed-conditions for the correct way.) If you want to update multiple fields at once on a single record, use save:
public function update()
{
if( isset($this->request->data["id"]) )
{
$fields = array('name');
if(!empty($this->request->data['address'])) array_push($fields, 'address');
$this->Spot->save($this->request->data, true, $fields);
}
}
Read more on saving: http://book.cakephp.org/2.0/en/models/saving-your-data.html#saving-your-data
OK, this is the situation. In my beforeSave function I want to manipulate some $this->request->data entries.
This is my component:
<?php
App::uses('Component', 'Controller');
class GetStationComponent extends Component {
public function getStationId ($station) {
$stationInstance = ClassRegistry::init('Station');
$conditions = array('OR' => array(
array('Station.code LIKE' => $station),
array('Station.naam LIKE' => $station),
array('Station.naam_overig LIKE' => $station)
));
$result = $stationInstance->find('list', array('conditions'=>$conditions));
$value = array_values($result);
$value = $value[0];
return $value;
}
}
?>
And this is my beforeSave function in my Controller:
public function beforeSave($options = array()) {
if (!empty($this->request->data['Experience']['vertrekstation']) && !empty($this->request->data['Experience']['aankomststation'])) {
$this->request->data['Experience']['vertrekstation'] = $this->GetStation->getStationId($this->request->data['Experience']['vertrekstation']);
$this->request->data['Experience']['aankomststation'] = $this->GetStation->getStationId($this->request->data['Experience']['aankomststation']);
}
return true;
}
It should return an ID of the stations name. But in the Database the name itself is stored (which is filled in by the user) instead of the ID. What do I need to change in my Component (I guess...) to return the right values?
(P.S. The query itself in the component returns an ID, because at first I'd put the 'beforeSave' directly into my function which saves the data, but then my validation error said that it wasn't a right value. Which is correct...)
To complement the other answers; to get just the value of a single field, use Model::field()
return $stationInstance->field('id', $conditions);
It is best to add a sort order to this statement to make sure that the results will always be returned in the same order:
return $stationInstance->field('id', $conditions, 'code ASC');
Since you only perform a single query on the Model, and don't do anything afterwards, you don't even need the intermediate $stationInstance variable. Your code can be further simplified to:
return ClassRegistry::init('Station')->field('id', $conditions, 'code ASC');
Some observations
Because of the 'fuzzy' matching on the name of the station, the first result may not always be the station intended by the user it's best to offer an 'autocomplete' functionality in your front-end and have the user pick the correct station (e.g. To prevent picking Den Haag when the user meant Den Haag HS)
If the station does not fully matches a station, you should present a warning that the station wasn't found
You didn't surround your search-terms with % for the LIKE queries. If you intend to search for 'name "contains", you should use '%' . $station . '%'. For "starts with" use $station . '%'
As #mark suggested; beforeSave() is a callback of the Model and should be located there.
Also; beforeSave() is triggered after validation has taken place, so it will probably be too late. beforeValidate() is the best callback for this
If the Experience model is already attached to the Station model, you don't need to use a component, because you can directly access the Station model. It's best to put the search-method inside the Station model;
Moving it all to the right(*) location
*) Other options are always possible, this is just a possible approach
Add the 'search' method to the Station-model;
app/Model/Station.php
public function getStationIdByName($name)
{
$name = trim($name);
if (empty($name)) {
return null;
}
$name = '%' . $name . '%';
$conditions = array(
'OR' => array(
array($this->alias . '.code LIKE' => $name),
array($this->alias . '.naam LIKE' => $name),
array($this->alias . '.naam_overig LIKE' => $name),
)
);
return $this->field('id', $conditions, 'code ASC');
}
..and use it in the Experience Model
app/Model/Experience.php
public function beforeValidate(array $options = array())
{
if (
!empty($this->data[$this->alias]['vertrekstation'])
&& !empty($this->data[$this->alias]['aankomststation'])
) {
// Directly access the Station-model from within the Experience Model
$this->data[$this->alias]['vertrekstation']
= $this->Station->getStationIdByName($this->data[$this->alias]['vertrekstation']);
$this->data[$this->alias]['aankomststation']
= $this->Station->getStationIdByName($this->data[$this->alias]['aankomststation']);
}
// Call parent-callback after setting the values
return parent::beforeValidate($options);
}
[UPDATE] Using the Conventions, prevent unwanted behavior
After writing the previous example, I noticed there are some flaws in your current setup;
If vertrekstation and aankomststation should hold the 'foreign key' of the station (the station-id) they are not named according to the CakePHP Model and Database Conventions
Because of 1) By putting this code inside the beforeValidate(), it will also be triggered when updating an existing record. Because you're using the aankomststation and vertrekstation field both to hold the name of the station (inside the Form) and the id (inside the database), the Model will attempt to look-up the station-id via the id when updating. NOTE that inside the form you'll still be using vertrekstation and aankomstation as field-name. These field names are not present in your database, and therefore will not be able to directly update data inside your database, that's where the beforeValidate() callback is used for
Because the Experience model needs two relations to the Station model (once as departure station ('vertrekstation'), once for arrival station ('aankomststation')), you will need an alias for the Station-model. See: Multiple relations to the same model
app/Model/Experience.php
class Experience extends AppModel {
/**
* Station will be associated to the 'Experience' Model TWICE
* For clarity, using an 'alias' for both associations
*
* The associated Models will be accessible via;
* $this->DepartureStation
* $this->ArrivalStation
*
* To stick to the CakePHP conventions, name the foreign keys
* accordingly
*/
public $belongsTo = array(
'DepartureStation' => array(
'className' => 'Station',
'foreignKey' => 'departure_station_id',
),
'ArrivalStation' => array(
'className' => 'Station',
'foreignKey' => 'arrival_station_id',
)
);
public function beforeValidate(array $options = array())
{
// vertrekstation and aankomststation hold the 'names' of the
// stations and will only be present if the form has been submitted
if (
!empty($this->data[$this->alias]['vertrekstation'])
&& !empty($this->data[$this->alias]['aankomststation'])
) {
// Directly access the Station-model from within the Experience Model
// using the *aliases*
$this->data[$this->alias]['departure_station_id']
= $this->DepartureStation->getStationIdByName($this->data[$this->alias]['vertrekstation']);
$this->data[$this->alias]['arrival_station_id']
= $this->ArrivalStation->getStationIdByName($this->data[$this->alias]['aankomststation']);
// Invalidate the vertrekstation and aankomststation fields if lookup failed
if (empty($this->data[$this->alias]['departure_station_id'])) {
// Unable to find a station. Mark the Form-field invalid
$this->invalidate('vertrekstation', __('A station with this name was not found'));
}
if (empty($this->data[$this->alias]['arrival_station_id'])) {
// Unable to find a station. Mark the Form-field invalid
$this->invalidate('aankomststation', __('A station with this name was not found'));
}
}
// Call parent-callback after setting the values
return parent::beforeValidate($options);
}
}
The find('list') option of Cake returns an array like
array( 1 => 'name1',
3 => 'name2',
//etc...
)
where the index is the id and the value is the display field you set on the model.
So, when you do $value = array_values($result);, you're extracting the values of the array (meaning, the display fields). I'm assuming you're not using the id as the displayField, so that's why it's returning the names and not the id.
I'm not sure why you're using find('list') instead of find('first') or other alternative, but if you don't want to modify that for whatever reason, the fix that should return the first id obtained by the search is
reset($result); //possibly not needed, but just in case
$value = key($result );
First you must understand how Cake works
There is no $this->request in your models. Its part of the controller.
In your model your passed data will be in $this->data directly.
public function beforeSave($options = array()) {
parent::beforeSave($options); // you also forgot the parent call
if (!empty($this->data[$this->alias]['vertrekstation']) && ...)) {
$this->data[$this->alias]['vertrekstation'] = ...;
}
return true;
}
Your find call also looks pretty screwed up. I dont know what you want to do.
But I strongly advice you to use debug() etc to find out what is returned and correct your code accordingly.
You probably need find(first) if you are only interesting in a single value.
I am trying to update a row in the database but haven't found a way to do this in the CakePHP way (unless I query the row to retrieve and update).
UPDATE mytable (field) VALUES (field+1) WHERE id = 1
In CodeIgniter, it would have been as simple as:
$this->db->set('field', 'field+1', FALSE);
$this->db->where('id', 1);
$this->db->update('mytable');
How do I do this without querying the row first, retrieve the value, then updating the row with the information I got?
I don't think CakePHP has a similar method for doing this in a normal save() on a single row.
But the updateAll() method, which updates multiple rows, does support SQL snippets like so:
$this->Widget->updateAll(
array('Widget.numberfield' => 'Widget.numberfield + 1'),
array('Widget.id' => 1)
);
The first param is an array of fields/values to be updated, and the second param are the conditions for which rows to update.
Apart from that I think the only thing is to use:
$this->Widget->query('YOUR SQL QUERY HERE');
Which lets you query with raw SQL. [EDIT: but this is not recommended as it bypasses the ORM.]
Try this
<?php
class WidgetsController extends AppController {
public function someFunction( $id = null ){
if( $id ){
// read all fields from the model
// alternately you can $this->Widget->read( array( 'field' ), $id );
$this->Widget->read( null, $id );
// grab the 'field' field so we don't have to type out the data array
$field = $this->Widget->data[ 'Widget' ][ 'field' ];
// where field is the name of the field to be incremented
$this->Widget->set( 'field', $field + 1 );
$this->Widget->save( );
}
// someday cake devs will learn to spell referrer
$this->redirect( $this->referer( ));
}
}
?>
Basically you are passing the id, if it exists you read the Widget model (see the notes above, null as 1st param read the entire table) and then you are using Model::set to st the field to a value one greater than itself - remember to cast to int if you store the field as a char/varchar - and then save the model.
I have two tables,Forms and Attributes. I'm tring to retrieve the last inserted id from the Forms table and insert it to the form_id column of the Attributes table, along with the other field columns.
Earlier I retrieved the form Id from the Forms table and used it to update the value of the Form name column. It worked fine.The code for that is given below:
function saveFormName($data)
{
$this->data['Form']['formname']=$data['Form']['formname'];
$this->data['Form']['id']=$this->find('all', array(
'fields' => array('Form.id'),
'order' => 'Form.id DESC'
));
$this->id=$this->data['Form']['id'][0];
$this->saveField('name',$this->data['Form']['formname']);
}
But When I tried to do it in a similar way for updating the attributes table,the row is not saved in the database,since the value of $this->data['Form']['id'][0] is an 'Array'.
Even in the saveFormName function, the value of $this->data['Form']['id'][0] is an 'Array',but the form name gets updated correctly. Someone explain me the concept.
function saveFieldEntries($data)
{
$this->data['Form']['id']=$this->find('all', array(
'fields' => array('Form.id'),
'order' => 'Form.id DESC'
));
$this->data['Attribute']['form_id'] = $this->data['Form']['id'][0];
$this->data['Attribute']['label']= 'Label';
$this->data['Attribute']['size']='20';
$this->data['Attribute']['type']=$data['Attribute']['type'];
$this->data['Attribute']['sequence_no'] = $data['Attribute']['sequence_no'];
$this->Attribute->save($this->data);
}
EDIT:
Ok, here is the corresponding code in the controller.
function insertFormName()
{
$this->data['Form']['formname']=$this->params['form']['formname'];
$this->Form->saveFormName($this->data);
}
function insertFieldEntry()
{
$this->data['Attribute']['type']=$this->params['form']['type'];
$this->data['Attribute']['sequence_no'] = $this->params['form']['sequence_no'];
$this->Form->saveFieldEntries($this->data);
}
The parameters form name,type and sequence no are passed to the controller from the corresponding view file.
$this->data['Form']['id'][0] holds an array because find('all') returns an array.
So if you need first ID from this array, you need to pick it properly in function saveFieldEntries:
...
$this->data['Attribute']['form_id'] = $this->data['Form']['id'][0]['Form']['id'];
...