Capstone: blog

The spec

Build a small blog with posts and comments: visitors read the list and single pages, authors create and edit posts, readers leave comments, and a smoke test proves the pages answer. Four moving parts, each one borrowed from an earlier lesson.

Posts: list, single page, create, edit Comments: add to a post, shown under it Validation: title required, body required Tests: pages answer with status 200

Posts CRUD

Two routes carry the whole feature: a GET route that lists posts and a POST route that stores them. Validation runs before the insert, and a redirect sends the reader to the fresh page instead of re-posting on refresh.

Route::get("/posts", [PostController::class, "index"]); Route::post("/posts", [PostController::class, "store"]); public function store(Request $request) { $data = $request->validate([ "title" => "required|max:255", "body" => "required", ]); $post = Post::create($data); return redirect()->route("posts.show", $post); }

Comments

A comment belongs to a post, so the Post model declares a hasMany relation and the comment form posts to a nested route. The controller validates the body, attaches the comment through the relation, and redirects back to the post.

public function comments() { return $this->hasMany(Comment::class); } public function store(Request $request, Post $post) { $data = $request->validate(["body" => "required"]); $post->comments()->create($data); return redirect()->back(); }

Smoke tests

Smoke tests ask the simplest question: does the page answer. Hit the list and a single post, assert a 200 status, and assert that a known title appears. Green smoke means the wiring holds before you polish the design.

public function test_post_pages_answer(): void { $list = $this->get("/posts"); $list->assertOk(); $one = $this->get("/posts/1"); $one->assertOk(); }

Next: Capstone: JSON API

Article author: Arthur Isaev

Banner image