Still learning Phalcon through the tutorials but I have an issue with displaying form error messages beside form fields.
The form code is as follows
<?php
use Phalcon\Forms\Form;
use Phalcon\Forms\Element\Text;
use Phalcon\Forms\Element\TextArea;
use Phalcon\Validation;
use Phalcon\Validation\Validator\PresenceOf;
use Phalcon\Validation\Validator\Email;
class ContactForm extends Form
{
public function initialize()
{
// Name
$fullname = new Text('fullname');
$fullname->setFilters(array('striptags', 'string'));
$fullname->addValidators(array(
new PresenceOf(array(
'message' => 'Full Name is required'
))
));
$this->add($fullname);
// Email and text area fields with validators
/**
* Prints messages for a specific element
*/
public function messages($name)
{
if ($this->hasMessagesFor($name)) {
foreach ($this->getMessagesFor($name) as $message) {
$this->flash->error($message);
}
}
}
}
The form in using volt engine is below
{{ form('pages/contact') }}
<div class="controls controls-row">
{{ form.render('fullname') }}
{{ form.render('email') }}
<span class="help-inline error">{{ form.messages('fullname') }}</span>
<span class="help-inline error">{{ form.messages('email') }}</span>
</div>
<div class="controls">
<span class="help-inline error">{{ form.messages('comments') }}</span>
{{ form.render('comments') }}
</div>
<div class="controls">
{{ submit_button('Send It', 'class': 'btn btn-primary pull-right') }}
</div>
</form><!--end form-->
The code handling the form in the contactAction() is below
$form = new ContactForm();
if ($this->request->isPost() == true)
{
if ($form->isValid($_POST)==false)
{
$form->messages("fullname");
$form->messages("email");
$form->messages("comments");
}else
{
//send email
}
}
$this->view->form =$form
By calling the $form->messages("fullname") and for other fields the validation message will be printed using {{ flash.output() }} which is usually at the top of the page where I placed the code. How do I get the flash messages to display beside the form fields?
Please help. Thanks
After a lot of playing around, I simply changed the messages function in the ContactForm class to return message and not flash the message.
public function messages($name)
{
if ($this->hasMessagesFor($name)) {
foreach ($this->getMessagesFor($name) as $message) {
return '<font color="FF0000">'.$message.'</font>';
}
}
}
other parts of the code still remains the same.
Related
I have a simple input form with a text input field and a submit button. I am trying to get the value from the input field to be displayed again on the same page after the submit button is clicked. So far laravel always throws an error that the variable is undefined.
Route:
Route::get('/find/names', "FindController#get_name")->name('names');
Controller
public function get_name(){
$name = Input::get('name_by_user');
return $name;
}
view
<form role="form" method="GET">
<div class="row">
<div class="form-group">
<div class="input-group">
<input type="text" name="name_by_user"/>
<span class="input-group-btn">
<button class="btn search-button" type="submit">
<span aria-hidden="true">
<span>Search</span>
</button>
</span>
</span>
</div>
</div>
</div>
</form>
display name after submitting: {{$name}}
I would do something like this
Route
Route::name('names')->get('/find/names', "FindController#get_name");
Controller
public function get_name(){
$collection = Input::all();
$name = $collection->pluck('name_by_user');
return view('view_file_in_resources', compact('name'));
}
Now you will have a $names collection in your view.
But if you only want to fetch result from one row, you controller should look like this:
public function get_name($name){
$name = Input::where('name_by_user', $name)->get();
return view('view_file_in_resources', compact('name'));
}
And your routes file
Route::name('names')->get('/find/names/{name}', "FindController#get_name");
When generating a view inside a controller for a route, you can do the following in a function to return a view with data depending on whether it exists.
public function showNameView() {
if(is_null(Input::get('name_by_user'))
{
return view('my.view')->with(['name' => Input::get('name_by_user')]);
}
else
{
return view('my.view')->with(['name' => Input::get('name_by_user')]);
}
}
You need to return the same view:
public function get_name(Request $request)
{
return view('same.view', ['name' => $request->name]);
}
Or you can redirect back:
return redirect()->back()->with('name', $request->name);
And display name like using session data:
#if (session()->has('name'))
{{ session('name') }}
#endif
Is there a way to display error messages underneath the input fields of a form if a field didn't pass validation? Can I somehow process form in the same action (User/index in my case) that the form is displayed and then send those error messages to view? What I have is : index.volt:
<div class="loginForm">
<form action=<?= $form->getAction(); ?> method="POST">
<label for="username">Username: </label>
<?= $form->render('username'); ?>
<br/>
<label for="password">Password: </label>
<?= $form->render('password'); ?>
<br>
<?= $form->render('login'); ?>
</form>
</div>
LoginForm.php:
<?php
use Phalcon\Forms\Form,
Phalcon\Forms\Element\Text,
Phalcon\Forms\Element\Password,
Phalcon\Forms\Element\Submit,
Phalcon\Validation\Validator\PresenceOf,
Phalcon\Validation\Validator\StringLength;
class LoginForm extends Form {
public function initialize()
{
$this->setAction('login');
$username = new Text('username');
$username->addValidator(new PresenceOf(array (
'message' => 'Can\'t be empty'
)));
$password = new Password('password');
$password->addValidator(new PresenceOf(array (
'message' => 'Can\'t be empty'
)));
$submit = new Submit('login', array('value' => 'Login'));
$this->add($username);
$this->add($password);
$this->add($submit);
}
}
And UserController.php:
<?php
class UserController extends \Phalcon\Mvc\Controller
{
/**
* login form
* #var LoginForm
*/
private $_loginForm;
public function initialize()
{
$this->_loginForm = new LoginForm();
}
public function indexAction()
{
$this->view->setVar('form', $this->_loginForm);
}
public function loginAction()
{
if($this->request->isPost()) {
if (!$this->_loginForm->isValid($this->request->getPost())) {
foreach ($this->_loginForm->getMessages() as $message) {
echo $message. '<br />';
// redirect to User/index and pass error messages to view to display them to a user
}
}
}
}
}
EDIT:
Or it would be even better to process this form on the same action that it is displayed. How can I do this?
First, what you have provided in your index.volt isn't volt content. See here how to configure Volt and use the Volt language in your views.
What you're asking for is called flashing messages in Phalcon.
Unfortunately, in the current version you can just flash messages based on type(success, error, warning, etc.) but you can create your own type, so let's fake that the type means the input name.
UserController.php
...
public function loginAction()
{
if($this->request->isPost()) {
if (!$this->_loginForm->isValid($this->request->getPost())) {
$messages = $this->_loginForm->getMessages();
$userMessage = $messages->filter('username');
if(count($userMessage))
$this->flash->message('username', $userMessage[0]);
$passMessage = $messages->filter('password');
if(count($passMessage))
$this->flash->message('username', $passMessage[0]);
return $this->dispatcher->forward(["action" => "index"]);
} else {
//Login
}
}
index.volt
<div class="loginForm">
<form action="{{form.getAction()}}" method="POST">
<label for="username">Username: </label>
{{form.render('username')}}<br/>
{{ flash.has('username') ? flash.output('username') : '' }}
<label for="password">Password: </label>
{{form.render('password')}}<br>
{{ flash.has('password') ? flash.output('password') : '' }}
{{form.render('login')}}
</form>
</div>
I've followed the guide and gotten the registration working just fine.
The only issue I have with this is the Form has a title:
User
I do not want this title. I want to customize this. How do I change this title.
As for code everything is as in the guide except my controller which is:
/**
* #Route("/SignUp", name="wx_exchange_signup")
* #Template("WXExchangeBundle:User:signup.html.twig")
* #Method({"GET"})
* User sign up - Open to public
* Creates new users based on information they provide
*/
public function signupAction(Request $request)
{
if ($this->get('security.context')->isGranted('IS_AUTHENTICATED_REMEMBERED'))
{
// redirect authenticated users to homepage
return $this->redirect($this->generateUrl('wx_exchange_default_index'));
}
$registration = new Registration();
$form = $this->createForm(new RegistrationType(), $registration, array(
'action' => $this->generateUrl('wx_exchange_signup_create'),
));
return array('form' => $form->createView());
}
The answer I finally found is to only display the form elements that you want:
<form action="/app_dev.php/SignUp/create" method="post" name="registration">
<div id="registration">
<div>
<div id="registration_user">
<div>
{{ form_label(form.user.email) }}
{{ form_widget(form.user.email) }}
</div>
<div>
{{ form_label(form.user.username) }}
{{ form_widget(form.user.username) }}
</div>
<div>
{{ form_label(form.user.password.password) }}
{{ form_widget(form.user.password.password) }}
</div>
</div>
<div>
{{ form_label(form.terms) }}
{{ form_widget(form.terms) }}
</div>
<div>
<button name="registration[Sign Up]" id="registration_Sign Up" type="submit">Sign up</button>
</div>
{{ form_widget(form._token) }}
</div>
</form>
This is documented in the Symfony forms chapter.
I'm trying to update an object:
public function editAction(Artist $artist)
{
if (!$artist) {
throw $this->createNotFoundException('Unable to find Artist entity.');
}
// We create the form from the external re-usable form made in TestBundle/Form/artist.php
$form = $this->createForm( new ArtistType, $artist);
// We get the request type
$request = $this->get('request');
// If it is a POST request, the user validated the form
if ($request->isMethod('POST')) {
// We make the link Request <-> Form
// Now, $request = Values entered by the user
$form->bind($request);
// We validate the values
if ($form->isValid()) {
// We save $artist in the DB
$em = $this->getDoctrine()->getManager();
$em->persist($artist);
$em->flush();
$this->get('session')->getFlashBag()->add('info', 'Artist edited successfully');
// Everything is fine, we redirect the user
return $this->redirect($this->generateUrl('ymtest_Artist'));
}
}
// We pass the createView() form method to the viexw so that it can print the form if the user arrived on this page with a GET method (he didnt validate the form yet)
return $this->render('YMTestBundle:Musician:edit.html.twig', array(
'form' => $form->createView(),
'artist' => $artist
));
}
But when I'm validating the form, I get an exception:
Expected argument of type object or array, string given
My form looks like this:
{# src/YM/TestBundle/Resources/views/Musician/add.html.twig #}
{% extends "YMTestBundle::layout.html.twig" %}
{% block bodyAdmin %}
<div class="container">
<form action="{{ path('ymtest_EditArtist', {'id': artist.id}) }}" method="post" {{ form_enctype(form) }}>
<div class="row">
{% if form_errors(form)|length != 0 %}
<div class="span12 alert alert-error" style="margin-left:0px">
{# Les erreurs générales du formulaire. #}
{{ form_errors(form) }}
</div>
{% endif %}
</div>
<div class="row">
<div class="span10 BoxesW">
<div>
{{ form_label(form.name, "Artist Name") }}
{{ form_errors(form.name) }}
{{ form_widget(form.name) }}
</div>
<div>
{{ form_label(form.biography, "Artist Biography") }}
{{ form_errors(form.biography) }}
{{ form_widget(form.biography, {'attr':{'class': 'span10' }, 'id': 'wysiwyg' }) }}
</div>
{{ form_rest(form) }}
</br>
<div>
<input type="submit" class="btn btn-primary" />
</div>
</div>
</div>
</form>
</div>
{% endblock %}
And the route is correct since I get the form before validating it.
Thanks for your help
UPDATE:
Here is My new controller:
<?php
namespace YM\TestBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Doctrine\ORM\EntityRepository;
use YM\TestBundle\Entity\Artist;
use YM\TestBundle\Form\ArtistType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
class MusicianController extends Controller
{
/**
* #Route("/Artist/edit/{id}")
* #ParamConverter("artist", class="YMTestBundle:Artist")
*/
public function editAction(Artist $artist)
{
if (!$artist) {
throw $this->createNotFoundException('Unable to find Artist entity.');
}
// We create the form from the external re-usable form made in TestBundle/Form/artist.php
$form = $this->createForm( new ArtistType, $artist);
// We get the request type
$request = $this->get('request');
// If it is a POST request, the user validated the form
if ($request->isMethod('POST')) {
// We make the link Request <-> Form
// Now, $request = Values entered by the user
$form->bind($request);
// We validate the values
if ($form->isValid()) {
// We save $artist in the DB
$em = $this->getDoctrine()->getManager();
$em->persist($artist);
$em->flush();
$this->get('session')->getFlashBag()->add('info', 'Artist edited successfully');
// Everything is fine, we redirect the user
return $this->redirect($this->generateUrl('ymtest_Artist'));
}
}
// We pass the createView() form method to the viexw so that it can print the form if the user arrived on this page with a GET method (he didnt validate the form yet)
return $this->render('YMTestBundle:Musician:edit.html.twig', array(
'form' => $form->createView(),
'artist' => $artist
));
}
}
EDIT
We found in the chat that the problem was in the entity annotations.
#Assert\Valid() was used on a string variable.
You have this action="{{ path('ymtest_EditArtist', {'id': artist.id}) }}" that I suppose generates an url like editArtist/1234 (so passing a string).
And then you have this public function editAction(Artist $artist) that's requiring an object of type Artist.
You should change it to something like:
public function editAction($artistid)
{
$em = $this->getDoctrine()->getManager();
$artist= $em->getRepository('YourBundle:Artist')->find($artistid);
if (!$artist) {
throw $this->createNotFoundException('No artist found for id '.$artistid);
}
//Do whatever you want
}
Remark: You don't need to call $em->persist($artist); when updating an object (http://symfony.com/doc/current/book/doctrine.html#updating-an-object).
I have set up a controller with some validation.
public function attemptLogin()
{
$rules = array(
'email'=>'required|email',
'password'=>'required'
);
$validator = Validator::make(Input::all() , $rules);
if($validator->fails()){
return Redirect::to('login')->withErrors($validator);
};
}
If I output the messages directly in the controller
$messages = $validator->messages();
print_R($messages->all());
I get the validation errors - however if I redirect:
return Redirect::to('login')->withErrors($validator);
The $errors array available in the view is always coming up empty.
From laravel four documentation
Note that when validation fails, we pass the Validator instance to the
Redirect using the withErrors method. This method will flash the error
messages to the session so that they are available on the next
request.
Variable $errors it's not an array.
The $errors variable will be an instance of
MessageBag.
For some reason I don't really like #seamlss idea.
You can use this instead.
#if(Session::has('errors'))
<? $errors = Session::get('errors'); ?>
<div class="alert alert-error">
<button type="button" class="close" data-dismiss="alert">×</button>
<ul>
<li id="form-errors" >
<h3> {{ $errors->first('email') }}</h3>
</li>
</ul>
</div>
#endif
I've used some bootstrap components, don't get confused, the only thing you need is the lines with the curly braces and the magical # sign.
From laravel docs error-messages-and-views
So, it is important to note that an $errors variable will always be
available in all of your views, on every request, allowing you to
conveniently assume the $errors variable is always defined and can be
safely used.
You can also check this
#if( $errors->has('email') )
<div class="control-group error">
<label class="control-label" for="email">Email</label>
<div class="controls">
<input type="text" id="email" placeholder="Email" name="email">
<span class="help-inline">{{ $errors->first('email') }}</span>
</div>
</div>
#endif
I've had success redirecting using ->withErrors($validation)
And then in the view checking the condition #if( $errors->count() > 0 )...
#if( $errors->count() > 0 )
<p>The following errors have occurred:</p>
<ul id="form-errors">
{{ $errors->first('username', '<li>:message</li>') }}
{{ $errors->first('password', '<li>:message</li>') }}
{{ $errors->first('password_confirmation', '<li>:message</li>') }}
</ul>
#endif
Have you tried:
return Redirect::to('login')->withErrors($validator);
or
return Redirect::to('login')->withErrors($validator->messages());
Here is another possibility when you have difficulty showing errors, and when they are passed correctly via ->withErrors(). If you redirect to a controller action that has to perform any sort of its own state checking and itself calls ->withErrors(), it will shadow the $errors first injected.
Try the following
return Redirect::to('login')->with('errors',$validator->errors->all());
then from your view loop through this errors
foreach($errors as $error) {
print $error;
}