강좌게시판

제목 [14-09-29] ci, php 코드조각 관리(css, js 로딩포함)
글쓴이 darkninja 작성시각 2014/09/11 20:06:03
댓글 : 6 추천 : 0 스크랩 : 0 조회수 : 29411   RSS
이 코드 조각들이 하는 일은 ci 의 위젯 형태가 아닙니다.
단순히 ci, php 로 된 코드조각을 include 하는 것이 전부 입니다.
따라서 필요한 데이타는 미리 가공하여 준비해 두거나
자체 해결하여야 합니다.
초기 버젼이므로 차후 어떻게 변할지 모릅니다.

CREATE TABLE IF NOT EXISTS `controller_setting` (
  `controller` varchar(30) NOT NULL,
  `controller_list` text,
  `iclw_list` text,
  `reg_date` datetime NOT NULL,
  `modify_date` datetime NOT NULL,
  PRIMARY KEY (`controller`)
);
INSERT INTO `controller_setting` (`controller`, `controller_list`, `iclw_list`, `reg_date`, `modify_date`) VALUES
    ('default', 'main,board,board_category', 'weather=>weather,', '2014-09-10 20:06:13', '2014-09-16 23:53:32');

controller 는 ci 의 controller 입니다.
controller_list 는 ci 의 controller list 입니다.
문자열 형태로 쉼표로 분리합니다.
iclw_list 는 include 해야할 파일 목록을 담고 있는 테이블의 키값 리스트입니다.
php 의 배열형태로 쉼표로 분리합니다.
iclw 는 include cimodule library widget 의 앞글자를 의미하는데
의미가 약간 변질되었지만 작명 재주가 신통치 않으므로 그대로 사용합니다.

CREATE TABLE IF NOT EXISTS `iclw_setting` (
  `iclw` varchar(30) NOT NULL,
  `active` tinyint(4) NOT NULL DEFAULT '0',
  `skin` varchar(30) NOT NULL,
  `type` enum('cimodule','library','widget') NOT NULL,
  `position` enum('head','top','left','center','right','bottom','tail') NOT NULL,
  `css` text,
  `php` text,
  `js` text,
  `reg_date` datetime NOT NULL,
  `modify_date` datetime NOT NULL,
  PRIMARY KEY (`iclw`)
);
INSERT INTO `iclw_setting` (`iclw`, `active`, `skin`, `type`, `position`, `css`, `php`, `js`, `reg_date`, `modify_date`) VALUES
    ('free', 1, '', 'cimodule', 'center', '', 'free.php(1) => /iclw/cimodule/free/free.php,', '', '2014-09-13 19:04:45', '2014-09-13 19:08:38'),
    ('weather', 1, '', 'widget', 'right', 'jquery.bxslider.css(1) => /ci-22-tank/css/jquery.bxslider.css,', 'weather.php(1) => /iclw/widget/weather/weather.php,', 'jquery.bxslider.js(1) => /ci-22-tank/js/jquery.bxslider.js,\r\nweather.js(2) => /ci-22-tank/iclw/widget/weather/weather.js,', '2014-09-10 18:10:45', '2014-09-13 18:20:56');

iclw 는 include 할 cimodule, library, widget 의 이름입니다.
active 는 iclw 의 동작,멈춤을 설정합니다.
skin 은 아직 사용되지 않습니다.
type 은 작업소스에서 include 할때 특정 타입만 include 할때 사용합니다.
position 은 작업소스에서 배치할 화면상의 위치에서
특정 포지션만 include 할때 사용합니다.
css 는 css 파일의 목록입니다. 분리자로 쉼표를 사용하며 전체경로 입니다.
php 는 php 파일의 목록입니다. 분리자로 쉼표를 사용하며 전체경로 입니다.
js 는 js 파일의 목록입니다. 분리자로 쉼표를 사용하며 전체경로 입니다.
php 의 배열형태로 쉼표로 분리합니다.
이름(로딩순서)=>파일전체경로 형태입니다.
uasort($iclw_arr['js'], 'compare_rank');
이렇게 배열을 정렬하여 로딩순서를 정합니다.

My_Controller.php
ci 의 기본 컨트롤러를 확장한 것입니다.
간단히 사용하시려면 사용하고 계시는 컨트롤러에 합치셔도 됩니다.
기본적으로 필요한 것들(helper,library,model,,,)을 먼저 로딩하고
필요한 최소한의 작업을 합니다.

주소줄에서 컨트롤러 이름을 가져와야 하는데
$controller = $this->router->fetch_class();
이게 제일 간단해 보였습니다.

그 다음으로
$iclw_arr = array(
  'css'=>array(),
  'js'=>array(),
  'php_include'=>array()
);
배열에 기본값을 줍니다.

//default setting
모든 컨트롤러에 적용되는 설정값을 읽어 오는 부분입니다.
$cm = $this->controller_setting_model->get(ICLW_CONTROLLER_DEFAULT);
if (isset($cm) && $cm) { // 디폴트 값이 없을수도 있으므로 확인이 필요합니다.
  $pos = strpos($cm->controller_list, $controller);
  // 컨트롤러리스트(쉼표로 구분)에 현재 작업 컨트롤러의 이름이 포함되었으면
  if ($pos!==false) {
    // 테이블 자료를 배열로 바꾼후에
    $iclw_list = string2KeyedArray($cm->iclw_list);
    // 배열에 합칩니다.
    $iclw_arr = array_replace_recursive($iclw_arr, $this->iclw_setting_model->get_iclw_arr($iclw_list));
  }
}    

//controller setting
// 작업 컨트롤러의 이름으로 설정값을 읽어 옵니다.
$cm = $this->controller_setting_model->get($controller);
if (isset($cm) && $cm) {
  $iclw_list = string2KeyedArray($cm->iclw_list);
  $iclw_arr = array_replace_recursive($iclw_arr, $this->iclw_setting_model->get_iclw_arr($iclw_list));
}    

// 완성된 배열을 필요한 부분에 넘겨줍니다.
// 제가 사용하고 있는 형태이며 각자 상황에 맞게 고쳐 쓰시면 됩니다.
$this->data = Array(
  'head_data' => Array(
    'css' => $iclw_arr['css'],
    'php_include' => $iclw_arr['php_include'],
  ),
  'view_data' => Array(
    'php_include' => $iclw_arr['php_include'],
  ),    
  'tail_data' => Array(
    'php_include' => $iclw_arr['php_include'],
    'js' => $iclw_arr['js'],
  ),
);    

my_array_helper.php
배열 헬퍼에 있는 함수입니다.
string2KeyedArray 함수를 조금 수정한 것입니다.
이 함수는 iclw_setting_model 의
get_iclw_arr 함수(클래스 함수는 메서드라고 불러야 되나요?)에서 사용됩니다.
function mystr2KeyedArray($string, $type, $delimiter = ',', $kv = '=>', $k1 = '(', $k2 = ')') {
  if ($a = explode($delimiter, $string)) { // create parts
    $ka = array();
    foreach ($a as $s) { // each part
      if ($s) {
        if ($pos = strpos($s, $kv)) { // key/value delimiter
                    
                    // iclw 테이블의 css, js, php 필드의 값을 배열로 바꿉니다.
                    // 이름(로딩순서)=>파일전체경로
                    // $tm_n 은 로딩순서입니다.
                    $pos1 = strpos($s, $k1);
                    $pos2 = strpos($s, $k2);
                    $tm_n = trim(substr($s, $pos1+1, $pos2-$pos1-1));

                    // $tm_k 는 include 되는 css js php 파일의 이름입니다
                    $tm_k = trim(substr($s, 0, $pos1));
                    $tm_k = preg_replace('/\\.[^.][\w\d-_ ]{0,10}$/', '', $tm_k);

                    // $tm_v는 파일전체경로입니다.
                    $tm_v = trim(substr($s, $pos + strlen($kv)));
                    $tm_v = preg_replace('/\\.[^.][\w\d-_ ]{0,10}$/', '', $tm_v);

          // $ka[파일이름] =  ('rank' => 로딩순서, [css|js|php] => 파일전체경로)
          $ka[$tm_k] = array('rank' => $tm_n, $type => $tm_v);
        } else { // key delimiter not found
          // 실제로는 사용되지 않는 부분인데 넣어 놨습니다.
          $ka[] = array('rank' => '0', $type => trim($s));
        }
      }
    }
    }    
  else {
    // 비어있는 필드값을 받았을때
    이 값이 array_replace_recursive 함수로 합쳐지는데
    빈 배열을 리턴해야 할까요?
    //$ka = array(); ?
    $ka = false;
  }
  return $ka;
}

constants.php
// ci 의 상수 정의 파일입니다.
// j:/htdocs/ci-22-tank/index.php 일때
$_SERVER['SCRIPT_NAME'] 은 /ci-22-tank/index.php 입니다.
basename($_SERVER['SCRIPT_NAME']) 은 index.php 입니다.
ROOT_PATH 가 가지는 값은 /ci-22-tank 입니다.
마지막 / 를 제거하였습니다.

htdocs 의 서브디렉토리에서 홈페이지를 관리할때
root_path 를 경로에 붙여주면 됩니다.
ci 에서 관리하는 redirect 같은 경우에는 붙여주지 않아도 됩니다.

$root_path = str_replace(basename($_SERVER['SCRIPT_NAME']), '', $_SERVER['SCRIPT_NAME']);
define('ROOT_PATH', substr($root_path, 0, -1));
    
define('HTTP_HTTPS', ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS']=='on') ? 's' : ''));
define('HTTP_HOST', 'http'.HTTP_HTTPS.'://'.$_SERVER['HTTP_HOST']);

define('HTTP_HOST_ROOT', HTTP_HOST.ROOT_PATH);
HTTP_HOST_ROOT 는 html 에서 표현할때 필요합니다.

define('DOCUMENT_ROOT', $_SERVER['DOCUMENT_ROOT'].ROOT_PATH);
DOCUMENT_ROOT 는 파일을 관리할때 필요합니다.

define('JS_PATH', '/js');
define('CSS_PATH', '/css');
define('PHP_PATH', '/php_include');

define('ICLW_PATH', '/iclw');
define('ICLW_CIMODULE_PATH', '/cimodule');
define('ICLW_LIBRARY_PATH', '/library');
define('ICLW_WIDGET_PATH', '/widget');

define('JS_DIR', ROOT_PATH.JS_PATH);
define('CSS_DIR', ROOT_PATH.CSS_PATH);
define('PHP_DIR', ROOT_PATH.PHP_PATH);

define('ICLW_DIR', ROOT_PATH.ICLW_PATH);

define('CONTROLLER_SETTING','controller_setting');
define('ICLW_SETTING', 'iclw_setting');
define('ICLW_CONTROLLER_DEFAULT', 'default');

main.php
// 메인 컨트롤러는
htdocs/ci/application/config/routes.php 파일에서
$route['default_controller'] = "main"; <- 이렇게 설정되어 있는데
홈페이지주소/index.php 에서 이어지는
실행 시작 지점입니다.

class Main extends My_Controller { // My_Controller 를 상속합니다.

  public function __construct() {
    parent::__construct(); // My_Controller 의 생성자를 호출합니다.
  }

  public function index() {
        $data = Array(
            'head_data' => array(
                'title' => 'blog main',
            ),
            'view_data' => array(
            ),
        );
        // $this->data 는
            My_Controller 에서 var $data = array(); <- 이렇게 선언한 변수 입니다.
        // 두 배열을 합칩니다.
        // array_replace_recursive 함수에 대한 충분한 이해가 필요합니다.
        // (a, b) 두 배열을 넘겨주면
        // 중복되는 키값이 있으면 a 보다 b 에 있는 값이 우선합니다.
        결과 값을 다시 $data 에 받는데 헷갈리지 않으시길.
        $data = array_replace_recursive($this->data, $data);

        // 결과 배열을 실제 작업이 이루어지는 부분으로  넘겨 줍니다.
        $this->load->view('main_head', $data['head_data']);
        $this->load->view('main_view', $data['view_data']);
        $this->load->view('main_tail', $data['tail_data']);
  }

   // 이건 테스트하는 코드입니다.
    view 에 있는 php 파일이름을 주면  그냥 로드 하는 것입니다.
    public function test_php($phpfile) {
        $view_data = array(
        );
        $this->load->view($phpfile, $view_data);
    }
    
    public function test_ci_php($phpfile) {
        $data = Array(
            'head_data' => array(
                'title' => 'test filename',
            ),
            'view_data' => array(
            ),
            'tail_data' => array(
            )    
        );
        $data = array_replace_recursive($this->data, $data);

        $this->load->view('main_head', $data['head_data']);
        $this->load->view($phpfile, $data['view_data']);
        $this->load->view('main_tail', $data['tail_data']);
    }
    
}

controller_setting.php
iclw_setting.php
에 대한 설명은 별게 없으니 생략합니다.
많은 삽질의 결과로 저 나름의 형태로 만들어진 것이므로
따라 하실 필요는 없습니다.

My_pagination.php
// ci 3.0 dev 에 있는 코드를 그대로 가져와서 약간 수정한 것입니다.
// 기존의 용도가 변경되거나 추가된 변수만 설명하겠습니다.
class MY_Pagination {

    protected $base_url    = '';
    protected $prefix = '';
    protected $suffix = '';
    protected $total_rows = 0;
    protected $per_page = 10;
    // 기존에는 num_links 값을 주면 자동으로 처리되었는데
    // 여기에서는 별 의미가 없는 임시변수 정도입니다.
    // 그래도 내부에서 사용되므로 건드리지 않는게 좋습니다.
    protected $num_links; //program use, do not set. it mean ($num_links_prev + $num_links_next)
    // num_links 를 보조하는 변수로서 두개의 변수를 사용합니다.
    // 현재 포지션의 앞뒤로 보여질 링크의 갯수이며
    // 임의로 지정이 가능합니다.
    // 에, 실제로 될거 같은데 테스트는 안해봤네요 ㅋ
    // 앞 뒤갯수 더하기 현재포지션값 1 하면 총 보여지는 링크갯수입니다.
    protected $num_links_prev    = 5;
    protected $num_links_next    = 4; //total 10 links display!
    protected $cur_page = 0;
    protected $use_page_numbers    = TRUE;
    protected $first_link = 'First';
    protected $next_link    = 'Next';
    protected $prev_link    = 'Prev';
    protected $last_link = 'Last';
    protected $uri_segment = 3;
    protected $full_tag_open    = '';
    protected $full_tag_close    = '';
    protected $first_tag_open = '';
    protected $first_tag_close = '';
    protected $last_tag_open = '';
    protected $last_tag_close = '';
    protected $first_url = '';
    protected $cur_tag_open    = '';
    protected $cur_tag_close    = '';
    protected $next_tag_open = '';
    protected $next_tag_close = '';
    protected $prev_tag_open = '';
    protected $prev_tag_close = '';
    protected $num_tag_open = '';
    protected $num_tag_close = '';
    protected $page_query_string    = FALSE;
    protected $query_string_segment = 'per_page';
    protected $display_pages = TRUE;
    protected $_attributes    = '';
    protected $_link_types    = array();
    protected $reuse_query_string = FALSE;
    protected $data_page_attr = 'data-ci-pagination-page';
    protected $CI;
    // 다음 링크들을 항상 보일지 설정하는 부분입니다.
    // 거짓이면 필요할때만 보일건데
    // 정확히 테스트를 다했는지는 기억에 엄습니다 --;;;
    protected $view_first_link = true; //show allways link if true!
    protected $view_prev_link = true;
    protected $view_next_link = true;
    protected $view_last_link = true;
    // 처음과 마지막 링크에 스타일입니다.
    protected $link_info_style = 'style="background-color:yellow;"'; //first and last link style.

    // 중간에 계산하는 부분입니다.
    // Calculate the start and end numbers. These determine
    // which number to start and end the digit links with.
    // 현재 포지션이 앞에 보여질 링크갯수보다 크면
    if ($this->cur_page > $this->num_links_prev) {
      // 시작위치 = 현재위치 - 앞에 보여질 링크갯수
      $start = $this->cur_page - $this->num_links_prev;
    } else {    
      $start = 1;
    }    
    // 현재위치 + 다음에보여질 링크갯수 < 전체 페이지수
    if (($this->cur_page + $this->num_links_next) < $num_pages) {
      // 끝위치 = 현재위치 + 다음에 보여질 링크갯수
      $end = $this->cur_page + $this->num_links_next;
      // 전체(디비의) 페이지수가 설정된 보여질 링크갯수보다 적을때
      if ($num_pages <= ($this->num_links + 1)) { // num_pages < num_links + 1
        // 끝위치 = 전체 페이지수
        $end = $num_pages;
      }    
    } else {    
      $end = $num_pages;
    }    
   
    // 대충 계산한 값을 확인하는 부분입니다.
    //Re Calculate the start and end numbers.
    //For fixed num links!
    // 임시 끝위치 = 시작위치 + (앞에 보여질 링크갯수 + 다음에 보여질 링크갯수)
    $tend = $start + $this->num_links; // start + num_links != cur_page + num_links_next;
    // 임시끝위치가 읽어온 전체 페이지 수보다 크면
    if ($tend > $num_pages) {
      // 시작위치 = 천체 페이지수 - (앞에 보여질 링크갯수 + 다음에 보여질 링크갯수)
      $start = $num_pages - $this->num_links;
      // 제일 앞에 도착하였으면
      if ($start < 1) {
        $start = 1;
      }
    }    
    // 끝위치 < 임시끝위치 and 임시끝위치 <= 전체페이지수
    if ($end < $tend && $tend <= $num_pages) {
      // 끝위치가 전진합니다.
      $end = $tend;
    }    
    // 확인 사살, 필요없는 부분일거 같은데 넣어둠
    if ($start > $end) {
      $start = $end;
    }

    소스 끝부분에 주석처리해둔 아래코드를
    주석해제하면 변수 값들이 어떻게 움직이는지 보입니다.
        //$output .= " total_rows:".$this->total_rows;
        //$output .= " num_pages:".$num_pages;
        //$output .= " start:".$start;
        //$output .= " cur_page:".$this->cur_page;
        //$output .= " end:".$end;
        //$output .= " tend:".$tend;
        //$output .= " num_links_prev:".$this->num_links_prev;
        //$output .= " num_links_next:".$this->num_links_next;
        //$output .= " per_page:".$this->per_page;


코드 구문강조를 사용하면 가독성이 좀더 나을텐데
구문강조를 여러개 사용하면 버그가 나타나 화면이 이상한 동작을 하는 경우가 있어
걍 적습니다.
코딩을 하면서 주석을 달면 이렇게 설명을 할 필요가 없겠구나
이런 생각이 듭니다.
전 주석을 안 다는 편이라

삽질은 계속될까...
글 써(쓰?)본지 하도 오래되서
한글을 맞게 쓰(써?)고 있는지 모르겠네요!
첨부파일 ci_widget_management.zip (86.2 KB)
 다음글 [게임서버] controller 단위 스트링 분리하기 (3)
 이전글 CSS 레이아웃을 배웁시다 (4)

댓글

한대승(불의회상) / 2014/09/12 10:38:43 / 추천 0
오.. 기대되는군요. ^^
중복을 제거하기 위해 헬퍼로 요렇게 만들어서 뷰에서 직접 호출 하고 있습니다.

* load_helper.php
/**
 * load_css
 *
 * CSS를 등록 한다.
 *
 * @param string css file 경로
 * @return void
 */
if ( ! function_exists('load_css'))
{
 function load_css($css_file)
 {
  $key = md5($css_file);

  if(!isset($GLOBALS['hoksi_css'][$key]) || $GLOBALS['hoksi_css'][$key]) {
   $GLOBALS['hoksi_css'][$key] = $css_file;
  }
 }
}

/**
 * load_js
 *
 * 자바 스크립트를 등록 한다.
 *
 * @param string javascript file 경로
 * @return void
 */
if ( ! function_exists('load_js'))
{
 function load_js($js_file)
 {
  $key = md5($js_file);

  if(!isset($GLOBALS['hoksi_js'][$key]) || $GLOBALS['hoksi_js'][$key]) {
   $GLOBALS['hoksi_js'][$key] = $js_file;
  }
 }
}
* view.php
<?php
 // 필수 CSS 로드 
 load_css('<link rel="stylesheet" type="text/css" media="screen" href="//ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/themes/blitzer/jquery-ui.css" />');
 load_css('<link rel="stylesheet" type="text/css" media="screen" href="/aqua/css/ui.jqgrid.css" />');
 // 필수 JS 로드
 load_js('<script src="/js/plugins/jqgrid/4.6.0/grid.locale-kr.js" type="text/javascript"></script>');
 load_js('<script src="/js/plugins/jqgrid/4.6.0/jquery.jqGrid.min.js" type="text/javascript"></script>');
?>
<html>
<head>
  <title>Title</title>
    <?php
        //CSS 파일을 로드 한다.
        if(isset($GLOBALS['webis_css']) && $GLOBALS['hoksi_css']) { foreach($GLOBALS['hoksi_css'] as $css_file) { echo $css_file . PHP_EOL;}}
    ?>
</head>
<body>
Body

    <?php
        // 자바 스크립트 파일을 로드 한다.
        if(isset($GLOBALS['hoksi_js']) && $GLOBALS['hoksi_js']) { foreach($GLOBALS['webis_js'] as $js_file) { echo $js_file . PHP_EOL;}}
    ?>
</body>
</html>

kaido / 2014/09/12 11:53:42 / 추천 0
깨알같은 팁 하나 적어보자면...

라이브러리나 헬퍼는 중복으로 가져와도 중복으로 로드 안 됩니다.
메모리 사용량만 봐도 변화가 없음이 나타나지요.
darkninja / 2014/09/12 18:01:18 / 추천 0
글 작성했는데 로그인이 풀려 버리네요 이런 경우는 없었던거 같은데
올리려던 글에 문제가 있었나...
방금 썻던거 기억이 가물가물 ㅠㅠ
코드조각만 일단 보여드릴게요.
데모는 한달이상 기다리셔야 나올걸로 예상됩니다.
오 드디어 지원사격이 ㄳㄳ

코드조각들이 조금씩 정리되고 있습니다.
가능하면 단순하게 처리하는 방향으로 갈 것입니다.
변종원(웅파) / 2014/09/12 18:08:02 / 추천 0
darkninja / 불순한(?) 사용자가 있어서 ban 처리하고 세션 모두 날렸는데 그때 마침 걸리셨나 봅니다. ^^;;;
하늘치 / 2015/01/17 22:33:48 / 추천 0
한대승// 작명 센스가.. 멋지신대요?

혹시 css?
혹시 js?

ㅋㅋㅋ 잘 쓰겠습니다~ :)
한대승(불의회상) / 2015/01/18 09:58:31 / 추천 0
하늘치// ㅎㅎㅎ 그렇게 되나요?