본문으로 건너뛰기

관리자 글 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/postsGETindex()글 목록
/admin/posts/createGETcreate()작성 화면
/admin/postsPOSTstore()저장
/admin/posts/{post}/editGETedit()수정 화면
/admin/posts/{post}PUT/PATCHupdate()수정 저장
/admin/posts/{post}DELETEdestroy()삭제

글 목록

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()으로 한 번 더 물어봅니다.