PHP강좌/[라라벨]게시판만들기강좌

[라라벨+mysql]게시판 만들기 강좌 #25. 댓글 수정하기 1/2

에스크리토 2024. 8. 7. 15:23
반응형

댓글에다가 수정과 삭제 버튼을 달아 보자.

 

/resources/views/boards/view.blade.php

@extends('boards.layout')

@section('header')
    @include('boards.toptitle', ['toptitle'=>'게시판 보기', 'multi'=>$boards->multi])
@endsection

@section('content')

    <table class="table table-striped table-hover">
        <tbody>
            <tr>
                <th width="200">제목</th>
                <td>{{ $boards->subject }}</td>
            </tr>
            <tr>
                <td colspan="2">글쓴이 : {{ $boards->userid }} / 조회수 : {{ number_format($boards->cnt) }} / 등록일 : {{ $boards->regdate }}</td>
            </tr>
            <tr>
                <th width="200">내용</th>
                <td>{!! nl2br($boards->content) !!}</td>
            </tr>
            @if(count($attaches)>0)
            <tr>
                <th width="200">첨부 이미지</th>
                <td>
                    <div class="row row-cols-1 row-cols-md-6 g-4" id="attachFiles" style="margin-left:0px;">
                    @foreach ($attaches as $att)
                    <div id='af_{{ $att->id }}' class='card h-100' style='width:120px;margin-right: 10px;margin-bottom: 10px;'><a href="#" onclick="window.open('/boards/imgpop/{{ $att->filename }}','imgpop','width=600,height=400,scrollbars=yes');"><img src='/images/{{ $att->filename }}' width='100' /></a></div>
                    @endforeach
                    </div>
                </td>
            </tr>
            @endif
        </tbody>
    </table>
    <div align="right">
        @auth()
            @if($boards->userid==auth()->user()->userid)
                <a href="/boards/write/{{ $boards->multi }}/{{ $boards->bid }}"><button type="button" class="btn btn-secondary">수정</button></a>
                <a href="/boards/delete/{{ $boards->bid }}/{{ $boards->pagenumber }}" class="btn btn-secondary" onclick="return confirm('삭제하시겠습니까?');">삭제</a>
            @endif
        @endauth
        <a href="/boards/{{ $boards->multi }}/?page={{ $boards->pagenumber }}" class="btn btn-primary">목록</a>
    </div>
    <div style="padding:10px;">
    </div>

    <!--댓글 리스트 시작 -->
    <div id="reply">
        @foreach ($memos as $m)
        <div class="card mt-2">
            <div class="card-header p-2">
                <table>
                    <tbody>
                        <tr class="align-middle">
                            <td rowspan="2" class="pr-2">
                                <svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" fill="currentColor" class="bi bi-chat-square-dots" viewBox="0 0 16 16">
                                    <path d="M14 1a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1h-2.5a2 2 0 0 0-1.6.8L8 14.333 6.1 11.8a2 2 0 0 0-1.6-.8H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1zM2 0a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2.5a1 1 0 0 1 .8.4l1.9 2.533a1 1 0 0 0 1.6 0l1.9-2.533a1 1 0 0 1 .8-.4H14a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2z"/>
                                    <path d="M5 6a1 1 0 1 1-2 0 1 1 0 0 1 2 0m4 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0m4 0a1 1 0 1 1-2 0 1 1 0 0 1 2 0"/>
                                </svg>
                            </td>
                            <td class="ml">{{ $m->userid }}</td>
                        </tr>
                        <tr>
                            <td>
                                {{ disptime($m->created_at) }}
                            </td>
                        </tr>
                    </tbody>
                </table>
            </div>
            <div class="card-body" id="{{ 'memolist_'.$m->id }}">
                <p class="card-text">
                    @if($m->filename)
                        <img src='/images/{{ $m->filename }}' width='100' />
                    @endif
                    {!! nl2br($m->memo) !!}
                </p>
                @auth()
                    @if($m->userid==auth()->user()->userid)
                        <span class="badge bg-secondary" style="cursor:pointer;padding:10px;"><a onclick="memo_modify('{{ $m->id }}')">수정</a></span>
                        <span class="badge bg-secondary" style="cursor:pointer;padding:10px;"><a onclick="memo_delete('{{ $m->id }}','{{ $boards->num }}')">삭제</a></span>
                    @endif
                @endauth
            </div>
        </div>
        @endforeach
    </div>
    <!-- 댓글 리스트 끝 -->

    <!-- 댓글 입력 -->
    <div class="input-group" id="firstmemo" style="margin-top:10px;margin-bottom:10px;">
        <span class="input-group-text" id="memo_image_view" style="display:none;"></span>
        <button type="button" id="attmemoimg" class="btn btn-secondary">이미지첨부</button>
        <input type="hidden" name="memopid" id="memopid" value="{{ time() }}">
        <input type="hidden" name="memo_file" id="memo_file">
        <input type="file" name="upfile" id="upfile" accept="image/*" style="display:none;">
        <textarea class="form-control" aria-label="With textarea" style="height:100px;" name="memo" id="memo" placeholder="댓글을 입력해주세요"></textarea>
        @auth()
            <button type="button" class="btn btn-secondary" style="float:right;" id="memo_submit" onclick="memoup()">입력</button>
        @else
            <button type="button" class="btn btn-secondary" style="float:right;" id="memo_submit" onclick="alert('로그인 하셔야 입력할 수 있습니다.');">입력</button>
        @endauth
    </div>
    <!-- 댓글 입력 끝-->
    <div style="padding:20px;">
    </div>
    <script>
        function memoup(){
            var memo=$("#memo").val();
            var memo_file=$("#memo_file").val();
            var data = {
                memo : memo,
                memo_file : memo_file,
                bid : {{ $boards->bid }}
            };
            $.ajax({
                headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
                type: 'post',
                url: '{{ route('boards.memoup') }}',
                dataType: 'json',
                data: data,
                success: function(data) {
                    location.reload();
                },
                error: function(data) {
                console.log("error" +JSON.stringify(data));
                }
            });
        }

    $("#attmemoimg").click(function () {
        $('#upfile').click();
    });
   
    $("#upfile").change(function(){
        var formData = new FormData();
        var files = $('#upfile').prop('files');
        for(var i=0; i < files.length; i++) {
            attachFile(files[i]);
        }
    });

    function attachFile(file) {
        var memopid = $("#memopid").val();
        var formData = new FormData();
        formData.append("file", file);
        formData.append("pid", memopid);
        formData.append("code", "memoattach");
        $.ajax({
            headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
            url: '{{ route('boards.saveimage') }}',
            data: formData,
            cache: false,
            contentType: false,
            processData: false,
            dataType : 'json' ,
            type: 'POST',
            success: function (return_data) {
                var html = "<img src='/images/"+return_data.fn+"' style='max-width:100%;height:88px;'>";
                $("#memo_image_view").html(html);
                $("#memo_image_view").show();
                $("#attmemoimg").hide();
                $("#memo_file").val(return_data.fn);
            }
            , beforeSend: function () {
                $("#attmemoimg").hide();
                $("#memo_image_view").show();
                $('#memo_image_view').html('<div class="spinner-border text-dark" role="status"><span class="visually-hidden">Loading...</span></div>');
            }
            , complete: function () {
            }
            });
    }

    function memo_modify(m){
            var data = {
                memoid : m
            };
            $.ajax({
                headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
                type: 'post',
                url: '{{ route('boards.memomodi') }}',
                dataType: 'json',
                data: data,
                success: function(data) {
                    var html='<div class="input-group" id="firstmemo" style="margin-top:10px;margin-bottom:10px;"><span class="input-group-text" id="memo_image_view" style="display:none;"></span><button type="button" id="attmemoimg" class="btn btn-secondary">이미지첨부</button><input type="hidden" name="memopid" id="memopid" value="'+m+'"><input type="hidden" name="memo_file" id="memo_file"><textarea class="form-control" aria-label="With textarea" style="height:100px;" name="memomodify_'+m+'" id="memomodify_'+m+'">'+data.memos.memo+'</textarea><button type="button" class="btn btn-secondary" style="float:right;" id="memo_modifyup" onclick="memomodifyup('+m+')">수정</button></div>';
                    $("#memolist_"+m).html(html);
                },
                error: function(data) {
                    console.log("error" +JSON.stringify(data));
                }
            });
    }

    function memomodifyup(m){
            var memo=$("#memomodify_"+m).val();
            var data = {
                memo : memo,
                memoid : m
            };
            $.ajax({
                headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
                type: 'post',
                url: '{{ route('boards.memomodifyup') }}',
                dataType: 'json',
                data: data,
                success: function(data) {
                    location.reload();
                },
                error: function(data) {
                    console.log("error" +JSON.stringify(data));
                }
            });
    }
    </script>
    @endsection    

 

소스는 가급적이면 전체를 다 복사해서 쓰는걸 추천한다. 왜냐면 중간중간 아무말 안하고 내가 수정하는 소스가 있기때문이다. 

 

본인이 작성한 댓글이면 수정과 삭제 버튼이 보이도록 수정했다. 수정 버튼을 클릭하면 memomodify함수가 작동하는데 memomodify 함수에 지정돼 있는 경로를 라우터에 추가해주고 콘트롤러도 추가해주자.

 

/routes/web.php

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\BoardController;
use App\Http\Controllers\MemberController;

Route::get('/', function () {
    return view('welcome');
});

//게시판
Route::get('/boards/{multi?}', [BoardController::class, 'index'])->name('boards.index');
Route::get('/boards/show/{id}/{page}', [BoardController::class, 'show'])->name('boards.show');

Route::middleware('auth') -> group(function (){
    Route::get('/boards/write/{multi}/{bid?}', [BoardController::class, 'write'])->name('boards.write');
    Route::post('/boards/create', [BoardController::class, 'create'])->name('boards.create');
    Route::post('/boards/saveimage', [BoardController::class, 'saveimage'])->name('boards.saveimage');
    Route::post('/boards/deletefile', [BoardController::class, 'deletefile'])->name('boards.deletefile');
    Route::get('/boards/imgpop/{imgfile}', [BoardController::class, 'imgpop'])->name('boards.imgpop');
    Route::post('/boards/update', [BoardController::class, 'update'])->name('boards.update');
    Route::get('/boards/delete/{bid}/{page}', [BoardController::class, 'delete'])->name('boards.delete');
    Route::get('/boards/summernote/{multi}/{bid?}', [BoardController::class, 'summernote'])->name('boards.summernote');
    Route::post('/boards/memoup', [BoardController::class, 'memoup'])->name('boards.memoup');
    Route::post('/boards/memomodi', [BoardController::class, 'memomodi'])->name('boards.memomodi');
    Route::post('/boards/memomodifyup', [BoardController::class, 'memomodifyup'])->name('boards.memomodifyup');
});

//회원
Route::get('/login', [MemberController::class, 'login'])->name('auth.login');
Route::get('/signup', [MemberController::class, 'signup'])->name('auth.signup');
Route::post('/signupok', [MemberController::class, 'signupok'])->name('auth.signupok');
Route::post('/emailcheck', [MemberController::class, 'emailcheck'])->name('auth.emailcheck');
Route::post('/loginok', [MemberController::class, 'loginok']) -> name('auth.loginok');
Route::post('/logout', [MemberController::class, 'logout']) -> name('auth.logout');

 

함수명과 콘트롤러명이나 별명등은 모두 나와 달라도 된다. 하지만 일관성있게 바꿔야 한다.

 

/app/Http/Controllers/BoardController.php


    public function memomodi(Request $request)
    {
        $memos = Memos::findOrFail($request->memoid);
        if(Auth::user()->userid==$memos->userid){
            $attaches = FileTables::where('pid',$memos->id)->where('code','memoattach')->where('status',1)->get();
            return response()->json(array('msg'=> "succ", 'memos'=>$memos, 'att'=>$attaches), 200);
        }else{
            return response()->json(array('msg'=> "fail"), 200);
        }
    }

 

간단하게 메모의 내용을 그대로 받아서 다시 뷰에 보내주는 방식이다. 이 콘트롤러의 내용을 받아서 뷰가 이미 수정돼 있다.

var html='<div class="input-group" id="firstmemo" style="margin-top:10px;margin-bottom:10px;"><span class="input-group-text" id="memo_image_view" style="display:none;"></span><button type="button" id="attmemoimg" class="btn
btn-secondary">이미지첨부</button><input type="hidden" name="memopid" id="memopid" value="'+m+'"><input type="hidden" name="memo_file" id="memo_file"><input type="file" name="upfile" id="upfile" accept="image/*" style="display:none;"><textarea class="form-control" aria-label="With textarea" style="height:100px;" name="memomodify_'+m+'" id="memomodify_'+m+'">'+data.memos.memo+'</textarea><button type="button" class="btn btn-secondary" style="float:right;" id="memo_modifyup" onclick="memomodifyup('+m+')">수정</button></div>';
$("#memolist_"+m).html(html);
 

 

데이터를 받아서 memolist_라는 아이디가 있는 곳에 다시 보여주도록 수정했다.

 

이렇게 댓글이 보여지던 부분에 댓글을 수정할 수 있도록 글 작성 화면이 생겼다.

 

댓글을 수정하고 수정 버튼을 누르면 memomodifyup 함수가 작동하고 메모를 수정한 다음에 새로고침을 하도록 이미 소스를 수정해 놨다. 거기에 맞게 라우터도 수정해 놨으니 콘트롤러만 수정해 보자.

 

/app/Http/Controllers/BoardController.php


    public function memomodifyup(Request $request)
    {
        $memos = Memos::findOrFail($request->memoid);
        if(Auth::user()->userid==$memos->userid){
            $form_data = array(
                'memo' => $request->memo
            );
            Memos::where('id', $request->memoid)->update($form_data);
            return response()->json(array('msg'=> "succ", 'data'=>$request->memoid), 200);
        }else{
            return response()->json(array('msg'=> "fail"), 200);
        }
    }
 

 

메모를 update 해주고 새로고침 하도록 돼 있다.

 

메모가 제대로 수정되는지 확인해보자.

 

지금까지는 메모에 첨부 이미지가 없는 경우를 해 보았다. 다음 시간에는 메모에 첨부파일이 있는 경우의 수정과 첨부파일은 없었지만 새롭게 이미지를 첨부하는 경우를 작업해 보도록 하자.

반응형