How to create a form with validation?


#1

Hello,

I am trying to create a form with Slim. I have These routes:

$app->get('/cats/new', '\CatController:new');
$app->post('/cats', '\CatController:create');

So the first one shows the input form and the second takes its input. The create method validates the input and should redirect back to the new route on errors.

I am not yet sure how to redirect those error Messages to the new route. As far as I know there’s $response->withRedirect(<TARGET>) but no way to add data.

How do I ideally structure those two methods? Should I just Show the form from within the create method? How do I pass the errors to new (to show them inside the form)?

Thank you in advance.


#2

My method would look something like this:

$cat = new Cat;
$cat->name = $request->getParsedBodyParam('name');
$cat->color = $request->getParsedBodyParam('color');
// in my Cat model, I have a validator method I'm calling in this example, but you could put it here instead
$validator = $cat->getValidator($cat);
if (!$validator->validate()) {
    $data [
      'cat' => $cat,
      'errors' => $validator->errors()
    ];
    return $this->view->render($response, 'cat/create.html.twig', $data); // errors available in view as `errors`
}
// All looks good, save the cat and redirect somewhere...

You can see another example of this in akrabat’s slim-bookshelf example.


#3

Thanks for you Reply. So you put post and get in one method? Hmm… I was used to put them in different methods when I used Laravel. That’s why I thought I should Redirect.

OK, I’ll try that Approach. I combined get and post in my routes with $app->map(['GET', 'POST'], '/cats/new', '\CatController:new');

Thank you so far!


#4

No I don’t put both routes in one method. I do it pretty similar to Laravel with index, create, store, show, edit, update, and destroy methods.

I define my routes/controllers for a resource kind of like this.

    $this->get('/users', ['App\Controllers\UsersController', 'index'])->setName('users.index');
    $this->get('/users/create', ['App\Controllers\UsersController', 'create'])->setName('users.create');
    $this->post('/users', ['App\Controllers\UsersController', 'store'])->setName('users.store');
    $this->get('/users/{userId}', ['App\Controllers\UsersController', 'show'])->setName('users.show');
    $this->get('/users/{userId}/edit', ['App\Controllers\UsersController', 'edit'])->setName('users.edit');
    $this->patch('/users/{userId}', ['App\Controllers\UsersController', 'update'])->setName('users.update');
    $this->delete('/users/{userId}', ['App\Controllers\UsersController', 'destroy'])->setName('users.destroy');


#5

Thank you for your answer.

Okay, so your code in your first answer was inside the store (POST) method? So you just show the create.html.twig in your create and store method? Like this:

public function create($request, $response)
{
    return $this->view->render($response, 'cat/create.html.twig', $data);
}

public function store($request, $response) 
{
    // your code from your first answer...
}

Right? I wasn’t sure if you called the template in both methods.

Thanks!


#6

I think so. :slight_smile: In my create method I call the create view like you showed. In my store method, if validation fails I return the create view with the (valid) old views as well as the error message. If validation passes I will redirect to various places depending on what is appropriate for the app… sometimes the index view, sometimes the show view, etc.


#7

Okay, thanks for clarification.

I now do it the same, but I don’t use /cats for the post route. I think it looks a bit weird if you where at /cats/new and suddenly you’re at ``/cats`.


#8

I don’t think most users would notice, but I completely agree. Just haven’t changed the pattern in most of my apps.


#9

Hi tflight;

I have still a problem , how can I redirect to same page if validation failed. I am using a main.php which have two sections for login and for signup. I have used pagination for moving 1 to 2 or 2 to 1 section. In this case how can I redirect to same page if validation failed. I have used like this for log in :

$app->post(’/login’, function ($request, $response, $args) {
// Sample log message
$this->logger->info(“Slim-Skeleton ‘/’ route”);

// TODO:verify login information

// login validatuon page
include_once ‘lib/loginValidation.php’;

if ($validation){

  return $this->renderer->render($response, 'main.phtml', $args);

}else


return $this->renderer->render($response, 'index.phtml', $args);

});

validation codes are in loginValidation.php
Thank you
Regards


#10

I do something like this.

if (!$validation) {
    return $response->withRedirect($this->router->pathFor('login'), 403);
}

#11

Thank you tflight ,

But I have single page with two section , how i redirect to same page same section of page if validation failed. it means I am using main.php which includes one section for login and other for signup , now i want to redirect the page to login if login validation failed . Could you please send me sample of files and structure.

Thank you


#12

You can route to different methods within a class in a couple of different ways, consult the Container Resolution section of the documentation for full info, but here are some examples.

$app->get('/', '\HomeController:home');
// routes to the home method of the HomeController class

$app->get('/', \HomeController::class . ':home');
// same as above, but in a different format

So in your example it might look something like this

$app->get('/', '\MainController:login')->setName('login');
$app->get('/', '\MainController:signup')->setName('signup');

// or maybe you like this better

$app->get('/', \MainController::class . ':login'->setName('login'));
$app->get('/', \MainController::class . ':signup')->setName('signup');

#13

Dear tFlight

I am using respect/validation in my project , the following fatal error found :

Fatal error: Call to a member function setName() on string in C:\myproject\app\Validation\Validator.php on line 29

with codes

<?php namespace App\Validation; use Respect\Validation\Validator as Respect; use Respect\Validation\Exceptions\NestedValidationException; class Validator { protected $errors; public function validate($request, array $rules) { foreach ($rules as $field => $rule) { try { $rule->setName(ucfirst($field))->assert($request->getParam($field)); } catch(NestedValidationException $e){ $this->errors[$field] = $e->getMessages(); } } When I try to remove setName(), it display same error for assert(), how can I Solve this . what happen in line 29? Please, have any solution ? Thank you .

#14

How are you instantiating the Validator class?