How to pass conditional variabiles to Laravel Blade Component - laravel

I have a blade component for password fields and this is how I pass my information:
<x-store.password-field
label="Password"
name="password"
placeholder="••••••••"
error="Error message"
/>
As you can see, I have a variabile called error in order to display an error message if available. Now, I know how to display it in the page:
#error('password')
{{ $message }}
#enderror
Is there a way to add this logic directly in the component so it should be something like if $error then add error="$message" to the component? The example below will not work but it's just for illustration to show you what I mean:
<x-store.password-field
label="Password"
name="password"
placeholder="••••••••"
#error('password') error="{{ $message }}" #enderror
/>

Without going too much into details I wold like to describe who Laravel validation works. So this way I believe everything makes more sense.
So when Laravel validates a form based on the rules you have set, it knows what kind of data it is expecting and if there are invalid data provided an ValidationException will be thrown. This
Illuminate\Validation\ValidationException.php
class has all these methods and properties. But I will mention the errorBag property that holds the error messages. So the exception obj($e) have access to all the error messages. If you do not take the responsibility to handle this exception, Laravel will do the handling as well. There is an
Illuminate\Foundation\Exceptions\Handler.php
class and you can see how it is handled in details. But shortly this is where the exception is converted to http response, by default it redirects back, and there are two other method called withInput and withErrors. Both of these flash the data to the request session. So this way form input data and error messages are stored in the session and they can be retrieved anywhere from the application. As you may know retrieving data from the session can be performed using session() helper or Session facade or any other way. But for convenience Laravel provides helper method old() and in the other hand shares the $errors attribute to all the views. So old() is used to retrieve old from input data and $errors is an
Illuminate\Support\MessageBag.php
obj that provides some useful methods to retrieve validation errors. Well $errors is shared to all the views something we would do inside the AppServiceProvider like View::share('errors', session()->get('errors')); but instead of ServiceProvider Laravel has done it using middleware:
Illuminate\View\Middleware\ShareErrorsFromSession
. And that's because no matter if there are errors or not you don't have to test if $errors isSet etc.
Inside of any view file the $errors attribute is available as well as the old() method. So it's all set for you to use. Anyway how you decide to retrieve data from the session is is your choice, but since there are all these things provided as well as Laravel blade instead of:
<?php
if($errors){
?>
//do something
<?php
}
?>
// Blade offers:
#if($errors->has('password'))
//do something
#endif
// Or even more cleaner like:
#error('password')
//do something
#enderror
Since $errors or MessageBag instance is shared to all the views, I don't think you would ever want to pass it like a prop. And that because you can access it directly from the component.
However if you want to send it as a component prop you could do it. But don't forget the $errors is a MessageBag object. As well every single input field can have more than one error message for example name can contain both 'Min length is two characters' and 'Name should contain only letters'. For this reason $errors->get('name') will return an array. Since $errors is an MessageBag instance there are useful methods like $errors->first('name'); that will return the first element on the array and that is the error message, in this case 'Min length is two characters'. I want to mention that the error messages may not be identically to the one Laravel provides. But just to show the point. So in your case try something like this:
<x-store.password-field
label="Password"
name="password"
placeholder="••••••••"
:error="$errors->first('password')"
/>
Notice that when the data you want to provide is not just a simple string, you should add : in front of the attribute name. Otherwise the content it will be considered as a string.

What you can try is take a look inside your blade input component (components/input.blade.php). There you should be able to condition the variable $error with session.

Related

Laravel populate form with cloned model data

I'm implementing a "Clone" button in my application, which should allow to perform the following:
create a copy of the chosen model;
redirect to the create view, whose form field should be populated with the cloned model's data;
allow the user edit some fields;
save the new model.
So far, my ModelController#clone method is:
$newModel = $existingModel->replicate();
$newModel->title = "Copy of ".$existingModel->title;
$newModel->created_at = now() // not sure if necessary, or if it'll be changed once the model is stored in the database
return redirect(route('models.create')); // I know this doesn't do what I need
As it is, obviously, nothing gets passed to the create view, but I can't find any clue on how to do that.
I have tried adding ->withInput(compact($newModel)) to the redirect() call, but I don't see the field being populated.
In the models.create view, I have set up the form field to use the old(...) data, if available.
This answer is almost what I need, but it would imply changing every field to check if there is some sort of input other than the old session data, like this:
<input [other attributes omitted] value="{{ $newModel['title'] ?? old('title') }}">
Is it the right way to do so, or is there a quicker/more standardized way of proceeding?
you could overriding the session old input data by:
Session::put('_old_input', $newModel);
and then just render the old() in form inputs

Laravel 5/ Form security (require clarification)

Not entirely confident I have understood security in Laravel forms enough. For example, if a form contains
<input type="hidden" name="user_id">
then obviously a hacker could change the value before submitting an update.
While I have looked here at CSRF, I've not fully understood if this is enough protection?
E.g. Taking the above, if I go to a site and open a form to edit a record I'm permitted to view but not change, and maliciously alter the "user_id", is it enough that the form is protected with {{ csrf_field() }} or must I employ some further security such as Crypt::encrypt($id) to hide the user_id (held in a database) and Crypt::decrypt($id)?
Is it considered a bad practice to expose a row id (like a user id) in a client browser (even though everything is sent over https)?
Many Thanks
No, it's not enough to use just CSRF token in this case. You also need to use policies, guards, middleware to protect your app.
In this case, someone can alter the user_id if you read it from the form and use after that, so you need to use a policy like this one to protect data (this example is from the docs):
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
Also, when you need to use user ID, always use auth()->id() or auth()->user() if you need whole object. Never read user ID from the form.
The Laravel framework stores the value of this CSRF field like a session variable and matches it when you submit it.
When you submit the form Laravel checks that value from the session value stored. if there is a mismatch an error is thrown !
:)
CSRF token protect the site from cross-site requests, means an external user can't duplicate the form and send a post request. Laravel create a random session token which we place in the hidden field using csrf_field() or Session::token() function. Laravel checks the session with hidden field value from the form before processing the form.
Try removing the form action. It should work.

Laravel-4 Empty PUT data being sent to a resourceful controller

G'day,
I'm having issues with PUT requests made via Chrome Postman to a controller, the PUT data is not present, POST data works fine.
I had performed a composer update prior to ensure that the latest version of vendor products where available and even removed bootstrap/compiled.php.
Is anybody else having similar issues?
The update function with both section_id and data being empty in the response:
public function update($id)
{
$section_id = Input::get('section_id');
$data = Input::all();
return Response::json(array('id' => $id, 'section_id' => $section_id, 'data' => $data));
}
I've debugged the code all the way to ParameterBag.php and $this->request's parameter list is empty, I'm not sure what's supposed to contain any values but all through the code the input values are empty. Not sure what to do now, short of using post instead of put.
PUT parameters don't work "out of the box" because PHP itself has some security restrictions around them. See: http://www.php.net/manual/en/features.file-upload.put-method.php
Laravel does implement a common workaround for this, though.
In Postman (or your form, or curl, or whatever client you're using), simply add a URL parameter name: "_method" value: PUT
Example 1:
?_method=PUT
Example 2:
<input type="hidden" name="_method" value="PUT" />
Laravel uses the symfony Http Foundation which checks for the _method variable and if it's present it routes based on its value, instead of the actual HTTP method used.
You have to send a POST request with adding an extra parameter _method with value PUT and it will works fine.

Laravels flash session stores data twice

this ones a head ache! From my understanding of laravel's flash method for sessions, once it has been set then called, it will be destroyed...
Session::flash( 'key', $data );
somewhere down the line
{{ Session::get( 'key' ) }}
I am using this for form validation. Now when the form does not validate, the application displayed the error, if I amend the form and post again, the database updates, the details are displayed correctly, but the error appears again! This is the same for if I post the form that doesn't validate, it displays the error, but if I then click the navigation link for the same page, it displays again!
Anyone come across this?
regards
Luke
I had this problem once when I did a return view() / return View::Make when it should be a return redirect()->route() in my Controller#update method.
Since Laravel 5.1, you can use the now() method which will only affect the current request :
Session::now('key', 'message');
or
session()->now('key', 'message');
Out of the laravel docs:
The flash method stores an item in the session that will expire after the next request. It's useful for storing temporary data like status or error messages.
This means, it's available at the current and also the next request. It does not get flushed automatically if you access it. To do so, use Session::flush('key');.
Session Flash preserves the session data for 2 requests because it was meant to be used during redirection.
However, I've came across a use case where I do want to use flash for just 1 request in the next view and found an easy way to do it, which is to pull from the session rather than get it. The Session::pull() gets the session data and removes from the session.
#if (Session::has('message'))
<div class="alert alert-message">{{Session::pull('message'}}</div>
#endif
Hope this helps!
It's probably some other issue with your code, if you could share your code it would help us get a better insight into this issue.
You can use the below code snippet to Flash Error messages to your laravel blade template.
#if (Session::has('message'))
<div class="alert alert-success">{{Session::get('message')}}</div>
#endif
I once had similar issue because i used Session::reflash() in my controller.
Ensure you don't have Session::reflash() somewhere in your controller or anywhere in your application, as it flashes whole session... use example: Session::keep(array('username', 'email')); to reflashing only A Subset Of Flash Data
An easy way to flash a message once when you are creating a view (and not redirecting) is:
Session::flash($key, $value);
Session::push('flash.old', $key);
Refer here.
The flash method is meant for storing the data for the next request. Ideally after flashing the data to the session you should be redirecting to a new route. (This is what the accepted answer suggests.)
But if you are not redirecting you can call the forget method after displaying the session in your blade template:
{{ session()->flash('key') }}
#php
session()->forget('flash-info');
#endphp

Codeigniter: Pass form variable into URI

Not sure if this can be done but it seems my main issue is because i have a default route to a method called "index." I want to be able to list all users tagged with a specific keyword. In addition, users can search for other users based on these keywords.
i.e.
www.domain.com/tags/apples
www.domain.com/tags/oranges
www.domain.com/tags/blueberry
It works fine if I go to the URL manually. I'm having issues getting it to work with a form field.
Snippet of the form_open:
<?=form_open('tags/');?>
<p>Search for Tag: <input type="text" name="tag" /></p>
<p><input type="submit" value="Search" /></p>
Here's a snippet of my controller:
function index() {
$data['result'] = $this->tags_model->searchByTag($this->uri->segment(2));
$this->load->view('tags_view', $data);
}
Here's a snippet of my router:
$route['tags'] = "tags/index";
$route['tags/(:any)'] = "tags/index/$1";
Now, I can easily fix all this if I have a method called search, but I don't want the URL to show up as www.domain.com/tags/search/orange.
When you create your form you set it to use POST variables instead of GET, that way they don't go through the url, that's codeigniter's default method for forms.
So your form_open code will generate the following code:
<form method="post" action="tags/" />
If you want them to got through url though, call the form opener this way instead:
form_open('tags/', array('method' => 'get'));
The same applies to any other attributes you want to specify for the form, just follow the same pattern attribute_name => attribute_value inside the array.
More info on the user guide
The problem here is that your form will be submitting all it's data to "/tags", with nothing trailing it, as POST data doesn't come in as part of the URL. Even if it was a GET request however, I don't think that CodeIgniter will take anything out of the querystring and use it as part of the routing segments.
I think what you should do is have a small Javascript function that automatically updates the form action parameter to be tags/<select option value> whenever the select value is changed. This way it will submit to the right place. In order to handle non-javascript enabled browsers, you could have a default action called tags/search that would simply analyze your form data and put out a 301 redirect to the proper tags/<location> once you'd figured it out.
It seems like a bit of overkill here however, as you could really point the form at tags/index and not worry about it. I'm not sure search engines index form submission locations, and even if they did, they certainly wouldn't index a form that submits to dynamic URIs in the way that you want it to. You could still link to the search result pages using tags/apples, etc, but the form could work quite normally just by going to tags/index.
I ended up redirecting the URL and passed the keyword into the URI.
i.e. domain.com/tags/view/

Resources