How to change a ARO parent in CakePHP - cakephp

I am having trouble updating the aros table in my CakePHP app. Here is the code I've got:
$aro = new Aro();
$targetAro = $aro->find('first', array('conditions' => array('foreign_key' => intval($user['User']['id']))));
$newParent = $aro->find('first', array('conditions' => array('alias' => $userUpdate->level)));
$targetAro['Aro']['parent_id'] = $newParent['Aro']['id'];
$aro->updateAll($targetAro);
I've put in debug log statements to make sure that I'm getting the correct results from the find calls. But when I check the database after the updateAll call, the data is unchanged. Any ideas what might be going on?
I also tried calling $aro->setParent but I couldn't get that to work either.

I found the problem by tracing through the code that does a similar thing in the cake console. Instead of using $aro->updateAll($targetAro) I should be using $aro->saveAll($targetAro).
updateAll() is used to save changes to a number of entities. The first argument should be an array of values. saveAll() is used when you just want to save the changes to a single entity, as is the case in the code above.
Althought it seems like I should, I couldn't find error output anywhere in the logs or the script output because of the incorrect arguments I was passing to updateAll().

Related

CakePHP actsAs Translate and $Model::find()

I have attached the Translate behavior to one of my models and I have some shortcomings regarding this:
1) If I don't save data in all fields passed as params when attaching the behavior to the model, $Model::find() method doesn't get the inserted rows.
public $actsAs = array(
'Translate' => array(
'title' => 'title_Translation',
'description' => 'description_Translation',
'description_long' => 'description_long_Translation'
)
);
Ex: if i pass to $Model::save() method only a value for 'title', the data is saved, even in the i18n table, but the $Model::find() doesn't get anything. I must pass data for all the fields.
Can I force it to retrieve those records ?
2) How can I get all the records in the admin side of the application (regardless of the language in which a record is saved) in order to list them so the user can alter it (edit data, save data in multiple languages)? Right now, I can only get the records that correspond to the current language (read from Configure or set explicitly)..
Thank you!
I kind of solved it, I copied the TranslateBehavior to app/Model/Behavior (just to avoid problems on future upgrades and keep the original one just in case) then I changed the _addJoin(...) method of the behavior, just changed the join type from INNER to LEFT on line 255 (I use cake 2.2.3).
Now if a record exist it is always retrieved, even if translated fields are missing.
Don't see any drawbacks besides the need to check if the translation field is empty.
OK, I might be a bit late, but anyway...
1) Cake uses an INNER JOIN when fetching a row and it's associated translations, so basically there's no easy way around this. You have to make sure you save every translatable field, every time - even if you just save it as blank. The only alternative would be to go hacking round the core to make it use a left join rather than an inner join - but don't do that.
2) The cookbook explains how to fetch all records here: http://book.cakephp.org/2.0/en/core-libraries/behaviors/translate.html#retrieve-all-translation-records-for-a-field
Now, probably most of the time you want to get just one translation, so you don't want to modify the definition of your $actsAs['Translate'] array in your model. So what I did, was set up a method in AppModel.php which modifies the $actsAs['Translate'] array on the fly:
/*
* See http://book.cakephp.org/2.0/en/core-libraries/behaviors/translate.html#using-the-bindtranslation-method
* This is for making it so we fetch all translations, as opposed to just that of the current locale.
* Used for eg. editing (multiple translations) via the admin interface.
*/
public function bindAllTranslations(){
$translatableFields = $this->actsAs['Translate'];
$keyValueFields = array();
foreach($translatableFields as $field){
$keyValueFields[$field] = $field.'Translation';
}
$this->bindTranslation($keyValueFields,false); // false means it will be changed for all future DB transactions in this page request - and won't be reset after the next transaction.
}
So, if it's an admin method (or any other situation you want all translations) you call that code before doing a find:
$this->MyModel->bindAllTranslations();
$this->MyModel->find('all');
Hope that helps!
Not exactly sure if it will help in your case, but you can also use
array to set locale before you call find()
$this->YourModel->locale = array("ENG", "GER", "JAP");
This way you will always get all records even if they don't have all possible translations.
Thanks a lot eleonzx, I'm having this problem since a decade, and with your simple answer I can now move forward ! So thanks again.
And maybe this code can help a lot of people :
in my AppController beforeFilter method I call _setLanguage
private function _setLanguage() {
if($this->Session->read('Config.language')){
$locale = $this->Session->read('Config.language');
$this->{$this->modelClass}->setLocale($locale);
}else{
$this->{$this->modelClass}->Behaviors->disable('Translate');
}
}
With the else condition I disable the Translate Behavior on the fly to get the original contents if there is no locale set in the session (I use basic links to switch between languages).

Getting related data using Wizard Component

The short version of this question is:
How can I take data that only exists in an array (ie: not saved in a model yet) and relate it to a value in a $this->find('list') array from a model?
The long version of this question is:
I'm using CakePHP and the Wizard Component for a 3-step application form.
The 3 form steps are contact, course and details.
After these steps, there is a 'review' step which I want to display all of the submissions from the previous 3 form steps for the user to check one last time before pressing submit. For the most part, this works very well. I just need to do the following in the controller:
function _prepareReview() {
$contact = $this->Wizard->read('contact');
$course = $this->Wizard->read('course');
$details = $this->Wizard->read('details');
$this->set(compact('contact','course','details'));
}
Then, in review.ctp I can reference things like $contact['Contact']['firstname']; to get the person's firstname etc.
However, the problem is getting data from 'related' models. For example, there's a "Nationality" field which is just an ID. In the 'details' step, I use find('list') to get a list of all nationalities from the Nationality model as a dropdown menu which displays properly and then Cake saves the corresponding ID as it should do.
But, when I come to the 'review' step in the Wizard Component, I only get the actual ID from the Wizard Component's array. I couldn't really expect to get anything else.
I can't see any obvious way to access $details[Detail][Nationality][name] (or something like this) from the context of the Wizard Component because setting recursive doesn't work because the data isn't actually in the model at this stage, it's just an array of form data.
So, in other words, I have data in an array (NOT coming from a model, but from a form subsmission) as follows:
Array
(
[Details] => Array
(
[firstname] => Test
[nationality_id] => 3
)
)
Then I have the following coming from a $this->Detail->Nationality->find('list') which looks like this:
Array
(
[0] => American
[1] => Australian
[2] => British
[3] => Canadian
)
So how can I get $details['Details']['nationality_id']; from the Wizard Component to display 'Canadian' instead of '3' for example? How to I make the relationship when only one of the arrays is coming from a model? I only need this to momentarily confirm all of the data to the user. The id '3' will, of course, be written to the Model once the user presses submit, and this is already working as it should do.
Or is this a completely crazy way of doing things and I should look at a totally different approach such as saving the data first? I don't really want to save it until the user clicks the final submit, though.
I can see what you are getting at here - CakePHP isn't automatically querying these related models for you (as you aren't pulling from the database) but you can't help but think you are missing out on some of the framework's free functionality.
If you were still using FormHelper::input() it would automatically select the correct option (given you perform a Model::find('list') and passed the options list to the view first), but I'm assuming you wish for the review screen to be free of form inputs (disabled or not).
The most straightforward approach would be to simply perform the same Model::find('list') calls you do for each step in the wizard, set the data from each to the view, and print out the appropriate value manually:
// controller
$nationalities = $this->Review->Details->Nationality->find('list');
$this->set(compact(/*..., */ 'nationalities'));
// view
<?php echo $nationalities['Nationality'][$details['Detail']['nationality_id']]; ?>
outputs 'Canadian' (the value for $nationalities['Nationality'][3])
It might be possible to get CakePHP to do it for you by calling DboSource::queryAssociation() just right - if your up for the challenge - but is probably overkill for this particular problem.
// model
$db =& ConnectionManager::getDataSource($this->useDbConfig);
$data = $db->queryAssociation($model, $linkModel, $type, $association, $assocData, $queryData, $external, $resultSet, $recursive, $stack) {

How to creating ACOs?

I think I have undestood the bases of almost everything in CakePHP until the Access Control Lists, now I'm on 11.2.5 Creating ACOs (Access Control Objects) and I don't understand where I have to put the AclComponent methods:
$this->Acl->Aco->create(array('parent_id' => null, 'alias' => 'controllers'));
$this->Acl->Aco->save();
In which file I should insert this code?
Have I to inserti in some specific statement?
I don't undestand what this line does exactly, so I can't locate where and when it should run, what this code does, and when It should work?
The idea with the ACO records creation is that if your project is over (meaning no new actions are created), you don't need to insert anything anymore in the acos datatable. These methods are intended to be called once, or only a few times.
It is then up to you to decide where you want to place these lines of code, as anyway you will probably remove them afterwards.
Alternatively, to fill the acos datatable, you could also use this plugin http://www.alaxos.net/blaxos/pages/view/plugin_acl that automatically detects new actions and propose to complete the acos datatable accordingly.
you can insert that wherever you need to create Aco-s. in app_controller, or in any controller.
This page in the Cookbook gives you a very nice automated tool for creating ACOs:
http://book.cakephp.org/view/647/An-Automated-tool-for-creating-ACOs
If you add the build_acl function to the AppController, you can run it from any controller and it generates your ACOs for you. It's always worked very nicely in my ACL-using applications.

How to use sql 'between' command with CakePhp requestAction()

I have an element that grabs data from mysql. Here is my working code:
$this->requestAction('posts/index/sort:id/direction:desc');
I want to grab only posts between id 1 and 6. How can I run that query via requestAction? Some of scripts that I have tried are below. None is working:
$this->requestAction('posts/index/sort:id/direction:desc', array('id between ? and ?' => array('1,6')));
or
$this->requestAction('posts/index/sort:id/between:1,6/direction:desc');
You may see my project at http://bake.yemeklog.com/ I want this code for third column (Last 30 days faves)
If I was going to be calling it via requestAction (!) then I would write a custom method in my controller, then I'd probably pass the two id's into that method as parameters.
Then you can process the parameters and formulate your query.
$this->Model->find('all', 'conditions' => array('id'=>array(1,2,3,4,5,6)));
Not ideal by any means, but I'm not quite sure how else I would approach this kind of problem.
If it's static id's then perhaps
$this->Model->find('all', null, null, 'order' => 'id ASC LIMIT 0,6');
** Now I'm not sure if that would work as I haven't tried it, but I often hacked little things like this into Cake with some success. So perhaps give it a try, be sure to set debug = 2 so you can see the query, or grab DebugKit from OhLo
PS, Dont forget if you write requestAction methods, to check that $this->params['requested'] is true, so you know it come from a requestAction

CakePHP 1.3 controller save() does not save data

I want to add a new user to the database, and do so by making them fill in a form and submitting this. All seems to work fine. Just before my save() call in the controller I return all data and all necessary data is there. It is valid(ated), since no errors are returned.
But then nothing happens. I'm being returned to my form without any errors being shown.
This is my 'save-line':
if($this->Registratie->save($this->data)) {
I'm not making use of any beforeSave() methods.
Using debug($this->validationErrors); shows:
app/controllers/registratie_controller.php (line 45)
Which is the line of code from above.
I've been going through my code over and over. What could the problem be?
When you create a form using the FormHelper it will generate input names like:
<input type='text' name='data[Registratie][first_name]'>
Once the form is submitted cake will push that into the $this->data array with an index of 'Registratie'
You probably need to pass the index to the model when saving
if ($this->Registratie->save( $this->data['Registratie'] ) ) {
I would also do a var_dump($this->data) or print_r($this->data) to make sure your form fields are coming through.
I had the same problem, fixed doing exactly what Jack B Nimble told. Using CakePHP 1.3
Sample:
Model: Contacts
$this->data['contact']

Resources