Laravel Idea for PhpStorm - Full-featured IDE for productive artisans!

Catching Laravel Validation Errors in Vue

Published on by

Catching Laravel Validation Errors in Vue image

Validation is a way to ensure that the data submitted from your front end forms or requests meets your expectations and ensures that we are storing the correct data in our database. When a form or request is submitted, before we run any logic with that data, we need to make sure it has met all of our requirements. If the data does not meet our requirements, we need to tell the user there is a problem with the data they submitted so they can fix it.

The Project

Currently, I am working on a SAAS app called SavvyHouseHunting.com – it is a platform that allows real estate agents to create and share 360 videos and images, documents, and messages with their clients. One of the key differences with this application is that it is a Vue SPA, leveraging Vue Router, so we aren’t able to use the Blade helper @errors as we would in a blade view.

In one area of the site, users of certain types are allowed to “invite” people to join their team; upon receiving the invitation, the user needs to provide some necessary user data to get started.

Getting started

First, since we don’t want users to accept the invitation more than once, when generating the invitation email we create a Signed URL like so:

$invitation = Invitation::create([
'to' => request('email'),
'user_id' => auth()->user()->id,
'type' => request('type')
]);
 
$link = URL::signedRoute('accept-invitation', ['invitation' => $invitation->id]);
Mail::to(request('email'))->send(new InvitationMail(auth()->user(), $link));

The email will fire off to the invited user, with the signed url, and when the user clicks the link in the email we receive that request and render our form. But to ensure that the signature is valid, we can use a simple abort_if method which takes a boolean, an http status code, and an optional message to send back to the user.

More Logic

Additionally, we need to lookup the invitation and make sure it hasn’t been accepted yet, there is an accepted column on the model that we mark as true later in the process. One additional caveat to allow for is in the scenario that this invited user actually already exists in our system, maybe they also belong to someone else’s team. Here is the accept method:

abort_if(!request()->hasValidSignature(), 403, 'YOU CAN\'T DO THAT');
$invitation = Invitation::find(request('invitation'));
abort_if($invitation->accepted, 403, 'That invitation has already been accepted');
 
$from = User::find($invitation->user_id);
try {
$user = User::where('email', $invitation->to)->firstOrFail();
$this->createAssociations($user, $invitation, $from);
$invitation->accepted = true;
$invitation->save();
auth()->login($user);
return redirect('/dashboard/communication');
} catch (ModelNotFoundException $m) {
return view('invitation.accept', compact('invitation'));
}

You can see we are running through a number of instances to validate against. Abort if the request doesn’t have a valid signature, lookup the invitation and abort if it has already been accepted, get the invitee’s email and try to find them in the database, if so, it’s ok, create the new associations with the inviter, mark the invitation as accepted, log them into the app and redirect to the dashboard, if not Laravel will throw a ModelNotFoundException. We can send them to our form with the invitation object.

Instantiating the component is straight-forward enough, we just need to pass the invitation as a prop:

<accept-invitation :invitation="{{$invitation}}"></accept-invitation>

Handling Laravel Errors in Vue

The Vue form is a bit long, but essentially, we are passing the invitation, into the Vue component and adding the invitation object to a hidden field, and instantiate what we expect from our user:

props: {
invitation: {
type: Object,
required: true,
},
},
data() {
return {
user: {
first_name: '',
last_name: '',
email: '',
phone_number: '',
password: '',
password_confirmation: '',
},
errors: null,
};
},

The errors object is where we will put any errors caught from the post request. I use Tailwind for all of my styling, the div looks like this:

<div v-if="errors" class="bg-red-500 text-white py-2 px-4 pr-0 rounded font-bold mb-4 shadow-lg">
<div v-for="(v, k) in errors" :key="k">
<p v-for="error in v" :key="error" class="text-sm">
{{ error }}
</p>
</div>
</div>

And finally, the post method:

methods: {
submit() {
axios
.post('accept-invitation', {
user: this.user,
invitation: this.invitation
})
.then(data => {
location.href = '/dashboard/communication';
})
.catch(e => {
this.errors = e.data.errors;
});
},
},

Let’s check out the controller method that handles this request:

Route::post('accept-invitation', 'InvitationController@submit');

Laravel Form Request Validation

For this method, since we are passing several fields to the controller let’s leverage Laravel’s Form Request, here we are using two methods: rules() and messages() like so:

public function rules()
{
return [
'user.first_name' => ['required', 'string', 'max:255', 'min:2'],
'user.last_name' => ['required', 'string', 'max:255'],
'user.phone_number' => ['required', 'max:255'],
'invitation.type' => ['required', 'string', 'max:255'],
'invitation' => ['required'],
'user.password' => ['required', 'min:6', 'confirmed'],
];
}
 
public function messages()
{
return [
'user.first_name.required' => 'Your first name is required',
'user.first_name.min' => 'Your first name must be at least 2 characters',
'user.first_name.max' => 'Your first name cannot be more than 255 characters',
'user.first_name.string' => 'Your first name must be letters only',
 
'user.last_name.required' => 'Your last name is required',
'user.last_name.min' => 'Your last name must be at least 2 characters',
'user.last_name.max' => 'Your last name cannot be more than 255 characters',
'user.last_name.string' => 'Your last name must be letters only',
 
'user.phone_number.required' => 'Your phone number is required',
'user.phone_number.max' => 'Your phone number cannot be more than 255 characters',
 
'user.password.required' => 'You must create a password',
'user.password.min' => 'Your password must be at least 6 characters long',
'user.password.confirmed' => 'Your password confirmation does not match',
];
}

Let’s test this out and see what we get if we pass a mismatched password:

Voila! We were able to parse Laravel Validation errors in our Vue form with ease!

Pro Tip!

One thing that I don’t see a lot of people doing is using the validated data in their controller, instead of the request data. Once the data is validated you can set that to a variable that ONLY contains the data that is validated as an array.

$validated = $request->validated();

Typically, I see developers validate the request and then use the request data with the assumption that since the validation passed, the data is correct. But what if you forgot to validate a field? Your validation would pass, but the data might not be any good. This approach will make your validations stronger and increase your code confidence!

Shane D Rosenthal photo

I am a technology junkie, family man, community leader, pilot and musician. Since the mid 80's I have been taking things apart to see how they work and trying to put them back together again, sometimes in better condition than before. Along the way I met the love of my life, made a family, found purpose in leading and teaching others and recently became a pilot. I am passionate about flying, sharing the world with those around me and catching some live metal shows when I can.

Looking to the future I intend to own my own airplane, remain instrument rated, continue teaching and guiding our youth, grow my YouTube channel and followers and share my passions to influence whoever I can.

Filed in:
Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.

Laravel Forge logo

Laravel Forge

Easily create and manage your servers and deploy your Laravel applications in seconds.

Laravel Forge
Tinkerwell logo

Tinkerwell

The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

Tinkerwell
No Compromises logo

No Compromises

Joel and Aaron, the two seasoned devs from the No Compromises podcast, are now available to hire for your Laravel project. ⬧ Flat rate of $7500/mo. ⬧ No lengthy sales process. ⬧ No contracts. ⬧ 100% money back guarantee.

No Compromises
Kirschbaum logo

Kirschbaum

Providing innovation and stability to ensure your web application succeeds.

Kirschbaum
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
Bacancy logo

Bacancy

Supercharge your project with a seasoned Laravel developer with 4-6 years of experience for just $2500/month. Get 160 hours of dedicated expertise & a risk-free 15-day trial. Schedule a call now!

Bacancy
Lucky Media logo

Lucky Media

Get Lucky Now - the ideal choice for Laravel Development, with over a decade of experience!

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar: Laravel E-Commerce

E-Commerce for Laravel. An open-source package that brings the power of modern headless e-commerce functionality to Laravel.

Lunar: Laravel E-Commerce
LaraJobs logo

LaraJobs

The official Laravel job board

LaraJobs
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Multi-tenant Laravel SaaS Starter Kit that comes with all features required to run a modern SaaS. Payments, Beautiful Checkout, Admin Panel, User dashboard, Auth, Ready Components, Stats, Blog, Docs and more.

SaaSykit: Laravel SaaS Starter Kit
Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate logo

Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate

Build your SaaS application in hours. Out-of-the-box multi-tenancy and seamless Stripe integration. Supports subscriptions and one-time purchases, allowing you to focus on building and creating without repetitive setup tasks.

Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate
Rector logo

Rector

Your partner for seamless Laravel upgrades, cutting costs, and accelerating innovation for successful companies

Rector
MongoDB logo

MongoDB

Enhance your PHP applications with the powerful integration of MongoDB and Laravel, empowering developers to build applications with ease and efficiency. Support transactional, search, analytics and mobile use cases while using the familiar Eloquent APIs. Discover how MongoDB's flexible, modern database can transform your Laravel applications.

MongoDB

The latest

View all →
Asymmetric Property Visibility in PHP 8.4 image

Asymmetric Property Visibility in PHP 8.4

Read article
Access Laravel Pulse Data as a JSON API image

Access Laravel Pulse Data as a JSON API

Read article
Laravel Forge adds Statamic Integration image

Laravel Forge adds Statamic Integration

Read article
Transform Data into Type-safe DTOs with this PHP Package image

Transform Data into Type-safe DTOs with this PHP Package

Read article
PHPxWorld - The resurgence of PHP meet-ups with Chris Morrell image

PHPxWorld - The resurgence of PHP meet-ups with Chris Morrell

Read article
Herd Executable Support and Pest 3 Mutation Testing in PhpStorm 2024.3 image

Herd Executable Support and Pest 3 Mutation Testing in PhpStorm 2024.3

Read article