What to expect in the next generation of Laravel Forge. Read the blog post
Rewatch this lesson
Courses
/
Getting Started with Laravel
/
Creating and storing Chirps

Next video in… 10

Edit and delete Chirps

Creating and storing Chirps

Getting Started with Laravel

Creating and storing Chirps
Build forms to let users create content and store it in the database. Learn about Laravel's form handling, validation, and mass assignment protection.

Our Chirper application is showing chirps that we have stored in the database, but we still don't have a way for our users to actually create their own chirps and store them in the database. A social network where you can only read? That's just a newspaper!

So that's what we're going to do in this lesson. We're going to build a form, handle the validation with that form, and store those chirps that users create in the database—all with the things that are built into Laravel. We're still saving the actual user authentication for a later lesson, but we are going to allow our users as anonymous users to create those chirps.

Step 1: Add a Create Form

Why don't we add the form at the top of our feed and we can get going from there. We really just want a form right after the "Latest Chirps" header, and before we actually list the chirps.

Let's update home.blade.php:

1<x-layout>
2 <x-slot:title>
3 Home Feed
4 </x-slot:title>
5 
6 <div class="max-w-2xl mx-auto">
7 <h1 class="text-3xl font-bold mt-8">Latest Chirps</h1>
8 
9 <!-- Chirp Form -->
10 <div class="card bg-base-100 shadow mt-8">
11 <div class="card-body">
12 <form method="POST" action="/chirps">
13 @csrf
14 <div class="form-control w-full">
15 <textarea
16 name="message"
17 placeholder="What's on your mind?"
18 class="textarea textarea-bordered w-full resize-none"
19 rows="4"
20 maxlength="255"
21 required
22 ></textarea>
23 </div>
24 
25 <div class="mt-4 flex items-center justify-end">
26 <button type="submit" class="btn btn-primary btn-sm">
27 Chirp
28 </button>
29 </div>
30 </form>
31 </div>
32 </div>
33 
34 <!-- Feed -->
35 <div class="space-y-4 mt-8">
36 @forelse ($chirps as $chirp)
37 <x-chirp :chirp="$chirp" />
38 @empty
39 <div class="hero py-12">
40 <div class="hero-content text-center">
41 <div>
42 <svg class="mx-auto h-12 w-12 opacity-30" fill="none" stroke="currentColor" viewBox="0 0 24 24">
43 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path>
44 </svg>
45 <p class="mt-4 text-base-content/60">No chirps yet. Be the first to chirp!</p>
46 </div>
47 </div>
48 </div>
49 @endforelse
50 </div>
51 </div>
52</x-layout>

There's nothing too special about this form right now—just one textarea and a button. But notice a couple of important things:

  • @csrf - Laravel's CSRF protection (prevents cross-site request forgery)

  • POST to /chirps route (which we'll create next)

If you tried to submit this form right now, nothing would actually happen because we haven't hooked it up on the Laravel side yet.

Step 2: Add the Store Route

So let's hook this up! In routes/web.php, add a route to handle form submission:

1use App\Http\Controllers\ChirpController;
2 
3Route::get('/', [ChirpController::class, 'index']);
4Route::post('/chirps', [ChirpController::class, 'store']);

Step 3: Create the Store Method

First, I want to create the method that we're going to be submitting this form to in our controller. Let's go to our ChirpController and add the store method. This store method is perfect for this because we're creating a resource (in this case, a chirp), and this is what the store method is intended for in a resource controller.

1public function store(Request $request)
2{
3 // Validate the request
4 $validated = $request->validate([
5 'message' => 'required|string|max:255',
6 ]);
7 
8 // Create the chirp (no user for now - we'll add auth later)
9 \App\Models\Chirp::create([
10 'message' => $validated['message'],
11 'user_id' => null, // We'll add authentication in lesson 11
12 ]);
13 
14 // Redirect back to the feed
15 return redirect('/')->with('success', 'Chirp created!');
16}

Let me break this down:

  1. Validation: We're validating the request and storing it in the $validated parameter. We're saying the message that we're receiving from this request is required, should be a string, and have a max of 255 characters. Notice we now have two places where this max is indicated—our validation as well as in the database itself.

  2. Creation: We're using the Chirp model to create this with a message from the validated data and a user_id of null (for now).

  3. Redirect: We're returning a redirect back to the homepage with a specific success message. In Laravel, this with function returns with a flash of data to the session—so after we redirect back, that initial load flashes a piece of data to the session with this success message.

Note: We're creating chirps without a user for now to keep things simple. In lesson 11, we'll add proper authentication so each chirp belongs to the logged-in user!

Step 4: Show Success Messages

Now, how can we grab this flash session information and display it to our user? In our app.css, we added that animate-fade-out parameter earlier—this is going to make things a lot easier when we create a notification component for any success messages.

We're not going to put this success message in our home.blade.php. Instead, we're going to put it in the layout so it can be used everywhere. Add this to your layout component (resources/views/components/layout.blade.php) right after the </nav>:

1<!-- Success Toast -->
2@if (session('success'))
3 <div class="toast toast-top toast-center">
4 <div class="alert alert-success animate-fade-out">
5 <svg xmlns="<http://www.w3.org/2000/svg>" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
6 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
7 </svg>
8 <span>{{ session('success') }}</span>
9 </div>
10 </div>
11@endif

In this case, if the session has a parameter of 'success', we're going to show a toast. The toast is using DaisyUI classes, but we also have that animate-fade-out class that we added to our app.css. This is saying we're just going to take that session value, the success message, regardless of if this is from posting a chirp or if we use this for logging in and registering later.

The toast will appear at the top center and automatically fade away after a few seconds!

Step 5: Display Validation Errors

Now let's test this out! But first, what if someone tries to submit an empty chirp? We have client-side validation with that required attribute, but we also have server-side validation in our chirp controller. Let's make sure we can show validation errors properly.

Laravel gives us an errors variable that gets passed to the view if there are any errors within the session. Let's update the form section in home.blade.php to handle this:

1<!-- Chirp Form -->
2<div class="card bg-base-100 shadow mt-8">
3 <div class="card-body">
4 <form method="POST" action="/chirps">
5 @csrf
6 <div class="form-control w-full">
7 <textarea
8 name="message"
9 placeholder="What's on your mind?"
10 class="textarea textarea-bordered w-full resize-none @error('message') textarea-error @enderror"
11 rows="4"
12 maxlength="255"
13 required
14 >{{ old('message') }}</textarea>
15 
16 @error('message')
17 <div class="label">
18 <span class="label-text-alt text-error">{{ $message }}</span>
19 </div>
20 @enderror
21 </div>
22 
23 <div class="mt-4 flex items-center justify-end">
24 <button type="submit" class="btn btn-primary btn-sm">
25 Chirp
26 </button>
27 </div>
28 </form>
29 </div>
30</div>

Notice a few things here:

  • If there is an error with the message parameter (that's what we're validating in the controller), we add the textarea-error class

  • We use @error('message') to show the error message

  • DaisyUI gives us the ability to have the textarea show as an error state with that CSS class

  • We use {{ old('message') }} so when we flash back after an error, the textarea displays the old input so the user doesn't have to retype everything

Laravel's validation automatically:

  • Redirects back on error

  • Provides error messages via @error

  • Preserves old input with old('message')

Step 6: Better Validation Messages

Now, to add just a little bit of UI nicety, let's customize our validation messages to be more "chirpy" and user-friendly. To show off the power of Laravel, we can actually customize these validation messages easily:

1public function store(Request $request)
2{
3 $validated = $request->validate([
4 'message' => 'required|string|max:255',
5 ], [
6 'message.required' => 'Please write something to chirp!',
7 'message.max' => 'Chirps must be 255 characters or less.',
8 ]);
9 
10 \App\Models\Chirp::create([
11 'message' => $validated['message'],
12 'user_id' => null,
13 ]);
14 
15 return redirect('/')->with('success', 'Your chirp has been posted!');
16}

Now all of a sudden, within a couple of seconds, because of what Laravel gives to us, we have the ability to modify something as simple as error messages without having to do hardly anything!

Step 7: Test It Out!

Let's test our form:

  1. Try submitting an empty form - You'll see our custom validation error: "Please write something to chirp!"

  2. Type more than 255 characters - The client-side maxlength attribute prevents it, but if someone bypassed that, our server-side validation would catch it with "Chirps must be 255 characters or less."

  3. Submit a valid chirp - You'll see the success message and your new chirp at the top of the feed!

Because we are redirecting, we automatically receive that new chirp because the / route is automatically getting all of the latest chirps and pushing them into our view.

Security Features We Get for Free

Laravel automatically protects you from:

  • CSRF attacks - That @csrf token ensures forms come from your site

  • SQL injection - Eloquent parameterizes all queries

  • Mass assignment - Only fields in $fillable can be set

  • XSS attacks - Blade {{ }} automatically escapes output

What About Rate Limiting?

Now, it is helpful to know that while we won't touch on it in this particular course, there's so much more that you could add here. What if you don't want your users to post five times within a second, or maybe a hundred times within a second? There are some bad eggs out there—really, you can't trust anyone on the web.

Well, Laravel has the ability to rate limit. What that means is that you can, within a matter of minutes, say "Hey, this particular chirp method can only be done by a user maybe one time every 60 seconds."

You could also prevent duplicate chirps if you wanted:

1$validated = $request->validate([
2 'message' => [
3 'required',
4 'string',
5 'max:255',
6 Rule::unique('chirps')->where(function ($query) use ($user) {
7 return $query->where('user_id', $user->id);
8 })
9 ],
10]);

This would ensure each user's chirps are unique. But before we get into something like that...

What We've Accomplished

Look at what we've built! Your app can now:

  • Accept user input through a beautiful form

  • Validate that input on both client and server side

  • Store chirps in the database

  • Show custom success and error messages with nice toasts

  • Protect against common security threats automatically

  • Redirect users back with flash messages

We have this form and it works! We're submitting anonymous data for now, but the validation works perfectly. Laravel automatically gives us both client-side validation and server-side validation.

But users can only create chirps—they can't edit or delete them yet. In the next lesson, we'll start working on editing and deleting chirps, even before we get to the user authentication piece. Let's check out how you can edit and delete chirps!

00:00
Our chirp application is showing chirps that we have stored in the database,
00:03
but we still don't have a way for our users to actually create their own chirps
00:07
and then store them in the database.
00:09
So that's what we're going to do in today's lesson.
00:12
We're going to build a form, we're going to handle the validation with
00:15
that form, and then we're gonna store those chirps that users create in the
00:19
database, and all of it happens with the things that are built into Laravel.
00:24
Now we're still saving the actual user authentication for a later lesson,
00:28
but we are going to allow our users as anonymous users to create those chirps.
00:33
So why don't we add the form at the top of our feed and we can get going from there.
00:37
We really just want to form right about here after the latest chirps header, and
00:42
then before we actually list the chirp.
00:44
So I'm gonna put a form there, but we're not gonna wire this up.
00:48
Nothing is actually going to happen when we submit this form.
00:51
It's just the plain HTML form.
00:54
There's nothing special about this with one text area and then a button to submit.
01:01
But yeah, there's nothing that actually happens when we hit submit.
01:03
If I was to type anything here and hit submit with this chirp button,
01:08
yes, the message comes up in a URL parameter up here, but we're
01:13
not actually doing anything with that information on Laravel side.
01:18
So why don't we hook this First, I actually want to create the method
01:22
that we're going to be submitting this form to in our controller.
01:26
So why don't we go to our chirp controller?
01:29
And this store method is going to be the perfect method to do
01:32
this because this is creating a resource, in this case, a chirp.
01:36
And this is what is intended for this resource controller.
01:40
We already received the request information, so we're just gonna
01:43
validate that this is validating the request, and then we're storing
01:46
it in this validated parameter.
01:48
We're saying, Hey, this message that we're receiving from this request is required.
01:53
It should be a string and a max of 255 characters.
01:56
So now we have two places where this max is indicated and our validation
02:02
as well as in the database itself.
02:05
Of course, we can also add this to the front end as well.
02:07
I believe we might actually be doing that.
02:10
Yes, max length of 255.
02:12
So this is great to have the front end validation as well
02:15
as the backend validation.
02:16
This is Laravel way of validating anything that comes in through a request.
02:21
We have specific validation parameters that we can add.
02:24
Most of them you'll find in the docs are pretty self-explanatory, and
02:28
believe me, there is a ton of them.
02:30
Next, we're gonna create the chirp.
02:31
So in this case, we can use the chirp model to say, Hey, we're gonna create
02:36
this with a message of the validated message and the user ID of null.
02:42
Of course, we can actually OMI this, but we're gonna add this back in when we get
02:46
to lesson 11 and we add authentication.
02:50
And lastly, we're gonna return a redirect.
02:52
In this case, we're redirecting back to the homepage with
02:57
a specific success message.
02:59
In this case, we're gonna say that there is a success that
03:02
the chirp has been created.
03:05
This is an easy way for our application to know that, hey, this was successful.
03:11
In Laravel, this width function is returning with a
03:15
flash of data to the session
03:16
so in this case, after we redirect back that initial redirect, that initial
03:22
load after this has happened, flashes a piece of data to the session, and
03:27
that data is this success message that has the value of chirp created.
03:33
will see in a little bit how we can grab this information if a chirp
03:37
has actually successfully been created and display that to our user.
03:41
For now, let's hook this up in our web route.
03:43
So similar to our route gi, we're going to have a route post and we'll say this
03:50
is the chirps route and we're gonna pass in the chirp controller, the store method.
03:57
Alright.
03:58
Now in our home, do blade php where we have this form, how do we tell this form
04:03
to submit this information to the slash chirps post route that we just set up?
04:09
Well, it's actually really simple.
04:11
For this form, we're gonna say that there is a method of post because we
04:15
are posting to that slash chirps route, and the action is going to be to that
04:19
specific route, the slash chirps.
04:22
Then there's one more thing that we need to do within Laravel.
04:25
It's this at Csrf directive.
04:29
What that CSRF directive is doing is the equivalent to a hidden
04:32
input field attaching A-C-S-R-F token like you might see here.
04:38
This is something Laravel automatically generates and gives you outta the box.
04:41
It's incredibly easy to use, but it's given to you so you can easily
04:45
protect your site and your application from cross site request forgery.
04:49
CSRF.
04:51
And that should be it.
04:52
Why don't we test this out and see if our form works the way we expect it to.
04:55
So one of our latest chirps.
04:57
This is a test of our new form.
05:02
Hi there, boot camp.
05:04
Chirp and look at that.
05:06
Because we are redirected, we automatically receive that new chirp
05:10
because the slash route is automatically getting all of the latest chirps
05:15
and pushing them into our view.
05:17
Now, here's one thing I want to make you aware of that we're not gonna touch
05:21
in this course, but something that you might want to look into in the future.
05:26
It's keeping this form up to date right now.
05:29
You can see here that when we posted it was zero seconds
05:32
ago, I haven't touched it.
05:34
But if anyone else accesses this page, it will see it in a
05:38
different timestamp because it's when they requested that data.
05:42
If I press refresh, okay, now it's one minute ago, so I'm not
05:46
going to see any new chirps until I actually refresh the page.
05:50
Well, Laravel has some great options such as web sockets within
05:54
something like Laravel Reverb.
05:56
In order to say, I want to watch if there are any chirps that are coming
06:00
in, or if there has been a new chirp that comes in, I want to maybe notify
06:06
all users that a new chirp has come in.
06:08
Again, not something that we're gonna be touching on in this course,
06:11
but it's helpful to know that those options are out there and it might be
06:15
a great way to kind of build on top of what we are building here with chirp.
06:19
Alright, we have this form and it works.
06:21
We're submitting anonymous data.
06:23
We don't have to worry about the validation error because if
06:26
I tried to chirp, uh, it says, please fill out this field.
06:29
Well, we have client side validation, but we also have
06:33
server side validation as well.
06:35
Why don't we remove the client side validation so that we see
06:38
what our server sends back.
06:40
I'm going to remove this required parameter, and just for fun, I'm going
06:44
to remove the max length as well.
06:46
Now when we submit our form, we would expect maybe some error, right?
06:50
Because we don't have the client side validation, and we do have the server
06:55
validation in our chirp controller.
06:57
We have validated that this message is required before we
07:01
even continue with the request.
07:04
And if we hit submit.
07:08
Well, we're refreshing the page, but nothing's happening.
07:12
Now the neat part is that Laravel gives if there is any errors within
07:15
the session that has been passed to the view, it has an errors prop.
07:21
In this case, this is a specific variable that we then have access to on our view.
07:28
So just for some quick debugging, we can add this if there are any errors.
07:34
Why don't we dd the all of those errors?
07:37
DD is a little quick helper function that Laravel has that is specifically saying,
07:43
we're going to die and dump in this case.
07:45
Shut down everything that Laravel is doing at that time and just show this.
07:51
Why don't we take a look?
07:52
So if I click chirp,
07:53
look at it.
07:54
We have an array that's our errors array, and it includes a single error.
07:58
The message field is required.
08:01
So how can we make this better for our users?
08:04
Well, let's go ahead and do this.
08:06
Instead of just having this text area, we're going to add a few things.
08:10
We're going to say that if there is an error with a message parameter, that's
08:14
what we're passing from the controller.
08:16
Well then let's go ahead and show that message.
08:19
And this error message is also coming from the text area with the name of message.
08:26
Day UI also gives us the ability to have the text area of an error,
08:30
and so that just displays it easily within the CSS as well.
08:35
We also have this old message, so when we flash a status session, we're redirecting
08:41
to the page then this message text area is going to display the old variables.
08:47
So if we for some reason went over 255 characters, we should then
08:52
redirect and see the old message in the text area with the error.
08:57
for now, just to show this off, we're going to say a max
09:00
of 2 55, but a men of five.
09:04
Now if I try to enter anything under five, let's say hi chirp.
09:10
The message field must be at least five characters.
09:13
That's a error message that Laravel automatically created for us
09:16
based on our validation errors.
09:18
We returned, we had a full redirect.
09:21
We see the high message still in this text area, but now we are
09:25
able to fix this high there chirp.
09:31
Perfect.
09:32
I'm pasting way more than 255 characters in.
09:35
We should get that same error.
09:37
Perfect.
09:40
Let's go ahead and reset everything.
09:45
And in our home, we're going to add back in that client side validation.
09:57
If I try to add any more characters, you might hear me
10:00
hitting my finger on the keyboard.
10:02
I can't because that max length is affecting this text area field.
10:07
Great.
10:08
But what about the success as well?
10:11
You might have remembered in our app css, we added this animate, fade out parameter.
10:17
This is so when we do create a component that is a notification component
10:22
for any success messages, this is going to make things a lot easier.
10:26
And actually we're not gonna put this success message in our home Blade pb.
10:33
Instead, we're gonna put this in the layout.
10:35
So I'm going to go to our layout blade php and just under
10:38
the nav, why don't I put this
10:40
in this case, if the session has a parameter of success,
10:44
why don't we show a toast?
10:46
The toast is going to be the toast of top, and this is all mostly Daisy ui,
10:51
but we also do have that animate fade out class that we inputted into our app css.
10:56
This is saying that we're just going to take that session value, the
10:59
success message, regardless of if this is from a posted chirp or if we use
11:06
this in logging in and registering,
11:08
let's post a new chirp.
11:13
And there we have the neat little notification up there at
11:16
the top that nicely fades out.
11:19
Now to add just a little bit of UI nicety before we get into the next lesson,
11:23
if we tried to create a new chirp and we have an error message, of course
11:27
we get the client side validation.
11:29
But if for some reason, uh, we create something that is over 255 characters or
11:34
we bypass client side validation, somehow we're just going to get the generic
11:39
error message that we saw initially.
11:42
What if we want to customize that?
11:44
In this case, if I remove that required client side validation, go
11:47
back into our form and click chirp, we get the message field is required.
11:52
It feels a little, I don't know, not chirp.
11:56
to show off the power of Laravel, we can actually customize
11:59
these validation messages.
12:00
We can say, uh, the message required texts should say, please write
12:05
something, a chirp or the message max text that then gets sent back.
12:10
If this validation does not pass, chirps must be 255 characters or less.
12:17
Why don't we even change up the success message as well?
12:20
Your chirp has been posted, so refreshing.
12:24
Chirp.
12:25
Please write something to chirp.
12:26
Now all of a sudden, within a couple of seconds, because of what Laravel gives
12:30
to us, we have the ability to modify something as simple as error messages
12:35
that we don't have to do hardly anything.
12:38
Now in the next lesson, we'll start working on editing and
12:40
deleting chirps even before we get to the user authentication piece.
12:45
But it is helpful to know that while we won't touch on it in
12:48
this particular course, there's so much more that you could add here.
12:51
Even for this particular lesson, what if you don't want your users to post
12:56
five times within a second, or maybe a hundred times within a second?
13:01
There are some bad eggs out there.
13:03
really you can't trust anyone on the web.
13:05
Well, Laravel has the ability to rate limit.
13:08
What that means is that you can, within a matter of minutes, say,
13:12
Hey, this particular chirp method can only be done by a user,
13:18
maybe one time every 60 seconds.
13:22
But before we get into something like that, why don't we check out
13:25
how you can edit and delete chirps?
13:26
See you in the next lesson.