관리자 글 CRUD
관리자 글 기능은 아래 컨트롤러가 담당합니다.
app/Http/Controllers/Admin/PostController.php
CRUD는 Create, Read, Update, Delete의 약자입니다.
| 기능 | 뜻 | zenoBlog 예시 |
|---|---|---|
| Create | 생성 | 새 글 작성 |
| Read | 조회 | 관리자 글 목록 |
| Update | 수정 | 글 수정 |
| Delete | 삭제 | 글 삭제 |
Resource 라우트
routes/web.php에는 이렇게 되어 있습니다.
Route::resource('posts', \App\Http\Controllers\Admin\PostController::class);
이 한 줄이 관리자 글 관련 URL 여러 개를 자동으로 만듭니다.
| URL | 메서드 | 함수 | 역할 |
|---|---|---|---|
/admin/posts | GET | index() | 글 목록 |
/admin/posts/create | GET | create() | 작성 화면 |
/admin/posts | POST | store() | 저장 |
/admin/posts/{post}/edit | GET | edit() | 수정 화면 |
/admin/posts/{post} | PUT/PATCH | update() | 수정 저장 |
/admin/posts/{post} | DELETE | destroy() | 삭제 |
글 목록
public function index()
{
$posts = Post::with('category')
->latest()
->paginate(20);
return view('admin.posts.index', compact('posts'));
}
관리자 목록은 공개/비공개 글을 모두 보여줍니다.
방문자 화면과 다르게 where('is_published', true)가 없습니다.
화면 파일은 다음입니다.
resources/views/admin/posts/index.blade.php
이 화면에는 글 제목, 카테고리, 공개 상태, 날짜, 수정/삭제 버튼이 있습니다.
글 작성 화면
public function create()
{
$categories = Category::all();
return view('admin.posts.create', compact('categories'));
}
글 작성 화면에서 카테고리 선택 박스를 보여줘야 하므로 Category::all()로 카테고리 목록을 가져옵니다.
화면 파일은 다음입니다.
resources/views/admin/posts/create.blade.php
입력 필드는 다음과 같습니다.
| 입력 | 서버로 보내는 name |
|---|---|
| 제목 | title |
| 카테고리 | category_id |
| 본문 | content |
| 태그 | tags |
| 요약 | excerpt |
| SEO 제목 | meta_title |
| SEO 설명 | meta_description |
| 공개 여부 | is_published |
글 저장
저장 함수는 store()입니다.
public function store(Request $request)
{
$request->validate([
'title' => 'required|string|max:255',
'content' => 'required',
]);
$post = Post::create([
'title' => $request->title,
'slug' => Str::slug($request->title) . '-' . time(),
'content' => $request->content,
'excerpt' => $request->excerpt,
'category_id' => $request->category_id,
'meta_title' => $request->meta_title,
'meta_description' => $request->meta_description,
'is_published' => $request->has('is_published'),
'published_at' => $request->has('is_published') ? now() : null,
]);
}
validate
'title' => 'required|string|max:255'
제목은 필수이고 문자열이어야 하며 최대 255자입니다.
'content' => 'required'
본문은 필수입니다.
slug 생성
Str::slug($request->title) . '-' . time()
제목을 URL에 쓰기 좋은 형태로 바꾸고, 뒤에 시간을 붙입니다.
예를 들어 제목이 Laravel 라우팅 공부라면 대략 이런 slug가 됩니다.
laravel-라우팅-공부-1710000000
공개 여부 처리
체크박스는 체크하지 않으면 요청 데이터에 값이 없습니다.
그래서 Laravel에서는 has()를 씁니다.
$request->has('is_published')
공개 체크를 하면:
'is_published' => true
'published_at' => now()
체크하지 않으면:
'is_published' => false
'published_at' => null
태그 저장
태그 입력값은 쉼표로 구분합니다.
Laravel, PHP, 배포
처리 흐름은 다음과 같습니다.
문자열을 쉼표로 분리
-> 각 태그 이름 trim
-> 태그가 없으면 생성
-> tag id 배열 만들기
-> post_tag 테이블에 연결
핵심 코드는 이것입니다.
$tag = \App\Models\Tag::firstOrCreate(
['slug' => Str::slug($tagName)],
['name' => $tagName]
);
firstOrCreate()는 먼저 찾고, 없으면 만듭니다.
$post->tags()->sync($tagIds);
sync()는 현재 글에 연결될 태그 목록을 통째로 맞춰줍니다.
글 수정
수정 화면은 edit()입니다.
public function edit(Post $post)
{
$categories = Category::all();
return view('admin.posts.edit', compact('post', 'categories'));
}
여기서 Post $post는 Route Model Binding입니다.
/admin/posts/3/edit
-> Laravel이 id 3번 글을 찾아서 $post에 넣어줌
수정 저장은 update()입니다.
'slug' => $post->slug,
수정할 때 slug를 바꾸지 않는 이유는 기존 URL을 유지하기 위해서입니다.
글 삭제
public function destroy(Post $post)
{
$post->delete();
return redirect()->route('admin.posts.index')
->with('success', '글이 삭제됐습니다.');
}
삭제 후 관리자 글 목록으로 돌아갑니다.
화면에서는 삭제 전에 confirm()으로 한 번 더 물어봅니다.