Skip to content
On this page

다중검색 만들기

다중검색에 대해서 포스팅 글 올려본다.

그누보드에서 다중검색은 약간 까다로운 부분이 있다. 하지만 완전히 뜯어 고칠 필요는 없고 약간 수정 하면 된다.

수정 방법에 대해서 설명 하겠습니다.

위에 사진 처럼 다중 검색을 하려면 get 파라메터에서 배열을 이용해서 검색을 하면 된다.

(사진은 복구 못해서 뺸다.)

이 방법대로 개발하게되면 브라우저 주소가 좀 지저분하게 보이고 속도 또한 약간 저하될 수 있다.

하지만 제이쿼리 ajax로 써서 하면 깔끔하게 만들수 있다.

아래 소스 코드 참고해서 응용하자

php
<form name="fsearch" method="get" class="form_bbs_search">
    <input type="hidden" name="bo_table" value="<?php echo $bo_table ?>">
    <input type="hidden" name="sca" value="<?php echo $sca ?>">
    <input type="hidden" name="sop" value="and">
    <?php if ($is_category) { ?>
    <nav id="bo_cate">
        <h2><?php echo $board['bo_subject'] ?> 카테고리</h2>
        <ul id="bo_cate_ul">
            <?php echo $category_option ?>
        </ul>
    </nav>
    <?php } ?>
    <div class="search_bar">
        <input type="text" name="address" id="address" disabled>
        <button type="button" class="btn btn_b03" id="btn_addr">주소검색</button>
        <input type="text" name="stx" value="<?php echo stripslashes($stx) ?>" id="stx" class="sch_input" size="25" maxlength="20" placeholder="매장명을 입력해주세요">
        <button type="button" id="searchbtn" class="btn btn_submit">검색</button>
    </div>
</form>

아래 소스코드는 list.skin.php 에서 맨 상위 부분에 넣으면 된다.

(원래 분류 코드가 있긴 하지만 게시판에서 따로 사용해야되고 수정한 부분이 있어서 아래 소스를 사용하면 된다.)

php
// 분류 사용 여부
$is_category = false;
$category_option = '';
if ($board['bo_use_category']) {
    $is_category = true;
    $categories = explode('|', $board['bo_category_list']); // 구분자가 , 로 되어 있음
    for ($i=0; $i<count($categories); $i++) {
        $start = $i + 1;
        $category = trim($categories[$i]);
        if ($category=='') continue;
        $category_option .= '<li>'.PHP_EOL;
        $category_option .= '<input type="checkbox" name="sca[]" id="sca_id_'.$start.'" value="'.$category.'"';
        $category_option .= ' '.($category == $_REQUEST['sca'] ? "checked" : "").'> <label for="sca_id_'.$start.'"><span>'.$category;
        $category_option .= '</span></label></li>';
    }
}

여기서 sca를 배열로 만들고 검색을 누르면 바로 ajax 로 출력된다.

아래 소스는 sca 배열을 | 로 구분해서 ajax통신해서 값이 전달 된다.

js
function actions()
{
    let checked = []
    $("input[name='sca[]']:checked").each(function () {
        checked.push($(this).val());
    });
    let arr = checked.join("|");
    let address = $('#address').val();
    let stx = $('#stx').val();
  
    $.ajax({
        type:'post',
        url : g5_plugin_url + '/list.ajax.php',
        data:{bo_table:g5_bo_table,sca:arr,address:address,stx:stx},
        success:function (data) {
            $('#gall_div').html(data);
        }
    });
}

ajax 내부에 보면 성공일때 #gall_div에서 출력 되는데 이렇게 gall_ul 위에다가 gall_div로 상위를 한번 감싸줬다.

한마디로 gall_div 내부에서 ajax 출력된다.

html
<div id="gall_div">
  <ul id="gall_ul">

list.ajax.php 페이지 만든다. 플러그인에 넣는다.

원래 쓰던거라서 게시판 목록에 있던 소스를 바꿔치기 해도 괜찮다. (하지만 내가 소스를 다듬어서 만든거라서 아래 소스 코드가 도움된다.)

여기서 중요한게 get_sql_search2 함수이다. 이건 새로 만든 함수인데 바로 아래 소스 참고하면 된다.

php
include_once "./_common.php";
  
$board = get_board_db($bo_table, true);
  
$sop = strtolower($sop);
if ($sop != 'and' && $sop != 'or')
    $sop = 'and';
  
// 분류 선택 또는 검색어가 있다면
$stx = trim($stx);
//검색인지 아닌지 구분하는 변수 초기화
$is_search_bbs = false;
  
if ($sca || $stx || $stx === '0') {     //검색이면
    $is_search_bbs = true;      //검색구분변수 true 지정
    $sql_search = get_sql_search2($sca, $sfl, $stx, $sop);
  
    // 가장 작은 번호를 얻어서 변수에 저장 (하단의 페이징에서 사용)
    $sql = " select MIN(wr_num) as min_wr_num from {$write_table} ";
    $row = sql_fetch($sql);
    $min_spt = (int)$row['min_wr_num'];
  
    if (!$spt) $spt = $min_spt;
  
    $sql_search .= " and (wr_num between {$spt} and ({$spt} + {$config['cf_search_part']})) ";
  
    $sql = " SELECT COUNT(DISTINCT `wr_parent`) AS `cnt` FROM {$write_table} WHERE {$sql_search} ";
    $row = sql_fetch($sql,true);
    $total_count = $row['cnt'];
} else {
    $sql_search = "";
  
    $total_count = $board['bo_count_write'];
}
  
if(G5_IS_MOBILE) {
    $page_rows = $board['bo_mobile_page_rows'];
    $list_page_rows = $board['bo_mobile_page_rows'];
} else {
    $page_rows = $board['bo_page_rows'];
    $list_page_rows = $board['bo_page_rows'];
}
  
if ($page < 1) { $page = 1; } // 페이지가 없으면 첫 페이지 (1 페이지)
  
// 년도 2자리
$today2 = G5_TIME_YMD;
  
$list = array();
$i = 0;
$notice_count = 0;
$notice_array = array();
  
// 공지 처리
if (!$is_search_bbs) {
    $arr_notice = explode(',', trim($board['bo_notice']));
    $from_notice_idx = ($page - 1) * $page_rows;
    if($from_notice_idx < 0)
        $from_notice_idx = 0;
    $board_notice_count = count($arr_notice);
  
    for ($k=0; $k<$board_notice_count; $k++) {
        if (trim($arr_notice[$k]) == '') continue;
  
        $row = sql_fetch(" select * from {$write_table} where wr_id = '{$arr_notice[$k]}' ");
  
        if (!$row['wr_id']) continue;
  
        $notice_array[] = $row['wr_id'];
  
        if($k < $from_notice_idx) continue;
  
        $list[$i] = get_list($row, $board, $board_skin_url, G5_IS_MOBILE ? $board['bo_mobile_subject_len'] : $board['bo_subject_len']);
        $list[$i]['is_notice'] = true;
        $list[$i]['num'] = 0;
        $i++;
        $notice_count++;
  
        if($notice_count >= $list_page_rows)
            break;
    }
}
  
$total_page  = ceil($total_count / $page_rows);  // 전체 페이지 계산
$from_record = ($page - 1) * $page_rows; // 시작 열을 구함
  
// 공지글이 있으면 변수에 반영
if(!empty($notice_array)) {
    $from_record -= count($notice_array);
  
    if($from_record < 0)
        $from_record = 0;
  
    if($notice_count > 0)
        $page_rows -= $notice_count;
  
    if($page_rows < 0)
        $page_rows = $list_page_rows;
}
  
// 관리자라면 CheckBox 보임
$is_checkbox = false;
if ($is_member && ($is_admin == 'super' || $group['gr_admin'] == $member['mb_id'] || $board['bo_admin'] == $member['mb_id']))
    $is_checkbox = true;
  
// 정렬에 사용하는 QUERY_STRING
$qstr2 = 'bo_table='.$bo_table.'&amp;sop='.$sop;
  
// 0 으로 나눌시 오류를 방지하기 위하여 값이 없으면 1 로 설정
$bo_gallery_cols = $board['bo_gallery_cols'] ? $board['bo_gallery_cols'] : 1;
$td_width = (int)(100 / $bo_gallery_cols);
  
// 정렬
// 인덱스 필드가 아니면 정렬에 사용하지 않음
if (!$sst) {
    if ($board['bo_sort_field']) {
        $sst = $board['bo_sort_field'];
    } else {
        $sst  = "wr_num, wr_reply";
        $sod = "";
    }
} else {
    $board_sort_fields = get_board_sort_fields($board, 1);
    if (!$sod && array_key_exists($sst, $board_sort_fields)) {
        $sst = $board_sort_fields[$sst];
    } else {
        // 게시물 리스트의 정렬 대상 필드가 아니라면 공백으로 (nasca 님 09.06.16)
        // 리스트에서 다른 필드로 정렬을 하려면 아래의 코드에 해당 필드를 추가하세요.
        // $sst = preg_match("/^(wr_subject|wr_datetime|wr_hit|wr_good|wr_nogood)$/i", $sst) ? $sst : "";
        $sst = preg_match("/^(wr_datetime|wr_hit|wr_good|wr_nogood)$/i", $sst) ? $sst : "";
    }
}
  
if(!$sst)
    $sst  = "wr_num, wr_reply";
  
if ($sst) {
    $sql_order = " order by {$sst} {$sod} ";
}
  
$sql_search_wr_3 = '';
if($_REQUEST['address']) {
    $sql_search_wr_3 = " and wr_3 = '{$_REQUEST['address']}'";
}
  
if ($is_search_bbs) {
    $sql = " select distinct wr_parent from {$write_table} where {$sql_search} {$sql_search_wr_3} {$sql_order} limit {$from_record}, $page_rows ";
} else {
    $sql = " select * from {$write_table} where wr_is_comment = 0 ";
    if(!empty($notice_array))
        $sql .= " and wr_id not in (".implode(', ', $notice_array).") ";
    $sql .= " {$sql_order} limit {$from_record}, $page_rows ";
}
  
if($page_rows > 0) {
    $result = sql_query($sql);
  
    $k = 0;
  
    while ($row = sql_fetch_array($result))
    {
        // 검색일 경우 wr_id만 얻었으므로 다시 한행을 얻는다
        if ($is_search_bbs)
            $row = sql_fetch(" select * from {$write_table} where wr_id = '{$row['wr_parent']}' ");
  
        $list[$i] = get_list($row, $board, $board_skin_url, G5_IS_MOBILE ? $board['bo_mobile_subject_len'] : $board['bo_subject_len']);
        if (strstr($sfl, 'subject')) {
            $list[$i]['subject'] = search_font($stx, $list[$i]['subject']);
        }
        $list[$i]['is_notice'] = false;
        $list_num = $total_count - ($page - 1) * $list_page_rows - $notice_count;
        $list[$i]['num'] = $list_num - $k;
  
        $i++;
        $k++;
    }
}
  
  
g5_latest_cache_data($board['bo_table'], $list);
  
$write_pages = get_paging(G5_IS_MOBILE ? $config['cf_mobile_pages'] : $config['cf_write_pages'], $page, $total_page, get_pretty_url($bo_table, '', $qstr.'&amp;page='));
?>
<ul id="gall_ul">
    <?php for ($i=0; $i<count($list); $i++) { ?>
        <li class="gall_li">
            <a href="<?php echo $list[$i]['href']?>">
                <div class="mark"><?php echo $list[$i]['ca_name']?></div>
                <div class="gall_img">
                    이미지
                </div>
                <div class="gall_info">
                    <dl class="gall_list">
                        <dt class="gall_tit"><?php echo get_text($list[$i]['wr_subject'])?></dt>
                        <dd class="gall_del_info">
                            <span class="line"><i class="fa fa-phone"></i> <?php echo $list[$i]['wr_1']?></span>
                            <span class="line"><i class="fa fa-bicycle"></i> <?php echo number_format($list[$i]['wr_7']) ?>원 이상 배달 가능</span>
                        </dd>
                    </dl>
                </div>
            </a>
        </li>
    <?php } ?>
    <?php if (count($list) == 0) { echo "<li class=\"empty_list\">찾는 정보가 없습니다.</li>"; } ?>
</ul>
  
<!-- 페이지 -->
<?php echo $write_pages; ?>
<!-- 페이지 -->

아래 소스를 설명하자면 먼저 | 구분 되면 expload로 제거 후에 impload 함수로 in으로 바꿔준다.

in내부에서 가나라,마바사 이렇게 다시 콤마로 구분 되서 쿼리가 동작된다.

php
function get_sql_search2($search_ca_name, $search_field, $search_text, $search_operator='and')
{
    global $g5;
 
    if($search_ca_name) {
        $arr_sca = explode("|", $search_ca_name);
 
        $str= "(";
 
        $ingredients = [];
        foreach ($arr_sca as $item){
            $ingredients[] = $item;
        }
 
        $str .=  " ca_name in('";
        $str .= implode("','", $ingredients);
        $str .= "'";
 
        $str .= "))";
    }else {
        $str = " (1) ";
    }
 
 
    $search_text = strip_tags(($search_text));
    $search_text = trim(stripslashes($search_text));
 
    if (!$search_text && $search_text !== '0') {
        if ($search_ca_name) {
            return $str;
        } else {
            return '0';
        }
    }
 
    if ($str)
        $str .= " and ";
 
    // 쿼리의 속도를 높이기 위하여 ( ) 는 최소화 한다.
    $op1 = "";
 
    // 검색어를 구분자로 나눈다. 여기서는 공백
    $s = array();
    $s = explode(" ", $search_text);
 
    // 검색필드를 구분자로 나눈다. 여기서는 +
    $tmp = array();
    $tmp = explode(",", trim($search_field));
    $field = explode("||", $tmp[0]);
    $not_comment = "";
    if (!empty($tmp[1]))
        $not_comment = $tmp[1];
 
    $str .= "(";
    for ($i=0; $i<count($s); $i++) {
        // 검색어
        $search_str = trim($s[$i]);
        if ($search_str == "") continue;
 
        // 인기검색어
        insert_popular($field, $search_str);
 
        $str .= $op1;
        $str .= "(";
 
        $op2 = "";
        for ($k=0; $k<count($field); $k++) { // 필드의 수만큼 다중 필드 검색 가능 (필드1+필드2...)
 
            // SQL Injection 방지
            // 필드값에 a-z A-Z 0-9 _ , | 이외의 값이 있다면 검색필드를 wr_subject 로 설정한다.
            $field[$k] = preg_match("/^[\w\,\|]+$/", $field[$k]) ? strtolower($field[$k]) : "wr_subject";
 
            $str .= $op2;
            switch ($field[$k]) {
                case "mb_id" :
                case "wr_name" :
                    $str .= " $field[$k] = '$s[$i]' ";
                    break;
                case "wr_hit" :
                case "wr_good" :
                case "wr_nogood" :
                    $str .= " $field[$k] >= '$s[$i]' ";
                    break;
                // 번호는 해당 검색어에 -1 을 곱함
                case "wr_num" :
                    $str .= "$field[$k] = ".((-1)*$s[$i]);
                    break;
                case "wr_ip" :
                case "wr_password" :
                    $str .= "1=0"; // 항상 거짓
                    break;
                // LIKE 보다 INSTR 속도가 빠름
                default :
                    if (preg_match("/[a-zA-Z]/", $search_str))
                        $str .= "INSTR(LOWER($field[$k]), LOWER('$search_str'))";
                    else
                        $str .= "INSTR($field[$k], '$search_str')";
                    break;
            }
            $op2 = " or ";
        }
        $str .= ")";
 
        $op1 = " $search_operator ";
    }
    $str .= " ) ";
    if ($not_comment)
        $str .= " and wr_is_comment = '0' ";
 
    return $str;
}

여기까지 설명하겠다.

맺음말 :

데모 플러그인에 보면 맛집 배달 게시판이 있다. 이거 참고하면 된다.

깃헙에 따로 올려놨다.