A model that I am using with both TranslateBehavior and TreeBehavior is failing to save. I suspect it is something to do with one of these behaviors. Here's the data going in:
array(
'Category' => array(
'id' => '3252',
'parent_id' => null,
'sort_order' => '0',
'name' => array(
'eng' => 'english name',
'fra' => 'french name',
'deu' => 'german name',
'ita' => 'italian name',
'spa' => 'spanish name'
),
)
)
And $this->Category->validationErrors is:
array(
'parent_id' => array(),
'sort_order' => array()
)
There are no validation rules defined in the model. There is no beforeSave() or beforeValidate() method defined.
I am not getting any validation errors displayed on screen, although all fields have the red "error" outline.
Edit - the save operation is not getting as far as Category::beforeSave(). I created that function and it does not get run. It gets as far as Category::beforeValidate().
$validates = $this->Category->validates($this->request->data);
debug($validates);
$saved = $this->Category->saveAll($this->request->data);
debug($saved);
In the above code, $validates is true, $saved is false. Category beforeSave is not called at any point in the above process. The validation seems to fail on the call to saveAll(). I need to use saveAll rather than save to save all translations at once (I am doing this elsewhere with another model with no problems).
So, after a while debugging I have found the problem:
public $hasMany = array(
'Category' => array('className' => 'Category', 'foreignKey' => 'parent_id', 'counterCache' => true),
...
I have no idea why I wrote this - I should have been aware that it was going to cause problems, I think I meant to write...
public $hasMany = array(
'Children' => array('className' => 'Category', 'foreignKey' => 'parent_id', 'counterCache' => true),
...
Anyway, changed it to the latter and these errors have gone.
Maybe it doesn't like the null and zero value of parent_id and sort_order? Also in the database what are their field types set as? Do they allow null values? etc. I'm guessing that as there are no validation rules in the model or parent/App model, then it must be some default validation with cake's lib linking to the database/mysql table itself. So I would check the Categories table structure for the parent_id and sort_order fields.
Related
I have the following models:
Company(id, name)
Employee(id, name, company_id, isRemoved) [Company has many Employees]
In the association specified, the employee has a default condition, that
public $hasMany = array(
'Employee' => array(
'className' => 'Employee',
'foreignKey' => 'company_id',
'dependent' => true,
'conditions' => array(
'Employee.isRemoved' => 0
),
)
);
The association has a default condition of an employee being not removed. I am using the following Find Query on company to get only those employees whose name matches a string:
$this->Company->find('all', array(
'contain' => array(
'Employee' => array(
'conditions' => array(
'Employee.name LIKE' => '%'.$search_text.'%')
),
'fields' => array('Employee.id, Employee.name')
)
)
));
The problem I am facing is that, When I use conditions within contain, the default condition specified in the association is not applied and when the conditions key is not specified, the default condition specified in the association is applied.
Is this a default behaviour of Cakephp and How to proceed about it? I am using Cakephp 2.8.4
I can not tell you if the conditions in the model being overwritten is default behaviour of CakePHP. I can however offer you a possible alternative:
By using the beforeFind() callback in your model you could add your 'Employee.isRemoved' => 0 condition.
So in your Company model you could do something like:
function beforeFind(array $queryData) {
if(isset($queryData['contain']['Employee'])) {
//Notice the extra [] to not overwrite the conditions set in the controller
$queryData['contain']['Employee']['conditions'][]['Employee.isRemoved'] = 0;
}
return $queryData;
}
Disclaimer: I did not test this code.
Source: https://stackoverflow.com/a/17544106/6786476
I came across this post that gave an answer on how to do this but it's not quite working for me.
I have a model called SitePage which has many SitePageGroup which in turn has many SitePageContent
// SitePage Model
public $hasMany = array(
'SitePageGroup' => array(
'className' => 'FoCMS.SitePageGroup',
'foreignKey' => 'site_page_id',
'dependent' => FALSE,
),
);
// SitePageGroup Model
public $belongsTo = array(
'SitePage' => array(
'className' => 'FoCMS.SitePage',
'foreignKey' => 'site_page_id',
),
);
public $hasMany = array(
'SitePageContent' => array(
'className' => 'FoCMS.SitePageContent',
'foreignKey' => 'site_page_group_id',
'dependent' => FALSE,
),
);
// SitePageContent Model
public $belongsTo = array(
'SitePageGroup' => array(
'className' => 'FoCMS.SitePageGroup',
'foreignKey' => 'site_page_group_id',
),
);
Using the answer in that linked question I am seeing the parent model, SitePage being duplicated, but the associated models are being removed from the original and associated with the new one.
$record = $this->SitePage->find('first', array('condition' => array('SitePage.id' => $id)));
unset($record['SitePage']['id'], $record['SitePageGroup']['id'], $record['SitePageGroup']['SitePageContent']['id'] /* further ids */);
$this->SitePage->create();
$record['SitePage']['name'] = $record['SitePage']['name'].'-copy';
$record['SitePage']['friendly_name'] = $record['SitePage']['friendly_name'].' Copy';
if($this->SitePage->saveAll($record)){
$this->Session->setFlash('The site page has been saved', 'fo_message');
$this->redirect(array('action' => 'index'));
}else{
$this->Session->setFlash('The site page could not be saved. Please, try again.', 'fo_message');
}
Update
Debugging the record that I'm trying to reset I see the following
array(
'SitePage' => array(
'name' => 'test',
'friendly_name' => 'Test',
'order' => '82',
'created' => '2015-09-03 19:16:40',
'modified' => '2015-09-03 19:20:27'
),
'SitePageGroup' => array(
(int) 0 => array(
'id' => '55e88087-a4dc-4c37-89dc-f9c172b40463',
'site_page_id' => '55e88078-16c8-46ce-bf02-fa5372b40463',
'name' => 'group-1',
'friendly_name' => 'Group 1',
'order' => '1',
'created' => '2015-09-03 19:16:55',
'modified' => '2015-09-03 19:16:55'
),
(int) 1 => array(
'id' => '55e8809e-d018-4ebe-a4cf-fbef72b40463',
'site_page_id' => '55e88078-16c8-46ce-bf02-fa5372b40463',
'name' => 'group-2',
'friendly_name' => 'Group 2',
'order' => '2',
'created' => '2015-09-03 19:17:18',
'modified' => '2015-09-03 19:17:18'
)
)
)
The way I am getting this result is by doing this
$sitePage = $this->SitePage->find('first', array(
'conditions' => array(
'SitePage.id' => $id,
),
));
unset($sitePage['SitePage']['id'], $sitePage['SitePageGroup']['id'], $sitePage['SitePageGroup']['SitePageContent']['id'], $sitePage['SitePageGroup']['site_page_id'], $sitePage['SitePageGroup']['SitePageContent']['site_page_group_id'] /* further ids */);
debug($sitePage);
die();
But also also as you can see in the debug output the 3rd level of associated models are not being included, each of the SitePageGroup should also have a SitePageContent
I think a simple loop over the array of SitePageGroup should reset the id's and set the foreign keys to null, but I guess I also need to somehow include the SitePageContent that belongs to the SitePageGroup so I can reset those as well.
You need to ensure that all primary and foreign keys are set to null before saving. You only appear to be resetting the primary keys of your models but Cake needs to know that the foreign keys need generating so that they reference the new records.
Before calling $record it might be worth using debug($record); to check that everything in that array has been set/reset appropriately to ensure the copy will work as expected.
Update
Based on the array contents you've posted in your updated question it appears that you are not removing all the primary and foreign keys from your save data. You need to make sure that these are removed from everything you are about to save including the has many associations.
If you look at your array you should be able to see that unset($sitePage['SitePageGroup']['id']) will not remove the primary IDs of your SitePageGroup data as what you are unsetting doesn't correspond to array paths in your $sitePage array.
You can use CakePHP's Hash utility to remove the primary keys from the array like this:-
$sitePage = Hash::remove($sitePage, 'SitePageGroup.{n}.id');
And similarly for the foreign keys:-
$sitePage = Hash::remove($sitePage, 'SitePageGroup.{n}.site_page_id');
Hey my problem is to define a condition for a HABTM Model
(int) 0 => array(
'Article' => array(
'id' => '',
'title' => '',
'body' => '',
'created' => null,
'modified' => null
),
'Channel' => array(
'id' => '',
'channelname' => '',
'created' => null,
'modified' => null
),
'Tag' => array(
(int) 0 => array(
'id' => '1',
'value' => 'example',
),
(int) 1 => array(
[maximum depth reached]
),
(int) 2 => array(
[maximum depth reached]
)
),
),
I want to define a condition that searches the Tag values for specific values .. like that:
$this -> Article -> find('all', array('conditions' => array('Tag.0.value' => 'test'))
So does someone knows how to "foreach" that array in this condition? thanks (:
You either need to:
Run your find from the other direction (using CakePHPs awesome Containable Behavior to get the Articles):
$this->Tag->find('all', array(
'conditions' => array(
'Tag.value' => 'test'
),
'contain' => array(
'Article'
)
));
Or, use JOINs. Run your find like you're doing, but do an INNER JOIN on tags where the tag value='test'
You cannot limit the main Model's results based on an associated model's conditions. The reason is, when you rely on recursive, or use Contain, CakePHP actually creates separate queries to pull in your different models' data. Therefore, you can't have a condition in one query affect the other query.
See similar question/answers:
How do I restrict query results based on sub-results in CakePHP?
Conditions in JOINed tables shows error CakePHP
cakephp contain filtering by parent model value
cakephp contain association conditions issue
Adding conditions to Containable in CakePHP
I am using containable with CakePHP. My tried code is ...
public function detail($slug = "") {
$this->Poet->contain('User.id', 'User.full_name', 'Song.id', 'Song.name', 'Song.name_hindi', 'Song.slug');
$result = $this->Poet->findBySlug($slug);
if (!$result) {
throw new NotFoundException(__('Invalid Poet - ' . $slug));
}
pr($result);
die();
$this->Poet->id = $result['Poet']['id'];
$this->set('result', $result);
}
Like this. Now I have Song.status as my association with Song table. I want to fetch only those records that has status = 1. Is it possible? Can I select only active records with my piece of code.
Use a normal find
While the magic findBy* methods are handy from time to time, it's a good idea to only use them for trivial queries - your query is nolonger trivial. Instead use a normal find call e.g.:
$result = $this->Poet->find('first', array(
'contain' => array(
'User' => array(
'id',
'full_name'
),
'Song' => array(
'id',
'name',
'name_hindi',
'slug',
)
),
'conditions' => array(
'slug' => $slug,
'Song.status' => 1 // <-
)
));
Does a Poet hasMany songs?
You don't mention your associations in the question, which is rather fundamental to providing an accurate answer, however it seems likely that a poet has many songs. With that in mind the first example will generate an sql error, as there will be no join between Poet and Song.
Containable does permit filtering associated data e.g.:
$result = $this->Poet->find('first', array(
'contain' => array(
'User' => array(
'id',
'full_name'
),
'Song' => array(
'id',
'name',
'name_hindi',
'slug',
'Song.status = 1' // <-
)
),
'conditions' => array(
'slug' => $slug
)
));
This will return the poet (whether they have relevant songs or not), and only the songs with a status of "1". You can achieve exactly the same thing by defining the condition in the association definition (either directly in the model or by using bindModel).
how to get name of (UserTransactionType.name) with Transaction.who_pay_fee_1,2,3 fields.
'user_transaction_type_id' works well but how to get the rest of fields work :(
//Transaction Model
public $belongsTo = array(
'UserTransactionType' => array(
'className' => 'UserTransactionType',
'foreignKey' => 'user_transaction_type_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
//UserTransactionType Model
public $hasMany = array(
'Transaction' => array(
'className' => 'Transaction',
'foreignKey' => 'user_transaction_type_id',
'dependent' => false,
))
This is the sample code for your controller:
$this->UserTransactionType->find('all',array(
'fields' => array('name'),
'contain' => array('Transaction')
)
);
If Models are associated you can specify in 'contain' which of them you want to get in the result.
If you want to have only some fields of related Model, you can determine them after 'Transaction' in 'contain' just like in the regular find() query:
'contain' => array('Transaction' => array('fields' => array('field_1',
'field_2') ))
But in your case, you don't need to specify fields, because by default you get all fields.
So no matter if you define or not fields "who_pay_fee_1,2,3" because if you use 'contain' by default you will get foreing_key - user_transaction_type_id.
I hope it's helpful
For people they like CakePhp :)
in Controller ->
get the list of 'UserTransactionType'
in View ->
after looping trough all the transactions; in Transaction Status column simply load the 'UserTransactionType'array and assign the number of array to $userTransactionTypes.
$userTransactionTypes[$transaction['Who_pay_fee_1']];
To be honest it was straight forward but needed a bit concentration :)