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

CRUD Step 2: Show the List (Read)

Fill the first controller methods and build the page that lists all your products in a table.

What you will learn

  • Write the index() and show() methods
  • Build a Blade view that loops over records
  • Use route model binding to fetch one record automatically

Read first — there is nothing to edit yet

We start with Read because the table is empty and there is nothing to create or edit yet. Read is two methods: index() lists all products, and show() displays one.

The index() method

Open ProductController and fill the index() method. It gets all the products and hands them to a view:

app/Http/Controllers/ProductController.php
public function index()
{
    $products = Product::all();                       // get every product
    return view('products.index', compact('products')); // send them to the view
}

Step by step: Product::all() fetches every row from the products table as a collection. compact('products') is shorthand for ['products' => $products] — it passes the data to the view under the name $products. view('products.index', ...) loads the file resources/views/products/index.blade.php (the dot means a folder).

You also need to add use App\Models\Product; at the top of the controller so it knows what Product means.

The index view — a table of products

Create the file resources/views/products/index.blade.php. It loops over $products and shows each one in a table row, with a link to add a new one:

resources/views/products/index.blade.php
<h1>Products</h1>

<a href="{{ route('products.create') }}">+ Add Product</a>

<table border="1" cellpadding="8">
  <tr><th>Name</th><th>Price</th><th>Actions</th></tr>

  @forelse ($products as $product)
    <tr>
      <td>{{ $product->name }}</td>
      <td>₹{{ $product->price }}</td>
      <td>
        <a href="{{ route('products.show', $product) }}">View</a> |
        <a href="{{ route('products.edit', $product) }}">Edit</a>
      </td>
    </tr>
  @empty
    <tr><td colspan="3">No products yet — add your first one!</td></tr>
  @endforelse
</table>

What the new pieces do: @forelse ... @empty ... @endforelse loops over the products, but if the list is empty it shows the “No products yet” row instead. {{ $product->name }} prints a value safely (it escapes HTML, blocking attacks). route('products.create') and route('products.edit', $product) build the correct URLs from the route names — so you never hard-code URLs.

Note: Output: A page titled “Products” with an “+ Add Product” link and a table listing every product (name, price, and View | Edit links). When there are none, it shows “No products yet”.

The show() method — one product

show() in ProductController
public function show(Product $product)
{
    return view('products.show', compact('product'));
}

Notice we type Product $product in the arguments. This is route model binding, and it is a huge time-saver: Laravel sees the {id} in the URL /products/5, automatically runs Product::find(5) for you, and passes the found product in as $product. If no product with that id exists, Laravel automatically shows a 404 Not Found — you write none of that.

resources/views/products/show.blade.php
<h1>{{ $product->name }}</h1>
<p>Price: ₹{{ $product->price }}</p>
<p>{{ $product->description }}</p>
<a href="{{ route('products.index') }}">← Back to list</a>

This view just prints the single product’s details and a link back to the list.

Tip: Route model binding (Product $product in the method) does the “find it or 404” work for you on show, edit, update and destroy — that is why those four methods can take a $product directly.

Q. In show(Product $product), how does Laravel know which product to load?

Answer: Route model binding uses the {id} in the URL to fetch the matching Product (or 404 if missing) and injects it into the method.

✍️ Practice

  1. Build the index() method and index.blade.php for your Tasks resource and confirm the list page loads.
  2. Add a “Created” column showing {{ $product->created_at->diffForHumans() }}.

🏠 Homework

  1. Explain, in your own words, what compact('products') does and what @forelse adds over a normal @foreach.
Want to learn this with a mentor?

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

Explore Training →