Is there any utility function in Laravel that allows you to give an alternative value for a specific input field if the old value is empty? Currently I have the following code:
<input type="text" class="form-control" id="title" name="title" value="{{ (!empty(Input::old('title'))) ? Input::old('title') : 'hey' }}">
But its not really that pretty. Any ideas?
use
Input::old('title', 'fallback value')
Yes! Don't use the input tag :)
If you use {{ Form you will get this, and much, much more!
{{ Form::text('email', null, array('class'=>'form-control', 'placeholder'=>'Email Address')) }}
Check out the docs here (http://laravel.com/docs/html & http://laravel.com/docs/requests) and you will notice that when the input is flashed to the session, this input box rendered by blade will automatically replace that "null" (the second parameter) with the flashed value from the session.
This removes the need to check for the old input or have any nasty if/else checks inside your template. Also, you no longer need to worry about any HTML code injections or XSS happening, because Form::text will ensure that the text is correctly converted to their HTML entities.
Where you are checking for errors, you should use a Laravel validator. Something similar to this:
protected function createUser(){
$rules = array(
'email'=>'required|email',
'password'=>'required|min:6|confirmed',
'password_confirmation'=>'required'
);
$validator = Validator::make(Input::all(), $rules);
if (! $validator->passes()) {
Input::flashExcept('password', 'password_confirmation');
return Redirect::to('my_form');
} else {
// do stuff with the form, it's all good
}
return Redirect::intended('/complete');
}
Additionally, in your template, you can show all of the errors from the form:
<ul>
#foreach($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
Or just select the first error and show it under the {{ Form::text
#if ($errors->has('first_name'))
<span class="error">{{$errors->first('first_name')}}</span>
#endif
Laravel has all of this built in, and you get it for free! Using Requests, Validators, Blade/HTML
Related
Quoting laravel documentation:
Likewise, you may use the * character when specifying your validation messages in your language files, making it a breeze to use a single validation message for array based fields:
'custom' => ['person.*.email' => ['unique' => 'Each person must have a unique e-mail address']]
It seems it does not work. I have a validation message:
'infos.*.*.*' => ['required' => 'My text']
Then I have some inputs in my view:
<input type="text" name="infos[1234][0][name]">
<input type="text" name="infos[1234][1][name]">
<input type="text" name="infos[5678][0][name]">
And in my controller I validate the input:
$this->validate($request, [
'infos.*.*.*' => 'required'
]);
And in view I have a error displayer:
#if (count($errors) > 0)
<strong>Oops. Errors:</strong>
<ul>
#foreach ($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
#endif
And if I leave all input empty I got:
My text
My text
My text
My text
My text
etc.
What's wrong with my code? Reading Laravel documentation I though it should have worked (I mean: it should have been displayed only once). Did I misunderstood something?
This is working as intended. Since you're passing multiple inputs in an array, the validation throws an error for each item. Therefore 3 inputs with 2 causing errors and 1 passing would obviously pass error for 2 inputs. So in your case the multiple errors are due to multiple inputs failing validation.
I've been working on migrating several of our forms to Laravel, but there's one last step I'm not entirely sure on how to go about. I have a form that does an Insert into a database, but instead of just having 2 pages--the form and the submission page--I have 3: the form, a confirmation and a submission page.
Here is what I have at the moment:
Routes:
Route::any('application/housing-form', array('as'=>'application.form', 'uses'=>'ApplicationController#form'));
Route::post('application/confirmation', array('as'=>'application.confirmation', 'uses'=>'ApplicationController#confirmation'));
Route::post('application/submit', array('as'=>'application.submit', 'uses'=>'ApplicationController#submit'));
ApplicationController:
public function form()
{
$application = new Application;
return View::make('application/form')->with(array('application'=>$application));
}
public function confirmation()
{
$input = Input::all();
//More here?
return View::make('application/confirmation')->with(array('input'=>$input));
}
public function submit() {
$input = Input::all();
DB::table('application')->insert(
array(
<field1> => $input('field1')
...
)
);
return View::make('application/submit');
}
Views:
//form
{{ Form::model($application, array('route'=>'application.confirmation')
//inputs
{{ Form::submit('Continue') }}
{{ Form::close() }}
//confirmation
{{ Form::open(array('route'=>'application.form') }}
{{ Form::submit('Back to my information') }}
{{ Form::close() }}
{{ Form::open(array('route'=>'application.submit') }}
{{ Form::submit('Submit') }}
{{ Form::close() }}
//submission
<p>Thank you for your submission!</p>
What I am unsure about is how to persist the data from the form through the confirmation page and into the submission page. From what I can tell, I can see a few options:
Reflash all of the input
Use a hidden field (or fields) to send the information
Insert the information into the database in the confirmation page and just do an update with an in-between query with the information.
I'm pretty sure it would be the first one: reflashing the data. But if so, I'm not sure where you're actually supposed to call Session::flash or Session::reflash. Or how many times I need to do it to get it through all of the requests. Any suggestions on how to go about that, or how to streamline the rest of the form would be greatly appreciated.
One extra note as well is that this particular form deals with a large number of input fields (around 60). That's part of why I want to avoid having to request each individual field to a minimum.
What I would do is to flash the input to the session in order to repopulate the form. This can be achieved by using the Input::flash() method like so:
public function confirmation(){
Input::flash(); //this will store the input to the session
return View::make('application/confirmation');
}
Then in your view, use the Input::old() method to retrieve input data from the previous request:
{{ Form::text('fieldname', Input::old('fieldname')) }}
I am submitting an array of inputs to my controller like so:
<input id="box-1-nickname" name="box-nickname[]" class="form-control" type="text" placeholder="Required">
<input id="box-2-nickname" name="box-nickname[]" class="form-control" type="text" placeholder="Required">
I am doing some validation like this:
$validator = Validator::make(Input::all(), array(
'supplies-count' => 'required|in:0,1,2,3,4',
));
$arrayValidator = Validator::make(Input::all(), []);
$arrayValidator->each('box-nickname', ['required|min:1|max:60']);
if( $validator->fails() || $arrayValidator->fails() ) {
return Redirect::route('route-2')
->withErrors($arrayValidator)
->withInput();
}
The problem is when I try to check the errors like this it doesn't work:
if( $errors->has('box-1-nickname') ) { echo ' has-error'; }
Displaying input array errors in the view (L5.8 onwards)
To get the first validation error for an input array:
{{ $errors->first('input_array.*') }}
To check if there is an error within an input array:
#if($errors->has('input_array.*'))
<h1>There is an error in your input array</h1>
<ul>
#foreach($errors->get('input_array.*') as $errors)
#foreach($errors as $error)
<li>{{ $error }}</li>
#endforeach
#endforeach
</ul>
#endif
Other examples:
#error('input_array.*')
<div class="alert alert-danger">{{ $message }}</div>
#enderror
From 5.8^ documentation
Working with error messages
If you are validating an array form field, you may retrieve all of the messages for each of the array elements using the * character:
foreach ($errors->get('attachments.*') as $message) {
//
}
Hope it helps!
You've probably long found a solution, but for anyone else who stumbles across this:
The validator uses array dot notation of the field array keys. For example box-nickname[0] becomes box-nickname.0
Therefore if( $messages->has('box-nickname.0') ) { echo ' has-error'; } should give you your desired result. However, you will need to dynamically generate the array key since as you've said, you won't know how many box-nicknames are being applied. I use this in my form view:
#if(!is_null(Input::old('box-nickname')))
#foreach(Input::old('box-nickname') as $n => $box-nickname)
#include('box-nickname-create-form-partial')
#endforeach
#endif
Then create a partial view called "box-nickname-create-form-partial.blade.php" or whatever you want to call it with the form field, which might look something like this:
<div class="form-group {!! $errors->has('box-nickname.'.$n) ? ' has-error' : '' !!}">
<input name="box-nickname[{{$n}}]" class="form-control" type="text" placeholder="Required">
</div>
I hope that's helpful.
The errors are collected by name property, not id, and Laravel's default MessageBag variable is $messages, not $errors:
if( $messages->has('box-nickname') ) { echo ' has-error'; }
http://laravel.com/docs/4.2/validation#working-with-error-messages
$errors is correct, but you should be checking for box-nickname. As you can see you will run into the issue of not being able to identify what box is what because of the generic name. I think the easiest way to to give each input a unique name (eg. box-1, box-2)and do a for loop on the server side to retrieve inputs that start with box-.
so I have a selection box that gives a dropdown menu to give messages a manager from the dropdown. It takes the input and then changes to a column in the database called manager for it's respective column. When I try to submit the selection menu it gives me the regular error for Laravel. But then when I put ?debug=1 at the end it submits but gives the row's manager column a value of just blank.
Here is what I have in the routes.php
Route::get('foo/{id}', 'fooController#bar');
Route::post('foo/{id}', 'fooController#bar');
This is the form.
{{ Form::open(array('url' => '/admin/foo' . $message->id)) }}
{{ Form::select('handler[]', array('unassigned', 'foo', 'bar'), null, array('style' => 'width: 127px')); }}
{{ Form::submit('Change manager') }}
{{ Form::close() }}
{{ $message->manager }}
and here is what is in the fooController
public function bar($id = null)
{
$message = Message::find($id);
$handler = Input::get('handler[]');
$message->manager = $handler;
$message->save();
return Redirect::action('AdminController#foo_bar');
}
I had a problem like this the other day, I have zero recollection of what I did. I really appreciate any help, thanks! The database is postgresql if that's any help
Try a dd(Input::all()) at the beginning of your controller and make sure you're seeing what you expect.
Also since you're sending an array perhaps you have to do Input::get('handler.0') -- see here right below the Input::only() and Input::except() code block.
It would seem as though because you are naming your select handler[], PHP is grabbing it as part of an array.
When setting up your message model, try this...
public function bar($id = null)
{
$message = Message::find($id);
$handler = Input::get('handler[]');
$message->manager = $handler[0];
$message->save();
return Redirect::action('AdminController#foo_bar');
}
Usually, you'd only use names in your forms post-fixed with [] when you are accepting multiple values like checkboxes/multi-selects etc... Otherwise, it's probably best to stick with not using it because it may cause confusion.
I managed to fix it in a almost frustratingly simple way by just changing the method to PUT.
like this
Form::open(array('url' => 'foo/bar', 'method' => 'put'))
I have a controller where I am creating a form witg two dropdown list inside.
When I am rendering my view, I would like to have the same form elements on the top and the bottom of the page. The problem is that the form elemetns (dropdownlists) are displayed only on the top of the page, even if I am asking twig to put them also on the bottom.
Here is what I would like to have:
The 1 and 2 are the dropdownlists. And I would like to duplicate this on the top and on the bottom of the page.
Any Idea on how can this be done?
The top content and the bottom content, where the two dropdownlists are inside are in a single sseparate twig file (searchPanel.html.twig) and this file is included in the page
{% include "MyBundle:Search:searchPanel.html.twig" %}
Here is the searchPanel.html.twig
<div class="searchPanel">
<form action="{{ path }}" method="POST" {{ form_enctype(form) }}>
Papers per page
{{ form_widget(form.papers_per_page, { 'class': 'ppp'}) }}
/ Sort by
{{ form_widget(form.sort_by, { 'class': 'sort'}) }}
{{ form_rest(form) }}
/ Papers ({{ papers_number }} results)
<input type="submit" class="updateSearchResults" value="Update"></input>
</form>
A problem in your approach is that Symfony's Form-component will render the form elements with id's which would be duplicated if you rendered the same form twice on your page. You might also run in trouble with the csrf_token. The gist being that forms are not intended to be duplicated.
Here is what I would do. Create a twig-template containing your paginator form without using Symfony\Form, i.e. create all form elements statically and pass it the paginator-object (or array) to get the data instead of using form_widget(). Something like this:
<form action="{{ path(app.request.attributes.get('_route') }}" method="POST">
<select name="paginator[per_page]">
{% for per_page in paginator.papers_per_page %}
<option value=""{{ per_page }}">{{ per_page }}</option>
{% endfor %}
</select>
</form>
The form action will automatically submit the data to your current route, so you can embed it in different actions and it will submit the data to the same action. On POST you can just create a paginator-object with the post-data and then add it as the form's data. After that you just use isValid() as usual.
In your controller you can get the data like this:
use Symfony\Component\HttpFoundation\Request;
// ...
public function PaperController()
{
public function listAction(Request $request)
{
if ($request->getMethod() == 'POST') {
$data = $request->request->get('paginator');
$paginator = new Paginator($data);
$form = new PaginatorFormType();
$form->setData($paginator);
if ($form->isValid()) {
// ...
}
}
}
}
You can easily embed the form in your view like this:
{{ include 'AcmeDemoBundle:Form:paginator.html.twig' with { 'paginator': paginator } }}
Basically you just use the Form-component in your controller for validation purposes. If you want to set some default values or add additional arguments you might want to create a macro from that template, but for your use case this should suffice. Maybe someone else has a better solution but this is how I went with a similar problem in one of my projects.
another option is user the render twig helper. That way is is possible render the same form in the page as many time as you want. A difference is that using this helper, is also necessary to treat the form renderization as an independent controller Action namely:
in every place in your twig template you want to render the form in the helper to invoke the form there's must be something like this:
{{ render(controller('BundleNameBundle:Controller:Action', {
'param': paramId
})) }}
Thereafter is just a matter of creating the controller...
Another option is in the controller to create 2 forms:
//first form
$form = $this->createForm(new MyFormType(), ...);
//second form: must have different form name that default,
//to render in twig the fields with different ids
$formType = new MyFormType();
$formType->setName('second_form_name');
$formSecond = $this->createForm($formType, ...);
Send both when rendering the twig form:
return $this->render( ...
'form' => $form->createView(), 'formSecond'=>$formSecond->createView()));
Then define the second with name as
formSecond
, and it will conflict with the first.