Using the same action controller to render and save in yii2 - yii2

the controllers actions of my yii2 application render and validate/save http input data. The first time (when the route was requested) it renders the form but with errors as follows:
I need to render form in the first time without error label. Is there way to solve this using the same action to render and save ?
This is my code:
public function actionCreate()
{
$model = new CourseForm;
$model->attributes = Yii::$app->request->post('CourseForm');
if ($model->validate()) {
// save...
} else {
return $this->render( 'create', [ 'model' => $model, 'errors' => $model->errors ]);
}
}

Because you are loading the attribute and calling the validate() every time the action is called and when the validation fails the view loads with the errors highlighted. So that pretty much makes sense why is it doing like that.
Secondly you can just use $model->load() and supply the post() array to it it will load the attributes automatically.
Your code can be reduced to the following and it will not show the error labels when the page loads untill unless you submit the form
public function actionCreate()
{
$model = new CourseForm();
if($model->load(Yii::$app->request->post()) && $model->validate()){
// save...
}
return $this->render('create', ['model' => $model, 'errors' => $model->errors]);
}

Related

Yii2 Stripe Webhook testing: "[ERROR] Failed to Post"

I set up a basic Webhook endpoint in a Yii2 controller just to test the connection:
class CreditCardController extends Controller
{
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'webhook' => ['post'],
],
],
];
}
public function beforeAction($action)
{
if ($action->id == 'webhook')
$this->enableCsrfValidation = false;
return parent::beforeAction($action);
}
... I just want to dump the payload and return HTTP 200 (roughly based on an example I followed here)
public function actionWebhook()
{
$payload = file_get_contents('php://input');
ob_start();
var_dump($payload);
error_log(ob_get_clean(), 4);
echo json_encode(['status' => 'success']);
}
I installed the Stripe CLI and forwarded the Webhook to my local test server:
stripe listen -f https://testsite.office/credit-card/webhook
When I trigger an event that includes something I am listening for:
stripe trigger invoice.payment_succeeded
I get this message:
[ERROR] Failed to POST: Post "https://testsite.office/credit-card/webhook": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
If I remove the POST rule for the action and code the URL in a browser, it works fine.
Any ideas?
Joe
For anyone slogging along with the same issue (I don't see a lot of Stripe/Yii2 webhook chatter online), here is what worked for me.
I noticed that Yii threw a HeadersAlreadySentException in the Apache error_log. So I had to had to specify the response format as JSON:
return new Response([
'format' => Response::FORMAT_JSON,
'statusCode' => 200,
'statusText' => 'Webhook Handled',
]);
Probably not consequential to this problem, but I also changed accessing the payload to the "Yii way":
$payload = json_decode(Yii::$app->request->getRawBody(), true);

How to close boostrap Dialog in yii2?

I'm trying to do this:
Open a bootstrap dialog with a button, through a URL. Action of the Url in controller is "myController/updateDialog".
By open dialog, this loads a page (myController/updateDialog) with a form to fill data (form has a button to submit).
If I submit the form by clicking the button, in my controller I tried close the dialog and refresh some things in the original page, this I do with JS. But it doesn't work. I always get the window navigator in the blank with the new URL; I expect to keep the original page where the dialog was invoked, to see the changes, same way that does CJUIDIALOG in Yii 1.x.
The controller's code is:
public function actionUpdatedialog($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post() && $model->save() )) {
$this->view->registerJs("window.close(); window.opener.fn.yiiGridView.update('books-heads'); return false;",
$this->view::POS_HEAD);//try close and finally
die(); //finally
}
return $this->render('update', [
'model' => $model,
]);
}
I tried, with :
"window.close();" and
"$(#Mymodal).modal('hide');" and others, but could not sort it out.
You should submit your form via ajax, accept and determine the return value. If it is successful, call $("#your modal box").modal('hide') to close your dialog. e.g:
public function actionUpdatedialog($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post() && $model->save() )) {
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
return ['code' => 200, 'message' => 'success'];
}
return $this->render('update', [
'model' => $model,
]);
}
In the page:
$("#formName").on('submit', function () {
// or use ajax
fetch(...).then(response => {
response.json().then(json => {
if (200 === json.code) {
$("yourModalBox").modal('hide');
} else {
alert(json.message);
}
});
});
});
Answer translation from Google Translate, hope this helps you.

yii2 send data in another view and save in this view with button

i have a problem, after passing the data model in another view with form and displayed, I would like to save them in db in the new view with button submitt
My controller
public function actionOffri()
{
$model = new Viaggi;
if($model->load(Yii::$app->request->post()) && $model->validate()){
// $model->save();
$request = Yii::$app->request;
$params = $request->bodyParams;
Yii::$app->session->setFlash('success', 'succes data');
return $this->render('preview', ['params' => $params]);
}else {
Yii::$app->getSession()->setFlash('error', 'error data');
return $this->render('offri', ['model' => $model]);
}
}
I haven't created an action PREVIEW but read data with pass params,
and I would save the data in the database with another button in view PREVIEW
An easy approach would be to create a form in the Preview view with all the data of $params inside of hidden fields, and after that add another button to submit the hidden form. Another way would be to save the data in the session and retrieve it when you needed.
Hope this helps

How to validate params comming from GET request in Yii2 RESTful service

I have a language parameter that needs to be sent to my documents endpoint. So I have to validate that user has sent this parameter in his GET request.
Making rule in my model didn't do anything:
public function rules()
{
return [
[['language'], 'required'],
];
}
Because of that I have tried this:
1) I have created ParamsValidator class:
<?php
namespace app\modules\v1\components;
use yii\web\UnprocessableEntityHttpException;
use yii\base\Component;
use Yii;
/**
* Class that is responsible for validating input params.
*/
class ParamsValidator extends Component
{
public function validate($params)
{
if (!isset($params['language'])) {
throw new UnprocessableEntityHttpException("Language parameter is required");
}
}
}
I am invoking its validate() method inside my controllers init() method:
public function init()
{
$this->_params = Yii::$app->request->queryParams;
$validator = new ParamsValidator();
$validator->validate($this->_params);
}
And this sort of work. Code works, but I get ugly response back. Instead of nice JSON response, I get bunch of html starting like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Unprocessable entity (#422)</title>
<style>
body {
font: normal 9pt "Verdana";
color: #000;
background: #fff;
}
Instead of this html, I would like some nice JSON response like this:
{
"name": "Forbidden",
"message": "You are not authorized to do this.",
"code": 0,
"status": 403,
"type": "yii\\web\\ForbiddenHttpException"
}
This nice JSON error that you see is made by:
$behaviors['authenticator'] = [
'class' => HttpBasicAuth::className(),
'auth' => [$this, 'authenticate']
];
But obviously my Validator is not doing this.
Questions:
1) How to validate params that comes via GET request ?
2) If my method is right, how to get this nice JSON error response ?
A simple option is by overriding the ActiveController::checkAccess method by adding this inside controller:
public function checkAccess($action, $model = null, $params = [])
{
if ($action === 'index' or $action === 'view')
{
$params = Yii::$app->request->queryParams;
if (!isset($params['language'])) {
throw new \yii\web\ForbiddenHttpException('You are not authorized to do this.');
}
}
}
In case you need to do it at model level you'll need to use the addError() method instead of directly throwing the error. Your model instance will hold it when invoking its validate method. Then inside your action you can simply return it. it will be serialized and errors will be outputted in the right format. There is many ways to achieve this. A simple example may be by using the load method to pass both query and body params to the model before validating it (scenarios may be required):
$queryParams = Yii::$app->request->queryParams;
$bodyParams = Yii::$app->request->bodyParams;
$params = array_merge($queryParams,$bodyParams );
$model = new $modelClass;
$model->load($params , '');
if ($model->validate() === false) return $model;
else {
// do whatever you need
}

Laravel setup for basic API and AJAX-calls

Laravel POST return type?
I'm setting up an API alpha version using basic auth single sign on.
Cant debug with postman/js because its not returning JSON but redirecting me instead. From what Ive read AJAX calls shouldnt be redirected? Do I have errors in the configuration?
Routes:
Route::post('/test1', 'tradesCtrl#test1']);
//Route::post('/test1', ['middleware' => 'auth.basic.once', 'uses' => 'tradesCtrl#test1']);
AJAX call:
$.post(
"http://localhost:8000/test1",
{
p1:"100a",
p2:80
},
function(data, status,jqXHR){
console.log(data);
});
Controller (this will echo HTTP (!):
public function test1(Request $request)
{
if($request->ajax()) {
echo "AJAX!";
} else {
echo "HTTP!";
}
}
Allow cross domain for now in App.php
// allow origin
header('Access-Control-Allow-Origin: *');
Kernel.php disable csrf protection by commeting out
app\Http\Middleware\VerifyCsrfToken::class
If I try to insert validation for parameters
public function test1(Request $request)
{
$this->validate($request, [
'p1' => 'Integer',
'p2' => 'Integer'
]);
echo "Serving stuff";
}
This immediately return in a 404 page not found when failing validation, probably a redirect to something else that's not working??