I used bake to generate the controller of my users table, now I need to test using PHPUnit and CakePHP 3 if a record has been deleted.
NO error was prompted, just failure in test.
I can not apply this solution: Solution
testDelete in UsersControllerTest:
public function testDelete()
{
$this->get('/users/delete/900000');
$users = TableRegistry::get('Users');
$query = $users->find('all', [
'fields' => ['Users.id', 'Users.email', 'Users.password',
'Users.username', 'Users.user_type_id', 'Users.created',
'Users.modified'],
'conditions' => ['Users.id' => 900000]
]);
$result = $query->hydrate(false)->toArray();
$this->assertEmpty($result);
}
delete method in UsersController:
public function delete($id = null)
{
$user = $this->Users->get($id);
if ($this->Users->delete($user)) {
$this->Flash->success(__('The user has been deleted.'));
} else {
$this->Flash->error(__('The user could not be deleted. Please, try again.'));
}
return $this->redirect(['action' => 'index']);
}
Datasource test:
'test' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
//'port' => 'nonstandard_port_number',
'username' => 'shop',
'password' => 'shop',
'database' => 'shoppingtest',
'encoding' => 'utf8',
'timezone' => 'UTC',
'cacheMetadata' => true,
'quoteIdentifiers' => false,
//'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
]
Note: CakePHP 3.0.11 and PHPUnit 4.8.6
Try that:
$query = $users->find('all', [
'fields' => ['Users.id', 'Users.email', 'Users.password',
'Users.username', 'Users.user_type_id', 'Users.created',
'Users.modified'],
'conditions' => ['Users.id' => 900000]
]);
$result = $query->hydrate(false)->toArray();
$result_count = count($result);
$expected_count = 0;
$this->assertEquals($expected_count, $result_count);
Related
I started using CakePHP 3.6 and I am having trouble to create the authentication login. The new auth function $this->Auth->identify() always returns false.
My code:
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler', [
'enableBeforeRedirect' => false,
]);
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'authorize' => ['Controller'],
'loginRedirect' => [
'controller' => 'Users',
'action' => 'index'
],
'logoutRedirect' => [
'controller' => 'Users',
'action' => 'login',
'home'
],
Here is where I set the fields:
'authenticate' => [
'Form' => [
'fields' => ['username' => 'username', 'password' => 'password']
]
]
]);
/*
* Enable the following components for recommended CakePHP security settings.
* see https://book.cakephp.org/3.0/en/controllers/components/security.html
*/
//$this->loadComponent('Security');
//$this->loadComponent('Csrf');
}
class UsersController extends AppController
{
// Other methods..
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
$this->Auth->allow(['logout']);
}
public function login()
{
if ($this->request->is('post')) {
This always returns false but why?
$user = $this->Auth->identify();
debug($this->request);
debug($user);
die;
if ($user) {
$this->Auth->setUser($user);
return $this->redirect($this->Auth->redirectUrl());
}
$this->Flash->error(__('Invalid username or password, try again'));
}
}
public function logout()
{
return $this->redirect($this->Auth->logout());
}
}
class User extends AppModel {
public $validate = array(
'username' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A username is required'
)
),
'password' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A password is required'
)
),
'email' => array(
'email' => array(
'rule' => array('email', true),
'message' => 'Please supply a valid email address.'
),
'required' => array(
'rule' => array('notEmpty'),
'message' => 'A email is required'
)
),
'role' => array(
'valid' => array(
'rule' => array('inList', array('admin')),
'message' => 'Please enter a valid role',
'allowEmpty' => false
)
)
);
public function beforeSave($options = array()) {
if (isset($this->data[$this->alias]['password'])) {
$passwordHasher = new SimplePasswordHasher();
$this->data[$this->alias]['password'] = $passwordHasher->hash(
$this->data[$this->alias]['password']
);
}
return true;
}
}
I followed the tutorials on the cakephp website with zero luck. Help!
Make sure that you test with a hashed password because the Auth component is expecting it to be hashed so this was my bad.
i use cakephp-2.5.4 and when i encounter a 404 error, user session killed & user logout from application.
can someone help?
public $components = array (
'Session'
);
Solution:
core.php
Configure::write('Exception.handler','AppErrorHandler::handleException');
bootstrap.php
App::uses('AppErrorHandler', 'Lib');
add AppErrorHandler class in Lib folder with following function
public static function handleException(Exception $exception)
{
if($exception instanceof MissingControllerException ){
return false;
}
$config = Configure::read('Exception');
//self::_log($exception, $config);
$renderer = isset($config['renderer']) ? $config['renderer'] : 'ExceptionRenderer';
if ($renderer !== 'ExceptionRenderer') {
list($plugin, $renderer) = pluginSplit($renderer, true);
App::uses($renderer, $plugin . 'Error');
}
try {
$error = new $renderer($exception);
$error->render();
} catch (Exception $e) {
}
}
To solve this issue:
Please do try this
public $helpers = array('Session', 'Html', 'Form');
public $uses = array('Users','Persons');
public $components = array('Auth' => array(
'authenticate' => array(
'Form' => array(
'fields' => array('username' => 'email_address'),
'passwordHasher' => array('className' => 'Md5', 'hashType' => 'md5'),
)
),
'loginAction' => array(
'controller' => 'user_masters',
'action' => 'login',
'admin' => true,
),
'loginRedirect' => array(
'controller' => 'user_masters',
'action' => 'dashboard',
'admin' => true,
), 'logoutRedirect' => array(
'controller' => 'user_masters',
'action' => 'login',
'admin' => true,
)), "Cookie", "Session", "Email", 'RequestHandler', 'Security');
Let me know if any issue.
I used bake to generate the controller of my users table, now I need to test using PHPUnit and CakePHP 3 if a record has been entered in the database but I can not see if this actually being inserted.
I can not apply this solution: Question
First error:
1) App\Test\TestCase\Controller\UsersControllerTest::testAdd Failed
asserting that '' contains "The user has been saved.".
If I remove the assertion (or change to assertResponseSuccess)
$this->assertResponseContains('The user has been saved.');
in assertEquals the array $result is empty.
add Action in UsersController:
public function add()
{
$user = $this->Users->newEntity();
if ($this->request->is('post')) {
$user = $this->Users->patchEntity($user, $this->request->data);
if ($this->Users->save($user)) {
$this->Flash->success(__('The user has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The user could not be saved. Please, try again.'));
}
}
$userTypes = $this->Users->UserTypes->find('list', ['limit' => 200]);
$this->set(compact('user', 'userTypes'));
$this->set('_serialize', ['user']);
}
testAdd in UsersControllerTest:
public function testAdd()
{
$this->get('/users/add');
$this->assertResponseOk();
//-------------------------------------------------------------------------
$data = [
'id' => 999999,
'email' => 'usuariocomum999999#gmail.com',
'password' => 'usuariocomum999999senha',
'username' => 'usuariocomum999999username',
'user_type_id' => 900000,
'created' => '2014-07-17 18:46:47',
'modified' => '2015-07-17 18:46:47'
];
$this->post('/users/add', $data);
$this->assertResponseContains('The user has been saved.');
//-------------------------------------------------------------------------
$expected = [
[
'id' => 999999,
'email' => 'usuariocomum999999#gmail.com',
'password' => 'usuariocomum999999senha',
'username' => 'usuariocomum999999username',
'user_type_id' => 900000,
'created' => new Time('2014-07-17 18:46:47'),
'modified' => new Time('2015-07-17 18:46:47')
]
];
$users = TableRegistry::get('Users');
$query = $users->find('all', [
'fields' => ['Users.id', 'Users.email', 'Users.password',
'Users.username', 'Users.user_type_id', 'Users.created',
'Users.modified'],
'conditions' => ['Users.id' => 999999]
]);
$result = $query->hydrate(false)->toArray();
$this->assertEquals($expected, $result);
}
Datasource test:
'test' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
//'port' => 'nonstandard_port_number',
'username' => 'shop',
'password' => 'shop',
'database' => 'shoppingtest',
'encoding' => 'utf8',
'timezone' => 'UTC',
'cacheMetadata' => true,
'quoteIdentifiers' => false,
//'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],
]
Note: CakePHP 3.0.11 and PHPUnit 4.8.6
You need to test that there was a redirect and a success message in the Session, The response will not contain your text as the response is just a redirect header code for the browser.
I'm building an App with CakePHP 3.0. I have a FactsTable and an InterferencesTable. Interferences has this fields:
[id][changed_fact_id][influenced_fact_id][trend][modified_by][modified_at][created_by][created_at]
associations look like this:
FactsTable:
`$this->belongsToMany('InfluencedFacts', [
'through' => 'Interferences',
'className' => 'Facts',
'foreignKey' => 'changed_fact_id'
]);
$this->belongsToMany('ChangedFacts', [
'through' => 'Interferences',
'className' => 'Facts',
'foreignKey' => 'influenced_fact_id'
]);`
InterferencesTable:
`$this->belongsTo('ChangedFacts', [
'className' => 'Facts',
'foreignKey' => 'influenced_fact_id',
]);
$this->belongsTo('InfluencedFacts', [
'className' => 'Facts',
'foreignKey' => 'changed_fact_id',
]);`
I baked my controllers and views. Saving a new Fact works fine but the association isn't saved. I tried to save the association manually but it doesn't work, too.
If i made a mistake in model-association, please tell me ;)
saving code from FactsController.php:
`public function add() {
$fact = $this->Facts->newEntity($this->request->data);
if ($this->request->is('post')) {
if ($this->Facts->save($fact)) {
$id = $fact->get('id');
$this->Flash->success('The fact has been saved.');
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error('The fact could not be saved. Please, try again.');
}
}
$aggregates = $this->Facts->Aggregates->find('list');
$plants = $this->Facts->Plants->find('list');
$influencedFacts = $this->Facts->InfluencedFacts->find('list');
$this->set(compact('fact', 'aggregates', 'plants', 'influencedFacts'));
}`
Fact Entity:
`protected $_accessible = [
'name' => true,
'short' => true,
'description' => true,
'modified_by' => true,
'modified_at' => true,
'created_by' => true,
'created_at' => true,
'aggregates' => true,
'plants' => true,
];`
Hopefully I'm missing something simple here. I'm using the CakeDC Search and Tags plugin for my cake (2.3.4) app.
Along with generic search by field functionality I want the user to be able to search by tags. I've almost got this working but the search will only display results if you search for a single tag not multiples. For example, if I add an article with the following tags - black, white, red. The article will only show in the search results if I search for a single tag (say, black) and not all 3, or even 2... What am I missing?
Heres my code:
Article.php model
class Article extends AppModel {
public $actsAs = array(
'Upload.Upload' => array(
'screenshot1' => array (
'fields' => array (
'dir' => 'dir'
),
'thumbnailMethod' => 'php',
'thumbnailSizes' => array(
'xvga' => '1024x768',
'vga' => '640x480',
'thumb' => '80x80'
),
),
),
'Search.Searchable',
'Tags.Taggable'
);
// Search plugin filters
public $filterArgs = array(
'title' => array('type' => 'like'),
'environment' => array('type' => 'like'),
'description' => array('type' => 'like'),
'error' => array('type' => 'like'),
'cause' => array('type' => 'like'),
'resolution' => array('type' => 'like'),
'live' => array('type' => 'value'),
'synced' => array('type' => 'value'),
'tags' => array('type' => 'subquery', 'method' => 'findByTags', 'field' => 'Article.id'),
array('name' => 'search', 'type' => 'query', 'method' => 'filterQuery'),
);
// This is the OR query that runs when the user uses the client side signle field search
public function filterQuery($data = array()) {
if(empty($data['search'])) { // search is the name of my search field
return array();
}
$query = '%'.$data['search'].'%';
return array(
'OR' => array(
'Article.title LIKE' => $query,
'Article.description LIKE' => $query,
'Article.error LIKE' => $query,
)
);
}
// Find by tags method
public function findByTags($data = array()) {
$this->Tagged->Behaviors->attach('Containable', array('autoFields' => false));
$this->Tagged->Behaviors->attach('Search.Searchable');
$query = $this->Tagged->getQuery('all', array(
'conditions' => array('Tag.name' => $data['tags']),
'fields' => array('foreign_key'),
'contain' => array('Tag')
));
return $query;
}
public $hasAndBelongsToMany = array(
'Category' => array(
'className' => 'Category',
'joinTable' => 'articles_categories',
'foreignKey' => 'article_id',
'associationForeignKey' => 'category_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
),
'Tag' => array('with' => 'Tagged')
);
Controller method
public function admin_advancedSearch() {
// Disable validation for this action
$this->Article->validate = array();
// For search plugin
$this->Prg->commonProcess();
// Set searched for details
$this->set('searchedFor', $this->passedArgs);
// If passed args are all empty
if ($this->passedArgs) {
if (empty($this->passedArgs['title']) AND
empty($this->passedArgs['environment']) AND
empty($this->passedArgs['description']) AND
empty($this->passedArgs['error']) AND
empty($this->passedArgs['cause']) AND
empty($this->passedArgs['resolution']) AND
empty($this->passedArgs['live']) AND
empty($this->passedArgs['synced']) AND
empty($this->passedArgs['tags'])) {
$this->Session->setFlash('Please enter at least one search criteria', 'flash_failure');
// Set this var for checks in view
$this->set('emptySeach', true);
} else {
$paginateConditions = $this->Article->parseCriteria($this->passedArgs);
$this->paginate = array('conditions' => array(
$paginateConditions),
'limit' => 10,
'order' => array('Article.modified' => 'DESC'));
$this->set('articles', $this->paginate());
// Count number of results
$count = 0;
foreach ($this->paginate() as $result) {
$count++;
}
$this->set('resultsCount', $count);
// Search was not empty - set flag for view
$this->set('emptySeach', false);
}
}
// Set layout
$this->layout = 'admin';
//debug($this->passedArgs);
}
All plugins are loaded successfully from bootstrap, searches work fine without tags. Searches with tags only work if one tag is entered....
*EDIT *
If I debug $query from the findByTags method I get this:
'SELECT `Tagged`.`foreign_key` FROM `knowledgebase`.`tagged` AS `Tagged` LEFT JOIN `knowledgebase`.`tags` AS `Tag` ON (`Tagged`.`tag_id` = `Tag`.`id`) WHERE `Tag`.`name` = 'kia, rio, red''
So it looks like its trying to find a single tag with all the searched for tags in its name. How can I make the WHERE part an IN?
For example:
WHERE `Tag`.`name` IN ('kia', 'rio', 'red')
Thanks
Use the "method" https://github.com/cakedc/search#behavior-and-model-configuration key her to pass the search args to a customized method.
The example here shows you exactly how this works https://github.com/cakedc/search#full-example-for-modelcontroller-configuration-with-overriding
public $filterArgs = array(
'some_related_table_id' => array('type' => 'value'),
'search'=> array('type' => 'like', 'encode' => true, 'before' => false, 'after' => false, 'field' => array('ThisModel.name', 'OtherModel.name')),
'name'=> array('type' => 'query', 'method' => 'searchNameCondition')
);
public function searchNameCondition($data = array()) {
$filter = $data['name'];
$cond = array(
'OR' => array(
$this->alias . '.name LIKE' => '' . $this->formatLike($filter) . '',
$this->alias . '.invoice_number LIKE' => '' . $this->formatLike($filter) . '',
));
return $cond;
}
Inside your custom method explode() the tags and make them an array so that CakePHP is using them as IN() or better (in can become slow) make it a chain of AND or OR.