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

[라라벨+mysql]게시판 만들기 강좌 #13. 첨부파일 방식 변경(ajax로 파일 첨부) 2/4

에스크리토 2024. 7. 25. 10:24
반응형

준비가 됐으면 글쓰기 파일을 조금 수정해야한다. 기존엔 첨부만 하고 등록 버튼을 누르면 첨부한 파일의 정보를 서버에 전송하는 식이었지만 이번엔 파일을 선택하면 바로 서버에 등록하고 화면에 보여주게끔 바꿔보겠다. 더불어 파일을 바로 수정도 가능하도록 해보자

 

/resources/views/boards/write.blade.php

@extends('boards.layout')

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

@section('content')
<br />
    <form method="post" action="/boards/create" enctype="multipart/form-data">
        @csrf
        @method('post')
        <input type="hidden" name="pid" id="pid" value="{{ $boards->bid??time() }}">
        <input type="hidden" name="code" id="code" value="boardattach">
        <input type="hidden" name="attcnt" id="attcnt" value="0">
        <input type="hidden" name="imgUrl" id="imgUrl" value="">
        <input type="hidden" name="attachFile" id="attachFile" value="">
        <div class="form-group">
            <div class="col-md-12">
                <input type="text" name="subject" id="subject" class="form-control input-lg" placeholder="제목을 입력하세요." />
            </div>
        <br />
        </div>
        <div class="form-group">
            <div class="col-md-12">
                <textarea class="form-control" id="content" name="content" rows="5" placeholder="내용을 입력하세요."></textarea>
            </div>
        </div>
        <br />
        <div class="form-group">
            <div id="attach_site" class="col-md-12">
                <div class="row row-cols-1 row-cols-md-6 g-4" id="attachFiles" style="margin-left:0px;">
                </div>
           </div>
           <div class="col-md-12">
                <input type="file" name="afile" id="afile" multiple accept="image/*" multiple class="form-control" aria-label="Large file input example">
           </div>
        </div>
        <br />
        <br />
        <div class="form-group">
            <div class="col-md-12 text-center">
                <button type="button" name="edit" class="btn btn-primary input-lg" onclick="sendsubmit()">등록</button>
            </div>
        </div>
    </form>
<script>

    $("#afile").change(function(){
        var formData = new FormData();
        var attcnt=$("#attcnt").val();
        var files = $('#afile').prop('files');
        var totcnt=parseInt(attcnt)+parseInt(files.length)

        if(totcnt>5){
            alert('5개까지만 등록할 수 있습니다.');
            return false;
        }

        for(var i=0; i < files.length; i++) {
            attachFile(files[i]);
        }
    });  

    function attachFile(file) {
        var formData = new FormData();
        var pid = $("#pid").val();
        var code = $("#code").val();
        formData.append("file", file);
        formData.append("uptype", "attach");
        formData.append("pid", pid);
        formData.append("code", code);
        $.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) {
    //          console.log(JSON.stringify(return_data));
                if(return_data.result=='fail'){
                    alert(return_data.msg);
                    return false;
                }else{
                    //var img="<img src='"+data+"' width='50'><br>";
                    var html = "<div id='af_"+return_data.fid+"' class='card h-100' style='width:120px;margin-right: 10px;margin-bottom: 10px;'><img src='/images/"+return_data.fn+"' width='100' /><div class='card-body'><button type='button' class='btn btn-warning' onclick=\"deletefile('"+return_data.fn+"', '"+return_data.fid+"')\">삭제</button></div></div>";
                        $("#attachFiles").append(html);
                   
                    var rcnt=parseInt(attcnt)+1;
                    $("#attcnt").val(rcnt);
                    var attachFile=$("#attachFile").val();
                    if(attachFile){
                        attachFile=attachFile+",";
                    }
                    $("#attachFile").val(attachFile+return_data.fn);
                }
            }
            , beforeSend: function () {
                var width = 0;
                var height = 0;
                var left = 0;
                var top = 0;
                width = 50;
                height = 50;

                top = ( $(window).height() - height ) / 2 + $(window).scrollTop();
                left = ( $(window).width() - width ) / 2 + $(window).scrollLeft();

                if($("#div_ajax_load_image").length != 0) {
                        $("#div_ajax_load_image").css({
                                "top": top+"px",
                                "left": left+"px"
                        });
                        $("#div_ajax_load_image").show();
                }
                else {
                        $('body').append('<div id="div_ajax_load_image" style="position:absolute; top:' + top + 'px; left:' + left + 'px; width:' + width + 'px; height:' + height + 'px; z-index:9999;" class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div>');
                }

        }
            , complete: function () {
                        $("#div_ajax_load_image").hide();
        }
        });

    }

    function sendsubmit(){
        var subject=$("#subject").val();
        var content=$("#content").val();
        var pid = $("#pid").val();
        var code = $("#code").val();
        var data = {
            multi : '{{ $multi }}',
            subject : subject,
            content : content,
            pid : pid,
            code : code
        };
        $.ajax({
            headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
            type: 'post',
            url: '{{ route('boards.create') }}',
            dataType: 'json',
            enctype: 'multipart/form-data',
            data: data,
            success: function(data) {
                location.href='/boards/show/'+data.bid+'/1';
            },
            error: function(data) {
                console.log("error" +data);
            }
        });
    }
</script>    
@endsection

 

이전에 비해서 뭔가 많이 바뀌었다. 파일을 등록하면 화면에 보이는 부분을 미리 만들어 두었고 파일 첨부방식도 multiple로 바꿔서 여러개를 한꺼번에 등록할 수 있도록 바꿨다.

 

이미지 파일을 선택하면 attachFile 함수가 작동해 파일을 서버에 등록한다. 등록에 필요한 라우터와 콘트롤러를 추가해보자.

 

/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}', [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::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 saveimage(Request $request)
    {
        $request->validate([
            'file' => 'required|image|max:2048'
        ]);

        if(auth()->check()){
            $image = $request->file('file');
            $new_name = rand().'_'.time().'.'.$image->getClientOriginalExtension();
            $image->move(public_path('images'), $new_name);
            $fid = rand();
            $form_data = array(
                'pid' => $request->pid,
                'userid' => Auth::user()->userid,
                'code' => $request->code,
                'filename' => $new_name
            );
            $rs=FileTables::create($form_data);
            return response()->json(array('msg'=> "등록했습니다.", 'result'=>'succ', 'fn'=>$new_name, 'fid'=>$fid), 200);
        }else{
            return response()->json(array('msg'=> "로그인 하십시오", 'result'=>'fail'), 200);
        }
    }

 

파일을 선택하면 서버에 등록하는 함수다. 새로만든 FileTables 모델을 사용했다. 그말은 모델을 인클루드 해줘야한다.

namespace App\Http\Controllers;
use App\Models\Board;
use App\Models\FileTables;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

 

상단에 추가해준다. 추가 안하면 오류나니까.

 

이렇게 해주고 화면에서 이미지 파일을 첨부해보자.

이렇게 보여지게 된다.

 

첨부한 파일은 file_tables 테이블에 등록됐다.

 

pid는 게시판의 bid값을 넣을건데 아직 게시판 테이블에 등록하지 않았기때문에 현재 시간을 timestamp값으로 넣어줬다. 게시판 테이블에 등록을 하면 고유 bid가 생길것이고 그때 pid값을 업데이트 해줄 것이다.

 

다음엔 삭제 버튼을 클릭하면 첨부한 이미지를 삭제하는걸 해보자.

반응형