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.
Related
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
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
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()
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)
I have a nodes table (Node model). I'd like it to be associated to different data types, but only if one if it's field is set to 1.
Example:
My nodes table has a data_article field (tinyint 1). I only want the Node to $hasMany Article IF that field is a 1.
I tried this:
public $hasMany = array(
'Article' => array(
'conditions' => array('Node.data_articles' => '1')
),
);
But I get an error:
Column not found: 1054 Unknown column 'Node.data_articles' in 'where
clause'
Because the association is doing the Article find in it's own query:
SELECT `Article`.`id`, `Article`.`title`, `Article`.`node_id`, ...more fields...
FROM `mydatabase`.`articles` AS `Article`
WHERE `Node`.`data_artiles` = '1'
AND `Article`.`node_id` = ('501991c2-ae30-404a-ae03-2ca44314735d')
Obviously that doesn't work, since the Node table isn't being Joined at all in this query.
TLDR:
Is it possible to have associations or not based on a field in the main model? If not, how else can I keep different data types in multiple tables, and not have to query them all every time?
I don't think that there's a standard way to do this with CakePHP (at least I can't imagine a way). What definitely is possible would be binding associated models dynamically.
So you might query your model without associations by passing the recursive parameter as -1 to the find() method and based on the result unbind the associated models dynamically. Afterwards you would have to query again, for sure. You might build a behavior out of this to make it reusable.
A quite elegant solution would be possible, if Cake could make a two-step query with a callback after the main model was queried, but before associated models are queried, but this isn't possible at the moment.
Edit: There might be a non-Cake way to archieve this more performantly with a custom Query including IF-statements, but I'm not the SQL expert here.
You should do it from the other side:
public $belongsTo = array(
'Node' => array(
'conditions' => array('Node.data_articles' => '1')
),
);