I have made a contact form and I want to send the user to a thank you page or back to the contact form with an error message saying what fields were missing that need to be filled.
Initially I tried:
public function submit() {
if (Input::has('name')) {
$name = Input::get('name');
} else {
return Redirect::to('apply')->with('message', 'Login Failed');
}
$this->thanks($name);
}
public function thanks() {
return View::make('apply.thanks', array('metaTitle' => 'Application Sent | Thank you'));
}
But this gives me an error.
So I replaced the line $this->thanks($name); with return Redirect::to('apply/thanks')->with('message', 'Login Failed');
And it works.
My two questions are:
Is this the right way to send the user to the thank you page?
If there is an error, I would like to send back which errors occurred (what required fields weren't filled out). How is this achieved?
I have tried to implement route names:
routes file
Route::post('apply/submit', array('as' => 'applicationSubmit', 'uses' => 'ApplyController#submit'));
Route::post('apply/thanks', array('as' => 'applicationThanks', 'uses' => 'ApplyController#thanks'));
apply file
public function submit() {
$validationErrors = array();
if (Input::has('name')) {
$name = Input::get('name');
} else {
$validationErrors[] = 'You must enter your name';
}
if($validationErrors) {
if(Request::ajax()){
} else {
return Redirect::to('apply')->with('validationErrors', $validationErrors);
}
} else {
if(Request::ajax()){
} else {
return Redirect::route('applicationThanks')->with('name', $name);
}
}
}
But I'm getting the error Symfony \ Component \ HttpKernel \ Exception \ MethodNotAllowedHttpException (I found out how to turn the detailed error messages on :) )
I think you want something like this:
public function submit() {
if (Input::has('name')) {
$name = Input::get('name');
} else {
return Redirect::to('apply')->with('message', 'Login Failed');
}
return Redirect::to('thanks')->with('name', $name);
}
public function thanks() {
return View::make('apply.thanks', array('metaTitle' => 'Application Sent | Thank you'));
}
Of course you need to have defined route to thanks.
But you could also make it simpler without using thanks method at all:
public function submit() {
if (Input::has('name')) {
$name = Input::get('name');
return View::make('apply.thanks', array('metaTitle' => 'Application Sent | Thank you'));
}
return Redirect::to('apply')->with('message', 'Login Failed');
}
I would rather go for second method (there's no need to make extra redirection just to display view).
You could also consider using named routes in case you will change your urls in future
EDIT
Route for thanks need to use get method and not post because you make redirection to it (no form data are sent by user to this route), so instead of:
Route::post('apply/thanks', array('as' => 'applicationThanks', 'uses' => 'ApplyController#thanks'));
it should be rather:
Route::get('apply/thanks', array('as' => 'applicationThanks', 'uses' => 'ApplyController#thanks'));
I advise you to look at Basic routing - you should really understand verbs and routing and in case you don't care you can always use Route::any to match all verbs: POST, GET, PUT, PATCH and DELETE - but in my opinion it's better to use correct verb and not all of them.
Related
Hey Guys I have the following Scenario and I can't think of a better way. Maybe you guys can provide a more DRY method
So update method BaseController from Laravel Voyager
public function update(Request $request, $id)
{
// Update Logic Here
// Redirect Logic
if (auth()->user()->can('browse', app($dataType->model_name))) {
$redirect = redirect()->route("voyager.{$dataType->slug}.index");
} else {
$redirect = redirect()->back();
}
}
return $redirect->with([
'message' => __('voyager::generic.successfully_updated')." {$dataType->getTranslatedAttribute('display_name_singular')}",
'alert-type' => 'success',
]);
Custom Controller that extends the above Base Controller
public function update(Request $request, $id)
{
// Update Logic Copied and Pasted from Base ( Yuck :( )
// Small Change to the Redirect Logic
if (auth()->user()->can('browse', app($dataType->model_name))) {
$redirect = redirect()->route("voyager.{$dataType->slug}.index");
} else {
$redirect = redirect()->route("voyager.{$dataType->slug}.show",$id);
}
}
return $redirect->with([
'message' => __('voyager::generic.successfully_updated')." {$dataType->getTranslatedAttribute('display_name_singular')}",
'alert-type' => 'success',
]);
So my question is with the current structure of the Base Controller Is there any other way to override the redirect logic without literally copying and pasting the whole lot of code
I do not want to edit the BaseController as it will stop me from updating the package
Any thoughts would be great
Cheer
Simply use smaller functions to extract that logic and override it, similar approaches with overriding function through inheritance for changing logic, is used by Laravel on Models see getRouteKey() for example.
In your BaseController.php, i would split it up like so.
{
if (auth()->user()->can('browse', app($dataType->model_name))) {
$redirect = redirect()->route("voyager.{$dataType->slug}.index");
} else {
$redirect = $this->browseRedirectLocation();
}
}
return $redirect->with([
'message' => __('voyager::generic.successfully_updated')." {$dataType->getTranslatedAttribute('display_name_singular')}",
'alert-type' => 'success',
]);
}
protected function browseRedirectLocation() {
{
return $redirect = redirect()->back();
}
Now you should be able to override redirect location in your CustomController.php, instead of the whole function in your implementation class. As i could see it was only the redirect that was changed.
protected function browseRedirectLocation() {
{
return redirect()->route("voyager.{$dataType->slug}.show",$id);
}
my delete function like this:
help me guys before I added this line return Redirect::route('attribute.index');
I had no error but after I have this error
405 Method Not Allowed
public function update($id)
{
$input = Input::all();
$validator = Validator::make($input, CapacityModel::rules());
// process the save
if ($validator->fails()) {
Session::flash('message', trans('messages.error_save'));
} else {
// store
$this->capacity->update($input['capid'], $input);
// redirect
Session::flash('message', trans('messages.success_save'));
return Redirect::route('attribute.index');
}
}
my route like this:
Route::resource('/reference/attribute','nez\attribute\SeiAttributeController',['names'=>'attribute']);
my controller's index like this:
public function index()
{
return View::make($this->view_path.'.index');
}
This looks questionable:
$this->capacity->update($input['capid'], $input);
Where does $this->capacity get resolved? Are you certain the attribute is always set at this point in the method?
The update method typically takes a single parameter, which is an array of keys with their new values.
If you have correctly resolved $this->capacity to the record you want to update, then you would be best off with something along these lines:
$this->capacity->update([
'field_1' => Arr::get($input, 'field_1'),
'field_2' => Arr::get($input, 'field_2'),
//...etc
]);
I'm having trouble displaying the validation errors of a form using a custom validator.
The errors does exist as the debug method shows, it just won't be displayed in the form.
I'd like to be able to show the error message under (or above, or anywhere) the field.
What I've tried
Well, the documentation does state:
When using View\Helper\FormHelper::control(), errors are rendered by
default, so you don’t need to use isFieldError() or call error()
manually.
Nevertheless, I added the following in the form (just below the email control), which didn't do anything more. No message displayed.
if ($this->Form->isFieldError('email')) {
echo $this->Form->error('email', 'Yes, it fails!');
}
I've also found several questions and answers about this issue on SO, but they look outdated (from '09 to '13) and do not seem to correspond to today's CakePHP syntax.
What I've done
Users/forgot_password.ctp
<?= $this->Form->create() ?>
<?= $this->Form->control('email', ['type' => 'email']) ?>
<?= $this->Form->button(__('Reset my password')) ?>
<?= $this->Form->end() ?>
UsersController.php
(notice the specific validation set, as explained in documentation)
public function forgotPassword()
{
if ($this->request->is('post')) {
$user = $this->Users->newEntity($this->request->getData(), ['validate' => 'email']);
if ($user->errors()) {
debug($user->errors()); // <- shows the validation error
$this->Flash->error(__('An error occurred.'));
} else {
// ... procedure to reset password (which works fine!) and redirect to login...
return $this->redirect(['action' => 'login']);
}
}
}
UsersTable.php
public function validationEmail(Validator $validator)
{
$validator
->email('email')
->notEmpty('email', __('An email address is required.'));
return $validator;
}
What it looks like
Update
Thanks to #ndm comment, here is the correct way to display the error.
In UsersController.php:
public function forgotPassword()
{
// user context for the form
$user = $this->Users->newEntity();
if ($this->request->is('post')) {
$user = $this->Users->patchEntity(§user, $this->request->getData(), ['validate' => 'email']); <- validation done on patchEntity
if ($user->errors()) {
$this->Flash->error(__('An error occurred.'));
} else {
// ... procedure to reset password and redirect to login...
return $this->redirect(['action' => 'login']);
}
}
// pass context to view
$this->set(compact('user'));
}
And in the view forgotPassword.ctp:
<?= $this->Form->create($user) ?>
//modify your function as below
public function forgotPassword()
{
if ($this->request->is('post')) {
$user = $this->Users->newEntity($this->request->getData(), ['validate' => 'email']);
if ($user->getErrors()) {
debug($user->getError('email')); // <- shows the validation error
$this->Flash->error(__($user->getError('email')['_empty']));
} else {
// ... procedure to reset password (which works fine!) and redirect to login...
return $this->redirect(['action' => 'login']);
}
}
}
I am trying to prevent double submission through the back button in a simple voting application
I was doing this before. After voting it returns this view
return View::make('votes.votesresults')->with('candidates',$candidates)->with('count',$count);
This passes two variables to votesresult view but unfortunately if someone clicks the back button they can resubmit their votes. I looked around and came across the PRG pattern. Am supposed to use a redirect to prevent this. So i tried this
return Redirect::route('votes.votesresults')->with('candidates',$candidates)->with('count',$count);
Here are my controllers
public function votesuccess()
{
$rules = array(
'name' => 'required'
);
$validator = Validator::make(Input::all(),$rules);
if ($validator->fails()) {
return Redirect::to('votes/index')
->withErrors($validator)
->withInput(Input::except('password'));
} else {
// store
$vote = new Vote;
$candidates = Candidate::all();
$candidate_id =Input::get('name');
$candidate = Candidate::find($candidate_id);
$vote = $candidate->votes()->save($vote);
//$count = DB::table('votes')->where('candidate_id','=','$candidate_id')->count();
$count = DB::table('votes')->count();
// redirect
Session::flash('message', 'Successfully Cast your vote!');
//return View::make('votes.voteresults')->with('candidates', $candidates)->with('count',$count);
return Redirect::route('voteresults')->with('candidates', $candidates)->with('count',$count);
}
}
public function voteresult()
{
$candidates = Candidate::all();
return View::make('votes.voteresults');
}
My routes are like this
Route::post('votesuccess', array('as' => 'votesuccess', 'uses'=>'VoteController#votesuccess'));
Route::get('voteresults', array('as' => 'voteresults', 'uses'=>'VoteController#voteresult'));
This does not work because it returns undefined variable: candidates. My results view requires these variables. So how do I implement the PRG pattern correctly such that I prevent double submission while being able to pass data to my view
You are redirecting to the route named voteresults, which is handled by the voteresult function and not the votesuccess function.
In your votesuccess function, you should load the view and include the candidates variable in that view. You can access the candidates variable stored in the session by using Session::get('candidates').
It would help to see you voter results view to see what's going on there, and where/how you've put in logic to avoid duplicate submission. Are you concerned about accidental re-submissions, or just multiple submissions from the same user? If the latter, then you would need to build in some logic that limits a user to only 1 vote. You could do this by check if the user has a related vote.
if ( ! $candidate->votes() ) {
// no votes exist, proceed with storing vote
}
Below is you controller refactored a bit:
public function votesuccess()
{
$rules = array(
'name' => 'required'
);
$validator = Validator::make(Input::all(),$rules);
if ($validator->fails()) {
return Redirect::back('votes/index')
->withErrors($validator)
->withInput(Input::except('password'));
} else {
// store
$candidate = Candidate::find(Input::get('name'));
$vote = $candidate->votes()->save(new Vote);
// redirect
Session::flash('message', 'Successfully Cast your vote!');
return Redirect::route('voteresults');
}
}
public function voteresult()
{
$candidates = Candidate::all();
$count = DB::table('votes')->count();
return View::make('votes.voteresults', compact('candidates', 'count'));
}
In my CakePHP application, I have setup the PersistantValidation plugin to validate my forms on the model level thanks to a kind previous suggestion. The plugin essentially makes it so that you can use model validation on a partial without having it redirect to the underlying page (ie. the register.ctp view or the login.ctp view, for example).
The validation works great for the login form, but it's not working properly on the user registration form for some reason.
The controller looks like this:
function register() {
if(!empty($this->data)) {
$name = $this->data['User']['name'];
$email = $this->data['User']['email'];
$password = $this->Password->generatePassword();
$this->data['User']['password'] = $this->Auth->password($password);
$this->User->create();
if($this->User->save($this->data)) {
$this->Session->setFlash(__('Your account has been created!', true));
$this->redirect(array('controller' => 'users', 'action' => 'offers'));
} else {
$this->redirect($this->referer());
}
}
}
The PresistentValidation component is also properly setup and included, since it works just fine in the login() function in the same controller. When I run this code, nothing happens. There is no redirect away from the partial, which is good, but the errors don't show up. Also, the errors do show up going to the register.ctp view, which means it isn't a problem with the validations themselves.
Does anyone have any ideas?
function register() {
if(!empty($this->data)) {
$this->data['User']['password'] = $this->Auth->password($password);
if($this->User->save($this->data)) {
$this->Session->setFlash(__('Your account has been created!', true));
$this->redirect(array('controller' => 'users', 'action' => 'offers'));
} else {
$this->redirect($this->referer());
}
}
}