Getting controller's name inside behavior - cakephp

Im writing ClearCache behavior.
It's purpose is to delete some of custom cache files on every afterSave and afterDelete event of the model.
In order to delete right files i need to know name of controller and the name of action that called ModelWithClearCacheBehavior->save() or ModelWithClearCacheBehavior->delete()
My question is:
How to get those names inside behavior?

There is no an elegant solution about this (at least I don't know it).
You can do it with a Configure::write class for example:
in your AppController's beforeFilter() you can add the following code:
Configure::write('current_controller', $this->name);
Configure::write('current_action', $this->action);
later on in your behavior you can access them with
Configure::read('current_controller');
Configure::read('current_action');
You can access it because you set them before any model iterations.
For sure it's not elegant but it's working.

Not something I've really done anything with, but a brief reading of the book seems to indicate that the model is (or should be) available inside the behaviour -
When creating behavior methods you automatically get passed a reference of the calling model as the first parameter. All other supplied parameters are shifted one place to the right.
You should then be able to access the model via $Model

this is a bit late but for future reference, in cakephp 2.0 can be done this way in a behavior (using CakeRequest)
beforeFind(&$model, $query){
global $Dispatcher;
$request = new CakeRequest();
$request = $Dispatcher->parseParams($request, $additionalParams = array());
pr($request->params->controller);
return $query;
}

Related

CakePHP: Cannot use modParams with indexes that do not exist

I am writing a datasource to access a external web service, this works fine as expected, but after the web service has run, I get the following error:
CakeException: Cannot use modParams with indexes that do not exist.
in /project/lib/Cake/Utility/ObjectCollection.php on line 128
After a little googling, I found this similar question:
What does this error actually mean?
Unfortunately, this didn't help me solve my answer.
I understand that the error is expecting me to remove (or add extra) params, but I don't understand where I am setting these params to edit them.
(answering my own question to help others with the same issue!)
The Issue
The issue I had that in my AppModel, I have set public $actsAs = array('Containable');, I do this so all my models attach the containable behaviour, (I then set recursive to -1 by default for all models, and specify the recursion as required (with the call).
So in this case, my datasource model (which I use to interact with the datasource), had the containable behaviour attached to it (and so CakePHP thought this was correct and processed it as per normal model (which is not the case as this has no relations or database table).
The solution
The solution was to simply add public $actsAs = false; in my datasource model (which removed the inherited containable behaviour).
;)

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.

Data logic on load using a component - cakePHP

I have a project I'm developing which includes articles that can be commented on (comments stored in separate table of course). I want to perform pre logic on a field from each comment, wherever they are loaded through-out the app. The data logic I want to performed is from a custom written component.
The logical place to me that this could be achieved globally is from the comment model, but I could be wrong.
I'm not even 100% if I can use a component from a model, but I've been trying to do this logic using the afterFind() call-back function:
function afterFind($results) {
foreach ($results as $key => $val) {
if (isset($val['Comment']['created'])) {
$results[$key]['Comment']['created'] = $this->Dateconvert->howLongAgo($val['Comment']['created']);;
}
}
return $results;
}
I have tried echoing from inside this function and it doesn't actually seem to be getting called but searching hasn't revealed any functions that do, but I believe afterFind() is best to illustrate what I'm trying to achieve.
So I am looking for a solution where I can performed the post-load logic on articles comments, whether they are being loaded from other controllers with associations to comments or in the comments controller. Basically a global one hit solution :D
cakephp indicates that components are for controllers and behaviours for models and helpers for view...
knowing that first, you may also know that you can use any part of it wherever you want because cake still php, though is not recomended... if is a library of functions you may want to put it inside the libs folders and access it from there.
how, easy use App::import('component', 'nameComponent'); component can be lib, controller, etc..
Having said that, afterFind is a good place to do after load things, remember that this function is call ONLY when a find is used, if you use, any other like query or save or update it won't be called.
hope this helps you :)

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