I'm trying to validate an username and check if this already exists in the system. Now the problem's that I've the user list in the table: ea_user
and the user settings in the table: ea_user_settings. Now:
ea_user > contain the id of the user that isn't remove from the system, the field 'data'
ea_user_settings > contain the username
So my function is this:
public function validate_username($username, $user_id)
{
$num_rows = $this->db->join('ea_user_settings', 'ea_user_settings.id = ea_user.id', 'inner')
->get_where('ea_user_settings',
array('username' => $username, 'id_users <> ' => $user_id, 'ea_user.data' => 0))->num_rows();
return ($num_rows > 0) ? FALSE : TRUE;
}
The problem's I get this error:
Fatal error: Call to a member function num_rows() on a non-object
Try this
public function validate_username($username, $user_id)
{
$this->db->select(*);
$this->db->from('table_name');
$this->db->join('ea_user_settings', 'ea_user_settings.id = ea_user.id', 'inner')
$this->db->get_where('ea_user_settings', array('username' => $username, 'id_users <> ' => $user_id, 'ea_user.data' => 0));
$query = $this->db->get();
$result = $query->result_array(); # add this
$count = count($result);
if (!empty($count)) {
return true;
}
else{
return false;
}
}
}
It isn't documented, but any of the get() functions (get_where() in your case) can return FALSE. (Learned this the hard way.) That would seem to be your problem. FALSE->num_rows() has no meaning as FALSE is not a object.
This usually happens because the query does not build correctly.
The compiled query can be examined by doing this.
public function validate_username($username, $user_id)
{
$this->db->select(*)
->from('ea_user_settings')
->join('ea_user_settings', 'ea_user_settings.id = ea_user.id', 'inner')
->where(array('username' => $username, 'id_users <> ' => $user_id, 'ea_user.data' => 0));
$compiled_select = $this->db->get_compiled_select(NULL, FALSE);
//inspect or var_dump($compiled_select) to see what query statement was built.
After setting up all the join, where and other pieces of a query I have taken to checking the truthiness of get() before using its return. In your case something like this. Code picks up after (or instead of) the line $compiled_select = $this->...
if($query = $this->db->get())
{
return ($query->num_rows() > 0) ? FALSE : TRUE;
}
else
{
throw new Exception("get call returned FALSE");
}
}
For me, the Exception is useful mostly during development. But you could put your call to the validate_username() in a try catch block if it would be useful.
Related
Often I need to validate if given value is existing in certain column (attribute) of a table (model).
This can be useful in foreign keys of a model, to check if the given values exists.
Most probably the validation logic can be mostly the same as for Uniqueness, except the comparison here can be something like > 0.
A possible usage scenario could be like below:
$validator->add(
'organization_id',
new ExistenceOnDbValidator(
[
'model' => Organization::class,
'expr'=> ' id = %s ',
'excludeNullValue'=> true,
'message' => 'Organization does not exist.',
]
)
);
Finally I implemented myself a validator called ExistenceOnDbValidator and it works fine.
Usage
$validator = new Validation();
$validator->add(
'organization_id',
new ExistenceOnDbValidator(
[
'model' => Organization::class,
'expr' => ' id = %s ',
'ignoreNullValue' => false,
'message' => 'Selected organization does not exist.',
]
)
);
Implenentation
use Phalcon\Messages\Message;
use Phalcon\Validation;
use Phalcon\Validation\AbstractValidator;
use Phalcon\Validation\ValidatorInterface;
class ExistenceOnDb extends AbstractValidator implements ValidatorInterface
{
public function validate(Validation $validator, $attribute): bool
{
$expr = $this->getOption('expr');
$model = $this->getOption('model');
$value = $validator->getValue($attribute);
$ignoreNullValue = true;
if ($this->hasOption('ignoreNullValue')) {
$ignoreNullValue = $this->getOption('ignoreNullValue');
}
if ((is_null($value) || empty($value)) && $ignoreNullValue == true) {
return true;
}
$expr = sprintf(
$expr,
$value,
);
$result = $model::findFirst($expr);
if ((is_null($result) || empty($result))) {
$message = $this->getOption('message');
$validator->appendMessage(new Message($message));
return false;
}
return true;
}
}
I've changed my codes a lot.
this is the code in my controller:
public function actionRequestTV() {
$this->layout = '//layouts/column1';
$this->bodyclass = 'bodygrey_without_leftbg';
$model = new Reqtv();
if (isset($_POST['Reqtv'])) {
if ($model->validate()) {
$model->attributes = $_POST['Reqtv'];
if ($model->save())
$this->redirect(array('step2', 'id' => $model->REQTVID));
}
}elseif (isset($_POST['BP'])) {
if ($model->validate()) {
$model->attributes = $_POST['BP'];
if (!$model->save()) {
print_r($model->getErrors());
}else
$this->redirect(array('step2B', 'id' => $model->REQTVID));
}
}else
$this->render('reqtvform_step1', array(
'model' => $model,
));
}
here is the rules :
public function rules() {
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
array('BPID, SK', 'numerical', 'integerOnly' => true),
array('BPMOBILE', 'numerical'),
array('TVID, TVPASS', 'length', 'max' => 50),
array('PROBLEM', 'length', 'max' => 250),
array('BPCP, BPMOBILE, BPEMAIL', 'length', 'max' => 255),
array('SK', 'ceksk'),
array('BPEMAIL', 'email'),
array('PSNO', 'cekPSNO'),
// array('SK, PROBLEM, TVID, TVPASS', 'required', 'on' => 'step2'),
// The following rule is used by search().
// Please remove those attributes that should not be searched.
array('REQTVID, BPID, TVID, TVPASS, PROBLEM, SK, BPCP, BPMOBILE, BPEMAIL, PSNO', 'safe', 'on' => 'search'),
);
}
after changing my code from this code into more simple like the code above, it already able to save the values. but, some rules doesn't work, like ceksk rule. this is the code of ceksk:
public function ceksk() {
if($this->SK){
if (!$this->SK == 1) {
$this->addError('SK', 'Maaf, anda harus mencentang persetujuan syarat & ketentuan sebelum melanjutkan');
return false;
}
}
}
UPDATE
my ceksk rule is already work. but I'm decided to use JQuery instead:
<?php
Yii::app()->clientScript->registerScript('JQuery', "
$('#kirim').click(function() {
if ($('#SK').attr('checked')) {
return true;
}else{
alert('Anda belum mencentang Syarat & Ketentuan');
return false;
}
});
");
?>
but my other rules still won't work. my cekPSNO rule is working actually. but when it catch the error, it bring me to a blank white page instead of staying in the form page and show the error. this is the code:
public function cekPSNO() {
if ($this->PSNO) {
$psno = Ps::model()->findByAttributes(array('PSNO' => $this->PSNO));
//check cdsn ada atau tidak
if ($psno === null) {
$this->addError('PSNO', 'Nomor PS tidak ditemukan, silahkan periksa Nomor PS anda !');
return false;
} else {
if (date('Y-m-d') > $psno->TGLBERAKHIR) {
$this->addError('PSNO', 'Premium Support sudah expired !');
return false;
}
}
}
}
please, your help are absolutely appreciated. thank you :))
if(!$this->SK == 1) is not going to produce the result you want.
Try with if($this->SK != 1).
See http://php.net/manual/en/language.operators.precedence.php
The negation from the ! occurs before the comparison operator, which means you are effectively executing ((!$this->SK) == 1).
This will cause the code block to never execute, for if $this->SK is present and can type type-juggled to true, then its negation will result on a false value, which when compared to 1 (a true value) is never true. Whereas if $this->SK is empty or type-juggles to false, the incorrect comparison later will never be executed, so your validation will never report failure.
try this:
public function ceksk($attribute) {
if (!$this->$attribute == 1)
$this->addError($attribute, 'Maaf, anda harus mencentang persetujuan syarat & ketentuan sebelum melanjutkan');
}
source:
http://www.yiiframework.com/wiki/168/create-your-own-validation-rule/
I've looked over various approaches, and various questions published on this subject, but with no luck. In my case, the controller code "appears" to work, and the message flashes up "Your changes have been saved", but the password database field is unchanged. Is there something I am missing?
Controller code
public function changepass($id = null) {
$this->layout = 'profile_page';
//$this->request->data['User']['id'] = $this->Session->read('Auth.User.id');
$user = $this->User->find('first', array(
'conditions' => array('User.id' => $this->Auth->user('id'))
)); // 'User.id' => $id
$this->set('user',$user);
if ($this->request->is('post') || $this->request->is('put'))
{
$this->User->saveField('password', AuthComponent::password($this->request->data['User']['newpass']));
// $this->User->saveField('password', $this->data['User']['password']);
// $this->data['User']['password']= $this->request->data['User']['newpass'];
if ($this->User->save($this->request->data))
{
$this->Session->setFlash(__('Your password has been changed!'));
$this->redirect(array('controller'=>'articles','action'=>'index'));
}
else
{
$this->Session->setFlash(__('Whoops! Something went wrong... try again?'));
$this->redirect(array('controller'=>'users','action'=>'changepass'));
}
}
$this->request->data = $this->User->read(null, $id);
unset($this->request->data['User']['password']); // tried commenting out
}
Model
public function beforeSave($options = array()) {
if (isset($this->data[$this->alias]['password'])) {
$this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']);
if (isset($this->data[$this->alias]['newpass'])) {
$this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['newpass']);
}
}
return true;
}
Of course later I'd put in existing password check, and confirm new password check, but i need to get the existing password update basic approach working.
Many thanks in advance for any light you can shed on this,
I think I've sussed this. First, major bloop on my part - in my view I'd put echo $this->Form->create('User', array('action' => 'edit')); -- of course change action to 'changepass'
New Controller code:
public function changepass ($id = null) {
$this->layout = 'profile_page';
$this->User->id = $id;
if (!$this->User->exists()) {
throw new NotFoundException(__('Invalid user'));
}
//debug($this->request->data);
if ($this->request->is('post') || $this->request->is('put')) {
$this->data['User']['password']= $this->request->data['User']['newpass'];
if ($this->User->save($this->request->data)) {
$this->Session->setFlash(__('Your password changes have been saved'));
$this->redirect(array('controller' => 'articles', 'action' => 'index'));
} else {
$this->Session->setFlash(__('The profile could not be saved. Please, try again.'));
}
} else {
if ($this->Auth->user('id')!= $id) {
$this->Session->setFlash('You are not allowed that operation!');
$this->redirect(array('controller' => 'articles', 'action' => 'index'));
}
$this->request->data = $this->User->read(null, $id);
debug($this->request->data);
unset($this->request->data['User']['password']);
}
}
Model - tidied up as per advice from eboletaire
public function beforeSave($options = array()) {
if (isset($this->data[$this->alias]['password'])) {
$this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['password']);
}
if (isset($this->data[$this->alias]['newpass'])) {
$this->data[$this->alias]['password'] = AuthComponent::password($this->data[$this->alias]['newpass']);
}
return true;
}
if ($this->request->is('post') || $this->request->is('put')) {
$this->data['User']['password']= $this->request->data['User']['newpass'];
if ($this->User->save($this->request->data)) {
Should be
if ($this->request->is('post') || $this->request->is('put')) {
$this->data['User']['id'] = $id;
$this->data['User']['password']= $this->request->data['User']['newpass'];
if ($this->User->save($this->data)) {
Watch out for $this->data vs $this->request->data.
First of all, you are saving the password twice. Remove/comment this line:
$this->User->saveField('password', AuthComponent::password($this->request->data['User']['newpass']));
Anyway, I think the problem is in your model. Check out your beforeSave method. Why are you setting password first with field password and then with field newpass???
PD. Cleaning up your code I've also seen that maybe the second if should be outside the first one.
I started to make some differences between those users which have authenticated and those that not. For this, i am using
Yii::app()->user->id;
However, in a determined view i put the following code:
<?php
if(Yii::app()->user->isGuest) {
print("Welcome back Guest!");
print("Your id is ".Yii::app()->user->id);
} else {
print("Welcome back ".Yii::app()->user->name);
print("Your id is ".Yii::app()->user->id);
}?>
And i always get the "welcome back guest!", whether i have logged in (successfully) or not. And if i have logged in, then it displays the welcome message together with the user's id!
EDIT
#briiC.lv
Hey.. sorry for the late reply, I hope you are still following this! I am not extending the given UserIdentity class. Is this mandatory? Since i still dont get very well the whole authorization issue, i thought it would be best to give a try with the class they provide, and then extend with my own functionality.. Anyway, next i post my UserIdentity class with its small tweaks.. maybe the problem lies here??
<?php class UserIdentity extends CUserIdentity{
private $_id;
public function authenticate()
{
$user = Users::model()->findAll('username=\''.$this->username.'\' AND password=\''.$this->encryptedPassword.'\'');
if(!isset($user[0]))
{
return false;
}
else
{
$this->setState('id', $user[0]->id);
$this->username = $user[0]->username;
$this->errorCode=self::ERROR_NONE;
return true;
}
}
public function getId()
{
return $this->_id;
}
}
Here is the output i got when i started to log as you suggested; i got this output immediately after successfully logging in.
[05:23:21.833][trace][vardump] CWebUser#1 (
[allowAutoLogin] => true
[guestName] => 'Guest'
[loginUrl] => array ( '0' => '/site/login' )
[identityCookie] => null
[authTimeout] => null
[autoRenewCookie] => false
[autoUpdateFlash] => true
[CWebUser:_keyPrefix] => '0f4431ceed8f17883650835e575b504b'
[CWebUser:_access] => array()
[behaviors] => array()
[CApplicationComponent:_initialized] => true
[CComponent:_e] => null
[CComponent:_m] => null
)
Any help is much appreciated!
Maybe you can try to debug harder:
change messages to something like this:
if(Yii::app()->user->isGuest) {
print("Not logged");
} else {
print_r(Yii::app()->user);
print("Welcome ".Yii::app()->user->name);
print("Your id is ".Yii::app()->user->id);
}
And check session variable in your config/main.php file
...
'session' => array(
'autoStart'=>true,
),
...
The error is in the following line
$this->setState('id', $user[0]->id);
As seen in the official yii documentation regarding auth & auth, setState should be used for anything but the id field. In order to implement the key Yii will use to identify your user, return a unique value per user in the Identity getId() function.
In your case, this means you simply have to change the above line into the following:
$this->_id = $user[0]->id;
Regarding the actual inner working of the login procedure, I'd recommend a look at the CWebUser class, and especially at its login function, which is responsible for the actual storage of the Identity getId() return value.
when you call authenticate function login user as
$userIdentity = new UserIdentity($username, $password);
$userIdentity->authenticate();
if ($userIdentity->errorCode===UserIdentity::ERROR_NONE) {
Yii::app()->user->login($userIdentity,0);
}
and fetch id as
echo 'id='.Yii::app()->user->getId();
apply this code and check
I have faced same problem and found that only one line in UserIdentity Component will resolve this issue.
This is your code:
else
{
$this->setState('id', $user[0]->id);
$this->username = $user[0]->username;
$this->errorCode=self::ERROR_NONE;
return true;
}
Update this code by this one
else
{
$this->_id = $user[0]->id;
$this->setState('id', $user[0]->id);
$this->username = $user[0]->username;
$this->errorCode=self::ERROR_NONE;
return true;
}
First of all, you need to know condition that sets guest and logged-in user apart.
Based on Yii master's branch CWebUser::getIsGuest():
public function getIsGuest()
{
return $this->getState('__id')===null;
}
Compared to your code:
$user = Users::model()->findAll('username=\''.$this->username.'\' AND password=\''.$this->encryptedPassword.'\'');
if(!isset($user[0])) {
// false
} else {
$this->setState('id', $user[0]->id); // this is for persistent state sakes
...
}
}
In short: you did supply 'id' to Identity persistent state but Yii CWebUser expecting '__id' based on UserIdentity::getId().
Solution is pretty dead simple. You just need to set $this->_id
$user = Users::model()->findAll('username=\''.$this->username.'\' AND password=\''.$this->encryptedPassword.'\'');
if(!isset($user[0])) {
// false
} else {
$this->setState('id', $user[0]->id); // this is for persistent state sakes
$this->_id = $user[0]->id; // this is UserIdentity's ID that'll be fetch by CWebUser
...
}
}
This routine explains how CWebUser get UserIdentity's ID: https://github.com/yiisoft/yii/blob/master/framework/web/auth/CWebUser.php#L221
Please do test it out.
Please try following code. Its working well
//config/main.php
return array (
'component' => array(
'session' => array(
'savePath' => INSTANCE_ROOT.DS.'runtime'.DS.'session',
'autoStart' => true,
),
)
);
// LoginController
class LoginController extends CController {
public function actionLogin () {
if(isset($_POST['LoginForm']))
{
$form = new LoginForm;
$form->setAttributes($_POST['LoginForm']);
if ($form->validate()) {
$user = Users::model()->find('upper(username) = :username', array(
':username' => strtoupper($form->username)));
if($user)
return $this->authenticate($user, $form);
else {
Yii::log( 'som.....', 'error');
$form->addError('password', Yii::t('Username or Password is incorrect'));
}
return false;
}
}
}
protected function authenticate($user, $form) {
$identity = new UserIdentity($user->username, $form->password);
$identity->authenticate();
switch($identity->errorCode) {
case UserIdentity::ERROR_NONE:
$duration = $form->rememberMe ? 3600*24*30 : 0; // 30 days
Yii::app()->user->login($identity,$duration);
return $user;
break;
case UserIdentity::ERROR_EMAIL_INVALID:
$form->addError("password",Yii::t('Username or Password is incorrect'));
break;
case UserIdentity::ERROR_STATUS_INACTIVE:
$form->addError("status",Yii::t('This account is not activated.'));
break;
case UserIdentity::ERROR_STATUS_BANNED:
$form->addError("status",Yii::t('This account is blocked.'));
break;
case UserIdentity::ERROR_STATUS_REMOVED:
$form->addError('status', Yii::t('Your account has been deleted.'));
break;
case UserIdentity::ERROR_PASSWORD_INVALID:
Yii::log( Yii::t(
'Password invalid for user {username} (Ip-Address: {ip})', array(
'{ip}' => Yii::app()->request->getUserHostAddress(),
'{username}' => $form->username)), 'error');
if(!$form->hasErrors())
$form->addError("password",Yii::t('Username or Password is incorrect'));
break;
return false;
}
}
}
class UserIdentity extends CUserIdentity {
const ERROR_EMAIL_INVALID=3;
const ERROR_STATUS_INACTIVE=4;
const ERROR_STATUS_BANNED=5;
const ERROR_STATUS_REMOVED=6;
const ERROR_STATUS_USER_DOES_NOT_EXIST=7;
public function authenticate()
{
$user = Users::model()->find('username = :username', array(
':username' => $this->username));
if(!$user)
return self::ERROR_STATUS_USER_DOES_NOT_EXIST;
if(Users::encrypt($this->password)!==$user->password)
$this->errorCode=self::ERROR_PASSWORD_INVALID;
else if($user->status == YumUser::STATUS_INACTIVE)
$this->errorCode=self::ERROR_STATUS_INACTIVE;
else if($user->status == YumUser::STATUS_BANNED)
$this->errorCode=self::ERROR_STATUS_BANNED;
else if($user->status == YumUser::STATUS_REMOVED)
$this->errorCode=self::ERROR_STATUS_REMOVED;
return !$this->errorCode;
}
}
class Users extends CActiveModel
{
const STATUS_INACTIVE = 0;
const STATUS_ACTIVE = 1;
const STATUS_BANNED = -1;
const STATUS_REMOVED = -2;
// some ..........
public static function encrypt($string = "")
{
$salt = 'salt';
$string = sprintf("%s%s%s", $salt, $string, $salt);
return md5($string);
}
}
check your security configuration for cookies and sessions.
disable session.use_only_cookies & session.cookie_httponly in php.ini
file.
in PHP.INI => session.use_only_cookies = 0
So far found plenty of help to get the pagination working for a get(table) command.
What I need is to pick only few of the entries from a couple of linked tables based on a sql where statement.
I guess the query command is the one to use but in this case how do I do the pagination since that command does not take extra parameters such $config['per_page']
Thanks for the help
Without any more info to go on I think that what you're looking for is something like the following.
public function pagination_example($account_id)
{
$params = $this->uri->ruri_to_assoc(3, array('page'));
$where = array(
'account_id' => $account_id,
'active' => 1
);
$limit = array(
'limit' => 10,
'offset' => (!empty($params['page'])) ? $params['page'] : 0
);
$this->load->model('pagination_model');
$data['my_data'] = $this->pagination_model->get_my_data($where, $limit);
foreach($this->uri->segment_array() as $key => $segment)
{
if($segment == 'page')
{
$segment_id = $key + 1;
}
}
if(isset($segment_id))
{
$config['uri_segment'] = $segment_id;
}
else
{
$config['uri_segment'] = 0;
}
$config['base_url'] = 'http://'.$_SERVER['HTTP_HOST'].'/controller_name/method_name/whatever_your_other_parameters_are/page/';
$config['total_rows'] = $this->pagination_model->get_num_total_rows();// Make a method that will figure out the total number
$config['per_page'] = '10';
$this->load->library('pagination');
$this->pagination->initialize($config);
$data['pagination'] = $this->pagination->create_links();
$this->load->view('pagination_example_view', $data);
}
// pagination_model
public function get_my_data($where = array(), $limit = array())
{
$this->db
->select('whatever')
->from('wherever')
->where($where)
->limit($limit['limit'], $limit['offset']);
$query = $this->db->get();
if($query->num_rows() > 0)
{
$data = $query->result_array();
return $data;
}
return FALSE;
}
This should at least get you on the right track
If this isn't what you're asking I'd happy to help more if you can be a little more specific. How about some of your code.
The only other options that I can think of would be to either code a count in your select statement or not limit the query and use array_slice to select a portion of the returned array.