loadModel always return true - cakephp

I put the following code in AppController or RecipesController in order to see the return value of loadModel. According to the loadModel API, it should return true when single model found and instance created.
However, I both got 1 which I considered it as true, even though safasfasfafafafas doesn't exist.
$this->log($this->loadModel('safasfasfafafafas')); // random string
$this->log($this->loadModel('Recipe')); // exist in my model
I'm quite new on cakephp, may I know where I have missed?

The docs are incorrect, please refer to a more recent version and also look at the source code as even the most recent docs are not really correct (or let's say they are misleading).
http://api.cakephp.org/2.5/class-Controller.html#_loadModel
http://api.cakephp.org/2.5/source-class-Controller.html#717-746
As can be seen, loadModel() will always return true, also ClassRegistry::init(), which is used to actually load the model, will make use of an instance of AppModel in case the actual requested model doesn't exist, so the exception will only be thrown in case even that class cannot be found.
See also http://book.cakephp.org/2.0/en/models.html#understanding-models
Also note that the model won't try to access the table when it's being instantiated, so the possible missing table exception will only be thrown once you actually try to use the model, for example for querying records.

Related

Laravel Eloquent: Find Action Performed By Function

My question concerns a few methods in the Laravel Eloquent ORM. Particularly, 3 methods have caught my attention:
firstOrCreate
firstOrNew
updateOrCreate
I clearly understand what these methods do but did not find a way to determine which operation was performed after calling them.
For example, after calling firstOrCreate, how do I figure out if the model was found in the database or created? I can run a find query first and then create the record if it was not found but that would be very inefficient.
Similarly, how to find out if an object was created or found in the database in case of firstOrNew and if the object was updated or created in case of updateOrCreate.
provided you have the timestamps turned on for those tables you could simply check their values in the returned object.
The "exists" property will return true if the model is backed by the database (false if "new" occurred).
The "wasRecentlyCreated" property will return true if the record was just created (false if it already existed).
This should let you disambiguate between all the cases you mentioned.

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).
;)

CakePHP UnitTest a method which calls another model

I'm writing a variety of Model Tests in CakePHP (PHPUnit)
In TravisCI, I get something like: "Base table or view not found: 1146 Table 'test.events'
In Cake's test runner I get an assertion failure.
The problem I am having is there are methods in my ModelClasses that I am trying to test which call other models with App::uses. For example:
Method on User model:
public function getOtherData() {
App::uses('Event', 'Model');
$this->Event = new Event;
return $this->Event->find('all');
}
And the test:
public function testGetOtherData() {
$result = $this->User->getOtherData();
$this->assertTrue(!empty($result));
}
Note the above example is just that. An example, simplified to show the problem. I understand that the above example has better 'cake' ways of doing it.
Also, I am using defining required fixtures and they work just fine. (I know this by another method in the model which uses a join in the find, instead of App::Uses())
EDIT:
The code when run works, BUT the UnitTest is looking for the other models data (When using App::uses) in the default database, and not the test database. Why doesn't it use the test database? Am I missing something?
LAST NOTE
Using App::uses() and then instantiating the class will work at runtime. But during testing it will fail, as it attempts to use the default database connection, instead of the test database connection.
Per the selected answer, rather than using App::uses, Cakes built in class registry, ClassRegistry::init('Model', true);, you can include a Model from inside another model method.
It's not generally a good idea to instantiate an object in the middle of your functions using the new statement. This is why -- there's no way to block or redirect that call. Also, it's not necessarily easy to get the right parameters to the object's constructor when it's in the middle of another function, so it's best to keep that code separate.
The right way to do this is to use a different method call to get your object. If you use Cake's ClassRegistry::init() to create model objects, they should use the test database.
If you need to create other non-Cake objects, it's best to create them using some other function, e.g. $this->fetchMeOneOThemEventThingies(). Then, during testing, you can mock out that function and have it return something else. Or, you could use some other DI container like pimple, which will take the same role as Cake's ClassRegistry.
If you need a mock model object for testing, be sure to pass the appropriate arguments to the model's constructor as the third parameter to getMock(), or it may use the production database.

Getting controller's name inside behavior

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;
}

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