How to redisplay a form with previous values when validation fails? - php

I have a form, which has to be passed by some other validations than unusual (about 4 fields are depending from each other). Thing is, when its failed, I redirect the user back, but then the form loses its values, I dont want it. I know it can be done with session, but there might be a "sanitier" way. Code is usual:
public function printAction()
{
if ($this->getRequest()->getMethod() == "POST")
{
$form->bindRequest($this->getRequest());
if ($form->isValid())
{
.... more validation.... Failed!
return $this->redirect($this->generateUrl("SiteHomePeltexStockStockHistory_print"));
// and this is when I lose the values.... I dont want it
}
}
}

You can use the same action for both GET and POST requests related to a form. If validation fails, just don't redirect and the same form will be redisplayed with entered values and validation error messages:
/**
* #Template
*/
public function addAction(Request $request)
{
$form = /* ... */;
if ($request->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
// do something and redirect
}
// the form is not valid, so do nothing and the form will be redisplayed
}
return [
'form' => $form->createView(),
];
}

You can passe your parametters to the new page when making the new redirection:
$this->redirect($this->generateUrl('SiteHomePeltexStockStockHistory_print', array('name1' => 'input1', 'name2' => 'input2', 'name3' => $input3, ....)));
or directly pass an array of post values:
$this->redirect($this->generateUrl('SiteHomePeltexStockStockHistory_print', array('values' => $values_array)));

You may want to do something like this
class FooController extends Controller
{
/**
* #Route("/new")
* #Method({"GET"})
*/
public function newAction()
{
// This view would send the form content to /create
return $this->render('YourBundle:form:create.html.twig', array('form' => $form));
}
/**
* #Route("/create")
* #Method({"POST"})
*/
public function createAction(Request $request)
{
// ... Code
if ($form->isValid()) {
if (/* Still valid */) {
// Whatever you do when validation passed
}
}
// Validation failed, just pass the form
return $this->render('YourBundle:form:create.html.twig', array('form' => $form));
}
}

Related

Symfony 4 - How to valid my token in controller?

in a function of my controller, I initialize a form that I pass in parameter to a view. The form must then redirect to another action of my controller, like this:
Controller : index()
/**
* #Route("/validation/absences", name="validation_index")
*/
public function index(PaginatorInterface $paginator, Request $request, AbsenceService $absenceService)
{
$refusAbsence = new Absence();
$formRefus = $this->createForm(RefusAbsenceType::class, $refusAbsence);
$formRefus->handleRequest($request);
return $this->render('validation/index.html.twig', [
"formRefus" => $formRefus->createView(),
]);
My form action goes to this function :
/**
* Refuser une demande d'absence
*
* #Route("validation/absences/refuser/{id}", name="validation_refuser")
*
* #param Absence $absence
* #return void
*/
public function refuser(Request $request, Absence $absence)
{
$token = $request->get('refus_absence')['_token'];
if (!$this->isCsrfTokenValid('refus_absence__token', $token)) {
throw new \Symfony\Component\Security\Core\Exception\AccessDeniedException('Accès interdit');
}
$commentaire = $request->get('refus_absence')['commentaire'];
dd($commentaire);
}
I get my token back with the request, but I can not get it to be validated. I still have the mistake.
Yet on Symfony's documentation, they say:
if ($this->isCsrfTokenValid('token_id', $submittedToken)) {
// ... do something, like deleting an object
}
And in my HTML, I've :
<input type="hidden" id="refus_absence__token" name="refus_absence[_token]" value="7bbockF5tz3r7Ne9f6dQB7Y5YMcwd1QRES4vHrhQEQE">
in your receiving function, just recreate the form:
$form = $this->createForm(RefusAbsenceType::class, new Absence());
$form->handleRequest($request);
// also checks csrf, it is enabled globally, otherwise, recreate parameters
// in the createForm call.
if($form->isSubmitted() && $form->isValid()) {
$absence = $form->getData();
// do whatever ... persist and stuff ...
}

Laravel Validator return error messages

public function search()
{
if ($this->articleValidator->validateSearch(request())) {
$response['response'] = TRUE;
$response['data']['articles'] = $this->articleService->searchArticles(request()->keyword, request()->category, request()->from, request()->to);
$response['html'] = view('partials/content-administrator-subsystem/articles', $response['data'])->render();
} else {
$response['response'] = $this->articleValidator->searchValidationErrors();
}
return json_encode($response);
exit;
}
I have this function inside my ArticlesPageController. I send a POST request with axios to this method.
class ArticleValidator implements ArticleValidatorInterface
{
protected $searchValidator;
/**
* Validates articles search request
*
* #param request - Request object
* #returns true/false if validation succeeded
*/
public function validateSearch($request)
{
$this->searchValidator = Validator::make($request->all(), [
'category' => 'array|min:1|exists:categories,id',
'from' => 'date',
'to' => 'date|after_or_equal:from'
]);
return !$this->searchValidator->fails();
}
/**
* Returns search validation errors
*
* #return validation errors or null if everything went well
*/
public function searchValidationErrors()
{
if ($this->searchValidator) {
print_r($this->searchValidator->errors()->getMessages());
return $this->searchValidator->errors();
}
return null;
}
}
This is validator class.
The problem is, if validator fails, I get such return:
{
"response": {
"to": ["validation.after_or_equal"]
}
}
As you can see, it is validation rule that has failed, the problem is, that I need to get actual messages not the rule that has failed.
I know that in normal flow, I can do return redirect()->withErrors($errors) and in a view I would get an $errors array, but now, when it is an AJAX call, I cant do any redirects. so how can I get actual messages and return them back?
As you can see here https://laravel.com/docs/5.5/validation#quick-writing-the-validation-logic, when you use the validate() method and your request was an AJAX one, you get the errors in JSON format in the response. You can see another way to use the Validator here https://laravel.com/docs/5.5/validation#automatic-redirection.
You may have to check for the corresponding version of the documentation because there were slight variations over time, but this should give you a kickstart.

Symfony form submit and validate from another method

I have made a form created after an Ajax request (after the first form (Test1Type) is submitted)
public function indexAction(Request $request): Response
{
$form = $this->createForm(Test1Type::class);
$form->handleRequest($request);
if ($request->isXmlHttpRequest()) {
$form = $this->createForm(Test2Type::class);
return new Response($this->renderView('test/_results.html.twig', [
'form' => $form->createView(),
]));
}
return $this->render('test/index.html.twig', [
'form' => $form->createView(),
]);
}
Then I want to submit, validate and get the datas from this Test2Type in another method
public function confirmAction(Request $request): Response
{
dump($form->getData());
return $this->render('test/confirm.html.twig', [
]);
}
But I don't have acces to my form variable and I will not re-use $form = $this->createForm(Test2Type::class);...
I think this is possible but I really don't have any clues to made this work...
Do you have some ideas ?
It's not possible, you must create $form variable before using it for submittion and validation. To avoid duplicate code on create Test2Type form, you should redirect to confirmAction in indexAction after the form submitted and valid.
public function indexAction(Request $request)
{
$form = $this->createForm(Test1Type::class)->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
return $this->redirectToRoute('confirm.action.route_name');
}
return $this->render('test/index.html.twig', [
'form' => $form->createView(),
]);
}
public function confirmAction(Request $request)
{
$form = $this->createForm(Test2Type::class)->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
return $this->render('test/confirm.html.twig', [
'data' => $form->getData()
]);
}
return $this->render('test/_results.html.twig', [
'form' => $form->createView(),
]);
}
You should add another action in your controller for the ajax request with another route!!
This is better readable code and works better with the history button of your browser. (less cache problems) furthermore you can of course simply change the 'action' attribute of the HTML <form> element. Follow this link to read how: http://symfony.com/doc/current/form/action_method.html

Hash password before saving with Laravel Backpacker

A simple question: how do I modify (hash) the request value before saving it with Laravel Backpacker CRUD admin?
As far as i understand, it should be done somewhere before these methods are executed in the crud controller:
public function store(StoreRequest $request)
{
return parent::storeCrud();
}
public function update(UpdateRequest $request)
{
return parent::updateCrud();
}
but I have no idea how to do it correctly.
Edit: the request is not a Request object, but rather StoreRequest or UpdateRequest that looks something like this:
Fix:
public function update(UpdateRequest $request)
{
// Hash password before save
if (!empty($request->password)) {
$request->offsetSet('password', Hash::make($request->password));
}
return parent::updateCrud($request); // <-- Pass the modified request, otherwise the CRUD reads it again from post data
}
You can update $request values using the offsetSet method
$request->offsetSet('name', $newName);
Edit: To update user password you can do something like this:
public function update_password(Request $request)
{
$user = User::find(Auth::user()->id);
if (Hash::check($request->old_password, $user->password)) {
$user->fill([
'password' => Hash::make($request->password)
])->update();
return redirect()->back()->with('message' => 'Your password has been updated.');
}
else {
return redirect()->back()->with('message' => 'The password entered do not match our records.');
}
}
I did not check the code but it should work. Now update it to your needs.
If you're asking about how to modify data in $request variable, you can just do this:
$request->property = 'New value';
Also, you can add data to reuqest itself (not into variable):
request()->request->add(['key' => 'value']);

Laravel 5 redirecting to auth/login causes failure

Working with Laravel 5 I'm facing an issue to where it routes to auth/login by default. When you login, it redirects to login causing an error. When I'm able to actually use http://localhost/login it actually routes to home like it should. Anything new that would be causing it behave like this?
HomeController shown below:
<?php namespace app\Http\Controllers;
class HomeController extends Controller {
public function __construct()
{
$this->middleware('auth');
}
/**
* Show the application dashboard to the user.
*
* #return Response
*/
public function index()
{
return view('home');
}
public function showLogin()
{
// show the form
return view('login');
}
public function doLogin()
{
// validate the info, create rules for the inputs
$rules = array(
'email' => 'required|email', // make sure the email is an actual email
'password' => 'required|alphaNum|min:3' // password can only be alphanumeric and has to be greater than 3 characters
);
// run the validation rules on the inputs from the form
$validator = Validator::make(Input::all(), $rules);
// if the validator fails, redirect back to the form
if ($validator->fails()) {
return Redirect::to('login')
->withErrors($validator) // send back all errors to the login form
->withInput(Input::except('password')); // send back the input (not the password) so that we can repopulate the form
} else {
// create our user data for the authentication
$userdata = array(
'email' => Input::get('email'),
'password' => Input::get('password')
);
// attempt to do the login
if (Auth::attempt($userdata)) {
// validation successful!
// redirect them to the secure section or whatever
// return Redirect::to('secure');
// for now we'll just echo success (even though echoing in a controller is bad)
echo 'SUCCESS!';
} else {
// validation not successful, send back to form
return Redirect::to('login');
}
}
}
public function doLogout()
{
Auth::logout(); // log the user out of our application
return Redirect::to('login'); // redirect the user to the login screen
}
}
I figured it out to be that constructor.
public function __construct()
{
$this->middleware('auth');
}
I removed that and changed the view to 'auth/login' and it works like a charm.

Categories