How to test CakeEmail is called from a model - cakephp

In CakePHP 2.1 I'm trying to test that the CakeEmail::to() method is called from my model test case with the correct "to" email (In this example: cat#gmail.com).
I want the following test to pass but I get:
Expectation failed for method name is equal to <string:to> when invoked 1 time(s).
Method was expected to be called 1 times, actually called 0 times.
Here's the code in the Model and the test case:
<?php
// Model/Job.php
App::uses('AppModel', 'Model');
App::uses('CakeEmail', 'Network/Email');
class Job extends AppModel {
public function emailCat() {
$CakeEmail = new CakeEmail();
$CakeEmail->to('cat#gmail.com');
$CakeEmail->subject('hello!');
$CakeEmail->emailFormat('text');
$CakeEmail->config('default');
$CakeEmail->send('hi');
}
}
// Test/Model/JobTest.php
class JobTestCase extends CakeTestCase {
public function setUp() {
parent::setUp();
$this->Job = ClassRegistry::init('Job');
}
public function testEmailCat() {
// I want to assert CakeEmail::to() is called with correct email
$CakeEmail = $this->getMock('CakeEmail' , array('to'));
$CakeEmail->expects($this->once())
->method('to')
->with($this->equalTo('cat#gmail.com'));
$result = $this->Job->emailCat();
}
}

The problem is that you're mocking a completely different class then the one that is actually used on the model. On your model function, you instantiate a brand new email class which will be mocked. Instead, you need to make sure that the CakeEmail object that the model uses is the one mocked.
class Job extends AppModel {
public $CakeEmail = null;
public function emailCat() {
if (!$CakeEmail) {
$this->CakeEmail = new CakeEmail();
}
$this->CakeEmail = new CakeEmail();
$this->CakeEmail->to('cat#gmail.com');
$this->CakeEmail->subject('hello!');
$this->CakeEmail->emailFormat('text');
$this->CakeEmail->config('default');
$this->CakeEmail->send('hi');
}
}
Then update your test case to set the mock object on your Job model.
class JobTestCase extends CakeTestCase {
public function setUp() {
parent::setUp();
$this->Job = ClassRegistry::init('Job');
}
public function testEmailCat() {
// I want to assert CakeEmail::to() is called with correct email
$CakeEmail = $this->getMock('CakeEmail' , array('to'));
$CakeEmail->expects($this->once())
->method('to')
->with($this->equalTo('cat#gmail.com'));
// use mock object instead of creating a brand new one
$this->Job->CakeEmail = $CakeEmail;
$result = $this->Job->emailCat();
}
}

Related

in cakephp4 how to access a model within a model

How do i access another model within a model in cakephp4.2? The docs on this issue isnt clear to me and i can then run a query on this ? TableRegistry is deprecated now.
error Unknown method "getTableLocator" called on App\Model\Table\LessonsTable
//none of these no longer work
in model {
use Cake\ORM\Locator\LocatorAwareTrait;
class LessonsTable extends Table
{
..
private function getlessonRevenue(){
//$clients = $this->getTableLocator()->get('Clients');
// $cleints = TableRegistry::get('Clients');
// $this->Table = TableRegistry::get('Clients');
$clients = $this->getTableLocator()->get('Clients');
https://api.cakephp.org/4.0/class-Cake.ORM.TableRegistry.html
Try:
<?php
use Cake\ORM\Locator\LocatorAwareTrait; //<------------ add here
class ArchivesTable extends Table
{
use LocatorAwareTrait; // <--------------------------- and add here
public function myMethod()
{
$clients = $this->getTableLocator()->get('Clients');
}
and read https://book.cakephp.org/4/en/orm/table-objects.html#using-the-tablelocator
and learn how to use php trait https://www.phptutorial.net/php-tutorial/php-traits/

CakePHP 2.3 getting error when using internationalization in AppModel

I'm trying to define some status constants in my AppModel to be available in every model. To get the string values of them I want to store them in an array, but when I try to internationalize the string it causes the following error:
Fatal error: Call to undefined function AppModel() in /home/dev/www/test/lib/Cake/Utility/ClassRegistry.php on line 181
My code:
class AppModel extends Model {
const STATUS_INACTIVE = 0;
const STATUS_ACTIVE = 1;
public $statuses = array(
self::STATUS_INACTIVE => __('Inactive'),
self::STATUS_ACTIVE => __('Active')
);
}
I had a look in the core and found that the following line fires up the error:
$instance = new $appModel($settings);
The $appModel($settings) statement causes it, I tried to debug it and got the same error.
Any help or idea to head to the right direction to solve this is appreciated.
I don't think you can define $statuses like that in PHP. The problem is that you cannot define a class property using the result of a function.
http://www.php.net/manual/en/language.oop5.properties.php
Class member variables are called "properties". You may also see them referred to using other terms such as "attributes" or "fields", but for the purposes of this reference we will use "properties". They are defined by using one of the keywords public, protected, or private, followed by a normal variable declaration. This declaration may include an initialization, but this initialization must be a constant value--that is, it must be able to be evaluated at compile time and must not depend on run-time information in order to be evaluated.
If you want to set the property with translated labels, move it to your constructor, which is meant for initialisation of Objects;
class AppModel extends Model {
const STATUS_INACTIVE = 0;
const STATUS_ACTIVE = 1;
public $statuses;
public function __construct($id = false, $table = null, $ds = null) {
parent::__construct($id, $table, $ds);
$this->statuses = array(
self::STATUS_INACTIVE => __('Inactive'),
self::STATUS_ACTIVE => __('Active')
);
}
}
Or move it to another method so that is is only generated when actually used, like this;
public function getStatusOptions()
{
return array(
self::STATUS_INACTIVE => __('Inactive'),
self::STATUS_ACTIVE => __('Active')
);
}
On another note; a nice trick to define class constants and make them re-usable, without adding them to the AppModel is by using an interface;
interface ActiveInactive {
const STATUS_INACTIVE = 0;
const STATUS_ACTIVE = 1;
}
interface FooBar {
const HELLO = 'world';
}
class MyModel extends AppModel implements ActiveInactive, FooBar {
public function test()
{
echo self::STATUS_INACTIVE; // outputs '1'
echo self::HELLO; // outputs 'world'
}
}
class MyHelper extends Helper implements ActiveInactive, FooBar {
public function test()
{
echo self::STATUS_INACTIVE; // outputs '1'
echo self::HELLO; // outputs 'world'
}
}
This way you can re-use those constants anywhere; in Helpers, Models, Controllers. And you can 'combine' those constants :)

cakephp call a function of another model error

I have a site develop in cakephp 2.x
I want into my controller call a function of another controller like this:
class ProductsController extends AppController {
public $name = 'Products';
public $scaffold;
public $uses = array('Product','Unit');
public function testFunction(){
$this->loadModel('Unit');
$this->Unit->test();
}
}
The function test into UintController.php is this:
public function test(){
echo("test");
}
My model name are Product and Unit.
When I call the function test give me this error:
Error: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'prova' at line 1
In the function now is empty but give me this error.
I have tried with:
public $uses = array('Unit');
and to cancel the line with $uses.
How can I solve it?
To call a function from another controller you can use the requestAction:
Definition
"This function calls a controller’s action from any location and returns data from the action. The $url passed is a CakePHP-relative URL (/controllername/actionname/params). To pass extra data to the receiving controller action add to the $options array".
Usage
This is what your code would looks like:
class ProductsController extends AppController
{
public $name = 'Products';
public $scaffold;
public $uses = array('Product','Unit');
public function testFunction() {
// Calls the action from another controller
echo $this->requestAction('/unit/test');
}
}
And then in the UnitController:
class UnitController extends AppController
{
public function test()
{
return 'Hello, I came from another controller.';
}
}
Warning
As said in the CakePHP Cookbook:
"If used without caching requestAction can lead to poor performance. It is rarely appropriate to use in a controller or model".
Best solution for you
But, the best solution for you, would be to create a function inside a model and then call from your controller, like this:
class ProductsController extends AppController {
public $name = 'Products';
public $scaffold;
public $uses = array('Product','Unit');
public function testFunction() {
echo $this->Unit->test();
}
}
And in the Unit model:
class Unit extends AppModel
{
public function test(){
return 'Hello, I came from a model!';
}
}

Define global variable for Models and Controllers at CakePHP 2.2

Currently i am using something like this:
//at bootstrap.php file
Configure::write('from', 'mymail#mydomain.com')
//at controllers or models files
$var = Configure::read('from')
The thing is, i would like to manage that variable through the database to be able to modify it in a simpler way.
I was thinking about doing it with AppModel but then it would only be accessible for Models and not controllers.
What should I do in this case?
Thanks.
You can create a separate model / plugin which will be mapped to a configuration table in your database. Then load it through $uses statement for controllers and App::import() for models.
class SystemSetting extends AppModel {
/**
* Return a list of all settings
*
* #access public
* #return array
*/
public function getSettings() {
return $this->find('all');
}
}
Then, in your controller:
class SomeController extends AppController {
var $uses = array('SystemSetting');
public function displaySettings() {
$settings = $this->SystemSetting->getSettings();
// .. your code
}
}
In model:
App::import('Model', 'SystemSettings.SystemSetting');
$settings = new SystemSetting();
$mySettings = $settings->getSettings();
This works just fine. Of course, you might also load settings in both AppController and AppModel to follow the DRY rule.
create the getSettings in your AppModel
in AppController you can write this method:
public function getSettings() {
return $this->{$this->modelClass}->getSettings();
}
this way the getSettings() method is available in any model and any controller
any model call:
$mysettings = $this->getSettings();
any controller call:
$mysettings = $this->MODELNAME->getSettings();

CakePHP: Find if is mobile browser in a helper (no access to request handler)

I need to know in a helper in a CakePHP application if the device is mobile, I would love to use $this->RequestHandler->isMobile(), but the request handler component is not available in helpers. Any ideas?
Thanks!
You can import the class and use it anywhere in the framework like so:
App::import('Component', 'RequestHandler'); // import class
$requestHandler = new RequestHandlerComponent(); // instantiate class
$isMobile = $requestHandler->isMobile(); // call method
var_dump($isMobile); // output: bool(true) or bool(false)
(Tested from helper and gives correct results for Firefox and iPhone)
Also, any options you set in the Controller::helpers property will be passed to the helper:
class AppController extends Controller {
public $components = array(/*...*/, 'RequestHandler');
public $helpers = array(/*...*/, 'MyHelper');
public function beforeFilter() {
$this->helpers['MyHelper']['mobile'] = $this->RequestHandler->isMobile();
}
}
You can catch the options array in your helper's constructor:
class MyHelper extends AppHelper {
protected $_defaultOptions = array('mobile' => false);
public function __construct($options) {
$this->options = array_merge($this->_defaultOptions, $options);
}
}
The accepted answer suggests using a component inside a helper which should be avoided as components are for use solely in controllers and will result in errors as mentioned by Anupal.
The simple solution is to use the CakeRequest class that RequestHandlerComponent uses. So in your helper you can do:-
App::uses('CakeRequest', 'Utility');
$isMobile = (new CakeRequest())->is('mobile');

Resources