CakePHP: how can i handle general model functions? - cakephp

I have some "global" functions not directly related to a model that need to be called from different controllers.
Where can i put them and how can they be correctly called from a controller?

It depends on what your functions do. CakePHP has two generic classes: AppController and AppModel. Every controller should extend AppController and every model should extend AppModel so the methods in these to classes should be available to you in every controller.
Another alternative is to package the functions as behaviors and have all models actAs them.
Depending on semantics you may want to choose one over the other options.

I would like to suggest you to create your own helper class if you want to use in views OR create your own component to use in controllers.
And group the related function in a single and give a meaningful name.
Such that you can use it in your any project just by copying them to your project.
Just add those files to your code wherever required by using the global array var $helpers = array('your_helper1','helper2'); and for components you can use var $components = array('your_component1','component2');

it sounds like a behavior to me if the data is model related.
if it is just some general method, use a component instead.
can you be more specific about the methods you want to use globally?

Related

should we forcefully unload a model loaded via loadModel()

Is there a function like unloadModel in cakePHP that should be called to unload a model that was loaded using loadModel() function?
I found an unload method,
http://api20.cakephp.org/file/Cake/Model/BehaviorCollection.php#method-BehaviorCollectionunload
But it seems to be used for Behavior. Im new to cake. Is there a function like that or does it get automatically unloaded when the called action loses scope?
One more doubt; is using loadModel against MVC's normal conventions? Does it have any adverse effects?
You do not need to unload your model. If you're going to use the model throughout the entire Controller, then use the $uses variable:
public $uses = array('MyModel', 'AnotherModel');
If you're going to just use it in a specific action(s), use loadModel:
$this->loadModel('MyModel');
That's it - no unloading necessary.
And no, it's not against MVC imo and I have seen no adverse effects.
It's VERY common to load a model. Example - most of my projects require a few "homepages" that have greatly-varying data from nearly ever model. In that case, I create a "DashboardsController", which doesn't even have a table - then I load each model when I need to access it's data. (Or with $uses if I'm going to use it's data in all the actions).
no, behaviors and models are two different things.
behaviors add functionality through hooks. Meaning: they alter the way other methods in models work. So if you want to geocode your data automatically, you use a geocoder behavior. Or if you want your results to be decrypted upon find, you add the decrypt behavior.
So there you NEED the option to detach/unload behaviors because you might not want this functionality there at some point.
Models are just access to the database or provide wrapper methods. They don't have to be removed in order for the rest of the site to function as they do not alter the way other methods work.
loadModel is just a way to dynamically load models that are not automatically related. its totally fine to do that from controller actions where you need those models.

CakePHP Sharing code between helpers

I have two types of data that are similar but different. They have some pieces of presentational code that they share but some that they implement differently.
I was thinking that I would have a factory function in a helper (Tracker is a TrackerHelper) like so;
$this->Tracker->getInstance("boolean"); // Returns a BooleanTrackerHelper
But I'm not sure how to return another helper from within another helper. I don't think I can just do a return new BooleanTrackerHelper() since CakePHP probably has it's own routines it wants to go through, and besides; that would force me to place all the classes in the same file.
There is a function in the manual that allows you to load a helper from within a view ($this->Helpers->load()) but I want to load a helper from another helper.
More generally; What do you do if you don't want to repeat in a bunch of different views that if data is of type A use helper A and if it's of type B use helper B, and where helpers A and B share some pieces of code.
There might be a brighter way of solving this, if so; please feel free to share!
If you simply want to use one helper from within another helper, just include it in the $helpers array where you want to use it:
class TrackerHelper extends AppHelper {
public $helpers = array('BooleanTracker');
public function someMethod() {
// Using the other helper
$this->BooleanTracker->someOtherMethod();
}
}

CakePHP: making a controller function not accessible

I have an admin controller that I would like to utilize functions in other controllers (these functions do not represent pages that someone would load in their browser), but it cannot utilize those functions because the functions in the other controllers are private. They are private because I don't want the public to access them. Is there a way to make a controller function not accesible to the public without making the function private or protected?
public function __blah(){
// function that can't be accessed from outside, but can be called from other functions
}
Based on what I've read in the comment of the answer Piotr gave you:
You don't use an admin controller. You want to use admin prefixes:
http://book.cakephp.org/view/950/Prefix-Routing
And authentication:
http://book.cakephp.org/view/1250/Authentication
If you call - and thats how your comment sounds like - one controller from another you're doing something totally wrong in an MVC framework. If it should be re-usable code it should go into components if it's about admin action use the prefix routing and admin_* methods, auth component and protected methods for what you call "helper" methods.
Yes.
You have a lot of information in the CakePHP Book about ACL (access control list) and that is exactly what you're looking for.
Or you may use Auth component.
I see three possible solutions (they can also be combined):
The first solution is to move the code you want to reuse to components (as mentioned by burzum).
The second solution depends on your code. It's possible that you do stuff in the controller which should be done in the model. In this case, move the respective code to the model.
The third solution is to put the code you want to reuse into plain old PHP classes and load them as vendor files.

CakePHP: how to use a model in the app_controller

Iv got a method in a model that I want to be executed everytime a page is requested so I think I need to call it from the app_controller but can't seem to get it to work. The model i want to use is called Blacklist and it has a method in it called check_blacklist() which is what I want to run every time a page is requested. Does anyone know how I should do it?
Thanks
Well, one way to do that would be adding:
var $uses = array('Blacklist');
In your AppController class.
Perhaps a better solution is using a CakePHP built-in method called: loadModel, like this:
$this->loadModel('Blacklist');
If you add Blacklist in the $uses array in your AppController, it will be available in all of your controllers, loadModel just loads the Model for a specific task.
Try to avoid using the $uses array as it adds some overhead to all actions, whether or not the model is used in that action.
As Pawel says, you can use $this->loadModel('Blacklist'); It should be located in the action, say view, just before $this->Blacklist->check_blacklist()
e.g.
function view($id)
{
if($id)
{
$this->loadModel('Blacklist');
$this->Blacklist->check_blacklist();
...
}
}
If this is very widely used, I'd probably write the function on app_model.
Edit:
Use of loadModel is covered here: http://book.cakephp.org/view/845/loadModel
$ModelName = ClassRegistry::init('ModelName');
$ModelName->find();
Unfortunately, due to bug #858, your best bet is to avoid using loadModel() in AppController for now, unless you aren't using any plugins (quite possible). The solution I used to replace $uses was along the lines of:
$this->Blacklist = ClassRegistry::init('Blacklist');
$this->Blacklist->check_blacklist();
Note that you should put this in your beforeFilter() or beforeRender() (see the cookbook) - depending on when you want it executed...
If you'd like to use that same code in other sites, or have it fire earlier in the loading chain, you might consider a Component - putting the same code in your Component initialize() function, or startup() function if the point in the load chain is less critical.

Is using the RequestHandlerComponent in a model possible?

I'm new to PHP and decided to use the cakePHP framework to help me get started.
I can't figure out one thing though, I want to call methods on the RequestHandlerComponent class to update a users last used IP address and other information, I figured the best place to put this would be in the beforeSave() method on the User model.
I can't figure out how to call the getClientIP method though.
The normal code that would otherwise go in the controller doesn't work. Is there another way to call this class if you're in the model and not the controller?
Class Level:
var $components = array('RequestHandler');
And in the function:
$this->data['User']['lastActiveIP'] = $this->RequestHandler->getClientIP();
Gives:
Undefined property: User::$RequestHandler
Call to a member function getClientIP() on a non-object
Components, by design, aren't available to models (without bypassing MVC convention - which you can do, of course). If you chose to force it to be available, look into ClassRegistry::init(). A better solution, I think, would be to employ the RequestHandler component in your controller (where it's meant to be used), set the lastActiveIp value in the controller (exactly as you've shown in your own example code) and pass the entire data array along to the model.
Now your component is being used where it should be and the model gets to remain ignorant about where it gets its data. At the risk of oversimplification, all the model should know is what to do with the data once it arrives; let the controller worry about collecting and packaging the data.
In addition to Rob's answer, maybe it's enough to put a bit of code together yourself that uses the general env('REMOTE_ADDR') or similar variables. Look at the RequestHandler code, it's not doing anything terrifically complicated.
You may even be able to call the component statically, which is slightly better than instantiating it in the model (still in violation of MVC though). Untested, but should work:
App::import('Component', 'RequestHandler');
RequestHandlerComponent::getClientIp();

Resources