반응형

댓글을 저장하는 테이블과 댓글에 첨부한 이미지에 대한 정보를 저장하는 테이블이 다르기때문에 어쩔 수없이 두 테이블을 join으로 불러와야 한다. 그런데 라라벨에서는 그게 좀 어렵더라.

 

일단 일반적인 쿼리부터 한번 보도록 하자.

 

SELECT * FROM memos m
left join (select pid, filename from file_tables where code='memoattach' and status=1) f on f.pid=m.id
where m.status=1
order by m.id desc;

 

이렇게 쿼리를 날리려고 한다. memos 테이블에 있는 status가 1인 데이터들과 file_tables에 있는 status가 1인 데이터들 중에서  file_tables의 pid가 memos 테이블의 id가 같은 데이터들을 뽑아야한다.

 

그런데 모든 댓글이 첨부 파일이 있는 것이 아니므로 left join을 사용했다.

 

콘트롤러를 위의 쿼리에 맞게 수정해 보자.

 

/app/Http/Controllers/BoardController.php


    public function show($bid,$page)
    {
        Board::find($bid)->increment('cnt');
        $boards = Board::findOrFail($bid);
        $boards->content = htmlspecialchars_decode($boards->content);
        $boards->pagenumber = $page??1;
        $attaches = FileTables::where('pid',$bid)->where('code','boardattach')->where('status',1)->get();
 
        //DB::enableQueryLog();
        $memos = DB::table('memos')
                ->leftJoinSub('select pid, filename from file_tables where code=\'memoattach\' and
status=1', 'f', 'memos.id', 'f.pid')
                ->select('memos.*', 'f.filename')
                ->where('memos.bid', $bid)->where('memos.status',1)
                ->orderBy('memos.id', 'asc')
                ->get();
        //print_r(DB::getQueryLog());
        return view('boards.view', ['boards' => $boards, 'attaches' => $attaches, 'memos' => $memos]);
    }

 

뭔가 복잡하다. 위에 보여줬던 쿼리를 라라벨 쿼리빌더로 쓰면 이렇게 된다는 것이다. 쿼리 빌더를 만들고 제대로 쿼리를 만들었는지 확인해 보려면 DB::getQueryLog()를 찍어보면 된다. 소스에 주석처리 돼 있는 부분을 모두 풀어주면 쿼리가 보인다.

 

이렇게 테이블에서 데이터를 가져와서 뷰에 던져주는 방법은 똑같다. 이제 뷰를 수정해 보자.

 

/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" id="{{ 'memolist_'.$m->id }}">
            <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">
                <p class="card-text">
                    @if($m->filename)
                        <img src='/images/{{ $m->filename }}' width='100' />
                    @endif
                    {!! nl2br($m->memo) !!}
                </p>
            </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 () {
            }
            });
    }
    </script>
    @endsection    

 

filename 값이 있으면 화면에 보여주고 없으면 안보여준다. 이건 머 간단하다. 쿼리 만드는게 어려워서 그렇지.

 

이렇게 보이면 성공이다. 물론 나랑 똑같이 보이진 않을테고 첨부한 이미지가 있는 댓글과 아닌 댓글이 모두 제대로 보인다면 성공이다.

 

콘트롤러에서 쿼리 만드는 것이 갑자기 난이도가 올라갔지만 지금까지 공부한 사람이라면 이해하지 못할 것은 없을 것이다.

 

잘 모르겠으면 일단 소스 긁어서 따라하고 나중에 자꾸 들여다 보도록 하자.

 

다음 시간엔 댓글을 수정하고 삭제하는 것을 해보자.

반응형

+ Recent posts