How to use the rule validation in FormRequest class? - laravel

I have a Company, where I like to validate the update request. Until now I validated the request inside the update() action but I like to move this to its own CompanyUpdateRequest.
In the validation I check of the uniqueness of the tax number but of course I like to allow the same tax number for the very company.
'tax_number' => [
'required',
'string',
Rule::unique('companies')->ignore($company->tax_number),
],
This works as long it is placed inside the action, where I have $company already:
public function update(Request $request, Company $company)
{
}
My question is now, how I get the company inside the CompanyUpdateRequest?
I know that I could put the ID of the company inside a hidden field in the form, send it along with the request, pull the company from DB ... but this feels kind of wrong. Does anybody have a better / another approach or idea?
Thanks a lot.

use route() method. Assume your route parameter name is company-
$this->route('company');
Note: parameter method inside route method needs to exactly same as url route parameter. In this case-
Route::post('yourUrl/{company}','SomeController#method');

You can pass any data through a form:
<input name="company_id" value="{{ $company->tax_number }}" type="hidden">
Then in the CompanyUpdateRequest class:
Rule::unique('companies')->ignore($request->company_id),
You can also change this rule to:
'companies' => 'unique:companies,company_id,' . $request->company_id,

You can get it with $this->route('paramName');
Rule::unique('companies')->ignore($this->route('company')),

Related

Bind model on Request in store method Laravel

I want to store books, with title, year and already existing Author. An author is saved in a table authors that have a one to many relationship with books table. To create a book i have a from with two text inputs and one select. Select is filled from database.
Now i want to store them and attach the author immediately.
I can't pass the author in the route because it's dynamically due the select-input.
Is there a possibility to do it like the call below?
Route:
Route::post('/store', [BookController::class, 'store'])->name('book.store');
Controller:
public function store(Request $request,Author $author_id)
{
$validated = $request->validate([
'title' => 'required',
'year' => 'required',
'book_id' => 'required'
]);
Book::create($request->all());
return redirect()->route('book.index');
}
If I understand correctly, you want to use route model binding for your Author, but that value is chosen with a <select> on the form.
Here's a solution to do that; if you don't have to use route model binding, it is much simpler, see the second solution below.
Route model binding solution
For route model binding, you'll need a route like this:
Route::post('/store/{author}', [BookController::class, 'store'])->name('book.store');
So you need to know the author ID to generate the form action URI - which you can't do at page load time because the user has not chosen an author yet. One way to solve this is with Javascript - every time the select chages, find the selected author, and update the form's action.
Here's a working HTML/JS snippet which does that, though you'll need to look at the form in your browser's developer tools to see the action changing.
let form = document.querySelector('form');
let defaultAction = form.dataset.action;
let select = document.querySelector('select');
let debugDisplay = document.querySelector('p');
let action;
select.addEventListener('change', () => {
action = defaultAction + select.value;
form.setAttribute('action', action);
debugDisplay.innerHTML = 'Form action is now "' + action + '"';
});
<form action="" method="post" data-action="/store/">
<select name="author_id">
<option>Choose an author</option>
<option value="1">William Gibson</option>
<option value="2">Iain M Banks</option>
</select>
</form>
<p></p>
And now, assuming you use the route, form, and Javascript shown above, and that you have your Author <-> Book relationships set up, your controller can do:
public function store (Request $request, Author $author) {
// Your validation ...
// Create the book for this author
$book = $author->books()->create($request->all());
return redirect()->route('book.index');
}
Note you should probably include some kind of front end Javascript validation that checks if an author is selected, otherwise the action is not set and the form will post to the current URL, which will probably result in a 404.
Simpler solution
If you can live without route model binding, this is trivially simple. Your existing route is fine:
Route::post('/store', [BookController::class, 'store'])->name('book.store');
Your form should include the author_id, exactly as in the example above.
And the controller just needs 1 extra line to find the selected author:
public function store (Request $request) {
// Your validation ...
// Find the author
$author = Author::find($request->author_id);
// Create the book for this author
$book = $author->books()->create($request->all());
return redirect()->route('book.index');
}
Second parameter must be sent in order to get Model from authors table.
Unless you modify your route to accept params and add JS listener to form on submit, change action URL with selected author, it is not possible per my knowledge..
Since you are not doing anything with Author model, maybe you can just add validation for author_id => "required,exists:authors" ?

Stumpped trying to pass two parameters from view form to controller

I have a Laravel 6 app and am trying to pass two parameters from my view's form to my controller via a resource route. I can pass one, no problem, but passing two gives the same error:
Too few arguments to function App\Http\Controllers\Admin\SubscriptionsController::update(), 1 passed and exactly 2 expected
I've tried many different arrangements suggested from other posts but none bring the desired result.
Here's my route:
Route::resource('subscriptions', 'Admin\SubscriptionsController')
Here's my form in my view:
{{ Form::open(['route' => ['admin.subscriptions.update', $plan->id, $coupon_code], 'method' => 'PUT', 'id' => 'role-' . $plan->id, $coupon_code]) }}
Coupon Code: <input type="text" name="coupon_code">
{{ Form::close() }}
Here's my controller. It doesn't reach the dd() test.
public function update($id, $coupon_code)
{
dd($coupon_code);
...
In the error Whoops! page, I can see the POST DATA that $coupon_code is being sent over.
However, if I remove the $coupon_code parameter from the controller (leaving public function update($id) ) it functions fine passing $id from form to controller, but I don't get the $coupon_code data I need to process. Adding the second parameter bombs it.
Any suggestions are very welcome.
As you are using the resource controller, it won't allow you to pass additional fields directly to its update() route. Instead, you have to override the update route.
Here is how you can do it, Immediately below your resource.
You can add a new route as below:
// web.php
Route::resource('subscriptions', 'Admin\SubscriptionsController')
// Make sure you add name when you are overriding the route
Route::put('subscriptions/{subscription}/{coupon_code?}', 'Admin\SubscriptionsController#update'])->name('admin.subscriptions.update');
I believe you won't be sending the coupon code every time so you can add {coupon_code?} which becomes optional.
In you controller's update() method, make the coupon_code optional
public function update($id, $coupon_code = null)
{
...
}
You can not add a new param for Route::resource. If you really want to take 2 params, you should create a new route.
for an example:
Route::resource('subscriptions', 'Admin\SubscriptionsController')->except('update');
Route::put('/subscriptions/{id}/{coupon_code}', 'Admin\SubscriptionsController#update')->name('subscriptions.update');
But I think it's better not using method params. Why not just using input form?
so we can process the coupon code like this:
request()->coupon_code;

Laravel route difference between {id} vs {tag}

I am new in Laravel pardon me if question is silly. I have seen a doc where they used
For get request
Route::get("tags/{id}","TagsController#show");
For put request
Route::put("tags/{tag}","TagsController#update");
What is the difference and benefit between this ? I understood 1st one, confusion on put route.
There’s no real difference as it’s just a parameter name, but you’d need some way to differential parameters if you had more than one in a route, i.e. a nested resource controller:
Route::get('articles/{article}/comments/{comment}', 'ArticleCommentController#show');
Obviously you couldn’t use just {id} for both the article and comment parameters. For this reason, it’s best to use the “slug” version of a model for a parameter name, even if there’s just one in your route:
Route::get('articles/{article}', 'ArticleController#show');
You can also use route model binding. If you add a type-hint to your controller action for the parameter name, Laravel will attempt to look up an instance of the given class with the primary key in the URL.
Given the route in the second code example, if you had a controller that looked like this…
class ArticleController extends Controller
{
public function show(Article $article)
{
//
}
}
…and you requested /articles/123, then Laravel would attempt to look for an Article instance with the primary key of 123.
Route model binding is great as it removes a lot of find / findOrFail method calls in your controller. In most instances, you can reduce your controller actions to be one-liners:
class ArticleController extends Controller
{
public function show(Article $article)
{
return view('article.show', compact('article'));
}
}
Generally there's no practical difference unless you define a custom binding for a route parameter. Typically these bindings are defined in RouteServiceProvider as shown in the example in the docs
public function boot()
{
parent::boot();
Route::model('tag', App\Tag::class);
}
When you bind tag this way then your controller action can use the variable via model resultion:
public function update(Tag $tag) {
// $tag is resolved based on the identifier passed in the url
}
Usually models are automatically bound so doing it manually doesn't really need to be done however you can customise resolution logic if you do it manually
Normal way
Route::get("tags/{id}","TagsController#show");
function($id)
{
$tag = Tag::find($id);
dd($tag); // tag
}
With route model bindings
Route::put("tags/{tag}","TagsController#update");
function(Tag $tag) // Tag model binding
{
dd($tag); // tags
}
ref link https://laravel.com/docs/5.8/routing#implicit-binding
It's just a convention. You can call it all you want. Usually, and {id} refers to the id in your table. A tag, or similarly, a slug, is a string value. A tag could be 'entertainment' for video categories, while 'my-trip-to-spain' is a slug for the description of a video.
You have to chose the words what you are comfortable with. The value will be used to find in your database what record is needed to show the correct request in the view. Likewise you can use video/view/{id}/{slug} or any combination thereof.
Just make sure your URLs don't get too long. Because search engines won't show your website nicely in search results if you do. Find the balance between the unambiguous (for your database) and logic (for your visitors).
Check this out: Route model bindings
Use id, Laravel will get the id from route, and it will be the tag's id, it is integer.
function show($id) {
$tag = Tag::find($id);
}
Use tag, Laravel automatically resolves Eloquent models defined in routes or controller actions whose type-hinted variable names match a route segment name.
In URL, your tag parameter is integer, however in your controller action $tag will be a model object:
function action(Tag $tag) {
$tag->name;
}
So you don't need to get the $tag by eloquent in your controller action. You just need to specify it is From model Tag $tag
It will do it automatically.

How to catch get parameter and cast it to int in Laravel?

I have the following controller:
class CustomerController extends Controller
{
public function index(int $customerId = 0) : Renderable
{
dd($customerId);
...
And I'm requesting it by the following url http://localhost/customers?customerId=123. I would like to catch customerId as int. But It always gives 0 (default value the for method's signature). Ok, I can do something like:
$customerId = $request->input('customerId');
But the approach with getting parameter from the method's signature is more attractive for me.
I have seen this answer. It looks like very nice! But it does not work for my case. Why? Where is the mistake? Thank you for attention!
Update
Here I will show my routes definitions:
Auth::routes(['register' => false, 'reset' => false]);
Route::middleware('auth')->group(function () {
Route::resource('customers', 'CustomerController');
});
If you want to use the Resource Route you have to pass the customerID parameter like below. And also you have to use one of these methods: show, edit,update or destroy in your controller.
Route::resource('customers', 'CustomerController')->parameters([
'customers' => 'customerId'
]);
For more details: https://laravel.com/docs/5.7/controllers#resource-controllers
AFAIK Laravel doesn't inject parameters this way.
If such URI is really needed - you should manually inspect request input for your variables.
If you want to use controller as written, you should use route parameters (e.g. /customers/{id}) instead of parameters after ?.
By default resource controllers use index() for displaying all entities and show() for displaying only one entity.
If you use Eloquent, you can also inject Customer model, e.g.
public function index(Customer $customer) : Renderable
Customer will be automatically found by id and injected in controller method.

Laravel 5.5 reset password without email field

I've not come across a site that asks you to confirm your email address during a reset request, it seems like an unnecessary step, i want my users to be able to enter only password and password confirmation but no email.
I'm wondering if there is a solution for this, i'm thinking of a simple solution i will pass the user email address as parameter in the reset link like this
website.com/verify/{{reset_token}}/{{email}}
Then i will retrieve this email parameter and pass it to the hidden input value but i don't know how to attach the email address to the reset link.
If everyone knows any solution please let me know in the comments
Thanks for your help
I hope you are using the default Laravel routes which are...
// Don't forget to add the names to these routes.
Route::get('password/reset', 'Auth\ForgotPasswordController#showLinkRequestForm')->name('password.forgot.get');
Route::post('password/email', 'Auth\ForgotPasswordController#sendResetLinkEmail')->name('password.forgot.post');
Route::get('password/reset/{token}', 'Auth\ResetPasswordController#showResetForm')->name('password.reset.get');
Route::post('password/reset', 'Auth\ResetPasswordController#reset')->name('password.reset.post');
Then run
php artisan make:mail PasswordResetMail
Then in your User model, add this function
public function sendPasswordResetNotification($token)
{
Mail::send(new PasswordResetMail($this));
}
Then in PasswordResetMail, then in your constructor, you will get the user
public function __construct($user)
{
$this->user = $user;
}
then pass the user to the blade view along with the token, and then in your blade file, add the route
route('password.reset.get', ['token' => $token, 'email' => urlencode($user->email)]);
This will pass the email as
?email=useremail%40gmail.com // Should be urlencoded
Then, in your reset form, add it as
<input type="hidden" value="{{ urldecode(request()->get('email')) }}" name="email">
Should be good to go :)

Resources