To create different post types (e.g., Blog
and Article
) in Laravel with separate tables and URL structures, follow these steps:
1. Create Migrations
Define separate tables for blogs and articles.
Blogs Migration:
php artisan make:migration create_blogs_table
// database/migrations/YYYY_MM_DD_create_blogs_table.php
Schema::create('blogs', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('slug')->unique(); // For SEO-friendly URLs
$table->text('content');
$table->timestamps();
$table->softDeletes();
});
Articles Migration:
php artisan make:migration create_articles_table
// database/migrations/YYYY_MM_DD_create_articles_table.php
Schema::create('articles', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('slug')->unique();
$table->text('content');
$table->timestamps();
$table->softDeletes();
});
Run migrations:
php artisan migrate
2. Create Models
Generate models for Blog
and Article
.
Blog Model:
php artisan make:model Blog
// app/Models/Blog.php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Blog extends Model {
use SoftDeletes;
protected $fillable = ['title', 'slug', 'content'];
// Use 'slug' instead of 'id' in URLs
public function getRouteKeyName() {
return 'slug';
}
}
Article Model:
php artisan make:model Article
// app/Models/Article.php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Article extends Model {
use SoftDeletes;
protected $fillable = ['title', 'slug', 'content'];
public function getRouteKeyName() {
return 'slug';
}
}
3. Define Routes
Set up distinct URL structures in routes/web.php
:
// Blogs
Route::prefix('blogs')->group(function () {
Route::get('/', [BlogController::class, 'index'])->name('blogs.index');
Route::get('/create', [BlogController::class, 'create'])->name('blogs.create');
Route::post('/', [BlogController::class, 'store'])->name('blogs.store');
Route::get('/{blog}', [BlogController::class, 'show'])->name('blogs.show');
});
// Articles
Route::prefix('articles')->group(function () {
Route::get('/', [ArticleController::class, 'index'])->name('articles.index');
Route::get('/create', [ArticleController::class, 'create'])->name('articles.create');
Route::post('/', [ArticleController::class, 'store'])->name('articles.store');
Route::get('/{article}', [ArticleController::class, 'show'])->name('articles.show');
});
4. Create Controllers
Generate controllers for each post type.
BlogController:
php artisan make:controller BlogController --resource
// app/Http/Controllers/BlogController.php
use App\Models\Blog;
use Illuminate\Http\Request;
class BlogController extends Controller {
public function index() {
$blogs = Blog::latest()->get();
return view('blogs.index', compact('blogs'));
}
public function show(Blog $blog) {
return view('blogs.show', compact('blog'));
}
public function store(Request $request) {
$validated = $request->validate([
'title' => 'required|string|max:255',
'content' => 'required|string',
]);
// Generate a unique slug
$slug = str()->slug($request->title);
$count = Blog::where('slug', $slug)->count();
if ($count > 0) {
$slug = $slug . '-' . ($count + 1);
}
Blog::create([...$validated, 'slug' => $slug]);
return redirect()->route('blogs.index');
}
}
ArticleController:
php artisan make:controller ArticleController --resource
// app/Http/Controllers/ArticleController.php
use App\Models\Article;
use Illuminate\Http\Request;
class ArticleController extends Controller {
public function index() {
$articles = Article::latest()->get();
return view('articles.index', compact('articles'));
}
public function show(Article $article) {
return view('articles.show', compact('article'));
}
public function store(Request $request) {
$validated = $request->validate([
'title' => 'required|string|max:255',
'content' => 'required|string',
]);
$slug = str()->slug($request->title);
$count = Article::where('slug', $slug)->count();
if ($count > 0) {
$slug = $slug . '-' . ($count + 1);
}
Article::create([...$validated, 'slug' => $slug]);
return redirect()->route('articles.index');
}
}
5. Create Views
Organize views into separate directories for clarity.
Blogs Index (resources/views/blogs/index.blade.php
):
@foreach ($blogs as $blog)
<a href="{{ route('blogs.show', $blog) }}">{{ $blog->title }}</a>
@endforeach
Articles Index (resources/views/articles/index.blade.php
):
@foreach ($articles as $article)
<a href="{{ route('articles.show', $article) }}">{{ $article->title }}</a>
@endforeach
Blog Show Page (resources/views/blogs/show.blade.php
):
<h1>{{ $blog->title }}</h1>
<p>{{ $blog->content }}</p>
Article Show Page (resources/views/articles/show.blade.php
):
<h1>{{ $article->title }}</h1>
<p>{{ $article->content }}</p>
6. Test the URLs
- Blogs:
http://yourapp.com/blogs/my-first-blog
- Articles:
http://yourapp.com/articles/my-first-article
Key Notes:
- Slug Uniqueness: Use a package like
spatie/laravel-sluggable
to automate slug generation. - Shared Logic: Extract common functionality (e.g., slug generation) into traits or parent controllers.
- Route Model Binding: Use
slug
instead ofid
by overridinggetRouteKeyName()
in models.
This approach ensures clean separation between post types while maintaining scalability.