I'm using CakePHP v2.2.1 stable. I have a UsersController with the action add(). I'm trying to send the user info via ajax (from the home page to /users/add) and save the data. My code is something like this:
// /app/View/Pages/home.ctp
<?php
$data = array('User' => array('username' => 'vegeta_super_sayajin',
'password' => 'over9000!', 'email' => 'vegeta#supersayajin.com',
'profile_pic' => '/home/pics/scouter.jpg', 'firstname' => 'Vegeta',
'lastname' => 'Vegeta', 'level_id' => '9001'));
?>
<script type="text/javascript">
var data = <?php echo json_encode($data); ?> //convert $data into json format
$.ajax({url: '/users/add', data: "data="+data, type: 'post'});
</script>
How do I receive this data in the UsersController, so that I can process and save it?
Currently, I'm trying:
// /app/Controller/UsersController.php
function add() {
if($this->request->is('post') {
//returns "Error: [object Object] in logfile
$this->log($this->request->data);
} else {
$this->Session->setFlash(__("The user could not be saved :("));
}
$this->autoRender = false;
}
$this->log($this->request->data) returns Error: [object Object] in the /app/tmp/logs/error.log file, and this user info does not exist in any of $this->request->params's indexes. All my googling so far has returned only complicated cakephp v1.3 techniques. How is this done in cakephp v2.2.1?
You can try the following code. It will work for you.
<?php
$data = array(
'User' => array(
'username' => 'vegeta_super_sayajin',
'password' => 'over9000!',
'email' => 'vegeta#supersayajin.com',
'profile_pic' => '/home/pics/scouter.jpg',
'firstname' => 'Vegeta',
'lastname' => 'Vegeta',
'level_id' => '9001')
);
?>
<script type="text/javascript">
var data = [<?php echo json_encode($data); ?>] //convert $data into json format
$.ajax({
url: 'checks/add',
data: "data="+JSON.stringify(data),
type: 'post'});
</script>
And in your controller's code:
// /app/Controller/UsersController.php
function add() {
if($this->request->is('post') {
$this->log(json_encode($this->request->data, true)); //returns "Error: [object Object] in logfile
} else {
$this->Session->setFlash(__("The user could not be saved :("));
}
$this->autoRender = false;
}
Here is the json_decode documentation. The second parameter true will convert the object into an array.
Related
In my REST API i want to use JWT fro Authorization.
So, I include this extension - https://github.com/sizeg/yii2-jwt
It clear how to create JWT token, but how to validate Token in API side ? I heart, i must use two tokens - auth_token and refresh_token. For what? What different when i whatt to validate and check user ?
I mean - ok, when i receive username and password, I create auth_token (JWT) and update token in users DB, after i return token to frontend.
After frontend will send auth token in each request, and I will validate token and check user in users DB and check access etc. How to realize refresh token and for what?
For example my controller:
class UploadController extends Controller {
public $serializer = [
'class' => 'yii\rest\Serializer',
'collectionEnvelope' => 'items',
];
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => JwtHttpBearerAuth::className()
];
return $behaviors;
}
public function actionIndex() {
//Work with User
}
}
And how to get token from headers ?
Controller
public function actionLogin()
{
$username = Yii::$app->request->post('username');
$password = Yii::$app->request->post('password');
$provider = new ActiveDataProvider([
'query' => User::find()
->where(['user_name' => $username])->asArray()->one(),
]);
$result = $provider->query;
if($result)
{
if (Yii::$app->getSecurity()->validatePassword($password, $result['user_pass']))
{
$tokenId = base64_encode(mcrypt_create_iv(32));
$issuedAt = time();
$notBefore = $issuedAt; //Adding 10 seconds
$expire = $notBefore + 5184000; // Adding 60 Days
$serverName = 'your-site.com';
$data = [
'iat' => $issuedAt, // Issued at: time when the token was generated
'jti' => $tokenId, // Json Token Id: an unique identifier for the token
'iss' => $serverName, // Issuer
'nbf' => $notBefore, // Not before
'exp' => $expire, // Expire
'data' => [ // Data related to the signer user
'id' => $result['user_id'],
'username' => $result['user_name'],
'mobile' => $result['user_mobile'],
'email' => $result['user_email'],
'city' => $result['user_city'],
'state' => $result['user_state'],
'country' => $result['user_country'],
'picture' => $result['user_picture'],
]
];
$jwt = JWT::encode(
$data,
JWT_KEY,
'HS512'
);
$response = [
'status' => true,
'message' => 'Login Success..',
'era_tkn' => $jwt,
];
}
else
{
$response = [
'status' => false,
'message' => 'Wrong username or password.',
];
}
}
else
{
$response = [
'status' => false,
'message' => 'Wrong username or password.',
];
}
return $response;
}
Make global method for check token
public function check_token()
{
$headers = Yii::$app->request->headers;
$token = $headers->get('era_tkn');
if($token)
{
try{
$valid_data = JWT::decode($token, JWT_KEY, array('HS512'));
$valid_data = $valid_data->data;
}catch(Exception $e){
$valid_data = $e->getMessage();
}
}
else
{
$valid_data = 'Required Authentication';
}
return $valid_data;
}
Call check_token mathod
$user_data = $this->check_token();
if (!empty($user_data->id))
{
echo $user_data->id;
}
else
{
echo "Invalid Token.";
}
I have a registration form with fields that are validated in User entity class. The validation works fine, however I can't return JsonResponse with form error messages in it.
My registration form controller method looks like this:
/**
* #Route("/register", name="register")
*/
public function registerAction(Request $request)
{
$user = new User();
$form = $this->createForm(RegistrationType::class, $user);
$form->handleRequest($request);
$errors = "";
if ($form->isSubmitted())
{
if ($form->isValid())
{
$password = $this->get('security.password_encoder')
->encodePassword($user, $user->getPlainPassword());
$user->setPassword($password);
$user->setIsActive(1);
$user->setLastname('none');
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
return new JsonResponse(
array(
'message' => 'Success! User registered!',
), 200);
}
else
{
$errors = ($this->get('validator')->validate($form));
return new JsonResponse(
array(
'message' => 'Not registered',
'errors' => $errors,
), 400);
}
}
return $this->render(
'ImmoBundle::Security/register.html.twig',
array('form' => $form->createView(), 'errors' => $errors)
);
}
I get the following json response when I submit the registration form with invalid data:
{"message":"Not registered","errors":{}}
Actually I'm expecting that "errors":{} will contain some error fields, but it doesn't. Does anyone know what the problem here is?
UPD:
My RegistrationType looks like this:
class RegistrationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstname', TextType::class)
->add('email', EmailType::class)
->add('plainPassword', RepeatedType::class, array(
'type' => PasswordType::class,
'first_options' => array('label' => 'Password'),
'second_options' => array('label' => 'Repeat password'),
'invalid_message' => "Passwords don't match!",
))
->add('register', SubmitType::class, array('label' => 'Register'));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'ImmoBundle\Entity\User',
'csrf_protection' => true,
'csrf_field_name' => '_token',
'csrf_token_id' => 'authenticate',
));
}
}
UPD2: Found the solution. I needed to do this iteration and then call for getMessage():
$allErrors = ($this->get('validator')->validate($form));
foreach ($allErrors as $error)
{
$errors[] = $error->getMessage();
}
Form validated when you call $form->handleRequest($request);
To get form errors use getErrors method
$errors = $form->getErrors(true); // $errors will be Iterator
to convert errors object to messages array you can use code from this response - Handle form errors in controller and pass it to twig
This is exapmle how i'm process errors in one of my projects
$response = $this->get('http.response_formatter');
if (!$form->isValid()) {
$errors = $form->getErrors(true);
foreach ($errors as $error) {
$response->addError($error->getMessage(), Response::HTTP_BAD_REQUEST);
}
return $response->jsonResponse(Response::HTTP_BAD_REQUEST);
}
It's worked for me.
And also this can help you - Symfony2 : How to get form validation errors after binding the request to the form
You must set error_bubbling to true in your form type by explicitly setting the option for each and every field.
I have a route group that looks like this:
Route::group(['prefix' => 'recipe','middleware'=>['auth.basic']], function (){
//Some things to do
});
When credentials are invalid Laravel outputs "Invalid credentials." How do I override this response with my own JSON response?
In AuthController, try this :
public function postLogin(Request $request)
{
$this->validate($request, [
'email' => 'required', 'password' => 'required',
]);
$credentials = [
'email' => $request->input('email'),
'password' => $request->input('password')
];
if (Auth::attempt($credentials, $request->has('remember')))
{
return redirect()->intended($this->redirectPath())
->with('success', 'You are successfully logged in');
}
return Response::json(array(
'success' => false,
'errors' => $this->getFailedLoginMessage(),
));
}
I just had a look at the Illuminate\Auth\SessionGuard. The method getBasicResponse() seems to be responsible for the response on a failed login attempt (with basic auth).
protected function getBasicResponse()
{
$headers = ['WWW-Authenticate' => 'Basic'];
return new Response('Invalid credentials.', 401, $headers);
}
How to actually overwrite it seems a little tricky though. You probably need to extend the SessionGuard Class and implement your own getBasicResponse() method. Thats the easy part, how to actually instantiate your own guard instead of the default one, I don't know yet.
I'm having an issue validation whether a submitted Email Address is Unique in the database.
When the User registers I need to validate whether the email address exists all of the other validation is working fine.
Is there a step missing when you are validating a using an Ajax form in Yii 2.
A User clicks on CTA to register on site/index
use yii\bootstrap\Modal;
use frontend\models\Register;
use yii\helpers\Html;
use yii\helpers\Url;
...
Modal::begin([
'id' => 'modal',
'size'=>'modal-lg',
'clientOptions' => ['backdrop' => 'static', 'keyboard' => FALSE],
]);
echo "<div id='modalContent'></div>";
Modal::end();
?>
<?= Html::button('Register', ['value' => Url::to(['register/create']), 'title' => 'Register', 'class' => 'btn btn-success','id'=>'modalButton']); ?>
This opens up a modal (register/create)
Model Register
class Register extends User
{
...
public function rules()
{
return [
['Email', 'filter', 'filter' => 'trim'],
['Email', 'required'],
['Email', 'email'],
['Email', 'unique', 'targetClass' => '\common\models\User', 'message' => 'This email address has already been taken.'],
];
}
public function signup()
{
$user = new User();
if ($this->validate()) {
$user->Email = $this->Email;
if ($user->save()) {
return $user;
}
} else {
return;
}
}
Register Controller
public function actionCreate()
{
$model = new Register(['scenario' => 'signup']);
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
Yii::$app->response->format = Response::FORMAT_JSON;
Yii::error($model);
return $model->validate();
}
if ($model->load(Yii::$app->request->post())) {
if ($user = $model->signup()) {
if (Yii::$app->getUser()->login($user)) {
return $this->goHome();
}
}
}
return $this->renderAjax('create', [
'model' => $model,
]);
}
The View file
<?php $form = ActiveForm::begin(['id'=> 'register', 'enableClientValidation'=>true, 'enableAjaxValidation'=>true, 'validateOnChange'=> true, 'validationUrl' => Url::to(['register/create'])] ); ?>
<div class="form-group">
<?= $form->field($model, 'Email') ?>
</div>
Javascript file
$script = <<< JS
$('body').on('beforeSubmit', 'form#register', function (event, jqXHR, settings) {
var form = $(this);
// return false if form still have some validation errors
if (form.find('.has-error').length) {
return false;
}
// submit form
$.ajax({
url: form.attr('action'),
type: 'GET',
data: form.serialize(),
success: function (response) {
// do something with response
$(document).find('#modal').modal('hide');
}
});
return false;
});
JS;
$this->registerJs($script);
I was facing a similar issue where my email field was not triggering the "unique" rule in the javascript validation, but the "unique" rule was getting triggered on the form submit.
It's awesome that you came up with a solution for your question that gets the job done, but I think what I just learned could also shed some insight on this question for others.
The key for me was learning/realizing that the "unique" validation rule must use a database lookup to verify if the user input is unique, thus it must use ajax. Other validation rules, such as "required" or "email" don't need ajax to validate. They just use javascript in the client to validate, so they are client validation, whereas the "unique" validator is actually ajax validation.
Note: my code below is not really addressing your code directly, but is intended to communicate the overall understanding and code structure that is needed to answer your question. Some of these steps you already have, but some are missing :)
First, you need a model with a field that requires a 'unique' rule, such as an email address for a user account.
In your Model
public function rules()
{
$rules = [
[['email'], 'unique'],
// ... and all your other rules ...
];
}
When using \yii\widgets\ActiveForm, client validation is enabled by default, but ajax validation is not.
So, next you need to directly turn on ajax validation in the view. This can be done either for the entire form, or just for a single field. The Yii2 Validation docs explain this best, but in my case, I chose to just enable ajax validation on my email field.
In your View
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($user, 'email', ['enableAjaxValidation' => true])->textInput();
//... other form fields and such here ...
<?php ActiveForm::end(); ?>
Next, you also need to handle the ajax validation in your controller, so your controller method could look something like this:
public function actionRegister()
{
$user = new User();
$post = Yii::$app->request->post();
$userLoaded = $user->load($post);
// validate for ajax request
if (Yii::$app->request->isAjax) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ActiveForm::validate($user);
}
// vaidate for normal request
if ($userLoaded && $user->validate()) {
$user->save();
return $this->redirect(['view', 'id' => $user->id]);
}
// render
return $this->render('create', ['user' => $user]);
}
And then here's the catch ... everything above is what you would need when working with a normal (non-ajax) form. In your question, you are working on a form in a modal window that is being submit via ajax, so the above controller method will not work. With ajax forms, it becomes pretty tricky to handle the ajax form validation and the ajax form submit in the same controller method.
As usual, Yii has this all figured out for us, and the validationUrl form parameter will save the day. All you have to do is create a new method in your controller that is specifically for ajax validation, and reference the controller/action URL in your form. Something like this should do the trick:
In your View
<?php $form = ActiveForm::begin([
'id' => 'form-id', // always good to set a form id, especially when working with ajax/pjax forms
'validationUrl' => ['user/validate-email'], //['controller/action'],
]); ?>
In your Controller
public function actionRegister()
{
$user = new User();
$post = Yii::$app->request->post();
// vaidate for normal request
if ($user->load($post) && $user->validate()) {
$user->save();
return $this->redirect(['view', 'id' => $user->id]);
}
// render
return $this->render('create', ['user' => $user]);
}
public function actionValidateEmail()
{
// validate for ajax request
if (Yii::$app->request->isAjax) {
Yii::$app->response->format = Response::FORMAT_JSON;
$user = new User();
$post = Yii::$app->request->post();
$user->load($post);
return ActiveForm::validate($user);
}
}
Cheers!
I managed to solve this myself by using Javascript to make an Ajax request and PHP to receive the quest and check whether the Email already exists in the database.
function checkEmail(){
var email_check;
// Get the value of the email input field
var input_value = document.getElementById('register-email').value;
// Send the value to a PHP page to check
$.ajax({
url: 'checkemail.php/',
type: 'POST',
data: {
email_check: input_value
},
success: function(response) {
// If we have a repsonse we need to check whether it is True or False
email_check = response;
if (email_check == 1) {
// If True add error class
$('.field-register-email').addClass('has-error');
$('.field-register-email .help-block').text('The email supplied has already been used');
} else {
$('.field-register-email').removeClass('has-error');
$('.field-register-email .help-block').text(' ');
}
}
});
};
This will send a POST request to the checkemail.php which will check whether the email address is in the database
I have problem with resolving my response which always resolve as true. I am submitting a form for forgotten password, and i have only one field there, that is e-mail. I check in the database for the record on base on the e-mail, and if the record is returned, i set the json to true, else to false. Here is the code from my Codeigniter controller:
public function checkEmail()
{
// set the validation rules
$this->form_validation->set_rules('checkemail', 'E-Mail', 'valid_email');
$this->form_validation->set_error_delimiters('<br /><p class=jsdiserr>', '</p><br />');
// if validation is passed
if ($this->form_validation->run() != FALSE)
{
$ids=array();
$ids[0]=$this->db->where('email', $this->input->post('checkemail'));
$query = $this->backOfficeUsersModel->get();
if($query)
{
$data = array(
'userid' => $query[0]['userid'],
'username' => $query[0]['username'],
'password' => $query[0]['password'],
'firstname' => $query[0]['firstname'],
'lastname' => $query[0]['lastname'],
'email' => $query[0]['email']
);
$currentUser = array();
$currentUser = $this->session->set_userdata($data);
echo json_encode(array("success" => "true"));
} else {
echo json_encode(array("success" => "false"));
}
// form validation has failed
} else {
$errorMessage = "Wrong email!";
}
} // end of function checkEmail
Now, when i check the result in my javascript file, i get always true. Here is the code:
$("#formSendPassword").submit(function(e){
e.preventDefault();
var email = $(this).find("#checkemail").val();
var obj = {email: email};
var url = $(this).attr("action");
$.post(url, obj, function(r){
if(r.success == "true") {
console.log(r.success);
$('#forgotPasswordForm').hide();
$('#successMailMessage').fadeIn()
} else {
$('#forgotPasswordForm').hide();
$('#errorMailMessage').fadeIn()
}
}, 'json')
})
Can anyone give me a hand with this?
Regards,Zoran
Firstly modify the PHP...
json_encode(array("success" => "true"));
to
json_encode(array("success" => true));
and also
json_encode(array("success" => "false"));
to
json_encode(array("success" => false));
Then modify the JS as follows by changing...
if(r.success == "true") {
to...
if(r.success === true) {
See how you go from there!
EDIT: In liaison with the OP we concluded that the actual issue was the way JS was posting the data.
var obj = {email: email};
Should have been...
var obj = {checkemail: email};
There were also a few specific problems with the PHP that were unrelated to the issue but have now been fixed.
if ($this->form_validation->run() != FALSE)
should be
if ($this->form_validation->run() !== FALSE)
or simply
if (!$this->form_validation->run())
Is the best way to chekc for false... != may not always do as you expect!
You need to parse the JSON in the JS: myObj = $.parseJSON(r);
then use myObj.success