Can You Access Model Data in Controller in CakePHP? - cakephp

This is probably a dumb question, but I can't find a definitive answer anywhere. Is it possible to access model data in a Controller, and if so, how?
I tried the following:
$this->set('mydata', $this->Model->find('all', 'conditions' => array('id' => 'someID')));
And accessing it via this in the controller:
$mydata['Model']['field']
But that appears to only be for the views.
Is there a way to access the model data in the controller? The reason is that I need to perform calculations on an associated model (belongsTo) that can't be done via hidden fields or anything because the ID of the associated model isn't passed until after the form is submitted.
Any help would be greatly appreciated!

Hmm, how about:
$myData = $this->Model->find('all', 'conditions' => /* ... */);
$myData['Model']['field'];
$myData['RelatedModel']['field'];
$this->set('mydata', $myData);
Simple enough :)
Model::find() returns your data, you don't need to pass it directly to Controller::set(), you can mess with it first and then pass it on to your views.
But, I'd advise against it, it's better if you have a Model::messWithData($data) and let models deal with data, and let controllers take care of application logic. Remember, fat models, skinny controllers!

Related

Choose models in related models

I have a model Post that have a lot of another models relationships.. in some part of the app I want select one post and just bring Comment model. I'm doing this:
$this->Post->find('fist', array('contain'=> 'Comment'));
Its working because is returning just Comment model and not a lot of another related models that i dont want.. the issue is. I want choose the models the Comment model will return, in this case I want the model User that is related to Comment model.
I want my array looks like that:
'Post'
title=> 'Title here'
'Comment'
text=> 'Comment here.. its good etc'
'User'
name=> 'Jason Miller'
The containable behavior can do deep associations. Assuming your Comment model is associated with User already, just do the following:
$this->Post->find('first', array('contain' => array('Comment' => array('User'))));

How to create a whitelist of updatable fields in a CakePHP's model?

I want to create a whitelist of fields that I want to be updatable in CakePHP. I know that I can pass a fieldList array in the call to Model::save(), but this isn't what I'm looking for. What I want is that every model "publish" a list of the valid fields, so if I call the Model::save() method without a fieldList and with data that mustn't be updatable (like the ownerId) this won't be updated.
What can I do to get this behavior? Maybe override the Model::save method in every Model to call at the "original" Model::save with the whitelist? I think this is a good idea, because I don't pollute all the controllers with lots of duplicated whitelists...
Thanks for your help!
Well, thanks you all for your answers, but I was wrong: I don't need to add this functionality.
The problem I was trying to solve was a security problem, I was trying to avoid form tampering (I've discovered the name just now), and as I am a novice CakePHP user, I didn't know that CakePHP already manages this problem.
The answer to my question is very easy: I must use CakePHP's Security Plugin and use the Form Tampering prevention (http://book.cakephp.org/2.0/en/core-libraries/components/security-component.html#form-tampering-prevention).
Can‘t say I’ve ever needed to do this in CakePHP, but CakePHP only saves the fields you pass it.
If you really need to create a white-list and you’re certain you only ever want these fields saving and never any others in your database (although I don’t understand why you have columns for them if you never touch them) then you could emulate this behavior in a model callback method:
<?php
class User extends AppModel {
public function beforeSave($options = array()) {
$whitelist = array('first_name', 'last_name', 'email');
foreach ($this->data[$this->alias] as $field => $value) {
if (!in_array($field, $whitelist)) {
unset($this->data[$this->alias][$field]);
}
}
return true;
}
}
This will just unset any data not in the $whitelist array, but if you really don’t want a column being updated then don’t pass a value for it.

elements or view extensions or something else? reference app?

In cakephp [2.2] I have baked everything and my "people" view is quite busy with relations and phones and addresses and other related data. I do want all of that information visible in the people view, though not quite in the baked layout.
How should I handle those portions of the related data? I'm not sure if I should use elements or extended views or plugins or what, I'm kinda new to this and the documentation wasn't clear to me (at my level) which should be used when. The baked code seemed to be a monolithic approach, so I didn't get much help looking there.
Once the user chooses to edit a phone number (for instance) from the listing on the person view, it takes them to the phone edit view and then returns them to the phone listing (index view) and not the person view that they were on. How do I get them back to the person view instead?
The blog example they provide is nice, but is there a "reference" application somewhere for cakephp that demonstrates best practices on a wide variety of their features? I couldn't find one, or anything more than just a simple app example.
Thanks, I appreciate the guidance.
This is a rather broad question, but I'm going to try and answer it. I'm not sure how advanced you're programming knowledge is, so forgive me if I'm rehashing things you already know. First, this article was a great help when I started to use the framework for the first time as it explains what code should go where and why. It's the closest I've seen to a "reference application", which would actually be a great learning tool. You could try and have a look at some of the higher profile Cake applications, like Croogo (a Cake-based CMS). But the codebase is bound to be a little bit complex.
Personally I would use elements when you want to actually reuse them in different views. The problem however, is feeding the element its data. There's a method called requestAction, but even the manual states that this should be used with moderation and in combination with caching. The problem is that using a lot of requestAction calls in different elements litters your Controllers with methods and doesn't adhere to the "Skinny Controllers, Fat Models" mantra.
I would put most of the related data calls in their respective Models and call those Model methods from the Controller and feed them to the View. So let's say you want the 10 latest PhoneNumbers and related Users.
You would have a method in your PhoneNumber model which returns an array of users and their phonenumbers. Use the Containable behaviour to limit the number of related models which are returned. The code below is an example, so the practical implementation might vary:
public function getRecentPhoneNumbers($limit=10) {
$phoneNumbers = array();
$phoneNumbers = $this->find('all', array(
'limit' => $limit,
'contain' => array('User'),
'order' => 'PhoneNumber.id DESC'
));
return $phoneNumbers;
}
If the PhoneNumber and User model are properly related you would be able to call getRecentPhoneNumbers() from the User model:
$this->PhoneNumber->getRecentPhoneNumbers(10)
Or from the Users Controller:
$this->User->PhoneNumber->getRecentPhoneNumbers(10)
Say you have an element which shows a list of those 10 numbers and it accepts a variable called $recentPhonenumbers, you then set the variable in the relevant UsersController method with the returned array from the getRecentPhoneNumbers call:
$this->set('recentPhonenumbers', $this->User->PhoneNumber->getRecentPhoneNumbers(10));
This will make it available to the View that contains the element.
The extended views are relatively new (from Cake 2.1 and onwards) and I haven't used them, but seem a great way to create conditional markup.
As for the second question, redirecting the user to the person view, rather than the index view. This is a matter of adjusting the redirect (see the manual for more details) in the edit() method of the Controller. Standard baked edit() methods accept an $id parameter you can use this to redirect to the view() (which probably also accepts an $id paramater).
So the redirect probably looks something like this:
$this->redirect(array('controller' => 'users', 'action' => 'index'));
Change it to:
$this->redirect(array('controller' => 'users', 'action' => 'view', $id));

CakePHP - multiple paginated datasets on same page from same model

I have an admin page that pulls data from my ExpenseClaim model. I want to have 3 different paginated tables, each table displaying data based on a different condition (a status). This is what I have (which doesn't work - nothing is paginated in the view) in my controller:
// Pending
$this->set('claims', $this->paginate('ExpenseClaim', array('ExpenseClaim.claim_status_id' => '2')));
// Get approved
$this->set('approvedClaims', $this->paginate('ExpenseClaim', array('ExpenseClaim.claim_status_id' => '3')));
// Get Declined
$this->set('declinedClaims', $this->paginate('ExpenseClaim', array('ExpenseClaim.claim_status_id' => '4')));
Does anyone know how I can achieve this, I've do a fair amount of searching but only found things relating to different models or hacks using jquery plugins. Surely this can be done in cake alone?
Thanks in advance
Did you wrote the above code inside ExpenseClaimsController? But in case if you in another controller, make sure you have a paginate variable as the following
public $paginate = array(
'ExpenseClaim' => array(
.......
)
);

cakePHP model associations lost after model imported into controller

Due to a client's desire to have URLs that defy Cake logic I have decided to use the pages_controller.php copied from cake/libs to organize my app.
Most of the site's functionality occurs from one url making calls with ajax to different controllers so I chose Pages as the home base.
Pages has no model but I still need to access some of the relevant models so I import them.
I have tried all three methods:
$this->loadModel('Inventories');
----
$Inventories =& ClassRegistry::init('Inventories');
----
App::import('Controller', 'Inventories');
$Inventories = new InventoriesController;
The Inventories Model seems to load fine with each but when I find some records:
$(...)->find("all", array(
'conditions' => array('id' => '1'),
'recursive'=>2)
);
I only get results as if recursive had been set to -1 or as if there were no associated models.
This happens no matter what I set recursive to.
When I load the model in another controller I get the appropriate recursive response.
Any ideas how i can get full access to a model from pages_controller.php?
A messy but instant solution would be to simply populate the $uses array in your controller:
var $uses = array('Inventories', 'User'); // or whatever models need to be loaded
This is considered poor practice and could quickly spiral out of control, however. Mostly handy for quick testing.
You could place the functionality in appropriately named controllers, and then route your client's desired URLs to the corresponding controller / action:
http://book.cakephp.org/view/945/Routes-Configuration
A little-known controller method (it's not in the cookbook, only the API) called setAction() might be the solution for you. See:
http://api13.cakephp.org/class/controller#method-ControllersetAction
also in Cake 2.0
http://api20.cakephp.org/class/controller#method-ControllersetAction

Resources