Laravel From Request Validaton doesn't show errors - forms

My Laravel Framework is 5.4.19
I have followed the tutorial:
http://www.easylaravelbook.com/blog/2015/08/17/creating-and-validating-a-laravel-5-form-the-definitive-guide/
and created form request file tours2_create_tableRequest.php:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class tours2_create_tableRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'doc_fullnumber' => 'unique:tourists'
];
}
}
(it tests if "doc_fullnumber" from request has no duplicates in 'tourists" model)
I have added this code to the form blade.php file:
#if($errors->count() > 0 )
<div>
#foreach ($errors->all() as $error)
{{ $error }}
#endforeach
</div>
#endif
it seems to me that validation works fine (it redirects the user back to create form page when "doc_fullnumber" is not unique), but it doesn't pass $errors variable.
I've browsed Stackoverflow and found several topics with the same problem. They suggest to wrap all my routes (in web.php) in:
Route::group(['middleware' => ['web']], function () {
});
I've done it, but it doesn't help...
here is the form page, actually (temporary): http://f832ee57.ngrok.io/tours_2/create
(the "doc_fullnumber" field is the last one. You can add "2001" there to check validation).
my project on git: https://github.com/Sergey1983/first
Appreciate any help!

To make the unique rule work, you need to have `'doc_fullnumber' field in DB or you need to specify a custom name field:
'doc_fullnumber' => 'unique:tourists,custom_fullnumber_field'

Related

TYPO3 extbase, the php based views do not work when migrating from 9 to 10 version

I have developed a custom TYPO3 extbase extension that was perfectly worked on 8 and 9 version of TYPO3.
Recently I upgraded my installation to TYPO3 10 version but the php based views of my TYPO3 extbase extension do not work anymore.
The error that is displayed is:
Sorry, the requested view was not found.
The technical reason is: No template was found. View could not be resolved for action "getIcal" in class "Luc\Lucevents2\Controller\EventController"
I have read the instructions in the page
https://docs.typo3.org/m/typo3/book-extbasefluid/10.4/en-us/8-Fluid/9-using-php-based-views.html, but there is nothing different from what I have already done in my extension.
So, I am very confused because there is no explanation why the code stopped to work, the above link doesn't include any instructions about deprecated code or possible changes after migration!!
Could you give me any help?
Thank you very much!
George
I hit this problem today. The documentation for "php based views" seems to be outdated.Below is a description of my setup and the solution for TYPO3 10
What I had:
My situation was that I had a Controller with a showAction() action method.
In typoscript, I had setup a new format through the type parameter. Something like
rss = PAGE
rss {
typeNum = 58978
10 =< tt_content.list.20.skevents_eventfeeds
}
For this type (parameter type=58978) I had setup a class named View\EventFeeds\ShowRss with a render() method.
TYPO3 automatically resolved this class based on the type (rss) and the action name (show). This does not work any more.
What solves this issue:
in order for TYPO3 to find the correct class with the render method, it is sufficient to set the defaultViewObjectName variable of the Controller to the correct class name for the specific action. This can be done in the controller with the following new method
public function initializeShowAction()
{
$this->defaultViewObjectName = \Skar\Skevents\View\EventFeeds\ShowRss::class;
}
So now, before calling the showAction method, defaultViewObjectName is set to my ShowRss class which produces the output.
Please note that my ShowRss class extends \TYPO3\CMS\Extbase\Mvc\View\AbstractView . According to https://docs.typo3.org/m/typo3/book-extbasefluid/master/en-us/8-Fluid/9-using-php-based-views.html this is deprecated in TYPO3 11 and will have to change for TYPO3 12
the static template is already included and all the other operations of the extension are working very well except the php based views. The ext_localconf.php looks as follows: <?php
defined('TYPO3_MODE') || die('Access denied.');
$boot = function () {
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
'Luc.Lucevents2',
'Luceventsdisplay',
[
'Event' => 'list, show, delete, edit, monthview, test, ajax, deleteConfirm, showRss, getIcal, addForm, add, editForm, importeventtoforum',
'Channel' => 'list, show',
'Tag' => 'filter, show'
],
// non-cacheable actions
[
'Event' => 'list, show, delete, edit, monthview, test, ajax, deleteConfirm, showRss, getIcal, addForm, add, editForm, importeventtoforum',
'Channel' => 'list, show',
'Tag' => 'filter, show'
]
);
};
$boot();
unset($boot);
The action is registered in EventController.php and looks as follows:
/**
* action geticalaction
*
* #param \Luc\Lucevents2\Domain\Model\Event $event
* #return void
*/
public function getIcalAction(\Luc\Lucevents2\Domain\Model\Event $event = NULL)
{
$base_url = $this->request->getBaseUri();
$channel=$this->settings['channel_for_simple_users'];
if($this->request->hasArgument('duration'))
{
if ($this->request->getArgument('duration')=='6month') {
$duration=$this->request->getArgument('duration');
}
else
{
$duration='month';
}
$events = $this->eventRepository->get_events_for_rss_and_ical($duration,$channel);
$eventsarray= array();
foreach ($events as $obj){
$this->uriBuilder->setCreateAbsoluteUri(true);
$uri = $this->controllerContext->getUriBuilder()
->setUseCacheHash(false)
->uriFor('show', array("event" => $obj->getUid()));
$eventsarray[] = array($obj->getTitle(), $obj->getDescription(), $obj->getStartdatetime(),$obj->getEnddatetime(), $obj->getBuildingid(), $obj->getRoomid(), $obj->getPlace(), $obj->getCrdate(), $uri, $obj->getUid());
}
$this->view->assign('events', $eventsarray);
$this->view->assign('baseurl', $base_url);
}
else
{
$this->uriBuilder->setCreateAbsoluteUri(true);
$uri = $this->controllerContext->getUriBuilder()
->setUseCacheHash(false)
->uriFor('show', array("event" => $event->getUid()));
$this->view->assign('event', $event);
$this->view->assign('baseurl', $base_url);
$this->view->assign('uri', $uri);
}
}
Thank you very much!

symfony error message in form when using custom validation method

I've inherited an app and I'm not (yet) a symfony expert. The app has a simple user entity (among other things, of course) and it has some unique constraints:
/**
* User
*
* #ORM\Table(name="users")
* #ORM\Entity
* #UniqueEntity(
* fields={"username", "school"},
* message="There's already a registered user with that login in this school."
* )
* #UniqueEntity(fields={"email"},
* message="That email is already registered")
* )
*/
When creating a form, and when violating such constraints, the messages are displayed on the form itself and the flow isn't back to the controller (which is perfectly OK).
I have a non-ORM property, with $plainPassword, which holds the entered text in the form, together with the usual $password keeping the crypted password. This is the form part for this field:
$builder->add(
'plainPassword',
TextType::class,
[
'required' => false,
'attr' =>
[
'autocomplete' => 'off',
],
'error_bubbling' => true,
]
)
;
Now, for the password, I have a custom validator, that can be seen in the next function:
public function isPasswordValid(string $p = null)
{
if (null==$p) {
$p = $this->getPlainPassword();
}
// If I'm not changing the password, there must be one previously
$success = strlen($this->getPassword())>0;
if (strlen($p)) {
$success = (strlen($p)>7 && preg_match('/[A-Za-z]/', $p) && preg_match('/[0-9]/', $p));
}
return $success;
}
This functions works nice by itself, but I want to attach it to the form so when the plainPassword field has an "invalid" password (i.e., the function returns false). So I've tried by using an annotation block right before the function:
/**
* #Assert\IsTrue(message = "The password is not a valid password")
*/
public function isPasswordValid(string $p = null)
{
if (null==$p) {
$p = $this->getPlainPassword();
}
// If I'm not changing the password, there must be one previously
$success = strlen($this->getPassword())>0;
if (strlen($p)) {
$success = (strlen($p)>7 && preg_match('/[A-Za-z]/', $p) && preg_match('/[0-9]/', $p));
}
return $success;
}
The message goes to form.errors, and I could show it when the form is called in non-modal mode in a very specific (but different) way, but the default form behaviour via ajax showing as pop-up even skips this behaviour, so I don't get this message. Then, I've tried with error_mapping in the field definition in the form to link the property and the function, but the result is the same (i.e, it's not shown).
I've even tried to build a custom validator. In all these cases, the behaviour when validating the password is OK, but when the password is an invalid one, the form is not submitted, but no error message at all (unlike the other fields defined in the #UniqueConstraint). So, I either Cancel or just enter a valid password and submit the form, but a user doesn't have that reference about the error (and the profiler or the dev logs don't show anything about this, so I have to figure out by changing the password).
So, there must be something I'm missing or it's simply that by the means I've tested it's not possible. I'm diving now with the expression language for a expression assert, but I'm running out of ideas and it's taking me a couple of days just for this, so I ask for your help at this point to find out what I did wrong, overlooked or didn't do yet.
Thanks in advance.
UPDATE: Added a custom validator, where I'm not sure if I manage correctly empty data or anything related.
// ValidPassword.php
namespace XXX\UsersBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* #Annotation
*/
class ValidPassword extends Constraint
{
public $message = 'InvalidPasswordMessage';
}
(I want the message to be handled by the translations file.)
// ValidPasswordValidator.php
namespace XXX\UsersBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use XXX\UsersBundle\Entity\User;
class ValidPasswordValidator extends ConstraintValidator
{
public function validate($p, Constraint $constraint)
{
$success = true;
if (strlen($p)) {
$success = (strlen($p)>7 && preg_match('/[A-Za-z]/', $p) && preg_match('/[0-9]/', $p));
}
if (!$success) {
$this->context->buildViolation($constraint->message)
->addViolation();
}
}
And in the entity:
use XXX\UsersBundle\Validator\Constraints as UserAssert;
...
/**
* #var string
* #UserAssert\ValidPassword
*/
private $plainPassword;
This is the part of the twig template to show the involved fields:
...
<div class="row">
<div class="col-md-4">
{{ form_row(form.name) }}
</div>
<div class="col-md-4">
{{ form_row(form.surname) }}
</div>
</div>
<div class="row">
<div class="col-md-4">
{{ form_row(form.email) }}
</div>
</div>
<div class="row">
<div class="col-md-4">
{{ form_row(form.plainPassword) }}
</div>
</div>
ANSWERING
Your code it almost done. Your trouble is that you are using
'error_bubbling' => true
This config changes the object for error to be appended to the parent field target or the form. So, in your case, errors in plainPasswordField will be added to the form instead to the field.
MORE INFORMATION
After made some tests I guess that there's a little bit confusing about Form Errors
and Form Field Errors.
If your view code does not have form_errors(form) or form(form), all form.vars.errors won't be shown.
Child form fields with error_bubbling=true or codes like:
// Generic Controller
$form->addError(new FormError('My Generic Form Error not associate with any field!'));
return $this->render('my_template.html.twig', [
'form' => $form->createView(),
]);
olny can be displayed in twig template with
form_errors(form)
Useful Tips:
When creating form templates debug your form view with form(form) (must be the first call on template) to show everything in the form object, like fields (with html form input|select,
field label, field errors, field helpers) and errors in the form itself
The doc say about form_rest() but it not show form.vars.errors
Use form.vars.errors|length to check for errors on form view variable in twig

CSRF field is missing when I embed my form with a requestAction in CakePHP 3

I want to embed a contact form in multiple places on my website.
I developed a contact form in a contact() function within my MessagesController.php:
// MessagesController.php
public function contact()
{
$this->set('title', 'Contact');
$message = $this->Messages->newEntity();
... // shortened for brevity
$this->set(compact('message'));
$this->set('_serialize', ['message']);
}
I loaded the CSRF component in the initialize() function of the AppController.php:
// AppController.php
public function initialize()
{
parent::initialize();
$this->loadComponent('Csrf');
... // shortened for brevity
}
The form is rendered with a contact.ctp and it works fine.
I followed CakePHP's cookbook which suggests using requestAction() within an element, then echoing the element where I want it:
// contact_form.ctp
<?php
echo $this->requestAction(
['controller' => 'Messages', 'action' => 'contact']
);
?>
And:
// home.ctp
<?= $this->element('contact_form'); ?>
The problem is that the form is rendered fine, but the CSRF hidden field is missing. It should be automatically added to the form since the CSRF component is called in the AppController.php.
I guess either using an element with a requestAction() isn't the solution for this particular case, or I am doing something wrong.
Any ideas? Thanks in advance for the input!
Request parameters need to be passed manually
requestAction() uses a new \Cake\Network\Request instance, and it doesn't pass the _Token and _csrf parameters to it, so that's why things break.
While you could pass them yourself via the $extra argument, like
$this->requestAction(
['controller' => 'Messages', 'action' => 'contact'],
[
'_Token' => $this->request->param('_Token'),
'_csrf' => $this->request->param('_csrf')
]
);
Use a cell instead
I would suggest using a cell instead, which is way more lightweight than requesting an action, also it operates in the current request and thus will work with the CSRF component out of the box.
You'd pretty much just need to copy your controller action code (as far as the code is concerned that you are showing), and add a loadModel() call to load the Messages table, something like
src/View/Cell/ContactFormCell.php
namespace App\View\Cell;
use Cake\View\Cell;
class ContactFormCell extends Cell
{
public function display()
{
$this->loadModel('Messages');
$this->set('title', 'Contact');
$message = $this->Messages->newEntity();
// ... shortened for brevity
$this->set(compact('message'));
$this->set('_serialize', ['message']);
}
}
Create the form in the corresponding cell template
src/Template/Cell/ContactForm/display.ctp
<?php
echo $this->Form->create(
/* ... */,
// The URL needs to be set explicitly, as the form is being
// created in the context of the current request
['url' => ['controller' => 'Messages', 'action' => 'contact']]
);
// ...
And then wherever you want to place the form, just use <?= $this->cell('ContactForm') ?>.
See also
API > \Cake\Routing\RequestActionTrait::requestAction()
Cookbook > Views > Cells

laravel 4 changing url structure with parameter passing

I'm just a beginner in laravel framework. i created a simple form and controller methods . i think by default the form method is post. but now i need to make it as get method and also wants to pass the selected inputs to controller and shows that parameters in the url also. currently i did like below but failed.
index.blade.php
<?php echo Form::open(array('url' => 'home/find','method' => 'get')); ?>
<select class="location" id="location" name='location'>
<?php
$id1 = /* get user db details based on locations*/
$location_id_drop_down='';
foreach($idl as $lrow)
{
$city_name=Location::where('location_id','=',$lrow['loc_id'])->first();
if($city_name==array()) continue;
if(Input::old('location')==$lrow['loc_id'])
{
$location_id_drop_down.="<option value='" . $city_name['location_name'] . "' selected='selected'>" .$city_name['location_name'] . "</option>";
}
else
{
$location_id_drop_down.="<option value='" . $city_name['location_name'] . "'>" . $city_name['location_name']. "</option>";
}
}
echo $location_id_drop_down;
?>
</select>
<input type="submit" name="search" id="search_submit" class="search_submit" value="Search" />
{{ Form::close() }}
HomeController.php
public function anyFind($s='',$d='',$l='') {
if(Input::get('search'))
{
$location=Input::get('location');
}
else
{
if($l!='~')
$location=$l;
}
/* queries to get the user details and images based on selected location and list them*/
}
Routes.php
Route::get('/find/{location}','HomeController#anySearch');
But this shows the url as mysite.com/home/find?location=Test&search=Search
I need mysite.com/home/find/location
is there any mistake in my code?
Edit
As a part of experiment i tried this method. i gave a redirect like below at the end of my controller function anyfind()
return Redirect::to('/home/find/'.$location);
But this redirects me but did anyone knows how to load the search.search_new.blade.php with this custom url??
Having a field from the form in your url (not query string) is simply not possible with Laravel. What you can do though, is just use javascript for that.
First lets put a placeholder in the action url
<?php echo Form::open(array('url' => 'home/find/%location%','method' => 'get')); ?>
jQuery
$('form').on('submit', function(){ // you maybe need to be a bit more precise with the selector here
var location = $('#location').val();
$(this).prop('action', $(this).prop('action').replace('%location%', location));
});
Vanilla Javascript (if you can't / don't want to use jQuery)
document.getElementsByTagName('form')[0].onsubmit = function(e){
var location = document.getElementById('location').value;
e.target.setAttribute('action', e.target.getAttribute('action').replace('%location%', location));
};
By the way: the code in your question has still some weird stuff in there. I'm just assuming this has happened because of copy paste etc. So if it still doesn't work, make sure you post the correct code
Insert at the end of your method anyFind() inside the HomeController.php where you set the view for the page:
if (Input::get('location') != '') {
return Redirect::route('home.findByLocation',Input::get('location'));
}
return View::make('home', compact('location));
Also in your routes.php add the following code:
Route::get('home/find/{location?}', array('as'=>'home.findByLocation', 'uses'=>'HomeController#anyFind'));

Laravel, Form::select unable to update many to many relationship

I'm new to Laravel, and I'm being dumb on this for sure, cause i've read the documentation and i've searched all over google but i'm not getting how to go over this. I have a M:M relationship between galleries and artist. Inside each gallery edit page I have a form to update the name and url for the gallery that is working fine. In the same page I have 2 other select forms, one for adding artists to the gallery and another to remove artists, that need to update a pivot table called "galleries_artists". I created 2 custom methods for both these forms called "postAdd" and "postRemove" but I can't put them to work regardless of what I try.
Below is the code i have so far. Hope somebody can help me understand the dumb mistakes i'm making.
Model - Artist.php
class Artist extends Eloquent {
protected $fillable = array('name');
public static $rules = array(
'name'=>'required|min:2'
);
public function galeries() {
return $this->belongsToMany('Gallery', 'galeries_artists', 'artist_id', 'gallery_id', 'stand_id');
}
}
Model - Gallery.php
class Gallery extends Eloquent {
protected $fillable = array('name', 'stand_id', 'url');
public static $rules = array(
'stand_id'=>'required|integer'
);
public function stand() {
return $this->belongsTo('Stand');
}
public function artist() {
return $this->belongsToMany('Artist', 'galleries_artists', 'gallery_id', 'artist_id', 'stand_id');
}
}
Controller - GalleriesController.php
public function postAdd($id, $aid) {
$input = array_except(Input::all(), '_method');
$v = Validator::make(Input::all(), Artist::$rules);
if ($v->passes()) {
$gallery = Gallery::find($id);
$add_artist = Input::get();
$add_artist->galleries()->attach(Input::get('add_artist'));
$add_artist->save();
return Redirect::route('admin.galleries.edit')
->with('message', 'Artist added successfully.');
}
return Redirect::route('admin.galleries.edit')
->with('message', 'Something went wrong')
->withErrors($v)
->withInput();
}
public function postRemove($id, $aid) {
$input = array_except(Input::all(), '_method');
$v = Validator::make(Input::all(), Artist::$rules);
if ($v->passes()) {
$gallery = Gallery::find($id);
$remove_artist = Input::get();
$remove_artist->galleries()->detach(Input::get('remove_artist'));
$remove_artist->save();
return Redirect::route('admin.galleries.edit')
->with('message', 'Artist removed successfully.');
}
return Redirect::route('admin.galleries.edit')
->with('message', 'Something went wrong')
->withErrors($v)
->withInput();
}
edit.blade.php
Add Form
{{ Form::open(array('class' => '', 'method' => 'put', 'action'=> array('GalleriesController#postAdd', $gallery->id , $add_artist->id ))) }}
<div class="form-group">
{{ Form::label('Add Artist:') }}
{{ Form::select('add_artist', $other_artists_name, null, array('class'=>'form-control')) }}
</div>
{{ Form::button('Add Artist', array('type' => 'submit', 'class'=>'btn btn-primary')) }}
{{ Form::close() }}
edit.blade.php
Remove Form
{{ Form::open(array('class' => '', 'method' => 'put', 'action'=>array('GalleriesController#postRemove', $id , 'aid'))) }}
<div class="form-group">
{{ Form::label('Remove Artist:') }}
{{ Form::select('remove_artist', $gallery_artists_name, null, array('class'=>'form-control')) }}
</div>
{{ Form::button('Remove Artist', array('type' => 'submit', 'class'=>'btn btn-danger')) }}
{{ Form::close() }}
Routes.php
Route::post('admin/galleries/{galleries}/add/{aid}', 'GalleriesController#postAdd');
Route::post('admin/galleries/{galleries}/remove/{aid}', 'GalleriesController#postRemove');
Route::resource('admin/galleries', 'GalleriesController');
I've been doing so many changes to the code that a lot of things might be mixed up. Sorry if that's the case.
You are making it pretty difficult. Here is what I did, but with checkboxes, which allowed me to cut down on the number of functions and forms I needed to work with. I've skipped the validation, but what I do have was tested and seems to work fine.
Swapping out the checkboxes for a select shouldn't be too much additional work, but I'd suggest going with a multi-select in that case, because again, it would be much simpler to work with for you and much easier to use from the user's standpoint. Let me know if I should modify my answer if it has to be selects.
Controller
class ArtController extends BaseController {
public function getIndex($artist_id)
{
// Get our artist with associated galleries
$artist = Artist::find($artist_id);
$artist->load('galleries');
// Get all galleries to populate our checkboxes
$galleries = Gallery::all();
// Show form
return View::make('art.gallery_update_form')->with('artist', $artist)->with('galleries', $galleries);
}
public function postIndex($artist_id)
{
// Grab our artist
$artist = Artist::find($artist_id);
// Sync the galleries. If no galleries were chosen, send it an empty array. Sync will perform both write and delete operations for you in one shot. Very handy.
$artist->galleries()->sync(Input::get('galleries', array()));
// Reshow the form
return $this->getIndex($artist_id);
}
}
View
#section('content')
{{ Form::open() }}
<!-- Get a list of our ID's so we can check/uncheck the checkboxes as we go -->
<?php $artist_galleries = $artist->galleries->lists('id'); ?>
<!-- Create checkbox for each gallery -->
#foreach($galleries as $gallery)
{{ Form::label($gallery->id, $gallery->name) }}
<!-- 3rd parameter is where the magic happens, it's looking in our list of galleries
we created a few lines up for the id to see if the artist belongs to that gallery or not -->
{{ Form::checkbox('galleries[]', $gallery->id, in_array($gallery->id, $artist_galleries), array('id' => $gallery->id)) }}
<br />
#endforeach
{{ Form::submit() }}
{{ Form::close() }}
#stop
Routes
Route::get('art/{artist_id}', array('uses' => 'ArtController#getIndex'));
Route::post('art/{artist_id}', array('uses' => 'ArtController#postIndex'));
Edit
Just getting your postAdd() method to work, all that should be required is something like this...
public function postAdd($id, $aid)
{
$input = array_except(Input::all(), '_method');
$v = Validator::make(Input::all(), Artist::$rules);
if ($v->passes()) {
$gallery = Gallery::find($id);
$gallery->artists()->attach($aid);
return Redirect::route('admin.galleries.edit')
->with('message', 'Artist added successfully.');
}
return Redirect::route('admin.galleries.edit')
->with('message', 'Something went wrong')
->withErrors($v)
->withInput();
}
I may be a little confused on the purpose of this function. As I wrote it, it will attach a selected artist to a gallery. I'm not sure of the purpose of using input. It looks as though you may also be attempting to save a new artist as though you wish your users to be able to create a new artist and assign that artist to a gallery when the artist is created? If you are passing in the artist id and the gallery id, that should be all you need and there is no need for the Input class.
Usually though, you'd be only passing in the gallery id which you would have generated a link for and it would be in the URI and the artist would be passed in via the form, in which case you would need to use Input::get('add_artist') and that would keep your URI much cleaner as well.
The postRemove() function would be the exact same, except you'd want to use detach() instead. This is all assuming of course all the rest of the functionality which is responsible for passing in the gallery id and artist id are working as well as the relationships themselves you've already setup in your models.