Build a CRUD App — Step by StepCore· 50 min read

CRUD Step 3: Add a Product + Validation

Build the “add new” form, save it safely, and validate the input — showing friendly errors when something is wrong.

What you will learn

  • Build a form that submits to store()
  • Validate input on the server with clear rules
  • Show validation errors and keep the user’s typed values

Create = show a form, then save it

“Create” is two methods working together: create() shows an empty form, and store() receives the submitted data, validates it, and saves it. Validation is the important new skill here — never trust what a user types.

The create() method

create() just shows the form
public function create()
{
    return view('products.create');
}

That is all create() does — return the empty form view. The real work, validating and saving, happens in store() further down.

The form (create.blade.php)

This form sends its data to the store route. It includes @csrf — a hidden security token Laravel requires on every form, or it rejects the request. It also shows an error message under each field and remembers what the user typed:

resources/views/products/create.blade.php
<h1>Add Product</h1>

<form method="POST" action="{{ route('products.store') }}">
  @csrf

  <label>Name</label>
  <input type="text" name="name" value="{{ old('name') }}">
  @error('name') <p style="color:red">{{ $message }}</p> @enderror

  <label>Price</label>
  <input type="text" name="price" value="{{ old('price') }}">
  @error('price') <p style="color:red">{{ $message }}</p> @enderror

  <label>Description</label>
  <textarea name="description">{{ old('description') }}</textarea>
  @error('description') <p style="color:red">{{ $message }}</p> @enderror

  <button type="submit">Save Product</button>
</form>

The key parts: method="POST" + action="{{ route('products.store') }}" send the form to store(). @csrf adds the required security token. Each name="..." must match the field name we validate and save. @error('name') ... @enderror shows a red message only if that field failed validation, and value="{{ old('name') }}" refills the box with what the user typed so they do not start over.

The store() method — validate, then save

This is the heart of it. store() does two jobs in order: validate the input, then save it.

store() in ProductController
public function store(Request $request)
{
    $validated = $request->validate([
        'name'        => 'required|string|max:255',
        'price'       => 'required|numeric|min:0',
        'description' => 'nullable|string',
    ]);

    Product::create($validated);

    return redirect()->route('products.index')
                     ->with('success', 'Product added!');
}

Line by line — this is the most important code in the lesson:

  1. $request->validate([...]) checks each field against its rules.
  2. If any rule fails, Laravel automatically stops, sends the user back to the form, attaches the error messages, and keeps their old input — you write zero code for that.
  3. If all rules pass, validate() returns only the clean, checked data as $validated.
  4. Product::create($validated) saves that clean data as a new row.
  5. redirect()->route('products.index')->with('success', '...') sends the user to the list with a one-time success message.

What each rule means

RuleMeans
requiredMust not be empty
stringMust be text
max:255At most 255 characters
numericMust be a number
min:0Number not below 0 (no negative price)
nullableAllowed to be empty

Note: Output: Submit the form empty → you are sent straight back, with “The name field is required.” and “The price field is required.” shown in red, and anything you did type is still in the boxes. Submit valid data → the product is saved and you land on the list with “Product added!”.

Watch out: ALWAYS validate on the server with $request->validate. The HTML required attribute is only a hint — a user can bypass it. Server-side validation is your real guard, and it is what protects your database.

Tip: Keep four names in sync for every field: the input name="price", the validation key 'price' => ..., the model $fillable entry, and old('price'). Same word everywhere.

Q. When $request->validate() finds an invalid field, what does Laravel do automatically?

Answer: On failure Laravel redirects back, flashes the errors (readable via @error / $errors) and the old input (old()), with no extra code from you.

✍️ Practice

  1. Add a stock field to the form, validate it with required|integer|min:0, and add it to $fillable.
  2. Submit the form empty and watch the red messages appear; then submit valid data and see it saved.

🏠 Homework

  1. Explain why server-side validation is essential even when your form already has the HTML required attribute.
Want to learn this with a mentor?

CodingClave runs guided, project-based training (28-day, 45-day & 6-month batches).

Explore Training →