반응형

CI4에서 ajax를 호출하는 방법은 기존과 크게 다르지 않다. 기존에는 jquery를 이용해 파일을 호출하고 파일에서 값을 리턴해줬지만 CI4에서느 jquery를 이용해 콘트롤러를 호출해주고 콘트롤러에서 리턴값을 주면 된다. 

 

글을 작성시 이미지를 첨부할거니까 write파일을 수정해보자

 

/app/Views/board_write.php

    <form method="post" action="<?= site_url('/writeSave') ?>" enctype="multipart/form-data">
        <input type="hidden" name="bid" value="<?php echo isset($view->bid)?$view->bid:0;?>">
        <input type="hidden" name="file_table_id" id="file_table_id" value="0"><!-- 첨부한 파일 아이디-->
        <div class="mb-3">
        <label for="exampleFormControlInput1" class="form-label">이름</label>
            <input type="text" name="username" class="form-control" id="exampleFormControlInput1" placeholder="이름을 입력하세요." value="<?php echo $_SESSION['username']?>">
        </div>
        <div class="mb-3">
        <label for="exampleFormControlInput1" class="form-label">제목</label>
            <input type="text" name="subject" class="form-control" id="exampleFormControlInput1" placeholder="제목을 입력하세요." value="<?php echo isset($view->subject)?$view->subject:'';?>">
        </div>
        <div class="mb-3">
        <label for="exampleFormControlTextarea1" class="form-label">내용</label>
        <textarea class="form-control" id="exampleFormControlTextarea1" name="content" rows="3"><?php echo isset($view->content)?$view->content:'';?></textarea>
        </div>
        <div class="mb-3">
                <input type="file" multiple name="upfile[]" id="upfile" class="form-control form-control-lg" aria-label="Large file input example">
        </div>
        <br />
        <div class="row row-cols-1 row-cols-md-6 g-4" id="imageArea"><!-- 첨부한 이미지가 나올 영역-->
        </div>
        <br />
        <?php
        $btntitle=isset($view->bid)?"수정":"등록";
        ?>
            <button type="submit" class="btn btn-primary"><?php echo $btntitle;?></button>
    </form>

    <script>

$("#upfile").change(function(){

    var files = $('#upfile').prop('files');
    for(var i=0; i < files.length; i++) {
        attachFile(files[i]);
    }

    $('#upfile').val('');

});  

function attachFile(file) {
    var formData = new FormData();
    formData.append("savefile", file);
    $.ajax({
        url:'/save_image',
        data: formData,
        cache: false,
        contentType: false,
        processData: false,
        dataType : 'json' ,
        type: 'POST',
        success: function (return_data) {
                fid = $("#file_table_id").val() + "," + return_data.fid;
                $("#file_table_id").val(fid);
                var html = "<div class='col' id='f_"+return_data.fid+"'><div class='card h-100'><img src='/uploads/"+return_data.savename+"' class='card-img-top'><div class='card-body'><button type='button' class='btn btn-warning' onclick='file_del("+return_data.fid+")'>삭제</button></div></div></div>";
                $("#imageArea").append(html);
        }
    });

}

function file_del(fid){

if(!confirm('삭제하시겠습니까?')){
return false;
}
   
var data = {
    fid : fid
};
    $.ajax({
        async : false ,
        type : 'post' ,
        url : '/file_delete' ,
        data  : data ,
        dataType : 'json' ,
        error : function() {} ,
        success : function(return_data) {
                $("#f_"+fid).hide();
        }
});

}

</script>    

 

파일을 첨부하기 위해 첨부하기 버튼을 클릭하고 이미지를 선택하면 ajax가 작동한다. /save_image로 이동하라고 해 놨으니 라우트에 등록해보자.

 

/app/Config/Routes.php

 

<?php

namespace Config;

// Create a new instance of our RouteCollection class.
$routes = Services::routes();

// Load the system's routing file first, so that the app and ENVIRONMENT
// can override as needed.
if (is_file(SYSTEMPATH . 'Config/Routes.php')) {
    require SYSTEMPATH . 'Config/Routes.php';
}

/*
 * --------------------------------------------------------------------
 * Router Setup
 * --------------------------------------------------------------------
 */
$routes->setDefaultNamespace('App\Controllers');
$routes->setDefaultController('Home');
$routes->setDefaultMethod('index');
$routes->setTranslateURIDashes(false);
$routes->set404Override();
// The Auto Routing (Legacy) is very dangerous. It is easy to create vulnerable apps
// where controller filters or CSRF protection are bypassed.
// If you don't want to define all routes, please use the Auto Routing (Improved).
// Set `$autoRoutesImproved` to true in `app/Config/Feature.php` and set the following to true.
// $routes->setAutoRoute(false);

/*
 * --------------------------------------------------------------------
 * Route Definitions
 * --------------------------------------------------------------------
 */

// We get a performance increase by specifying the default
// route since we don't have to scan directories.
$routes->get('/', 'Home::index');
$routes->get('/board', 'Board::list');
$routes->get('/boardWrite', 'Board::write');
$routes->match(['get', 'post'], 'writeSave', 'Board::save');
$routes->get('/boardView/(:num)', 'Board::view/$1');
$routes->get('/modify/(:num)', 'Board::modify/$1');
$routes->get('/delete/(:num)', 'Board::delete/$1');
$routes->post('/save_image', 'Board::save_image');
$routes->post('/file_delete', 'Board::file_delete');

//member
$routes->get('/login', 'MemberController::login');
$routes->get('/logout', 'MemberController::logout');
$routes->match(['get', 'post'], '/loginok', 'MemberController::loginok');
/*
 * --------------------------------------------------------------------
 * Additional Routing
 * --------------------------------------------------------------------
 *
 * There will often be times that you need additional routing and you
 * need it to be able to override any defaults in this file. Environment
 * based routes is one such time. require() additional route files here
 * to make that happen.
 *
 * You will have access to the $routes object within that file without
 * needing to reload it.
 */
if (is_file(APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php')) {
    require APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php';
}

 

이제 컨트롤러를 등록해보자.

 

/app/Controllers/Board.php

 

<?php

namespace App\Controllers;
use App\Models\BoardModel;//사용할 모델을 반드시 써줘야한다.

class Board extends BaseController
{
    public function list()
    {
        $db = db_connect();
        $page    = $this->request->getVar('page') ?? 1;//현재 페이지, 없으면 1
        $perPage = 10;//한 페이지당 출력할 게시물 수
        $startLimit = ($page-1)*$perPage;//쿼리의 limit 시작 부분
        $sql = "select b.*, if((now() - regdate)<=86400,1,0) as newid
        ,(select count(*) from memo m where m.status=1 and m.bid=b.bid) as memocnt
        ,(select m.regdate from memo m where m.status=1 and m.bid=b.bid order by m.memoid desc limit 1) as memodate
        ,(select count(*) from file_table f where f.type='board' and f.status=1 and f.bid=b.bid) as filecnt
        from board b where 1=1";
        $order = " order by bid desc";
        $limit = " limit $startLimit, $perPage";
        $query = $sql.$order.$limit;
        $rs = $db->query($query);
        $rs2 = $db->query($sql);
        $total = $rs2->getNumRows();//전체 게시물수

        $data['list'] = $rs->getResult();
        $data['total'] = $total;
        $data['page'] = $page;
        $data['perPage'] = $perPage;

        $pager = service('pager');//페이저를 호출한다.
        $pager_links = $pager->makeLinks($page, $perPage, $total, 'default_full');
        $data['pager_links'] = $pager_links;//페이징을 구현될 부분을 리턴한다.

        return render('board_list', $data);//view에 리턴
    }

    public function write()
    {
        if(!isset($_SESSION['userid'])){
            echo "<script>alert('로그인하십시오.');location.href='/login'</script>";
            exit;
        }
        return render('board_write');  
    }

    public function save()
    {
        if(!isset($_SESSION['userid'])){
            echo "<script>alert('로그인하십시오.');location.href='/login'</script>";
            exit;
        }
        $db = db_connect();
        $bid=$this->request->getVar('bid');//bid값이 있으면 수정이고 아니면 등록이다.
        $subject=$this->request->getVar('subject');
        $content=$this->request->getVar('content');
        $file_table_id=$this->request->getVar('file_table_id');

        if($bid){
            $query = "select * from board where bid=".$bid;
            $rs = $db->query($query);
            if($_SESSION['userid']==$rs->getRow()->userid){
                $sql="update board set subject='".$subject."', content='".$content."' where bid=".$bid;
                $rs = $db->query($sql);
                return $this->response->redirect(site_url('/boardView/'.$bid));
            }else{
                echo "<script>alert('본인이 작성한 글만 수정할 수 있습니다.');location.href='/login';</script>";
                exit;
            }
        }

        //$file = $this->request->getFile('upfile');//첨부한 파일의 정보를 가져온다.
        $files = $this->request->getFileMultiple("upfile"); //다중 업로드 파일 정보
        $filepath = array();
        foreach($files as $file){
            if($file->getName()){//파일 정보가 있으면 저장한다.
                $filename = $file->getName();//기존 파일명을 저장할때 필요하다. 여기서는 사용하지 않는다.
                //$filepath = WRITEPATH. 'uploads/' . $file->store(); 매뉴얼에 나와있는 파일 저장 방법이다.여기서는 안쓴다.
                $newName = $file->getRandomName();//서버에 저장할때 파일명을 바꿔준다.
                $filepath[] = $file->store('board/', $newName);//CI4의 store 함수를 이용해 저장한다. 저장한 파일의 경로와 파일명을 리턴, 배열로 저장한다.
            }
        }

        $sql="insert into board (userid,subject,content) values ('".$_SESSION['userid']."','".$subject."','".$content."')";
        $rs = $db->query($sql);
        $insertid=$db->insertID();
        foreach($filepath as $fp){//배열로 저장한 파일 저장 정보를 디비에 입력한다.
            if(isset($fp)){
                $sql2="INSERT INTO file_table
                        (bid, userid, filename, type)
                        VALUES('".$insertid."', '".$_SESSION['userid']."', '".$fp."', 'board')";
                $rs2 = $db->query($sql2);                
            }
        }

        if(isset($file_table_id)){//첨부한 이미지가 있으면
            $fti=explode(',',$file_table_id);
            foreach($fti as $fi){
                if(isset($fi)){
                    $sql2="update file_table set bid=".$insertid." where fid=".$fi;
                    $rs2 = $db->query($sql2);                
                }
            }
        }

        return $this->response->redirect(site_url('/boardView/'.$insertid));
    }

    public function view($bid = null)
    {
        $db = db_connect();
        $query = "select b.*,(select GROUP_CONCAT(filename) from file_table f where f.bid=b.bid and f.type='board') as fs from board b where b.bid=".$bid;
        $rs = $db->query($query);
        $data['view'] = $rs->getRow();
        return render('board_view', $data);  
    }

    public function modify($bid = null)
    {
        $db = db_connect();
        $query = "select * from board where bid=".$bid;
        $rs = $db->query($query);
        if($_SESSION['userid']==$rs->getRow()->userid){
            $data['view'] = $rs->getRow();
            return render('board_write', $data);  
        }else{
            echo "<script>alert('본인이 작성한 글만 수정할 수 있습니다.');location.href='/login';</script>";
            exit;
        }
       
    }

    public function delete($bid = null)
    {

        $db = db_connect();
        $query = "select * from board where bid=".$bid;
        $rs = $db->query($query);
        if($_SESSION['userid']==$rs->getRow()->userid){
            $query3 = "select * from file_table where type='board' and bid=".$bid;
            $rs3 = $db->query($query3);
            $fs=$rs3->getResult();
            foreach($fs as $f3){
                unlink('uploads/'.$f3->filename);
            }
            $query4 = "delete from file_table where type='board' and bid=".$bid;
            $rs4 = $db->query($query4);
            $query2 = "delete from board where bid=".$bid;
            $rs2 = $db->query($query2);
            return $this->response->redirect(site_url('/board'));
        }else{
            echo "<script>alert('본인이 작성한 글만 삭제할 수 있습니다.');location.href='/login';</script>";
            exit;
        }
    }

    public function save_image()
    {
        $db = db_connect();
       
        $file = $this->request->getFile('savefile');
            if($file->getName()){
                $filename = $file->getName();
                //$filepath = WRITEPATH. 'uploads/' . $file->store();
                $newName = $file->getRandomName();
                $filepath = $file->store('board/', $newName);
            }

            if(isset($filepath)){
                $sql2="INSERT INTO file_table
                        (bid, userid, filename, type)
                        VALUES('', '".$_SESSION['userid']."', '".$filepath."', 'board')";
                $rs2 = $db->query($sql2);
                $insertid=$db->insertID();                
            }

        $retun_data = array("result"=>"success", "fid"=>$insertid, "savename"=>$filepath);
        return json_encode($retun_data);
    }

    public function file_delete()
    {
        $db = db_connect();
        $fid=$this->request->getVar('fid');
        $query = "select * from file_table where fid=".$fid;
        $rs = $db->query($query);
        if(unlink('uploads/'.$rs->getRow()->filename)){
            $query2= "delete from file_table where fid=".$fid;
            $rs2 = $db->query($query2);
        }
       
        $retun_data = array("result"=>"ok");
        return json_encode($retun_data);
    }
}

 

이미지를 저장하는 부분과 삭제하는 부분까지 모두 등록해 놓았다. 컨트롤러에서 파일을 받아 저장하고 디비에 등록한 후 리턴값을 보내준다. 기존과 크게 다르지 않다. 

 

위 사진처럼 저 위치에 등록된다면 제대로 한 것이다. 삭제 관련된 소스들도 이미 등록해 놨으므로 이 상태에서 삭제 버튼을 누른다면 이미지가 삭제되는 것을 확인 할 수 있을 것이다.

 

등록버튼을 눌러 제대로 등록이 됐는지 확인해보자. 이제 리스트로 가보자.

 

/app/Views/board_list.php

 

    <table class="table">
        <thead>
            <tr>
            <th scope="col">번호</th>
            <th scope="col">글쓴이</th>
            <th scope="col">제목</th>
            <th scope="col">등록일</th>
            </tr>
        </thead>
        <tbody id="board_list">
            <?php
            $idNumber = $total - ($page-1)*$perPage;
            foreach($list as $ls){
            ?>
                <tr>
                    <th scope="row"><?php echo $idNumber--;?></th>
                    <td><?php echo $ls->userid;?></td>
                    <td><a href="/boardView/<?php echo $ls->bid;?>"><?php echo $ls->subject;?></a>
                    <?php if($ls->filecnt){?>
                        <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-card-image" viewBox="0 0 16 16">
                        <path d="M6.002 5.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z"/>
                        <path d="M1.5 2A1.5 1.5 0 0 0 0 3.5v9A1.5 1.5 0 0 0 1.5 14h13a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 14.5 2h-13zm13 1a.5.5 0 0 1 .5.5v6l-3.775-1.947a.5.5 0 0 0-.577.093l-3.71 3.71-2.66-1.772a.5.5 0 0 0-.63.062L1.002 12v.54A.505.505 0 0 1 1 12.5v-9a.5.5 0 0 1 .5-.5h13z"/>
                        </svg>
                    <?php }?>
                    <?php if($ls->memocnt){?>
                        <span <?php if((time()-strtotime($ls->memodate))<=86400){ echo "style='color:red;'";}?>>
                            [<?php echo $ls->memocnt;?>]
                        </span>
                    <?php }?>
                    <?php if($ls->newid){?>
                        <span class="badge bg-danger">New</span>
                    <?php }?>
                    </td>
                    <td><?php echo $ls->regdate;?></td>
                </tr>
            <?php }?>
        </tbody>
        </table>
        <!-- 페이징 -->
        <div style="padding-top:30px;">
            <?= $pager_links ?>
        </div>
        <p style="text-align:right;">
            <a href="/boardWrite"><button type="button" class="btn btn-primary">등록</button><a>
            <?php
            if(isset($_SESSION['userid'])){
            ?>
                <a href="/logout"><button type="button" class="btn btn-warning">로그아웃</button><a>
            <?php }else{?>
                <a href="/login"><button type="button" class="btn btn-warning">로그인</button><a>
            <?php }?>
        </p>

 

 

 

게시판 목록이나오는 화면을 조금 수정했다. 댓글도 표시하고 새로 등록한 게시물과 첨부한 이미지가 있는지 여부를 표시하도록 수정했다.

 

다음시간엔 게시물의 수정과 삭제에대해 알아보려한다.

 

반응형

+ Recent posts