{"id":615,"date":"2025-09-02T11:35:08","date_gmt":"2025-09-02T11:35:08","guid":{"rendered":"https:\/\/1v0.net\/blog\/?p=615"},"modified":"2025-09-02T11:36:36","modified_gmt":"2025-09-02T11:36:36","slug":"building-a-simple-blog-with-laravel-12-and-blade","status":"publish","type":"post","link":"https:\/\/1v0.net\/blog\/building-a-simple-blog-with-laravel-12-and-blade\/","title":{"rendered":"Building a Simple Blog with Laravel 12 and Blade"},"content":{"rendered":"\n<p>Building a simple blog is a great way to learn Laravel\u2019s fundamentals while producing something useful. In this article, we\u2019ll walk through setting up a blog with posts, categories, and tags. We\u2019ll also cover creating new posts, assigning them to categories and tags, and listing content based on filters. This will give you a clear foundation for building more advanced content-driven applications.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Setting Up Migrations and Models<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ database\/migrations\/2025_09_02_000000_create_posts_table.php<\/span>\nSchema::create(<span class=\"hljs-string\">'posts'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Blueprint $table)<\/span> <\/span>{\n    $table-&gt;id();\n    $table-&gt;string(<span class=\"hljs-string\">'title'<\/span>);\n    $table-&gt;text(<span class=\"hljs-string\">'content'<\/span>);\n    $table-&gt;foreignId(<span class=\"hljs-string\">'category_id'<\/span>)-&gt;constrained()-&gt;onDelete(<span class=\"hljs-string\">'cascade'<\/span>);\n    $table-&gt;timestamps();\n});\n\n<span class=\"hljs-comment\">\/\/ database\/migrations\/2025_09_02_000001_create_categories_table.php<\/span>\nSchema::create(<span class=\"hljs-string\">'categories'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Blueprint $table)<\/span> <\/span>{\n    $table-&gt;id();\n    $table-&gt;string(<span class=\"hljs-string\">'name'<\/span>);\n    $table-&gt;string(<span class=\"hljs-string\">'slug'<\/span>)-&gt;unique();\n    $table-&gt;timestamps();\n});\n\n<span class=\"hljs-comment\">\/\/ database\/migrations\/2025_09_02_000002_create_tags_table.php<\/span>\nSchema::create(<span class=\"hljs-string\">'tags'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Blueprint $table)<\/span> <\/span>{\n    $table-&gt;id();\n    $table-&gt;string(<span class=\"hljs-string\">'name'<\/span>);\n    $table-&gt;string(<span class=\"hljs-string\">'slug'<\/span>)-&gt;unique();\n    $table-&gt;timestamps();\n});\n\n<span class=\"hljs-comment\">\/\/ pivot for posts and tags<\/span>\nSchema::create(<span class=\"hljs-string\">'post_tag'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Blueprint $table)<\/span> <\/span>{\n    $table-&gt;foreignId(<span class=\"hljs-string\">'post_id'<\/span>)-&gt;constrained()-&gt;onDelete(<span class=\"hljs-string\">'cascade'<\/span>);\n    $table-&gt;foreignId(<span class=\"hljs-string\">'tag_id'<\/span>)-&gt;constrained()-&gt;onDelete(<span class=\"hljs-string\">'cascade'<\/span>);\n    $table-&gt;primary(&#91;<span class=\"hljs-string\">'post_id'<\/span>, <span class=\"hljs-string\">'tag_id'<\/span>]);\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We\u2019ll use three main entities: <code>Post<\/code>, <code>Category<\/code>, and <code>Tag<\/code>. Posts belong to a category, and can have many tags via a pivot table. This is a classic blog structure.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Defining Relationships in Models<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Models\/Post.php<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Post<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Model<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">protected<\/span> $fillable = &#91;<span class=\"hljs-string\">'title'<\/span>,<span class=\"hljs-string\">'content'<\/span>,<span class=\"hljs-string\">'category_id'<\/span>];\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">category<\/span><span class=\"hljs-params\">()<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">$this<\/span>-&gt;belongsTo(Category::class);\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">tags<\/span><span class=\"hljs-params\">()<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">$this<\/span>-&gt;belongsToMany(Tag::class);\n    }\n}\n\n<span class=\"hljs-comment\">\/\/ app\/Models\/Category.php<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Category<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Model<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">protected<\/span> $fillable = &#91;<span class=\"hljs-string\">'name'<\/span>,<span class=\"hljs-string\">'slug'<\/span>];\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">posts<\/span><span class=\"hljs-params\">()<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">$this<\/span>-&gt;hasMany(Post::class);\n    }\n}\n\n<span class=\"hljs-comment\">\/\/ app\/Models\/Tag.php<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Tag<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Model<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">protected<\/span> $fillable = &#91;<span class=\"hljs-string\">'name'<\/span>,<span class=\"hljs-string\">'slug'<\/span>];\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">posts<\/span><span class=\"hljs-params\">()<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">$this<\/span>-&gt;belongsToMany(Post::class);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>These relationships allow us to fetch a post\u2019s category, retrieve posts under a specific category, and query posts by tags.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Creating a Post with Category and Tags<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ routes\/web.php<\/span>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>\\<span class=\"hljs-title\">PostController<\/span>;\n\nRoute::resource(<span class=\"hljs-string\">'posts'<\/span>, PostController::class);\n\n<span class=\"hljs-comment\">\/\/ app\/Http\/Controllers\/PostController.php<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">PostController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Controller<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">create<\/span><span class=\"hljs-params\">()<\/span>\n    <\/span>{\n        $categories = Category::all();\n        $tags = Tag::all();\n        <span class=\"hljs-keyword\">return<\/span> view(<span class=\"hljs-string\">'posts.create'<\/span>, compact(<span class=\"hljs-string\">'categories'<\/span>,<span class=\"hljs-string\">'tags'<\/span>));\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">store<\/span><span class=\"hljs-params\">(Request $request)<\/span>\n    <\/span>{\n        $validated = $request-&gt;validate(&#91;\n            <span class=\"hljs-string\">'title'<\/span> =&gt; <span class=\"hljs-string\">'required|min:3'<\/span>,\n            <span class=\"hljs-string\">'content'<\/span> =&gt; <span class=\"hljs-string\">'required'<\/span>,\n            <span class=\"hljs-string\">'category_id'<\/span> =&gt; <span class=\"hljs-string\">'required|exists:categories,id'<\/span>,\n            <span class=\"hljs-string\">'tags'<\/span> =&gt; <span class=\"hljs-string\">'array'<\/span>\n        ]);\n\n        $post = Post::create($validated);\n        $post-&gt;tags()-&gt;attach($request-&gt;input(<span class=\"hljs-string\">'tags'<\/span>, &#91;]));\n\n        <span class=\"hljs-keyword\">return<\/span> redirect()-&gt;route(<span class=\"hljs-string\">'posts.index'<\/span>)-&gt;with(<span class=\"hljs-string\">'success'<\/span>,<span class=\"hljs-string\">'Post created!'<\/span>);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Here we load categories and tags into the form, validate user input, create a new post, and attach selected tags. Laravel handles the pivot table automatically.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Blade Template for Post Creation<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">&lt;!-- resources\/views\/posts\/create.blade.php --&gt;\n&lt;form action=<span class=\"hljs-string\">\"{{ route('posts.store') }}\"<\/span> method=<span class=\"hljs-string\">\"POST\"<\/span>&gt;\n    @csrf\n    &lt;label&gt;Title&lt;\/label&gt;\n    &lt;input type=<span class=\"hljs-string\">\"text\"<\/span> name=<span class=\"hljs-string\">\"title\"<\/span> required&gt;\n\n    &lt;label&gt;Content&lt;\/label&gt;\n    &lt;textarea name=<span class=\"hljs-string\">\"content\"<\/span> required&gt;&lt;\/textarea&gt;\n\n    &lt;label&gt;Category&lt;\/label&gt;\n    &lt;select name=<span class=\"hljs-string\">\"category_id\"<\/span>&gt;\n        @<span class=\"hljs-keyword\">foreach<\/span>($categories <span class=\"hljs-keyword\">as<\/span> $category)\n            &lt;option value=<span class=\"hljs-string\">\"{{ $category-&gt;id }}\"<\/span>&gt;{{ $category-&gt;name }}&lt;\/option&gt;\n        @<span class=\"hljs-keyword\">endforeach<\/span>\n    &lt;\/select&gt;\n\n    &lt;label&gt;Tags&lt;\/label&gt;\n    @<span class=\"hljs-keyword\">foreach<\/span>($tags <span class=\"hljs-keyword\">as<\/span> $tag)\n        &lt;input type=<span class=\"hljs-string\">\"checkbox\"<\/span> name=<span class=\"hljs-string\">\"tags&#91;]\"<\/span> value=<span class=\"hljs-string\">\"{{ $tag-&gt;id }}\"<\/span>&gt; {{ $tag-&gt;name }}\n    @<span class=\"hljs-keyword\">endforeach<\/span>\n\n    &lt;button type=<span class=\"hljs-string\">\"submit\"<\/span>&gt;Create&lt;\/button&gt;\n&lt;\/form&gt;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This form allows the user to set the title, content, category, and tags when creating a new post. Validation errors and success messages can be added with Blade conditionals.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Listing Posts by Category and Tags<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ routes\/web.php<\/span>\nRoute::get(<span class=\"hljs-string\">'\/category\/{slug}'<\/span>, &#91;PostController::class, <span class=\"hljs-string\">'byCategory'<\/span>]);\nRoute::get(<span class=\"hljs-string\">'\/tag\/{slug}'<\/span>, &#91;PostController::class, <span class=\"hljs-string\">'byTag'<\/span>]);\n\n<span class=\"hljs-comment\">\/\/ app\/Http\/Controllers\/PostController.php<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">byCategory<\/span><span class=\"hljs-params\">($slug)<\/span>\n<\/span>{\n    $category = Category::where(<span class=\"hljs-string\">'slug'<\/span>,$slug)-&gt;firstOrFail();\n    $posts = $category-&gt;posts()-&gt;latest()-&gt;get();\n    <span class=\"hljs-keyword\">return<\/span> view(<span class=\"hljs-string\">'posts.index'<\/span>, compact(<span class=\"hljs-string\">'posts'<\/span>,<span class=\"hljs-string\">'category'<\/span>));\n}\n\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">byTag<\/span><span class=\"hljs-params\">($slug)<\/span>\n<\/span>{\n    $tag = Tag::where(<span class=\"hljs-string\">'slug'<\/span>,$slug)-&gt;firstOrFail();\n    $posts = $tag-&gt;posts()-&gt;latest()-&gt;get();\n    <span class=\"hljs-keyword\">return<\/span> view(<span class=\"hljs-string\">'posts.index'<\/span>, compact(<span class=\"hljs-string\">'posts'<\/span>,<span class=\"hljs-string\">'tag'<\/span>));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>These methods let you display posts filtered by category or tag. The <code>slug<\/code> column provides SEO-friendly URLs like <code>\/category\/laravel<\/code> or <code>\/tag\/php<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">&lt;!-- resources\/views\/posts\/index.blade.php --&gt;\n&lt;h2&gt;Posts&lt;\/h2&gt;\n@<span class=\"hljs-keyword\">foreach<\/span>($posts <span class=\"hljs-keyword\">as<\/span> $post)\n    &lt;article&gt;\n        &lt;h3&gt;{{ $post-&gt;title }}&lt;\/h3&gt;\n        &lt;p&gt;Category: {{ $post-&gt;category-&gt;name }}&lt;\/p&gt;\n        &lt;p&gt;Tags:\n            @<span class=\"hljs-keyword\">foreach<\/span>($post-&gt;tags <span class=\"hljs-keyword\">as<\/span> $tag)\n                {{ $tag-&gt;name }}\n            @<span class=\"hljs-keyword\">endforeach<\/span>\n        &lt;\/p&gt;\n        &lt;p&gt;{{ Str::limit($post-&gt;content, <span class=\"hljs-number\">100<\/span>) }}&lt;\/p&gt;\n    &lt;\/article&gt;\n@<span class=\"hljs-keyword\">endforeach<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This Blade view lists posts with their categories and tags. It can be extended with pagination, search, and links to detailed post pages.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Wrapping Up<\/strong><\/h2>\n\n\n\n<p>We\u2019ve built a simple blog system using Laravel 12 with posts, categories, and tags. Posts are linked to categories and can have multiple tags for flexible content organization. With Blade templates, you can easily create forms for post creation and listing pages filtered by category and tag. This foundation can be expanded into a full CMS with authentication, rich text editors, and SEO optimizations.<\/p>\n\n\n\n<div class=\"wp-block-spacer\" style=\"height:100px\" aria-hidden=\"true\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What\u2019s Next<\/strong><\/h2>\n\n\n\n<p>Now that you can create and filter blog posts, check out these related guides:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/blog\/how-to-generate-seo-friendly-urls-and-slugs-in-laravel\">How to Generate SEO-Friendly URLs and Slugs in Laravel<\/a><\/li>\n<li><a href=\"\/blog\/adding-meta-tags-and-open-graph-data-dynamically-in-laravel\">Adding Meta Tags and Open Graph Data Dynamically in Laravel<\/a><\/li>\n<li><a href=\"\/blog\/laravel-seo-guide-optimizing-meta-slugs-and-sitemaps\">Laravel SEO Guide: Optimizing Meta, Slugs, and Sitemaps<\/a><\/li>\n<\/ul>\n\n","protected":false},"excerpt":{"rendered":"<p>Building a simple blog is a great way to learn Laravel\u2019s fundamentals while producing something useful. In this article, we\u2019ll walk through setting up a blog with posts, categories, and tags. We\u2019ll also cover creating new posts, assigning them to categories and tags, and listing content based on filters. This will give you a clear [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":619,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[96,138,139,142,141,140],"class_list":["post-615","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-laravel","tag-blade","tag-blog","tag-categories","tag-content-management","tag-posts","tag-tags"],"_links":{"self":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/615","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/comments?post=615"}],"version-history":[{"count":1,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/615\/revisions"}],"predecessor-version":[{"id":618,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/615\/revisions\/618"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media\/619"}],"wp:attachment":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media?parent=615"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/categories?post=615"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/tags?post=615"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}