PHP/Modern PHP

[PHP] 게시판 만들기 with MVC - 9부 Reply(List & Create)

DSeung 2023. 12. 28. 13:05

 

이전글 : [PHP] 게시판 만들기 with MVC - 1부 Migration

이전글 : [PHP] 게시판 만들기 with MVC - 2부 Routing

이전글 : [PHP] 게시판 만들기 with MVC - 3부 View(List & Create)

이전글 : [PHP] 게시판 만들기 with MVC - 4부 Controller

이전글 : [PHP] 게시판 만들기 with MVC - 5부 Pagination

이전글 : [PHP] 게시판 만들기 with MVC - 6부 Read

이전글 : [PHP] 게시판 만들기 with MVC - 7부 Update & Delete

이전글 : [PHP] 게시판 만들기 with MVC - 8부 Lock

 

Reply

이번에는 댓글 기능을 만들어봅시다.

지난번에 마이그레이션을 작업하면서 replies 테이블도 생성했었습니다

간단하게 Post에다가 댓글을 다는 기능을 담당합니다.

 

View

먼저 View를 만들어줍시다.

기존에 있던 Read.php를 수정합니다.

코드가 보기 어려울 것 같습니다만 주석으로 (//추가) 표시해 놓은 부분 아래 코드르 넣으시면 됩니다

- use Model\Reply;

- $reply = new Reply();

- 추천 버튼 아래에 <div> 부터 맨 아래까지

 

bbs/view/read.php

<!doctype html>
<?php

use Model\Post;
// 추가
use Model\Reply;

include "part/header.php";
?>
<body>
<div class="m-4">
    <div class="container mt-5">
        <h3 class="d-inline"><a href="/bbs">자유게시판</a></h3>/<h4 class="d-inline">글 읽기</h4>
        <p class="mt-1 mb-3">글의 상세 내용입니다.</p>
        <hr/>
        <?php
        $idx = $_GET['idx'];
        $post = new Post();
        // 추가 
        $reply = new Reply();
        
        ...

                <a href="./update?idx=<?= $postInfo['idx'] ?>" class="btn btn-primary">수정하기</a>
                <a href="/bbs" class="btn btn-secondary">목록</a>
                <a href="./delete?idx=<?= $postInfo['idx'] ?>" class="btn btn-dark">삭제하기</a>
                <button class="btn btn-success" id="thumbsUp">
                    추천 <?= $postInfo['thumbs_up'] != 0 ? "(" . $postInfo['thumbs_up'] . ")" : '' ?>
                    <span class="material-symbols-outlined" style="font-size:16px">thumb_up</span>
                </button>
                <!--추천에서 사용할 postIdx 값-->
                <input type="hidden" id="postIdx" value="<?= $idx ?>">
			
            	
                // 추가
                <div class="mt-2">
                    <hr/>
                    <h5>댓글 작성</h5>
                    <form action="/bbs/reply/create" method="post">
                        <div class="form-group">
                            <input type="hidden" name="post_idx" value="<?= $idx ?>">
                            <div class="form-row">
                                <div class="form-group col-md-6">
                                    <label for="name">Name</label>
                                    <input type="text" class="form-control" name="name" placeholder="Name을 입력해주세요.">
                                </div>
                                <div class="form-group col-md-6">
                                    <label for="password">Password</label>
                                    <input type="password" class="form-control" name="pw"
                                           placeholder="Password를 입력해주세요.">
                                </div>
                            </div>

                            <label for="content">내용:</label>
                            <textarea name="content" class="form-control" id="content" rows="3"></textarea>
                        </div>
                        <button type="submit" class="btn btn-primary">댓글 작성</button>
                    </form>
                </div>
                <hr/>
                <h3>댓글</h3>
                <?php
                $replies = $reply->getReplies($idx);
                if ($replies) {
                    foreach ($replies as $replyInfo) {
                        ?>
                        <!-- 댓글 섹션 -->
                        <div class="mt-4 card">
                            <div class="card-body">
                                <input type="hidden" class="reply-idx" value="<?= $replyInfo['idx'] ?>"/>
                                <div class="media-body mb-3">
                                    <h5 class="mt-0"><?= $replyInfo['name'] ?></h5>
                                    <p class="mb-0">작성일: <?= $replyInfo['created_at'] ?></p>
                                    <?= nl2br($replyInfo['content']) ?>
                                </div>
                                <button class="btn btn-primary btn-reply-edit" data-bs-toggle="modal"
                                        data-bs-target="#editModal">
                                    수정
                                </button>
                                <button class="btn btn-primary btn-reply-delete" data-bs-toggle="modal"
                                        data-bs-target="#deleteModal">
                                    삭제
                                </button>
                                <button class="btn btn-primary btnSubReply">
                                    대댓글
                                </button>
                            </div>
                        </div>
                        <?php
                    }
                }
            }
        } else {
            echo "<script>alert('존재하지 않는 게시물입니다.');history.back();</script>";
        }
        ?>
    </div>
    <script src="/bbs/assets/script/read.js"></script>
</body>
</html>

Route

신규 Route 파일인 ReplyRoute.php를 추가할 것입니다.

bbs/index.php

<?php
require_once "bootstrap.php";

use Route\PostRoute;
// 추가
use Route\ReplyRoute;

// URL 요청
$url = isset($_GET['url']) ? $_GET['url'] : '/';

// 루트 경로로 접근 시 게시글 목록으로 리다이렉트
if ($url == '/' || $url == '') {
    header('Location: post/list');
} else {
    $routes = array();
    $routes[] = new PostRoute();
    // 추가
    $routes[] = new ReplyRoute();

    $ok = false;
    foreach ($routes as $route) {
        // routing 함수를 돌며 false 리턴하는 게 있다면 404 페이지 출력
        $ok = $route->routing($url);
        if ($ok) {
            break;
        }
    }

    // 404 페이지 출력
    if (!$ok) {
        header("HTTP/1.0 404 Not Found");
        require_once "view/404.php";
    }
}


댓글 생성에 대한 라우팅을 만들어 줍시다

bbs/Route/ReplyRoute.php

<?php

namespace Route;

use Controller\ReplyController;

class ReplyRoute extends BaseRoute
{
    function routing($url): bool
    {
        $replyController = new ReplyController();

        if ($this->routeCheck($url, "reply/create", "POST")) {
            $replyController->create();
            return true;
        }
    }
}


Controller

Controller에도 댓글 생성을 위한 로직을 넣어줍니다.

미리 대댓글 기능을 위한 분기가 추가 되어있으니 참고만 하시면 되고, 다음 포스팅에서 마저 구현합니다.

bbs/Controller/ReplyController.php

<?php

namespace Controller;

use Model\Reply;

class ReplyController extends BaseController
{
    private $reply;

    public function __construct()
    {
        $this->reply = new reply();
    }

    /**
     * 댓글 생성 기능을 담당
     * @return void
     */
    public function create()
    {
        $postIdx = $_POST['post_idx'];
        $name = $_POST['name'];
        $pw = $_POST['pw'];
        $content = $_POST['content'];
        $parentIdx = $_POST['parent_idx'];

        if ($this->parametersCheck($postIdx, $name, $pw, $content)) {
            if (empty($parentIdx)) {
                if ($this->reply->create($postIdx, $name, $pw, $content)) {
                    $this->redirect('/bbs/post/read?idx=' . $postIdx, '댓글이 작성되었습니다.');
                } else {
                    $this->redirectBack('댓글 작성에 실패했습니다.');
                }
            } else {
                if ($this->reply->subReplyCreate($postIdx, $parentIdx, $name, $pw, $content)) {
                    $this->redirect('/bbs/post/read?idx=' . $postIdx, '댓글이 작성되었습니다.');
                } else {
                    $this->redirectBack('댓글 작성에 실패했습니다.');
                }
            }
        } else {
            $this->redirectBack('입력되지 않은 값이 있습니다.');
        }
    }
}

 

Model

마지막으로 Model Reply를 추가합니다.

bbs/Model/Reply.php

<?php

namespace Model;

use PDOException;

class Reply extends BaseModel
{
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * 댓글 만들기
     * @param $postIdx
     * @param $name
     * @param $pw
     * @param $content
     * @return bool
     */
    public function create($postIdx, $name, $pw, $content): bool
    {
        try {
            $hashed_pw = password_hash($pw, PASSWORD_DEFAULT);
            $query = "INSERT INTO replies (post_idx, name, pw, content) VALUES (:post_idx, :name, :pw, :content)";
            return $this->conn->prepare($query)->execute([
                'post_idx' => $postIdx,
                'name' => $name,
                'pw' => $hashed_pw,
                'content' => $content
            ]);
        } catch (PDOException  $e) {
            error_log($e->getMessage());
            return false;
        }
    }

    /**
     * 댓글 목록 가져오기
     * @param $postIdx
     * @return array
     */
    public function getReplies($postIdx): array
    {
        try {
            $query = "SELECT * FROM replies WHERE post_idx = :post_idx AND parent_idx = 0 ORDER BY idx DESC";
            $stmt = $this->conn->prepare($query);
            $stmt->execute([
                'post_idx' => $postIdx,
            ]);
            return $stmt->fetchAll();
        } catch (PDOException $e) {
            error_log($e->getMessage());
            return [];
        }
    }
}

 

여기까지 하면 다음과 같은 결과물을 볼 수 있습니다.

반응형