How to update an existing record in CakePHP? - cakephp

I am using CakePHP and following its tutorial. I want to update a record but when i do its create another record not updating. according to tutorial my code is given below
$data = array('Id' => $id, 'Approved' => 12);
$this->names->save($data);
it results in
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry
4 for key PRIMARY
And if I do this
$this->names->Id=$id;
It adds a new record. How should I fix this ?

$this->names->id=$id;
$this->names->set(array('Approved'=>12));
$this->names->save();

The key must be id and not Id. If in your table you can't use id (lowercase) and you have to use Id (uppercase) then you have to set it in your Model file
also you are not followeing the conventions: the model should be Name and not names (singular and CamelCase)

Unless you're intentionally not following Cake naming conventions (with a strong reason), you should stick to it. That means, models should be capitalized-singular named, and table fields should be lowercase. Also, your data array has to have the name of the model you want to save.
So:
$data = array('Name' => array('id' => $id, 'approved' => 12));
$this->Name->save($data);

To update a single field value when you have the primary key available, saveField() is also available.
Quoting from documentation:
Model::saveField(string $fieldName, string $fieldValue, $validate = false)
Used to save a single field value. Set the ID of the model
($this->ModelName->id = $id) just before calling saveField(). When
using this method, $fieldName should only contain the name of the
field, not the name of the model and field.
For example, to update the title of a blog post, the call to saveField
from a controller might look something like this:
$this->Post->saveField('title', 'A New Title for a New Day');
It also has facility for passing parameters like validate, callbacks etc using an alternate syntax:
saveField(string $fieldName, string $fieldValue, array $params = array())
References: Documentation, API

Related

Cakephp find('list') to get displayField from child model

I have a master model, with a child model. The child model has a foreign key that links to the master model id, following cakephp naming conventions. The child model defines the name/description of the records in the master model.
I'm trying to populate a dropdown listbox in my view with these values (id and name). But the Cakephp find('list') usually gets the information from one model only.
How can I do it so that my find('list') retrieves 2 fields such as :
- child.master_id
- child.name
or
- master.id
- child.name (where child.master_id = master.id).
Thanks!
To change the fields for the keys or values of find('list', $params) you can hand an array with the key fields and the names of the fields you want to use as the second parameter over.
In your example the following code would change the key of the returned array to child.master_id and the value to child.name:
$childs = $this->Child->find('list', array(
'fields' => array('Child.master_id', 'Child.name')
));
For further information on the find type 'list' read the cookbook.
Just define the fields
For the examples given in the question you need only specify the two fields in the find call:
$result = $Child->find('list', array(
'fields' => array('master_id', 'name')
));
When find list is passed two fields, they are used as the key and value of the resultant array.
If you do need to return fields from different models, you need to tell cake to make a join. The simplest way is to specify a recursive value of 0 (Assuming the relationship Child belongsTo Master has been setup correctly):
$sameResult = $Child->find('list', array(
'fields' => array('Master.id', 'Child.name'),
'recursive' => 0
));
Note that in this scenario it is probably necessary to specify the model name in the field list to avoid ambiguous sql being generated.

CakePHP 2 - Deriving table name of model within Behavior

Whats the usual practice in getting a behavior (linked to multiple models) to build filters for SQL queries, and then read the table that belongs to that model?
I have a Behavior function which is meant to do a database query with certain SQL conditions. I currently pass in the $this->request->data.
I have issues building the SQL conditions because i'm not sure how to derive the name of the table (that corresponds to the model). See below for example, I want to change "BillingCenterDetail" which is the table name (and also the model name), to something generic I can use across different models. I want this table name to be derived automatically based on the model name. I'm not sure if i can use the $model reference for that.
public function saveWithTimeConstraintCheck(Model $model, $data) {
//FIND ALL RECORDS THAT OVERLAP
$overlapfilter = array(
'BillingCenterDetail.billing_center_id =' => $data['BillingCenterDetail']['billing_center_id'],
'BillingCenterDetail.startdate <=' => $data['BillingCenterDetail']['enddate'],
'BillingCenterDetail.enddate >=' => $data['BillingCenterDetail']['startdate']
);
... after building the filter, I can use $model->find to execute the query, this should be OK because its generic.
$overlapresults = $model->find('all', array('conditions' => $overlapfilter));
I've answered my own question.
And actually to build filter conditions, I needed name of the model, not the name of the table, because the name of the table is a plural name with the "S" at the end.
I used
$Model->name
From:
CakePHP: get current model name in a controller
For table names i found out u can also use
$this->Model->table
cakephp - get table names and its column details

CakePHP how to create hasMany data without knowing the foreign key in one saveAll?

I was hoping to create and save the associated data like what I did with belongsTo data - the associated data are created on the fly and foreign ID is also generated and saved on the fly by a single call to saveAll or saveAssocated which has transaction built-in.
But this seems not the case for data in hasMany relationship. Take User and Comment as example. Comment has user_id as the foreign key.
It seems that I cannot save User $data by using single saveAll($data) on User
Array(
'name' => 'Jack',
'email' => 'jack#abc.com',
'Comment' => array(
array(
'title' => 'I like this article.'
)
)
)
I read some docs. It seems that I need to mention the user_id as the foreign key for the Comment data to create correctly.
If that's the case, since I don't have user ID until it's created, it seems that I need to code to let SAVE happen twice.
I really think that I am missing something. There should be a CAKE way for doing this.
This is done automatically by Cake as long as you follow the conventions and format the data accordingly. For hasMany associations, the main model data, and the associated model data, need to be set on string keys on the same level, like
array
(
'User' => array(),
'Comment' => array()
)
Also note that
The saveAll function is just a wrapper around the saveMany and
saveAssociated methods. it will inspect the data and determine what
type of save it should perform. If data is formatted in a numerical
indexed array, saveMany will be called, otherwise saveAssociated is
used.
This function receives the same options as the former two, and is
generally a backwards compatible function. It is recommended using
either saveMany or saveAssociated depending on the case.
So either will do.
Long story short, the model data needs to be separated and indexed by string keys, it's essentially the same format as a find() call would return it. That way Cake will know that it needs to save associated data, and will automatically insert the foreign key for the Comment records.
array
(
'User' => array
(
'name' => 'Jack',
'email' => 'jack#abc.com'
),
'Comment' => array
(
array
(
'title' => 'I like this article.'
)
)
)
See also
Cookook > Models > Saving Your Data > Associations: Linking Models Together > hasMany
Cookook > Models > Saving Your Data > Model::saveMany()
Cookook > Models > Saving Your Data > Model::saveAssociated()
Cookook > Models > Saving Your Data > Model::saveAll()

CakePHP - Passing an array of record IDs to paginate - is it possible?

Due to having to having to import data from an old non cake app and oddly built database table I need to pass paginate an array of records it is allowed to display - is this possible?
Normally I would reorganise the data into proper relationships etc but due to time scales etc this is not possible.
To give you more info - in my users table I have a field that contains a list of ID's that relate to documents they are allowed to view. The field will contain something like
123,23,45,56,765,122,11.9,71,25
Each ID refers to a document the documents model. I know that normally you would create proper ACOs and AROs and let the ACL/Auth componant handle which users can access what but this isnt an option this time around. So I thought if I could do it via paginate/find it might be an option?
Any help would be really appreciated.
Thanks in advance
You wouldn't pass it a list of IDs, you'd use the IDs in your paginate conditions - something like below.
(Code written off top of my head, so pardon any syntax errors...etc. It should give you the right idea/path at least):
//Controller
$this->loadModel('User');
$this->loadModel('Document');
$user = $this->User->findById($userId);
$documentIds = explode(',', $user['User']['doc_ids']);
$this->paginate = array(
'conditions' => array(
'id' => $documentIds
)
));
$documents = $this->paginate('Document');
When you pass an array as a condtion (eg. 'id'=>$arrayOfIds), it uses MySQLs "IN" - something like:
... WHERE id IN (45, 92, 173)

cakephp id convention saveField issue

I have a "production" table with field employee_id as a belongsTo relation to employees table.
The problem is my employees table uses field emp_appserial as primary key -as opposed to id field- (not every entry on employees table gets an id value, everyone gets auto increment emp_appserial)
I wonder if there is a way to use saveField using a field other than id to get to my record: (I mentioned I have records with no "id" value, only emp_appserial, which is pk)
$this->loadModel('Employees');
$this->Employees->id = $id;
$this->Employees->saveField('emp_hrnote', 'text to be saved');
I'd like to use:
$this->Employees->emp_appserial = $id;
instead of
$this->Employees->id = $id;
I that doable?
Aside from that, it may not be too late to re-design my tables, but I already have a lot of production data :-(
Thank you for any pointers.
The following should do the trick (btw, models should be singular):
class Employees extends Model {
public $primaryKey = 'emp_appserial';
}
If you don't set the $primaryKey attribute, it gets set to id: Source
Once you set that saveField() will use that field for the condition: Source
Edit: Just to make it clear, you would still use Model::$id to set the primary key value. Model::$id holds the primary key value, Model::$primaryKey holds the primary key field
Used
$this->Employee->updateAll(
array('Employee.emp_hrnote' => "$thetxt"),
array('Employee.emp_appserial' => $id)
);
As a workaround is just fine, however I wish I could solve my empty id issue... will post another issue with this on another post.

Resources