CI Filecache Interface 맹가서 쓰기.

CI 한국 포럼에 내가 올린글임.

팁이라 하기에도 모하고 소스코드로 올리기도 애매한 그런 글 입니다.

filecache를 기반으로 인터페이스를 플러그인에 지정해서 쓰는 방법을 올립니다.
버그라던가 고쳐야 할점을 말씀 해 주시면 정말 감사 하겠습니다.
그러나 비난 하시면 상처 받습니다.Orz..
참고로 이 글은 객체의 개념이 없으시다면 상당히 난해할것이라 생각이 듭니다.;

1. 왜 인터페이스를 만들어야 하는가 부터 집고 넘어갑니다.
   filecache를 기본으로 설명할테니 예제도 이를 기반으로 하겠습니다.
   그리고 cache format은 json으로 합니다.
   다른 format은 처리해줘야 할 부분들이 많아서 구찮거든요-_-

   사용하는 이유는
   1. 어떤 filecache라도 동일한 method와 사용법을 갖습니다.
   2. 따라서 사용하기가 쉬워집니다.
   3. filecache를 추가 하더라도 interface만 맞추면 loader를 이용해서 편하게 쓸 수 있습니다.

   기초Data를 file로 내리는 이유는 db컨넥션을 줄이는데 우선 목적을 둡니다.
   cache가 1개 라면 굳이 이런 번거로운 방법을 쓸 이유는 없겠지요.
   그런데 두 개 이상이라면 얘기는 달라집니다.

   이런경우에 객체지향이 빛을 발하게됩니다.
   기본적으로 filecache는 생성, 읽기, 초기화 의 특성을 지닙니다.
   (삭제는 빼겠습니다. 안쓰면 그만인것이 캐쉬 이므로..머 넣으셔도 상관 없습니다.-_-;)
   어떤 cache든간에 똑같은 동작을 하지만 내부적으로는 다른 처리과정을 거치게 됩니다.
   이럴때에 추상Class나 Interface를 사용해서 Class를 구현을 하게 되면
   Interface상에 정의된 함수를 호출 하는 경우에 실제 생성된 객체의 method가 호출 되게 됩니다.

   이제 코드를 봅시다.
   저는 Interface말고 추상Class를 씁니다.

abstract class Filecache
{
    protected $m_szFile = null;        // 파일 경로
    protected $m_oController = null;    // 컨트롤러 참조 변수
    protected $m_szBuffer = null;        // 파일 내용 버퍼
    protected $m_bExist = false;        // 파일 존재 유무

/*
    기본적으로 method의 인자를 넘기게 되면 이 값은 메모리상에서 copy가 일어납니다.
    그러나 &연산자를 붙여주면 메모리를 참조 하게 됩니다.
    php상에선 오히려 &연산자를 써서 참조 하는게 않좋다는 글을 봤지만...
    취향입니다.-_-
*/
    function Filecache($pFile, &$pController)
    {
        $this->m_szFile = $pFile;
       
        // controller를 참조 하는 이유는 아래에 나오는 init method의 방식 때문입니다.
        // 이는 구현 부분에서 다시 언급 합니다.
        $this->m_oController = $pController;

        $this->m_bExist = file_exists($this->m_szFile);
    }

    // json_decode를 이용해 data를 return합니다.
    function getJsonData()
    {
        if ($this->m_szBuffer != "" && $this->m_szBuffer != null)
            return json_decode($this->m_szBuffer, true);
        else return false;
    }

    // 혹시 몰라서 그냥 buffer채로 넘기는 함수도 있습니다.
    // 만약 json이 아니라 단순 txt라면 이 함수를 쓰면 되겠습니다.
    function getStringData()
    {
        return $this->m_szBuffer;
    }

    function cacheExist()
    {
        return $this->m_bExist;
    }

    /*
        이 3개의 추상 method가 Interface가 됩니다.
        이 Class를 상속받는 Class들은 반드시 구현을 해야 하는 부분입니다.
    */
    abstract function makeFile($pData);
    abstract function loadFile();
    abstract function initFile();
}

이 소스를 filecache_pi.php로 저장을 했습니다.
그러나 써먹으려면 이 Class를 구현한 놈이 있어야겠죠.?
CI구조상 이렇게 만든 class를 별개의 파일로 나누어서 확장을 하려면
require_once를 사용 하는 수 밖에 없습니다.
그래서 저는 이렇게 한 Interface를 만들고 이를 상속 받는 class들은 한 파일 안에
다 넣기로 했습니다.

자 구현을 해봅시다.
class Infofile extends Filecache
{
    function Infofile($pFile, &$pController)
    {
        parent::Filecache($pFile, $pController);

        // 파일이 있다면 load를 하고 없으면 init을 합니다.
        if (file_exists($this->m_szFile)) $this->loadFile();
        else $this->initFile();
    }

    function loadFile()        // cache 파일을 읽어옵니다.
    {
        if (!file_exists($this->m_szFile))
        {
            $this->m_szBuffer = null;
            return;
        }
       
        $fd = fopen($this->m_szFile, "r");
        if ($fd == FALSE) return false;

        $buffer = "";

        while(!feof($fd))
            $buffer .= fgets($fd);

        if (strlen($buffer) == 0) return false;

        $this->m_szBuffer = $buffer;

        fclose($fd);
    }

    function makeFile($pData)    // cache 파일을 새로 만듭니다.
    {
        if (!is_array($pData)) return false;

        $fd = fopen($this->m_szFile, "w+");
        if ($fd == FALSE) return false;

        $buffer = json_encode($pData);
        $r = fwrite($fd, $buffer);

        if ($r != strlen($buffer))
        {
            fclose($fd);
            unlink($this->m_szFile);
            return false;
        }

        fclose($fd);
        return true;
    }

    /*
        cache 파일을 초기화 합니다.
        여기서 controller를 참조하는 이유가 생깁니다.
        init Method에서 model을 load 해서 갱신될 Data를  받게 했기 때문입니다.
        init file에만 controller 참조를 인자로 넘겨서 하셔도 됩니다.
        init file에 직접 data를 넘겨주겠다 하시면 controller참조는 아예 존재하지 않아도 됩니다.
    */   
    function initFile()
    {
        $this->m_oController->load->model("_MODEL_NAME_");

        $this->makeFile($this->m_oController->_MODEL_NAME_->_DATA_METHOD_);
           
        $this->loadFile();
    }
}

정말 간단하지요.?

이제 중요한 loader입니다.
loader는 일반 library에 둡니다.
그 이유는 controller에서 load를 하게 되면 자동적으로 객체를 참조 할 수 있게 되기 때문입니다.-_-
만약 plugin 또는 helper로 선언만 하게 되면
쓸 때마다 변수를 선언하고 생성을 해줘야만 하니까요.
상위 class에서 한번 load하면 하위 객체에서도 다 쓸수 있고 말이죠.
일일이 핸들링 할 필요가 없어지니 편하죠.

class filecacheloader
{
    protected $m_oController = null;
    protected $m_oFile = array();

    function filecacheloader($pValue = null)
    {
        $this->m_oController = &get_instance();
       
        if ($pValue != null) $this->loadFile($pValue["type"]);
    }
   
    function loadFile($pIndex)
    {
        if (array_key_exists($pIndex, $this->m_oFile)) return;
       
        switch($pIndex)
        {
            case "SITEINFO" :
                $szFilePath = _MAKE_FILE_PATH_NAME;
                $this->m_oFile[$pIndex] = new Infofile($szFilePath, $this->m_oController);
                break;
        }
    }

    function getData($pIndex)
    {
        return $this->m_oFile[$pIndex]->getJsonData();
    }

    function cacheExist($pIndex)
    {
        return $this->m_oFile[$pIndex]->cacheExist();
    }

    function makeFile($pIndex, $pData)
    {
        $this->m_oFile[$pIndex]->makeFile($pData);
    }
   
    function initFile($pIndex)
    {
        $this->m_oFile[$pIndex]->initFile();
    }
}
loader는 참 간단하지요.
단순하게 예약된 cache type을 받아서 생성되어 있는지 확인하고
생성되어 있지 않다면 새로 생성 하기만 하면 되는겁니다.

실제 controller 상에서는.
$this->load->plugin("filecache");
이렇게 선언된 plugin을 한번 load 해주고 loader를 load합니다. (helper로 만들어도 동일합니다. helper로 로드 하면 되겠지요.)
$this->load->library("filecacheloader", array("type" =>"SITEINFO"));

if (!$this->filecacheloader->cacheExist("SITEINFO"))
{
    $this->load->model(_MODEL_NAME_);
    $data = $this->_MODEL_NAME_->_METHOD_
    $this->filecacheloader->makeFile("SITEINFO", $data);
    /*
    // 또는
    $this->filecacheloader->initFile("SITEINFO");
    $data = $this->filecacheloader->getData("SITEINFO");
    */
}
else
{
    $data = $this->filecacheloader->getData("SITEINFO");
}

이렇게 쓰기만 하면 cache부분은 따로 손을 더 댈 필요가 없겠지요.?
참 쉽지요.?

Posted by SADBLUE

2009/09/13 15:37 2009/09/13 15:37
, , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://sadblue.com/rss/response/269

Trackback URL : http://sadblue.com/trackback/269

php를 이용한 xml이용시 노드 확인 방법.

SimpleXmlElement를 이용하면 xml element에 접근 하는 방법이 쉬워지지만
node가 존재 하지 않는 경우 난감해지는 일이 생긴다.
보통 Node no longer exists Filename 이런 에러가 나타난다.
이를 해결하기 위해서는.
어쩔 수 없이 무식한 방법을 쓰는 수 밖에 없었다.-_-
asXML을 이용해서 find로 찾아보려 했지만 받아지지 않는 경우도 있어서.
dom_import_simplexml 함수를 이용하여 dom node를 생성하고
childNodes를 받아서 하나하나 확인 해 보는 방법이다.-_-
여기서 주의 해야 할것은 XMLReader에 정의된 nodeType을 모든 child로 가져 온다는것이다.
따라서
if ($tempChild->item($i)->nodeType == XMLReader::ELEMENT && $tempChild->item($i)->nodeName == $pNodeName)
이런식으로 nodeType과 nodeName을 모두 같이 비교 해 주어야만 한다.
아 오전 내내 삽질.~_~

Posted by SADBLUE

2009/04/07 11:33 2009/04/07 11:33
, , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://sadblue.com/rss/response/245

Trackback URL : http://sadblue.com/trackback/245

아 디런 페이징.-_-
덕분에 그나마 깔끔했던 xml이 아주 지저분해졌다.~_~;
페이징을 위한 param들도 머리를 아프게 했고.-_-
맨 첫 페이지와 맨 마지막 페이지의 링크를 없애는 방법도 머리 아팠다.~_~
여튼 결론은 table을 다 그리는게 어쩌면 더 편해 보일지 모르는 지저분해진 xml인것이다.;

변경점.
1. row data 가 0일때의 처리
2. xml data는 페이지 로딩 시 1번만 요청 하도록 수정
3. paging기능 추가.

고려사항
- paging으로 추가되는 내용역시 cell이기 때문에 기존에 만들어진
stCell클래스를 확장해서 사용. (소스에 주석 달려 있음)

이전에 좀 불친절 했던 소개라는 느낌이 들어서 좀 친절하게 설명 하겠음.-_-;;

view 역할을 하는 table.xml

<?xml version="1.0" encoding="UTF-8" ?>
<gabia>
    <tables>
        <notice>
            <rowCss>noticeRow1</rowCss>
            <rowCss>noticeRow2</rowCss>
            <rowCss>noticeRow3</rowCss>
            <row><![CDATA[noticeTitle^^<a href="#seq#">#title#</a>]]></row>
            <row><![CDATA[noticeAuthor^^#author#]]></row>
            <row><![CDATA[noticeCount^^#viewcount#]]></row>
            <row><![CDATA[noticeDate^^#regdate#]]></row>
            <paging>
                <pagingCss>pagingCell</pagingCss>
                <pageFunction>onclick="goPage('#PAGE#');"</pageFunction>
                <firstPage><![CDATA[<p class="firstPage" #FUNCTION#>FIRST</p> ]]></firstPage>
                <prevPage><![CDATA[<p class="nextPage" #FUNCTION#>PREV</p> ]]></prevPage>
                <currentPage><![CDATA[<p class="currentPage" >#PAGE#</p> ]]></currentPage>
                <otherPage><![CDATA[<p class="pageNumber" #FUNCTION#>#PAGE#</p> ]]></otherPage>
                <nextPage><![CDATA[<p class="nextPage" #FUNCTION#>NEXT</p> ]]></nextPage>
                <lastPage><![CDATA[<p class="firstPage" #FUNCTION#>LAST</p>]]></lastPage>
            </paging>
        </notice>
    </tables>
</gabia>
paging기능 문제로 paging element가 추가 되었다.
pagingCss : paging으로 추가된 td의 css name
pageFunction : page이동시 사용될 스크립트 함수의 형식. 인자는 Page번호만으로 한다.* 맨 마지막 페이지나 맨 처음 페이지일 경우에 링크를 막기 위해 이러한 구조를 취한다.
firstPage : 제일 앞으로 이동 하는 링크 template
prevPage : 현재 페이지 번호보다 1 작은 페이지로 이동하는 링크 template
currentPage : 현재 페이지 번호의 template
nextPage : 현재 페이지 번호보다 1 큰 페이지로 이동하는 링크 template
lastPage : 맨 마지막 페이지로 이동하는 링크 template

기본적으로 style의 cursor모양은 지정하지 않고 page번호 생성 할 때에 onmouseover event를 통해 style을 제어 하도록 한다.
cell을 생성하고 innerHTML을 이용해 문자열을 대입시 <p></p> 태그의 style 속성이 사라지는 버그인지 알수 없는 현상이 발생.

model 역할을 하는 table.php
<?
    $data = "[";
    $data .= "{\"seq\":\"166\",\"title\":\"test166\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
            {\"seq\":\"165\",\"title\":\"test165\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
            {\"seq\":\"164\",\"title\":\"test164\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
            {\"seq\":\"163\",\"title\":\"test163\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
            {\"seq\":\"162\",\"title\":\"test162\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
            {\"seq\":\"161\",\"title\":\"test161\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
            {\"seq\":\"160\",\"title\":\"test160\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
            {\"seq\":\"159\",\"title\":\"test159\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
            {\"seq\":\"158\",\"title\":\"test158\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"},
            {\"seq\":\"157\",\"title\":\"test157\",\"regdate\":\"2009.01.16\",\"author\":\"SADBLUE\",\"viewcount\":\"0\"}";
    $data .= "]";
   
    $param = "{\"layerid\":\"content\",\"listcount\":\"10\",\"pagingcount\":\"10\"}";

    $return_data = "{";
    $return_data .= "\"resultCode\" : 'SUCCESS',";
    $return_data .= "\"row\" : '10',";
    $return_data .= "\"total\" : '10',";
    $return_data .= "\"result\" : " . $data . ",";
    $return_data .= "\"message\" : '',";
    $return_data .= "\"layerID\" : '',";
    $return_data .= "\"pagingcount\" : '10',";
    $return_data .= "\"listcount\" : '10',";
    $return_data .= "\"param\" : " . $param . ",";
    $return_data .= "\"page\" : '1'";
    $return_data .= "}";

    echo($return_data);
?>
실제 개발을 한다라면 ajax call의 response는 위의 형식으로 되어야 함.
$pCode :결과코드
$pValue : json_encode를 이용해 만들어진 jsondata
$pMessage : message
$pLayerID : 작업 대상 layerid
$pParam : json_encode를 이용해 만들어진 parameter
$pRow : 실제 data의 row수
$pTotal : select된 전체 row수 - paging 관련 인자.
$pPage : 요청 받은 page ? paging 관련 인자.
$pPagingCount : page 번호가 보여지는 수. 몇 page부터  몇 page까지 하단에 보여지는지. - paging 관련 인자.
$pListCount : 게시판에 한번에 보여질 글의 수  - paging 관련 인자.

예전에 빠졌던 ajaxBasic.js

function getAjaxData(URL,params,func, interID)
{
    var myAjax = new Ajax.Request( URL,
        {
            asynchronous: true,
            method: "post",
            parameters: params,
            onSuccess: function(xmlHttp){
                //alert(xmlHttp.responseText);
                try
                {
                    var data = eval('(' + xmlHttp.responseText + ')');
                }
                catch(E)
                {
                    alert("AjaxCall : " + E + " - " + xmlHttp.responseText);
                    return;
                }
                //alert(xmlHttp.responseText);
                func(data);
            },
            onFailure : function (request)
            {
                func("FAIL");
                return;
            }
        }
    );
}

control 역할의 table.js (xmlParser는 이전 post참조.)

/********************************************************************************
    ajax를 이용한 json data를 parsing해서 table을 그리는 class
    작성자 : 이정훈 (ljhoon@gabia.com)
    작성일 : 2009.01.13
    수정일 : 2009.01.15 - paging 기능 완료
********************************************************************************/

var tableXML = "http://local.inmail.co.kr/xml/table.xml";
// 로컬에서 개발 하던 url이므로 테스트 하는 곳의 url로 수정

/********************************************************************************
 stCell : td를 구성하기 위한 기본 정보를 갖게 되는 구조체 성격의 Class
     만약 td에 정의 해야 할 속성이 늘어나면 xml에 ^^ 구분자를 이용해서
     추가를 하고 stCell class에도 속성을 추가 해 주면 된다.
********************************************************************************/
var stCell = Class.create();
stCell.prototype = {
    cssName : null,
    cellData : null,

    initialize : function(pData)
    {
        if (pData == null) this.setNullData();
        else
        {
            if (pData.length < 1)
            {
                this.setNullData();
            }
            else
            {
                this.cssName = pData[0];
                this.cellData = pData[1];
            }
        }
    },

    getCss : function()
    {
        return this.cssName;
    },

    getData : function()
    {
        return this.cellData;
    },

    setData : function(pValue)
    {
        this.cellData = pValue;
    },

    setNullData : function()
    {
        this.cssName = "nodata";
        this.cellData = "nodata";
    }
};

/********************************************************************************
 stPaging : paging cell의 내용을 위한 구조체 성격의 class
     기본적인 stCell보다 디자인적 요소가 많기 때문에 상속받아 구현.
     paging node를 인자로 받는다.
     다중 상속을 위해 임시 객체를 하나 더 만든다.
     paging또한 td(cell)이기 때문에 stCell객체를 확장한다.
********************************************************************************/
var stTemp = Class.create();
stTemp.prototype = Object.extend (new xmlParser, new stCell);

var stPaging = Class.create();
stPaging.prototype = Object.extend (new stTemp, {
    m_oFirst : null,
    m_oPrev : null,
    m_oNumber : null,
    m_oCurrent : null,
    m_oNext : null,
    m_oLast : null,
    m_oFunction : null,

    initialize : function(pData)
    {
        this.getPagingTemplate(pData);
    },

    getPagingTemplate : function(pData)
    {
        if (pData == null)
            return ("paging row Info is null");
        else
        {
            try
            {
                this.cssName = this.getValue(this.getNode(pData, "pagingCss"));
                this.m_oFirst = this.getValue(this.getNode(pData, "firstPage"));
                this.m_oPrev = this.getValue(this.getNode(pData, "prevPage"));
                this.m_oCurrent = this.getValue(this.getNode(pData, "currentPage"));
                this.m_oNumber = this.getValue(this.getNode(pData, "otherPage"));
                this.m_oNext = this.getValue(this.getNode(pData, "nextPage"));
                this.m_oLast = this.getValue(this.getNode(pData, "lastPage"));
                this.m_oFunction = this.getValue(this.getNode(pData, "pageFunction"));
            }
            catch(E)
            {
                alert("Paging Node select fail : " + E);
                return;
            }
        }
    },
   
    setData : function (pPage, pTotal, pListCount, pPagingCount)
    {
        //alert(pPage + " - " + pTotal + " - " + pListCount + " - " + pPagingCount);
        var firstPage = 1;
        var lastPage = Math.ceil(pTotal / pListCount);

        var half = parseInt(pPagingCount/2, 10);

        var startPage = (pPage > half)?(pPage - half) : 1;
        var endPage = startPage + pPagingCount - 1;

        if (pListCount < lastPage)
        {
            endPage = (endPage > lastPage)?lastPage : endPage;
            startPage = ( (endPage - pPagingCount) < startPage )?(endPage - pPagingCount + 1) : startPage;
        }
        else
        {
            endPage = lastPage;
            startPage = firstPage;
        }
       
        prevPage = ((pPage -1) <= 0)?1:pPage-1;
        nextPage = ((pPage+1) > endPage)?pPage : (pPage +1);

        var pagingBuffer = "";

        //alert(half + " - " + startPage + " - " + endPage + " - " + lastPage);
/*
    맨 끝 페이지나 맨 마지막 페이지의 링크를 막기 위해 이런 구조를 취함.
*/
        var regPage = new RegExp("#PAGE#", "ig");
        var overCursor = " onmouseover=\"this.style.cursor='pointer';\"";

        if (pPage == firstPage)
        {
            pagingBuffer += this.m_oFirst.replace(/#FUNCTION#/ig, "");
            pagingBuffer += this.m_oPrev.replace(/#FUNCTION#/ig, "");
        }
        else
        {
            pagingBuffer += (this.m_oFirst.replace("#FUNCTION#", this.m_oFunction + overCursor)).replace(regPage, firstPage);
            pagingBuffer += (this.m_oPrev.replace("#FUNCTION#", this.m_oFunction + overCursor)).replace(regPage, prevPage);
        }

        for (var i=startPage ; i<=endPage ; ++i)
            pagingBuffer += (i == pPage)? this.m_oCurrent.replace(regPage, i) : (this.m_oNumber.replace("#FUNCTION#", this.m_oFunction + overCursor)).replace(regPage, i);

        if (pPage == lastPage)
        {
            pagingBuffer += (this.m_oNext.replace("#FUNCTION#", "")).replace(regPage, nextPage);
            pagingBuffer += (this.m_oLast.replace("#FUNCTION#", "")).replace(regPage, lastPage);
        }
        else
        {
            pagingBuffer += (this.m_oNext.replace("#FUNCTION#", this.m_oFunction + overCursor)).replace(regPage, nextPage);
            pagingBuffer += (this.m_oLast.replace("#FUNCTION#", this.m_oFunction + overCursor)).replace(regPage, lastPage);
        }

        this.cellData = pagingBuffer;
    }
});

/********************************************************************************
 stRow : td 배열을 갖게 되는 row 구조체 class
     row별 배경색이 다른 경우를 위해 css이름 속성을 갖는다.
********************************************************************************/
var stRow = Class.create();
stRow.prototype = {
    m_stCells : null,
    m_oCss : null,
    length : 0,

    initialize : function()
    {
        this.m_oCss = new Array();
        this.m_stCells = new Array();
    },

    insert : function(pCell)
    {
        this.m_stCells[this.m_stCells.length] = pCell;
        this.length++;
    },

    setCss : function(pCss)
    {
        if (typeof(pCss) != "Object")
            this.m_oCss[this.m_oCss.length] = pCss;
        else if (pCss.length > 1)
        {
            var cssCount = pCss.length;
            for (var i=0 ; i<cssCount ; ++i)
                this.m_oCss[cssCount + i] = pCss[i];
        }
    },

    getCell : function(pIndex)
    {
        if (this.m_stCells.length >= pIndex)
        {
            return this.m_stCells[pIndex];
        }
    },

    getColumnCount : function()
    {
        return this.m_stCells.length;
    },

    getCssCount : function()
    {
        return this.m_oCss.length;
    },

    getCss : function(pIndex)
    {
        if (this.m_oCss.length > pIndex)
            return this.m_oCss[pIndex];
        else
            return this.m_oCss[0];
    }
};

/********************************************************************************
 TableTemplate : Table Template XML정보를 가져온다.
 (Table class 생성마다 xml을 읽어오게 되어 있어서 따로 분리)
********************************************************************************/
var TableTemplate = Class.create();
TableTemplate.prototype = Object.extend (new xmlParser, {
    xmlTemplate : null,

    initialize : function()
    {
        this.loadXML();
    },

/********************************************************************************
 table 구조 xml을 읽어 오는 부분. 동기로 처리 했음.
********************************************************************************/
    loadXML : function()
    {
        new Ajax.Request(tableXML, {
            asynchronous: false,
            method: "get",
            onSuccess: function(xmlHttp)
            {
                try
                {
                    xmlTemplate = xmlHttp.responseXML;
                    return true;
                }
                catch(E)
                {
                    xmlTemplate = null;
                    alert("XML Load Fail : " + E);

                    return false;
                }
            },
            onFailure : function (request)
            {
                xmlTemplate = null;
                alert("Table Template XML Request Fail");
                return false;
            }
        });
    },

    getTemplate : function()
    {
        return xmlTemplate;
    }
});

var TemplateInfo = new TableTemplate();

/********************************************************************************
 Table : stRow, stCell을 이용해 실제 Table row를 그린다.
********************************************************************************/
var Table = Class.create();
Table.prototype = Object.extend (new xmlParser, {
    m_nRow : 0,
    m_oData : null,
    m_oTableID : null,
    m_oRowTemplate : null,
    m_oDefaultRowCss : null,
    xmlBuffer : null,
    m_oRows : null,
    m_bHeaderExist : true,
    m_bPaging : true,
    m_oParam : null,
    m_nTotal : 0,
    m_nPagingCount :0,
    m_nListCount : 10,
    m_nPage : 1,
    m_oMessage : null,

/********************************************************************************
 pData : json data
 pTableID : target table id
 pHeaderExist : 테이블 column별 제목이 있다면 true 없다면 false.
      table을 그리기 전에 true이면 1줄을 남기고 없다면 모든 row를 지운다.
********************************************************************************/
    initialize : function(pData, pTableID, pHeaderExist, pPaging)
    {
        if (pData != null)
        {
            this.m_nRow = parseInt(pData.row, 10);
            this.m_oData = pData.result;
            this.m_oTableID = pTableID;
            this.m_bHeaderExist = pHeaderExist;
            this.m_bPaging = pPaging;
            this.m_nListCount = pData.listcount;
            this.m_nPagingCount = parseInt(pData.pagingcount, 10);
            this.m_nTotal = parseInt(pData.total, 10);
            this.m_oParam = pData.param;
            this.m_nPage = parseInt(pData.page, 10);

            xmlBuffer = TemplateInfo.getTemplate();
        }
        this.m_oMessage = pData.message;
    },

/********************************************************************************
 table의 row와 total값을 기준으로 table data가 있는지 여부를 확인한다.
********************************************************************************/
    hasRow : function()
    {
        return (this.m_nTotal > 0 && this.m_nRow > 0)?true : false;
    },
/********************************************************************************
 xml에 정의된 각 td별 설정을 읽어와 rowTemplate 객체를 만든다.
 row별 css정보도 여기서 설정한다. css가 명시된 수 만큼 적용된다.
********************************************************************************/
    getRowTemplate : function()
    {
        if (xmlBuffer == null)
            return ("table row Info is null");
        else
        {
            try
            {
                this.m_oRows = null;
                this.m_oRows = new stRow();

                var objectNode = this.getNode(xmlBuffer, this.m_oTableID);
                var cssTemp = this.getNodeAll(objectNode, "rowCss");

                for(var i=0 ; i<cssTemp.length ; ++i)
                    this.m_oRows.setCss(this.getValue(cssTemp[i]));

                var rowTemp = this.getNodeAll(objectNode, "row");

                for(var i=0 ; i<rowTemp.length ; ++i)
                {
                    var templateValue = this.getValue(rowTemp[i]);
                    this.m_oRows.insert(new stCell(templateValue.split("^^")));
                }
            }
            catch(E)
            {
                alert("Node select fail : " + E);
                return;
            }
        }
    },

/********************************************************************************
 실제로 table row를 생성 하는 부분
 row를 추가 하기 전에 기존에 그려진 모든 row를 지운다.
 json의 인덱스 명은 xml에서 명시한 delimiter명과 같아야 한다.
********************************************************************************/
    insertRow : function()
    {
        if (!this.hasRow())
        {
            var row = $(this.m_oTableID).insertRow(-1);
            var c = row.insertCell(-1);
           
            c.className = "nodata";
            c.colSpan = 10;
            c.innerHTML = this.m_oMessage;
            return;
        }
        if ($(this.m_oTableID) == null)
        {
            alert("Base Table is null.");
            return;
        }

        if ($(this.m_oTableID).rows.length > 1)
        {
            if (this.m_bHeaderExist)
            {
                for(var i=($(this.m_oTableID).rows.length-1) ; i>0 ; --i)
                    $(this.m_oTableID).deleteRow(i);
            }
            else
            {
                for(var i=($(this.m_oTableID).rows.length-1) ; i>=0 ; --i)
                    $(this.m_oTableID).deleteRow(i);
            }
        }

        this.getRowTemplate();

        for(var i=0 ; i<this.m_nRow ; ++i)
        {
            var nLoopCount = 0;
            var row = $(this.m_oTableID).insertRow(-1);
            row.className = this.m_oRows.getCss( (i+1)%this.m_oRows.getCssCount() );

            for (var j=0 ; j<this.m_oRows.length ; ++j)
            {
                var c = row.insertCell(-1);
                var tempCell = this.m_oRows.getCell(nLoopCount).getData();

                for(var tempIndex in this.m_oData[i])
                {
                    var delimiter = new RegExp("#" + tempIndex + "#", "ig");
                    tempCell = tempCell.replace(delimiter, this.m_oData[i][tempIndex]);
                }

                c.innerHTML = tempCell;
                c.className = this.m_oRows.getCell(nLoopCount).getCss();
                nLoopCount++;
            }
        }

        // page 표시가 true이면 표시 한다.
        if (this.m_bPaging) this.paging();
    },

/********************************************************************************
 table pagin 처리
********************************************************************************/
    paging : function()
    {
        var pagingCell = new stPaging(this.getPagingTemplate());

        var row = $(this.m_oTableID).insertRow(-1);
        var c = row.insertCell(-1);

        pagingCell.setData(this.m_nPage, this.m_nTotal, this.m_nListCount, this.m_nPagingCount);

        c.colSpan = this.m_oRows.getColumnCount();
        c.className = pagingCell.getCss();
        c.innerHTML = pagingCell.getData();
    },

    getPagingTemplate : function()
    {
        if (xmlBuffer == null)
            return ("paging row Info is null");
        else
        {
            try
            {
                var objectNode = this.getNode(xmlBuffer, this.m_oTableID);
                return this.getNode(objectNode, "paging");
            }
            catch(E)
            {
                alert("Paging Node select fail : " + E);
                return;
            }
        }
    },

    getParam : function()
    {
        return this.m_oParam;
    }
});

사용 sample table.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>test</title>
    <script language="javascript" type="text/javascript" src="/scripts/prototype.js"></script>
    <script language="javascript" type="text/javascript" src="/scripts/ajaxBasic.js"></script>
    <script language="javascript" type="text/javascript" src="/scripts/simpleXml.js"></script>
    <script language="javascript" type="text/javascript" src="/scripts/table.js"></script>
   
    <style type="text/css">
        p, table, div { float:left; position:relative; display:inline-block; padding:0; margin:0; }
       
        #list { width:800px; border:solid 1px #dddddd; }
        #list #notice {width:600px; font-size:11px; font-family : 맑은고딕, Lucida Grande, Verdana, Sans-serif;}
        #list #notice tr { height:20px; }
        #list #notice .noticeHeader {font-weight:bold; text-align:center;}
        #list #notice .noticeRow1 {border-bottom:solid 1px #dddddd; }
        #list #notice .noticeRow2 {border-bottom:solid 1px #dddddd; background-color:#dddddd; }
        #list #notice .noticeRow3 {border-bottom:solid 1px #dddddd; background-color:#aaaaaa; }
       
        #list #notice .noticeTitleHead {width:250px;}
        #list #notice .noticeDateHead {width:75px;}
        #list #notice .noticeCountHead {width:75px; text-align:right;}
        #list #notice .noticeAuthorHead {width:100px;}
        #list #notice .noticeBoardHead {width:100px;}

        #list #notice .currentPage {font-weight:bold; width:15px;}
        #list #notice .pageNumber { width:15px;}
        #list #notice .firstPage { width:35px;}
        #list #notice .nextPage { width:35px;}
       
        #list #notice .pagingCell { width:100%; height:25px; padding-left:8%;}
        #list #notice .nodata { width:100%; text-align:center; height:50px; font-weight:bold;}

        #list2 { width:800px; border:solid 1px #dddddd; }
        #list2 #freeboard {width:600px; font-size:11px; font-family : 맑은고딕, Lucida Grande, Verdana, Sans-serif;}
        #list2 #freeboard tr { height:20px; }
        #list2 #freeboard .freeHeader {font-weight:bold; text-align:center;}
        #list2 #freeboard .freeRow {border-bottom:solid 1px #dddddd; }
       
        #list2 #freeboard .freeTitleHead {width:250px;}
        #list2 #freeboard .freeDateHead {width:75px;}
        #list2 #freeboard .freeCountHead {width:75px;}
        #list2 #freeboard .freeAuthorHead {width:100px;}
        #list2 #freeboard .freeBoardHead {width:100px;}
    </style>
</head>
<body>
<div>
    <div id="list">
        <table id="notice">
            <tr class="noticeHeader"><td class="noticeTitleHead">제목</td><td class="noticeAuthorHead">등록자</td><td class="noticeCountHead">조회수</td><td class="noticeDateHead">작성일</td></tr>
        </table>
    </div>
</div>
<input type="button" value="test" onclick="getData();">

<script language="javascript">
    var objTable2 = null;    // page당 1번의 xml 로딩을 위해 페이지 전역변수로 선언
    var param = $H({'page' : 1, 'layerid' : "content", 'listcount' : 10, 'pagingcount' : 10});
    // 초기 값을 넘긴다.

    function getData()
    {
        getAjaxData('/table.php', param, rowAppend);
    }
   
    function rowAppend(pValue)
    {
        objTable2 = new Table(pValue, "notice", true, true);
        objTable2.insertRow();
    }
</script>
   
</body>

</html>

테스트 방법
1. 웹서버의 특정 위치에 table.xml을 저장한다.
2. table.js의 주소를 맞게 변경한다.
3. table.php를 웹 서버에 저장한다.
4. table.html의 getData() 함수에서 table.php를 호출 하는 주소를 맞게 수정한다.
5. table.html의 버튼을 누르면 table이 그려질 것이다.

Posted by SADBLUE

2009/01/16 11:39 2009/01/16 11:39
, , , , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://sadblue.com/rss/response/227

Trackback URL : http://sadblue.com/trackback/227


블로그 이미지

난 평생 녹지 않는 눈 속에서 살아갈게... 너와 본 꿈을 잊지 않도록.... As if nothing had happened...

- SADBLUE

Notices

Archives

Authors

  1. SADBLUE

Recent Trackbacks

  1. 드디어 내 손에!!! Clean out. As if nothing h... 2010

Calendar

«   2024/11   »
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

Site Stats

Total hits:
2242785
Today:
764
Yesterday:
1044