CakePHP 매뉴얼 (한글)

  • 개요
    • 원문 : http://manual.cakephp.org/
    • 한글판 공식 배포처 : http://manual.cakephp.co.kr/
    • CakePHP 한국 사용자 모임: http://www.cakephp.co.kr/
    • 역자 : 진성용 (a@cakephp.co.kr)
    • 최초 공개일 : 2007-07-17

    • 수정이력

      • 2007-07-20 : 전체 수정, 보완
      • 2007-10-29 : 전체 수정, 보완 (어법, 어휘 위주)
  • 주의
    • 이 매뉴얼은 직접적 이윤추구외의 목적으로 어떻게 사용하셔도 상관없습니다.
    • 이 매뉴얼은 공식 매뉴얼을 토대로 작성하였으나, 최신판과 다소 차이가 있을 수 있습니다.
    • 공식 매뉴얼(영문)은 온라인상으로 http://manual.cakephp.org/ 에서 확인 가능하며, http://cakeforge.org/projects/cakedocs/ 에서 다운로드 받으실 수 있습니다.
    • 기술적 용어들에 대해 일부, 역자가 주관적으로 해석한 부분이 있습니다.
    • 오역이나 적절치 못한 해석에 대해서 역자 메일로 피드백주시면 반영하도록 노력하겠습니다 : 투고하기

Special Thanx 2 My Wife.


00. 머리말 (Preface)

목차

  1. 0. 머리말(Preface)
    1. 0.1. 대상
    2. 0.2. CakePHP 는 무료이다
    3. 0.3. 커뮤니티

0. 머리말(Preface)#

0.1. 대상#

이 매뉴얼은 웹 애플리케이션을 보다 빠르고, 좀더 즐겁게 만들고 싶어하는 사람들을 위해 쓰여졌다. CakePHP는 모든 수준의 PHP 유저가 쉽고 빠르게, 견고하고 유지보수하기 쉬운 애플리케이션을 만드는 것을 지원한다.

이 매뉴얼은 PHP와 HTML의 기본 지식이 필요하다. Model-View-Controller (MVC) 프로그래밍 패턴에 대해 알고 있으면 도움이 된다. 그러나 MVC 에 처음 접하는 이들도 이해할 수 있도록 도중에 설명을 더할 것이다. 웹 서버의 설정과 트러블슈팅을 위한 설명이 있지만, 모든 점을 다루는 것은 이 매뉴얼의 범위 밖이다.

응급치료(심페기능 소생법)의 기초지식, 기초적인 생존 기술, 데이트 & 대인관계의 기초 지식도 권장되지만 그것도 이 문서의 범위 밖이다.

0.2. CakePHP 는 무료이다#

CakePHP 는 뮤료이다. 돈을 지불할 필요도 없으며, 마음대로 사용할 수 있다. CakePHP 는 MIT 라이센스에 따라 개발되었다. CakePHP 는 오픈소스 프로젝트이다. 이것은 여러분이 소스코드에 대해 모두 접근할 수 있다는 뜻이다. 최신 버전은 CakePHP 웹사이트 (http://www.cakephp.org) 에서 구할 수 있다. 또한 이 웹사이트에서 최신의 가장 좋은 코드를 열람할 수 있다.

0.3. 커뮤니티#

CakePHP 는 근면한 사람들의 커뮤니티에 의해 개발되었다. 전세계의 여러 나라에서 모였으며, 가능한 많은 사람에게 도움을 주는 CakePHP 프레임워크를 개발하기 위해 협력하고 있다. Cake 의 활동적인 개발자와 사용자 커뮤니티의 정보를 더 알고 싶다면 http://www.cakephp.org 를 방문하라.

CakePHP 의 IRC 채널은 항상 박식하며, 친절한 베이커(Baker)들로 가득 차 있다. 만일 여러분이 약간의 코드에 대해 열중해 있고, 들어줄 사람이 필요하거나, 코딩 규약에 대한 논쟁을 하기 원한다면, 들러 주길 바란다 : 당신의 의견을 듣고 싶다. irc.freenode.com 의 #cakephp 로 방문하라.


01. 소개 (Introduction to CakePHP)

  1. 1. 소개(Introduction to CakePHP)
    1. 1.1. CakePHP 는 무엇인가?
    2. 1.2. 왜 CakePHP 인가?
    3. 1.3. CakePHP 의 역사

1. 소개 (Introduction to CakePHP)#

1.1. CakePHP 는 무엇인가?#

CakePHP 는 PHP 용 무료 오픈 소스 초고속 개발 프레임워크이다. 웹 애플리케이션을 만드는 프로그래머를 위한 이것의 라이브러리 구조, 클래스와 런타임 인프라스트럭쳐는 원래 루비에 의해 영감을 받았다. 우리의 최우선 목표는 유연성을 잃지 않고, 구조화되고 신속한 방법으로 작업할 수 있게 하는 것이다.

1.2. 왜 CakePHP 인가?#

신속하고, 최소한의 혼란으로 애플리케이션을 개발하기 위한 프레임워크로서 CakePHP 가 탁월한 선택이 되는 몇가지 특징이 있다. 순서없이 몇가지를 열거하자면 다음과 같다 :

  • 활동적이고, 친밀한 커뮤니티
  • 유연한 라이센스
  • PHP4, PHP5 와의 호환성
  • 데이터베이스 인터랙션과 간략화한 쿼리를 위한 CRUD 가 통합됨
  • 애플리케이션 스캐폴딩
  • 모델 뷰 컨트롤러 (MVC) 구조
  • 보기좋은 맞춤 URL 의 요청 디스패처
  • 자료 검사 기능 내장
  • 빠르고 유연한 템플릿 기능 (PHP 문법, 각종 헬퍼 포함 )
  • AJAX, 자바스크립트, HTML 폼을 위한 각종 뷰 헬퍼
  • 보안, 세션, 리퀘스트 등을 처리하는 컴포넌트
  • 유연한 접근 제한 목록
  • 불법 자료 삭제
  • 유연한 뷰 캐시
  • 웹사이트의 어떤 서브 디렉토리에서든 작동, 아파치는 전혀 손대지 않거나 약간의 설정

1.3. CakePHP 의 역사#

2005년, Michal Tatarynowicz 는 PHP 내에서 초고속 애플리케이션 프레임워크의 최소버전을 짰다. 그는 매우 좋은 프레임워크의 시작이었다는 것을 알았다. Michal 은 Cake 라 부르기로 하며, MIT 라이센스로 개발자 커뮤니티에 공개했다. 현재 CakePHP 란 이름 아래 Cake 를 보수하고 있다.


02. 기본 개념 (Basic Concepts)

  1. 2. 기본 개념(Basic Concepts)
    1. 2.1. 머리말
    2. 2.2. MVC 패턴
    3. 2.3. Cake 의 파일 배치 개요

2. 기본 개념 (Basic Concepts)#

2.1. 머리말#

이 장에서는 Cake 에 내장되어 있는 MVC 개념에 대해 간략하게 소개한다. MVC (모델 뷰 컨트롤러) 패턴을 처음 대한다면, 여러분을 위한 장이 될 것이다. 우선, 일반적인 MVC 개념에 대해 검토한 뒤, CakePHP 의 MVC 의 구제척인 애플리케이션을 만든다. 그후 MVC 패턴을 사용한 CakePHP 의 간단한 예를 몇가지 소개하겠다.

2.2. MVC 패턴#

모델-뷰-컨트롤러는 코드를 논리적으로 분리하고, 재활용하기 용이하며, 관리하기 쉽고, 일반적으로 개선된 소프트웨어 디자인 패턴이다. 모델-뷰-컨트롤러는 집필그룹 Gang for Four 에 의해 처음 소개되었다. Dean Helman 은 다음과 같이 썼다 (Objective Toolkit Pro white paper 에서 인용) :

" MVC 패러다임은 애플리케이션, 또는 애플리케이션의 일부분까지도 모델, 뷰, 컨트롤러라고 하는 3개의 부분으로 분할하는 방법이다. MVC 는 본래부터 입력, 처리, 출력이라고 하는 지금까지 이용되어 왔던 역할을 하고, GUI 의 영역에서 확실히 자리매김하기 위해 개발된 것이다."

입력 -> 처리 -> 출력

컨트롤러 -> 모델 -> 뷰

"유저의 입력, 외부 세계의 모델링, 그리고 유저의 눈으로 보이는 피드백으로 각각 분할되어，모델，뷰 포트，컨트롤러 오브젝트에 의하여 처리된다．컨트롤러는 유저로부터 마우스와 키보드의 입력을 포착하고，유저의 액션을 커맨드에 결부시킨다．그것은 모델과(또는) 뷰 포트에 보내고, 적절한 변화를 가져온다．모델은 하나 또는 여러개의 데이터 요소를 관리하고, 그 상태에 관한 질의에 반응한다. 그리고 상태를 변경하라는 지시에 반응한다. 뷰 포트는 사각형의 디스플레이를 관리하고, 그래픽와 텍스트의 조합으로 유저에게 데이터를 제공한 책임을 갖는다."

Cake 의 용어에서, 모델은 특정한 데이터베이스의 테이블/레코드, 다른 테이블이나 레코드와의 관계를 나타낸다. 모델은 또한 데이터의 검증 규칙도 포함하고 있고, 모델 데이터가 삽입또는 갱신될 때 사용된다. view 는 Cake 의 출력 파일로, PHP 코드가 기록된 일반적인 HTML 파일이다. Cake 의 컨트롤러는 서버로부터의 리퀘스트를 처리한다. 컨트롤러는 유저의 입력(URL 과 POST 데이터)를 접수, 비즈니스 로직을 적용하여 모델을 사용하여 데이터베이스의 데이터를 읽고 쓰고, 마지막으로 출력 데이터를 적절한 출력 파일에 보낸다.

애플리케이션을 가능한 간단하게 조직화하기 위해 Cake 는 단지 오브젝트의 관계 관리만을 위해가 아니라 파일의 저장 방법까지 관리하기 위해 이 패턴을 이용한다. 자세한 것은 다음 부분에 설명한다.

2.3. Cake 의 파일 배치 개요#

서버에 Cake 의 압축을 풀면, 3개의 주요 폴더가 나타난다.

  1. app
  2. cake
  3. vandors

cake 폴더에는 Cake 의 핵심 라이브러리가 있으며, 일반적으로 건드릴 필요가 없다.

app 폴더에는 애플리케이션의 구체적인 폴더나 파일이 들어갈 것이다. cake 폴더와 app 폴더를 분리함으로써, 여러 app 폴더에 하나의 Cake 라이브러리를 공유할 수 있다. 이로써 CakePHP 의 업데이트도 간단하게 된다. 최신 버전의 CakePHP 를 다운로드하여 현재의 핵심 라이브러리를 덮어쓰기만 하면 된다. 자신의 app 를 위해 작성했던 코드가 덮어 씌워질 걱정이 없다.

vendors 디렉토리는 써드 파티의 라이브러리를 저장하는 장소이다. vendors 에 관해 나중에 좀더 설명하겠지만, 기본적인 개념으로 vendors 디렉토리에 저장된 클래스에는 Cake 의 vendor() 함수를 사용해 접근할 수 있다.

전체 파일 구조에 대해 보자

  1. /app
    /config - 데이터베이스, ACL 등의 설정 파일

    /controllers - 컨트롤러는 이 디렉토리에 둔다.
    /components - 컴포넌트는 이 디렉토리에 둔다.

    /index.php - /app 를 DocumentRoot 로 쓰이도록 한다.

    /models - 모델은 이 디렉토리에 둔다.

    /plugins - 플러그인은 이 디렉토리에 둔다.

    /tmp - 캐시와 로그로 사용

    /vendors - 이 애플리케이션에 이용하는 써드 파티 라이브러리

    /views - 뷰 파일은 이 디렉토리에 둔다.
    /elements - 엘러먼트, 소형 뷰들은 이 디렉토리에 둔다.
    /errors - 커스텀 에러 페이지는 이 디렉토리에 둔다.
    /helpers - 헬퍼는 이 디렉토리에 둔다.
    /layouts - 애플리케이션 레이아웃 파일은 이 디렉토리에 둔다.
    /pages - 동적으로 작성하지 않아도 좋은 파일은 이 디렉토리에 둔다.

    /webroot - 애플리케이션의 DocumentRoot
    /css
    /files
    /img
    /js

    /cake - Cake 의 핵심 라이브러리. 이 디렉토리에내의 어떤 파일도 편집해서는 안 된다.

    index.php

    /vendors - 서버 전체가 사용하는 써드 파티의 라이브러리

    VERSION.txt - 현재 사용하고 있는 Cake 의 버젼을 알 수 있다.


03. 설치 (Installing CakePHP)

  1. 3. 설치 (Installing CakePHP)
    1. 3.1. 머리말
    2. 3.2. 요구사항
      1. 3.2.1. 서버 요구사항
    3. 3.3. CakePHP 설치
      1. 3.3.1. 최신 안정판 구하기
      2. 3.3.2. 언패킹
    4. 3.4. CakePHP 셋업
      1. 3.4.1. 개발용 셋업(Development Setup)
      2. 3.4.2. 공개용 셋업(Production Setup)
      3. 3.4.3. 고급 셋업 : 다른 설치 방법
    5. 3.5. 아파치와 mod_write 설정
    6. 3.6. 작동 확인

3. 설치 (Installing CakePHP)#

3.1. 머리말#

그럼 이제, 여러분은 CakePHP 의 모든 라이브러리 구조와 목적에 관해 알고 있다, 또는 그런 것들에는 관심이 없고 바로 실행해보기 위해 여기로 건너 뛰었을 것이다. 어느 쪽이든, 팔을 걷어 붙히고 준비하라.

이 장에서는, 서버에 설치해야 할 것, 몇가지 서버설정의 방법, CakePHP 의 다운로드와 설치, CakePHP 의 기본 페이지의 표시, 원할히 진행되지 않는 경우의 몇가지 트러블 슈팅 팁에 대해 설명한다.

3.2. 요구사항#

CakePHP 를 사용하려면, 먼저 CakePHP 를 실행시키기 위한 모든 라이브러리와 프로그램이 설치된 서버가 필요하다.

3.2.1. 서버 요구사항#

CakePHP 를 작동시키기 위한 서버의 셋업 조건은 아래와 같다.

  • 다음 사항이 가능한 HTTP 서버 (아파치등) : session, mod_write (필수는 아니지만 권장사항)
  • PHP 4.3.2 이상. CakePHP 는 PHP4 와 PHP5 모두 잘 작동한다
  • 데이터베이스 엔진 (현재 MySQL, ProsgreSQL, ADODB 지원)

3.3. CakePHP 설치#

3.3.1. 최신 안정판 구하기#

CakePHP 를 입수하는 방법은 여러 가지가 있다 : CakeForge 에서 안정판 다운로드하기, 나이틀리 빌드 가져오기, 또는 SVN 으로 부터 프레시 버전 가져오기

안정판의 코드를 다운로드하려면, http://cakeforge.org/projects/cakephp/ 에서 CakeForge 의 CakePHP 의 섹션에 있는 파일을 체크한다.

나이틀리는 http://cakephp.org/downloads/index/nightly 에서 다운로드 받을 수 있다.이 나이틀리 릴리즈는 안정판이며 다음 안정판까지의 버그 픽스가 가끔 포함되어 있다.

프레시 버전을 SVN 저장소에서 가져오려면, 자신이 사용하고 있는 SVN 클라이언트를 사용하여 https://svn.cakephp.org/repo/trunk/cake/ 에 접속한 뒤, 버전을 선택한다.

3.3.2. 언패킹#

최신 버전을 다운로드하면, 압축되어 있는 패키지를 웹서버에 올린다. CakePHP 패키지의 패키지를 푸는 두가지 방법이 있다. 하나의 도메인의 아래에 다수의 CakePHP 애플리케이션을 두는 개발용 셋업, 또는 한개의 도메인에 한개의 CakePHP 애플리케이션을 두는 공개용 셋업이다.

3.4. CakePHP 셋업#

CakePHP 의 첫번째의 셋업 방법은 두번째 방법에 비해 안전하지 않기 때문에, 일반적으로 개발환경용으로 권장되는 방법이다. 두번째 방법은 보다 안전하며 공개환경으로 사용되어야 한다.

주의 : /app/tmp 는 웹서버를 사용하는 유저에게 반드시 쓰기 권한을 줘야 한다.

3.4.1. 개발용 셋업(Development Setup)#

개발시에는 특정 DocumentRoot 속에 Cake 의 인스톨 디렉토리를 아래와 같이 통채로 배치할 수 있다.

  1. /wwwroot
    /cake
    /app
    /cake
    /vendors
    .htaccess
    index.php

이 셋업에서 wwwroot 폴더는 웹루트와 같은 역할을 한다. 따라서, URL 은 다음과 같이 보일 것이다. (mod_rewrite 를 사용하고 있는 경우) :

  1. www.example.com/cake/콘트롤러명/액션명/파라미터1/파라미터2

3.4.2. 공개용 셋업(Production Setup)#

공개용 셋업을 사용하려면, 서버의 DocumentRoot 에 대한 변경 권한을 갖고 있어야 한다. 이렇게 하면 도메인 전부를 하나의 CakePHP 애플리케이션으로서 움직이게 할 수 있다.

공개용 셋업은 아래의 배치를 사용한다. :

  1. ../path_to_cake_install
    /app
    /config
    /controllers
    /models
    /plugins
    /tmp
    /vendors
    /views
    /webroot <-- 이 디렉토리는 새로운 DocumentRoot 가 된다.
    .htaccess
    index.php
    /cake
    /vendors
    .htaccess
    index.php

예 3.1. 공개용 httpd.conf 제안

  1. DocumentRoot /path_to_cake/app/webroot

이 셋업에서는 webroot 디렉토리가 웹루트로 작동한한다. 따라서, URL 은 다음과 같이 보일 것이다. (mod_rewrite 를 사용하고 있는 경우) :

  1. http://www.example.com/콘트롤러명/액션명/파라미터1/파라미터2

3.4.3. 고급 셋업 : 다른 설치 방법#

Cake 의 디렉토리를 디스크의 다른 장소에 두고 싶은 경우가 있다. 공유 서버의 제한 때문이거나 여러개의 app 가 동일한 Cake 라이브러리를 공유하도록 하고 싶은 때 등이다.

Cake 애플리케이션에는 3개의 주요 부분이 있다.

  • CakePHP 의 핵심 라이브러리 - /cake
  • 애플리케이션 코드 (컨트롤러, 모델, 레이아웃, 뷰등) - /app
  • 애플리케이션의 webroot 파일 (이미지, 자바스크립트, css) - /app/webroot

각각의 디렉토리는 파일 시스템의 어디든지 배치할 수 있다. 단, webroot 는 웹상에서 접근 가능하도록 해야 한다. Cake 의 설정을 변경하면 webroot 폴더 또한 app 폴더 밖으로 이동할 수 있다.

Cake 의 설치시의 설정에, /app/webroot/index.php 와 몇가지 변경할 필요가 있다. 수정해야 하는 상수는 다음 3개다 : ROOT, APP_DIR, CAKE_CORE_INCLUDE_PATH.

  • ROOT 는 여러분의 app 폴더를 포함하는 디렉토리의 경로를 설정한다.
  • APP_DIR 는 여러분의 app 폴더의 경로명을 설정한다.
  • CAKE_CORE_INCLUDE_PATH 는 Cake 라이브러리 폴더의 경로를 설정한다.

예 3.2. /app/webroot/index.php (일부분, 주석은 제외)

  1. if (!defined('ROOT'))
    {
    define('ROOT', dirname(dirname(dirname(__FILE__))));
    }

    if (!defined('APP_DIR'))
    {
    define ('APP_DIR', basename(dirname(dirname(__FILE__))));
    }

    if (!defined('CAKE_CORE_INCLUDE_PATH'))
    {
    define('CAKE_CORE_INCLUDE_PATH', ROOT);
    }

예제로 설명하는것이 알기 쉬울 것이다. 아래와 같은 설정으로 Cake 가 작동한다고 가정하자 :

  • Cake 라이브러리를 다른 애플리케이션과 공유하고 싶다. 경로는 /usr/lib/cake.
  • Cake webroot 디렉토리는 /var/www/mysite/ 가 되어야 한다.
  • 애플리케이션 파일은 /home/me/mysite 에 넣는다.

  1. 파일 설정은 이와 같이 한다 :

  2. /home
    /me
    /mysite <-- /cake_install/app 로 사용된다
    /config
    /controllers
    /models
    /plugins
    /tmp
    /vendors
    /views
    index.php
    /var
    /www
    /mysite <-- /cake_install/app/webroot 로 사용된다
    /css
    /files
    /img
    /js
    .htaccess
    css.php
    favicon.ico
    index.php
    /usr
    /lib
    /cake <-- /cake_install/cake 로 사용된다
    /cake
    /config
    /docs
    /libs
    /scripts
    app_controller.php
    app_model.php
    basics.php
    bootstrap.php
    dispatcher.php
    /vendors

이러한 설정의 경우, webroot 의 index.php 파일 (이 예에서는, /var/www/mysite.php) 을 다음과 같이 수정해야 한다 :

파일 경로의 구분자로 슬래시 보다 'DS' 상수를 사용하도록 한다. 그렇게 하면, 잘못된 구분자를 사용하여 'missing file' (파일을 찾을 수 없습니다) 에러가 발생하는 것을 방지할 수 있고, 코드를 이동시키기가 더 쉬워진다.

  1. if (!defined('ROOT'))
    {
    define('ROOT', DS.'home'.DS.'me');
    }

    if (!defined('APP_DIR'))
    {
    define ('APP_DIR', 'mysite');
    }

    if (!defined('CAKE_CORE_INCLUDE_PATH'))
    {
    define('CAKE_CORE_INCLUDE_PATH', DS.'usr'.DS.'lib'.DS.'cake');
    }

3.5. 아파치와 mod_write 설정#

CakePHP 는 mod_write 와 간단히 연동되도록 만들어져 있지만, 시스템이 잘 작동할 때 까지 고생하는 유저도 있을 수 있다. 바르게 작동하기 위해서 아래와 같이 시도해 볼 수 있다 :

  • /htaccess 의 override 가 허가되어 있는 것을 확인한다 : httpd.conf 내에 서버의 각 디렉토리를 정의하고 있는 부분이 필요하다. 해당 디렉토리에 AllowOverride 가 All 로 설정되어 있는지 확인한다.
  • 유저나 사이트 특유의 httpd.conf 가 아니라 시스템의 httpd.conf 를 수정하고 있는지 확인한다.
  • 어떤 이유로, 필요한 .htaccess 파일이 없는 CakePHP 소스를 입수하는 경우도 있다．운영 체제 중에는 '.' 로 시작된 파일을 숨김 속성으로 취급，복사하지 않기 때문이다. 공식 사이트의 다운로드 섹션이나 CakePHP 의 SVN 저장소의 소스인지 확인한다.
  • mod_write 가 올바르게 로딩되고 있는지 확인하라. httpd.conf 내에 LoadModule rewrite_module libexec/httpd/mod_rewrite.so 그리고 AddModule mod_rewrite.c 가 있을 것이다.
  • 만일 Cake 를 유저 디렉토리 (http://example.com/~myusername/) 에 설치했다면, Cake 설치 디렉토리의 .htaccess 파일을 수정해야 한다. 딱 한 줄 "RewriteBase /~myusername/" 을 추가하면 된다.
  • 만일 어떤 이유로, URL 뒤에 길고 알 수 없는 세션 아이디 (http://example.com/posts/?CAKEPHP=4kgj577sgabvnmhjgkdiuy1956if6ska) 가 붙는다면, 설치 루트 디렉토리의 .htaccess 파일에 "php_flag session.trans_id off" 를 추가하면 된다.

3.6. 작동 확인#

자, 작동이 되는지 확인하자. 어떤 설정을 사용하고 있는지에 따라, 브라우저에 http://www.example.com 또는 http://www.example.com/cake 를 쳐보자.이때에 CakePHP 의 기본 페이지가 표시되고, 현재의 데이터베이스 접속 상태에 관한 메시지가 나올 것이다.

축하한다! 여러분은 Cake 기반의 애플리케이션을 구축할 준비가 되었다.


04. 설정 (Configuration)

  1. 4. 설정(Configuration)
    1. 4.1. 데이터베이스의 설정
    2. 4.2. 전역 설정
    3. 4.3. 라우트 설정
    4. 4.4 고급 라우팅 설정 : 어드민 라우팅과 웹서비스
    5. 4.5. (옵션) 커스텀 인플렉션(어형 변화) 설정

4. 설정 (Configuration)#

4.1. 데이터베이스의 설정#

app/config/database.php 파일에서 모든 데이터베이스 설정을 한다. 처음 설치를 하면, database.php 는 존재 하지 않기 때문에, database.php.default 를 복사하여 작성하고, 파일명을 변경한다. 다음과 같이 나타날 것이다 :

예 4.1. app/config/database.php

  1. var $default = array('driver' => 'mysql',
    'connect' => 'mysql_connect',
    'host' => 'localhost',
    'login' => 'user',
    'password' => 'password',
    'database' => 'project_name',
    'prefix' => '');

기본값의 데이터베이스 접속 정보를 자신의 애플리케이션용으로 고친다.

'prefix' (접두사) 키에 대해 : 여기에 설정한 문자열이, 어떠한 SQL 호출의 테이블명 앞에 추가된다. 이 하나의 설정이 모든 곳에서 사용된다. 데이터베이스가 하나 밖에 없는 호스팅을 이용하는 경우에서도 이렇게 쓰는 것으로 Cake 의 테이블 규약을 따를 수 있다. 주의 : HABTM 조인 테이블은 한번만 접두사가 붙는다 : prefix_apples_bananas (O), prefix_apples_prefix_bananas (X).

CakePHP 는, 다음 데이터베이스 드라이버를 지원한다 :

  • mysql

  • postgres

  • sqlite

  • pear-드라이버명 (예를 들면, pear-mysql 등.)

  • adodb-드라이버명

$default 접속 중에 있는 'connect' 키로, 데이타베이스 접속을 지속적으로 할것인지 정할 수 있다. database.php.default 파일내에 있는 주석을 참고하여 데이타베이스의 셋업의 타입을 지정한다.

또한 데이터베이스 테이블은 다음 규약을 따를 필요가 있다 :

  • Cake 에서 사용하는 테이블명은, 영어의 복수형으로 한다. "users", "authors" 또는 "articles" 등이다. 주의 : 대응하는 모델의 이름은 단수형이다.

  • 테이블에는 'id' 라는 이름의 주요키 (primary key) 가 반드시 있어야 한다.

  • 테이블끼리 관련 지을 경우, 외부키 (foreign key) 는 'article_id' 와 같이 한다. 테이블명의 단수형으로 언더바를 붙이고, 맨마지막에 'id' 를 붙인다.

  • 테이블에 'created' (작성) 와/또는 'modified' (수정) 이라는 컬럼이 있으면, Cake 는 적절한 시기에 필드에 데이터를 자동으로 넣는다.

database.php 파일내에는 $test 접속 설정도 포함된다. 여기에 설정을 입력(또는 같은 포맷에 설정을 추가)하고 애플리케이션의 model 중에 다음과 같이 설정해 이용한다.

  1. var $useDbConfig = 'test';

이 방법으로 임의의 접속 설정을 추가할 수 있다.

4.2. 전역 설정#

CakePHP 의 전역 설정은 app/config/core.php 에서 한다. 설정 파일이라는 것을 정말 좋아하지 않지만, 작성할 수 밖에 없었다. 여기에서 몇가지를 변경할 수 있다. core.php 의 주석중에 각각의 설정에 관한 주의사항이 기재되어 있다.

DEBUG : 애플리케이션을 만들 때의 디버그 설정을 지정한다. 0 이 아닌 값이면 Cake 는 pr()과 debug() 함수를 호출하여 결과를 출력하고, flash 메시지에 의한 페이지 전송을 자동으로 멈춘다. 값을 2 이상으로 설정하면 페이지의 맨 마지막 부분에 SQL 명령문의 결과가 표시된다.

또한 디버그 모드(DEBUG의 값이 1 이상)일 때, Cake 는 "Missing Controller" (컨트롤러가 존재하지 않습니다) "Missing Action" (액션이 존재하지 않습니다) 라고 하는 에러 페이지를 생성하게 된다. 그러나 공개모드 (DEBUG의 값이 0)일 때, Cake 는 app/views/errors/error404.thtml 에 있는 "페이지가 존재하지 않습니다" 라고 하는 페이지를 표시한다.

CAKE_SESSION_COOKIE : Cake 애플리케이션에 세션을 사용할 때, 쿠키의 이름을 지정한다.

CAKE_SECURITY : 세션 체크 레벨을 지정할 수 있다. 설정에 따라 세션의 타임 아웃, 새로운 세션 ID의 생성, 오래된 세션의 파일의 삭제등을 한다. 설정값은 다음과 같다.

  • high : 10초 동안 아무것도 하지 않으면 세션 타임 아웃. 세션 ID를 요청할 때마다 재생성한다.
  • medium : 20분 동안 아무것도 하지 않으면 세션 타임 아웃.
  • low : 30분 동안 아무것도 하지 않으면 세션 타임 아웃.

CAKE_SESSION_SAVE : 세션 데이터의 저장 방법을 지정한다. 설정 가능한 값은 다음과 같다.

  • cake : 세션 데이터는 Cake 를 인스톨한 디렉토리내 tmp/ 에 저장된다.
  • php : 세션 데이터는 php.ini 에 저장된 경로에 저장된다.
  • database : 세션 데이터는 'default' 키로 정의된 데이터베이스에 접속해서 저장된다.

4.3. 라우트 설정#

"라우팅" 은 PHP 로 구현되는 mod_rewrite 의 축소판 비슷한 것이다. URL로부터 컨트롤러/액션/파라미터를 매핑하고 역으로도 실행한다. 보기좋은 URL(pretty URLs)을 좀 더 설정하기 쉽게 하고, mod_rewrite 가 없이 작동하기 위해 Cake 에 추가되었다. 그러나, mod_rewrite 를 사용하면 주소창은 훨씬 깔끔해진다.

라우트에서는 적합한 URL을 특정한 컨트롤러와 액션에 결부시키는 개별적인 룰이다. 라우트는 app/config/routes.php 파일내에 설정되며, 아래와 같이 설정한다.

예 4.2. 라우트 패턴 예제

  1. <?php
  2. $Route->connect (
    'URL',
    array('controller'=>'컨트롤러명',
    'action'=>'액션명', '최초의 파라미터')
    );
  3. ?>

  • URL 은 매핑하는 Cake URL 의 정규표현식이다.
  • 컨트롤러명은 호출되는 컨트롤러이다.
  • 액션명은 호출되는 컨트롤러의 액션명이다.
  • 그리고 최초의 파라미터는 임의의 액션 파라미터 값을 설정할 수 있다.

최초의 파라미터 이후의 이은 파라미터도 컨트롤러의 액션에 파라미터로 넘겨 받는다.

다음의 예에서는 /blog 의 모든 URL을 BlogController 에 접속한다. 기본 액션은 BlogController::index() 가 된다.

예 4.3. 라우트 예제

  1. <?php
  2. $Route->connect ('/blog/:action/*', array('controller'=>'Blog', 'action'=>'index'));
  3. ?>

/blog/history/05/june 이라는 URL 은 다음과 같이 조작된다.

예 4.4. 컨트롤러에서의 라우트 핸들링

  1. <?php
    class BlogController extends AppController
    {
    function history ($year, $month=null)
    {
    // .. 적절한 내용 표시
    }
    }
    ?>

URL 의 'history' 는 Blog 의 라우트의 :action 에 적합하다. * 에 적합한 URL 의 요소는 작동중인 컨트롤러가 다루고 있는 메소드에 파라미터로서 넘겨 받는다. 즉 $year 와 $month 가 된다. /blog/history/05 라는 URL이 호출된 경우 history() 에는 05 라는 하나의 파라미터만 넘겨 받는다.

다음 예는 PagesController::display('home') 를 사용하기 위한 CakePHP 의 기본 라우트 설정이다. Home 은 /app/views/pages/home.thtml 이라는 파일을 작성하는 것으로 이것을 표현하는 뷰가 된다

예 4.5. 라우트 초기 설정

  1. <?php
  2. connect ('/', array('controller'=>'Pages', 'action'=>'display', 'home'));
  3. ?>

4.4 고급 라우팅 설정 : 어드민 라우팅과 웹서비스#

/app/config/core.php 의 설정중에는 애플리케이션을 체계화 하고 개발자와 유저에 있어서 알기 쉬운 URL을 만들기 위해 도움이 되는 것이 있다.

먼저, 어드민 라우팅 (admin routing) 이 있다. 애플리케이션으로 ProductController 와 NewsController 가 있는 경우, 관리자 권한이 있는 사람만 접근 가능한 특별한 URL 을 준비하고, 컨트롤러가 특별한 액션으로 접근할 수 있도록 하고 싶은 경우가 있다. URL 을 이해하기 쉬운 좋은 상태로 하기 위해, 어떤 사람은 /admin/products/add 와 /admin/news/post 와 같은 것을 준비할 수 있겠지만, /product/adminAdd 와 /news/adminPost 와 같이 하고 싶은 사람도 있을 수 있다.

이와 같이 구현하려면, 우선 /app/config/core.php 파일에 있는 CAKE_ADMIN 의 아래행의 주석을 삭제한다. CAKE_ADMIN 의 기본값은 'admin' 이지만 원하는 대로 변경할 수 있다. 컨트롤러내에서의 관리자 액션을 추가할 때 필요하기 때문에, 이 문자열을 기억해 두어야 한다. 이 경우의 관리 액션은 admin_actinoName() 이라는 이름이 된다. 바람직한 URL 과 가능한 CAKE_ADMIN 과 컨트롤 액션의 설정의 예는 다음과 같다.

  1. /admin/products/add CAKE_ADMIN = 'admin'

    ProductsController 의 액션명은 = 'admin_add()'

    /superuser/news/post CAKE_ADMIN = 'superuser'

    NewsController 의 액션명은 = 'superuser_post()'

    /admin/posts/delete CAKE_ADMIN = 'admin'
    PostsController 의 액션명은 = 'admin_delete()'

어드민 라우팅을 사용하면, 로직을 지저분하게 하는 일 없이 라우팅을 아주 간단히 구현할 수 있다. 가능할 경우, 어드민 라우팅을 다음과 같이 사용한다면 쉽게 컨트롤러를 정의할 수 있다 :

  1. $this->params[CAKE_ADMIN];

또는

  1. $this->params['admin'];

('admin' 은 CAKE_ADMIN 의 값이라고 가정한다)

어드민 라우팅을 유효하게 하거나 이것을 사용하는 것은 어떤 인증이나 보안등이 실행될 수 없다는 점을 주의하라. 그것은 자신이 직접 이행해야 한다.

동일하게, Cake 의 웹서비스 라우팅을 쓸 수 있도록 하고, 간략화할 수 있다. 컨트롤러의 액션을 웹서비스로 공개하고 싶은가? 우선, /app/config/core.php 의 WEBSERVICES 를 'on' 으로 고친다. 루트 어드민 라우팅과 같은 방식으로 자동으로 라우팅되게 된다. 단 이미 정의된 몇가지 셋팅이 있다 :

  • rss
  • xml
  • rest
  • soap
  • xmlrpc

이것에 의해 /rss/컨트롤러명/액션명 이나, /soap/컨트롤러명/액션명 으로 사용할 수 있다. 다른 뷰를 준비할 수도 있다. 즉 두개의 뷰를 가진 하나의 액션을 만들 수 있다. 하나는 일반적인 HTML 을 보여주는 인용의 뷰, 다른 하나는 웹서비스를 이용하는 사람을 위한 뷰이다. 이렇게 하면, 애플리케이션의 많은 기능을 웹서비스로 공개할 수 있게 된다.

예를 들면, 애플리케이션 중에 사무실의 누가 전화중인가를 표시하는 로직이 있다고 하자. 이 데이터를 표시하는 HTML 의 뷰는 이미 있지만, XML로도 제공할 수 있도록 데스크톱 위젯이나 PDA의 애플리케이션 등에서도 사용할 수 있도록 하고 싶다. 우선, Cake 의 웹서비스를 사용할 수 있게 한다.

예 4.6. /app/config/core.php (일부)

  1. /**
    * The define below is used to turn cake built webservices
    * on or off. Default setting is off.
    */
    define('WEBSERVICES', 'on');

다음, 컨트롤러의 로직을 다음과 같이 구성한다 :

예 4.7. messages_controller.php

  1. <?php
    class PhonesController extends AppController
    {
    function doWhosOnline()
    {
    // 이 액션은, 누가 전화중인가 조사하는 모든 작업을 행하는 곳...

    // Cake 의 xml 웹서비스 라우트를 통해 이 액션을 공개하고 싶은 경우
    // /app/views/posts/xml/do_whos_online.thtml 내에 뷰를 포함시킨다.
    // 주의 : 여기서 사용하는 기본 view 는 /app/views/layouts/xml/default.thtml 이다.

    // 유저가 /phones/doWhosOnline 를 요청하면, HTML 버전의 데이터를 받게 된다.
    // 유저가 /xml/phones/doWhosOnline 를 요청하면, XML 버전의 데이터를 받게 된다.
    }
    }
    ?>

4.5. (옵션) 커스텀 인플렉션(어형 변화) 설정#

Cake 의 네이밍 규약은 꽤 편리하다. 모델 Box (단수) 에 이름을 붙여 컨트롤러 Boxes (복수) 가 되며, 모두 잘 작동한다. 하지만, Cake 의 인플렉터 (복수형, 단수형, 낙타표기법, 언더바를 만드는 클래스) 가 자신이 원하는대로 작동하지 않는 경우 (특히 영어외의 언어를 이용하고 있는 경우) 도 있다. 만일, Foci (fish) 를 인식해 주지 않는 경우, 커스텀 인플렉션 설정 파일을 수정한다.

/app/config/inflections.php 에는 Cake 의 클래스명에 사용된다. 수정 가능한 복수형, 단수형의 변수 목록이 있다. 또 불규칙형과 함께, 전혀 변화하지 않는 단어 (야외 스포츠 애호가인 Caker 들을 위한 Fish 나 Deer 와 같이) 도 정의된다.

파일 내에 있는 주의사항을 따르거나, 파일 내에 있는 예제들의 주석을 지우고 사용할 수 있다. 수정 전에 정규 표현을 알아 두는 것이 좋다.


05. 스캐폴딩 (Scaffolding)

  1. 5. 스캐폴딩(Scaffolding)
    1. 5.1 Cake 의 스캐폴딩은 꽤 쿨하다
    2. 5.2 스캐폴드 뷰의 커스터마이징

5. 스캐폴딩 (Scaffolding)#

5.1 Cake 의 스캐폴딩은 꽤 쿨하다#

프로덕션 애플리케이션에서도 사용하고 싶을 만큼 쿨할지 모른다. 지금, 우리도 꽤 쿨하다고 생각한다. 하지만 스캐폴딩은..그러니까...스캐폴딩(비계,임시가설물)이라는 것을 이해하기 바란다. 프로젝트의 시작시에는 정말 신속함으로 상당한 작업을 떠 맡아 주지만 완벽하게 유연하다는 의미는 아니다. 때문에 만일 로직이나 뷰를 커스터마이징하고 싶어지면, 스캐폴딩을 그만두고 여러분의 코드를 써야 한다.

스캐폴딩은 웹애플리케이션의 개발 초기 시점에서 매우 우수한 방법이다. 초기의 데이터베이스 스키마는 자주 변경되며, 변하기 쉬운 주제다. 이것은 설계 프로세스의 초기단계에서는 지극히 정상적인 것이다. 여기에는 단점 (웹개발자는 앞으로 사용되는 일이 없는 폼을 만드는 것을 싫어 한다) 을 가지고 있다. 개발자의 이 부담을 줄이기 위해서 스캐폴딩이 Cake 에 포함되어 있다. 스캐폴딩은 데이터베이스의 테이블을 분석하고 표준 목록과 추가, 삭제, 수정 버튼, 편집 폼, 데이터베이스의 하나의 아이템을 표시하기 위한 표준 뷰 등을 만들어 낸다. 애플리케이션으로 스캐폴딩을 추가하려면, 컨트롤러내에 $scaffold 변수를 추가한다 :

  1. <?php
    class CategoriesController extends AppController
    {
    var $scaffold;
    }
    ?>

스캐폴드에 대해서 중요한 사항이 하나 있다. 스캐폴드에서는 _id 로 끝나는 필드명은 언더바의 앞부분의 테이블명의 외부 키라고 판단한다. 예를 들어 카테고리를 만들었다면 parent_id 라는 컬럼을 갖게될 것이다. 이 릴리즈에서는 parentid 라고 부르는 것이 최선일 것이다. 또 만일 테이블에 외부키가 있는 경우 (예를 들어, titles 테이블에 있는 category_id) 가 있고, 모델이 적절하게 관련되어 있는 경우 (어소시에이션을 이해해야한다. 6.2 참조), show/edit/new 의 뷰 중에 셀렉트 박스가 표시되고, 거기에 외부 테이블 (카테고리) 의 열이 표시된다. 외부 테이블의 필드를 표시되게 설정하려면, 외부 모델의 $displayField 변수를 설정한다. 이 예제의 카테고리로 타이틀을 붙이려면 다음과 같이 한다.

  2. <?php
    class Title extends AppModel
    {
    var $name = 'Title';

    var $displayField = 'title';
    }
    ?>

5.2 스캐폴드 뷰의 커스터마이징#

스캐폴드의 view 를 조금 변경하고 싶다면, 직접 view 를 만들 수 있다. 공개용 애플리케이션에 이 기법을 사용하는 것은 권장하지 않지만, 프로토 타입 제작을 반복할 때 이 커스터마이징은 대단히 편리하다.

스캐폴드의 뷰를 변경하고 싶은 경우 스스로 직접 준비한다 :

예 5.1. 단일 컨트롤러용 커스텀 스캐폴딩 뷰

  1. PostsController 용의 커스텀 스캐폴딩 뷰를 다음과 같이 저장한다 :
  2. /app/views/posts/scaffold/index.scaffold.thtml
  3. /app/views/posts/scaffold/show.scaffold.thtml
  4. /app/views/posts/scaffold/edit.scaffold.thtml
  5. /app/views/posts/scaffold/new.scaffold.thtml

예 5.2. 애플리케이션 전체용 커스텀 스캐폴딩 view

  1. 모든 컨트롤러용 커스텀 스캐폴딩 뷰를 다음과 같이 저장한다 :

    /app/views/scaffold/index.scaffold.thtml
    /app/views/scaffold/show.scaffold.thtml
    /app/views/scaffold/edit.scaffold.thtml
    /app/views/scaffold/new.scaffold.thtml

컨트롤러의 로직을 이 시점에서 변경하고 싶다면, 스캐폴딩을 애플리케이션에서 제외하고 로직을 만들기 시작한다.

Cake 의 코드 생성기, Bake 도 편리하게 쓸만한 기능중 하나다. Bake 는 스캐폴딩된 코드를 생성하고, 자신의 애플리케이션의 필요에 따라 수정, 커스터마이징할 수 있게 한다.


06. 모델 (Models) - 업데이트중

  1. 6. 모델 (Models)
    1. 6.1. 모델이란?
    2. 6.2. 모델 함수
      1. 6.2.1. 사용자 함수
      2. 6.2.2. 데이터 추출
      3. 6.2.3. 복합 조건 검색 (배열 이용)
      4. 6.2.4. 데이터 저장
      5. 6.2.5. 모델 콜백
    3. 6.3. 모델의 변수
    4. 6.4. 관련 (association)
      1. 6.4.1. 소개
      2. 6.4.2. hasOne 정의하기와 질의하기
      3. 6.4.3. belongsTo 정의하기와 질의하기
      4. 6.4.4. hasMany 정의 및 질의하기
      5. 6.4.5. hasAndBelongToMany 정의 및 질의하기
      6. 6.4.6. 관련 데이터 저장
      7. 6.4.7. hasAndBelongsToMany 의 저장
      8. 6.4.8. 애플리이션 작동중 bindModel() 과 unbindModel() 이용하여 관련 변경하기

  • 역자주 : 현재 일부 섹션의 번역이 되어 있지 않습니다

6. 모델 (Models)#

6.1. 모델이란?#

모델은 어떤 일을 하는가? 도메인 로직을 표시 부분에서 분리하고, 애플리케이션의 로직만을 분리한다.

모델이란 일반적으로 데이터베이스를 나타낸다. 더 구체적으로 데이터베이스내의 특정 테이블을 나타낸다. 기본적으로 각각의 모델은 복수형의 이름이 붙은 테이블에 대응하는 단수형의 클래스가 된다. 예를 들면, 'User' 모델은 'Users' 테이블을 사용한다. 모델에는 또 데이터 검증 (validation) 을 위한 룰, 관련 (association) 정보, 테이블 특유의 메소드 등을 포함시킬 수 있다. 다음은 간단한 User 모델이 Cake 내에서 어떻게 되는지 보여준다.

예 6.1. User 모델의 예, /app/models/user.php 에 저장

  1. <?php

    //AppModel 에서는 Cake 의 모델의 기능을 전부 제공하고 있다

    class User extends AppModel
    {
    // 이 변수를 포함시는 것은 항상 좋은 습관이다
    var $name = 'User';

    // 이것은 검증 (validation) 에 사용된다, "자료 검증 (Data Validation)" 장을 보라.
    var $validate = array();

    // 관련 (association) 을 정의할 수 이다.
    // 6.3 섹션에 자세히 나온다.

    var $hasMany = array('Image' =>
    array('className' => 'Image')
    );

    // 사용자 함수를 포함시킬 수 있다:
    function makeInactive($uid)
    {
    // 자신 로직을 여기에...
    }
    }

    ?>

6.2. 모델 함수#

PHP 에서 보면, 모델은 AppModel 클래스를 확장 (extend) 한 클래스이다. 원래의 AppModel 클래스는 cake/ 디레토리에 선언되어 있지만, 직접 다시 만들 경우 app/app_model.php 에 둘 수 있다. 이 안에는 여러 모델을 공유하는 메소드를 넣는다. 이 클래스는 cake/libs/model/model.php 내에 선언되어 있는 표준 Cake 라이브러리의 하나인 모델 (Model) 클래스를 확장한 것이다.

이 섹션에서는 Cake 모델에 관해 자주 사용되는 함수에 대해 다루지만, 전체 레퍼런스는 http://api.cakephp.org 에 있는 것을 기억해 둬야 한다.

6.2.1. 사용자 함수#

모델내에 테이블 특유의 메소드를 쓰는 예로, 블로그의 포스트를 감추고/보이는 한쌍의 메소드를 보여준다

예 6.2. 모델 함수의 예

  1. <?php
    class Post extends AppModel
    {
    var $name = 'Post';

    function hide ($id=null)
    {
    if ($id)
    {
    $this->id = $id;
    $this->saveField('hidden', '1');
    }
    }

    function unhide ($id=null)
    {
    if ($id)
    {
    $this->id = $id;
    $this->saveField('hidden', '0');
    }
    }
    }
    ?>

6.2.2. 데이터 추출#

다음은 모델을 이용해 데이터를 추출하는 몇가지 표준적인 방법이다.

  • findAll
    • string $conditions
    • array $fields
    • string $order
    • int $limit
    • int $page
    • int $recursive

지정된 필드에서 (만일 지정되어 있을 경우), $conditions 에 입력된 것을 $page (기본값은 1 페이지) 페이지에서 $limit 레코드까지 반환한다. $conditions 는 SQL 문에서 처럼 (예를 들면), $conditions = "race = 'wookie' AND thermal_detonators > 3" 라고 쓴다.

$recursive 옵션이 정수값 1 보다 크게 설정 되어 있는 경우, findAll() 동작은, findAll() 에서 발견된 관련 모델에 관하여도 결과를 반환한다. 만일, 속성에 여러 소유자가 있고, 그 각 소유자에 여러 종속관계가 있는 경우, 속성 모델상의 재귀함수 findAll()은 연관되는 모델을 반환한다.

  • find
    • string $conditions
    • array $fields
    • string $order
    • int $recursive

$conditions 에 입력된 첫번째 레코드에서 지정된 필드(지정하지 않으면 모든 필드)를 반환한다.

$recursive 옵션이 정수값 1과 3 사이에 설정되면, find() 동작은, find() 에 발견된 모델에 관련되어 있는 모델도 반환한다. 재귀적 find 는, 3단계까지 진행할 수 있다. 만일, 속성에 여러 소유자가 있고, 그 각 소유자에 여러 종속관계가 있는 경우, 속성 모델상의 재귀함수 find() 는 연관되는 모델을 3단계까지 반환한다.

  • findAllBy<fieldName>
    • string $value

이 매직 함수는, 테이블의 특정 필드나 값을 빠르게 검색할 때 사용할 수 있다. 검색한 필드명을 낙타 표기법으로 추가한다. (컨트롤러내에서 사용할) 예제는 아래와 같다 :

  1. $this->Post->findByTitle('My First Blog Post');
    $this->Author->findByLastName('Rogers');
    $this->Property->findAllByState('AZ');
    $this->Specimen->findAllByKingdom('Animalia');

결과는, find() 나 findAll() 과 같은 방법으로 형식화된 배열로 결과를 반환한다.

  • findNeighbours
    • string $conditions
    • array $field
    • string $value

$field 와 $value 에서 지정한 필드를, $conditions 의 SQL 의 조건으로 필터링하고, 인접한 모델(지정된 필드만)의 데이타를 배열로 반환한다.

이것은 모델의 엔트리를 특정 순서에 따라 정렬하여, '이전' 또는 '다음' 의 링크가 필요할 때 편리하다. 이 함수는 숫자 또는 날짜의 필드의 경우만 작동한다.

  1. class ImagesController extends AppController
    {
    function view($id)
    {
    // 이미지를 표시하고 싶다고 말한다...

    $this->set('image', $this->Image->find("id = $id");

    // 하지만, '이전'과 '다음' 이미지도 필요한 경우에는..

    $this->set('neighbours', $this->Image->findNeighbours(null, 'id', $id);

    }
    }

이와 같이 하면, $ image['Image'] 배열 전체와, $neighbours['prev']['Image']['id'] 와 $ neighbours['next']['Image']['id'] 를 view 에서 사용할 수 있다.

  • field
    • string $name
    • string $conditions
    • string $order

$conditions 값에 입력되어 $order 로 정렬된 첫번째 레코드의 첫 필드를 문자열로 반환한다.

  • findCount
  • string $conditions

주어진 조건에 맞는 레코드의 수를 반환한다.

  • generateList
    • string $conditions
    • string $order
    • int $limit
    • string $keyPath
    • string $valuePath

이 함수는 키(key)/값(value) 한쌍으로 구성된 목록을 추출하는 바로가기이다. 특히, 모델의 목록으부터 html의 select 태그를 만들 때 편리하다. findAll() 과 같이, $conditions, $order, $limit 파라미터를 사용한다. $keyPath 와 $valuePath 에서 어떤 것을 key 와 value 로 할지 지정한다. 예를 들면, integer 의 id 를 key 라고 한 역할(role) 목록을 역할 모델로부터 만들고 싶은 경우에는 아래와 같이 하면 그만이다.

  1. $this->set(
    'Roles',
    $this->Role->generateList(null, 'role_name ASC', null, '{n}.Role.id', '{n}.Role.role_name')
    );

    // 이와 같이 반환될 것이다.
    array(
    '1' => 'Account Manager',
    '2' => 'Account Viewer',
    '3' => 'System Manager',
    '4' => 'Site Visitor'
    );

  • read
    • string $fields
    • string $id

현재 로딩된 레코드, 또는 $id 로 지정한 레코드부터, 필드와 값을 얻고 싶은 경우에 이 함수를 사용한다.

read() 동작은 모델의 $recursive 에 관계 없이, 관련되어 있는 모델의 하나의 레벨만 가져오는 점에 주의한다. 그 이상의 레벨까지 가져 오려면, find() 나 findAll() 을 사용한다.

  • query
    • string $query

  • execute
    • string $query

모델의 query() 와 execute() 메소드를 사용하면, 커스텀 SQL 을 호출할 수 있다. 이 둘의 차이점은, query() 가 커스텀 SQL 쿼리(결과가 반환된다)을 만들때 사용되고, execute() 는 커스텀 SQL 의 실행(반환 값은 필요 없음)을 한다 점이다.

예 6.3. query() 를 이용한 커스텀 SQL 호출

  1. <?php
  2. class Post extends AppModel
  3. {
  4. var $name = 'Post';
  5. function posterFirstName()
  6. {
  7. $ret = $this->query("SELECT first_name FROM posters_table
  8. WHERE poster_id = 1");
  9. $firstName = $ret[0]['first_name'];
  10. return $firstName;
  11. }
  12. }
  13. ?>

6.2.3. 복합 조건 검색 (배열 이용)#

대부분의 모델의 find 호출은 하나 또는 다른 조건 전달을 수반한다. 이것의 가장 간단한 접근은 SQL 에 WHERE 절을 사용하는 것이지만, 더 많은 조작이 필요하다면, 배열을 사용할 수 있다. 배열을 사용하면 더욱 읽기 쉽고, 깔끔하며 게다가 쿼리 작성이 쉬워진다. 이 문법은 신중하고 잘 다룰 수 있는 부분 속으로 쿼리의 요소 (필드, 값, 조작자, 등) 를 분리해 준다. 이것은 Cake 가 매우 효율적인 쿼리를 생성할 수 있도록 허락하고, 올바른 SQL 문법을 보장하며, 쿼리의 개별적 부분을 올바르게 뽑는다.

매우 기본적으로, 배열 기반의 쿼리는 다음과 같이 보여진다 :

예 6.4. 기본적인 find 조건 배열 사용예 :

  1. $conditions = array("Post.title" => "This is a post");

    //모델과 함께 사용한 예:
    $this->Post->find($conditions);

The structure is fairly self-explanatory: it will find any post where the title matches the string "This is a post". Note that we could have used just "title" as the field name, but when building queries, it is good practice to always specify the model name, as it improves the clarity of the code, and helps prevent collisions in the future, should you choose to change your schema. What about other types of matches? These are equally simple. Let's say we wanted to find all the posts where the title is not "This is a post":

  1. array("Post.title" => "<> This is a post")

All that was added was '<>' before the expression. Cake can parse out any valid SQL comparison operator, including match expressions using LIKE, BETWEEN, or REGEX, as long as you leave a space between the operator an the expression or value. The one exception here is IN (...)-style matches. Let's say you wanted to find posts where the title was in a given set of values:

  1. array("Post.title" => array("First post", "Second post", "Third post"))

Adding additional filters to the conditions is as simple as adding additional key/value pairs to the array:

  1. array
    (
    "Post.title" => array("First post", "Second post", "Third post"),
    "Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks"))
    )

By default, Cake joins multiple conditions with boolean AND; which means, the snippet above would only match posts that have been created in the past two weeks, and have a title that matches one in the given set. However, we could just as easily find posts that match either condition:

  1. array
    ("or" =>
    array
    (
    "Post.title" => array("First post", "Second post", "Third post"),
    "Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks"))
    )
    )

Cake accepts all valid SQL boolean operations, including AND, OR, NOT, XOR, etc., and they can be upper or lower case, whichever you prefer. These conditions are also infinitely nestable. Let's say you had a hasMany/belongsTo relationship between Posts and Authors, which would result in a LEFT JOIN on the find done on Post. Let's say you wanted to find all the posts that contained a certain keyword or were created in the past two weeks, but you want to restrict your search to posts written by Bob:

  1. array
    ("Author.name" => "Bob", "or" => array
    (
    "Post.title" => "LIKE %magic%",
    "Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks")
    )
    )

6.2.4. 데이터 저장#

모델에 데이터를 저장하려면, 저장하고 싶은 데이터를 준비한다. save() 메소드로 전달받은 데이터는 다음과 같은 형식으로 다뤄진다 :

  1. Array
    (
    [ModelName] => Array
    (
    [fieldname1] => 'value'
    [fieldname2] => 'value'
    )
    )

컨트롤러에 POST 방식으로 전달되는 데이타를 이 형태로 하는 가장 간단한 방법은, HTML 헬퍼를 사용하는 것이다. HTML 헬퍼는 Cake 가 이해하는 방법으로 폼 요소의 이름을 생성한다. HTML 헬퍼를 사용하지 않는 것도 가능하지만, 폼 요소는 data[모델명][필드명] 과 같이 해야 한다. 하지만, $html->input('Model/fieldname') 를 사용하는 것이 가장 간단하다.

폼으로부터 포스트 방식으로 전달된 데이타는, 자동으로 이와 같은 포맷이 되고, 컨트롤러내의 $this->data 에 들어 갑니다. 따라서, 웹 폼의 데이터를 간단히 저장할 수 있다. 속성(property) 컨트롤러의 edit 함수는, 다음과 같은 형태가 될 것이다 :

  1. function edit($id)
    {

    // 주의 : 속성 모델은 $this->Property 에 자동으로 불러낸다

    // 폼데이터가 존재하는지 검사...
    if (empty($this->data))
    {
    $this->Property->id = $id;
    $this->data = $this->Property->read();// 현재 지정된 열의 데이터를 폼 필드에 넣을 수 있다
    }
    else
    {
    // 데이터를 저장하는 부분이다. 자동으로 자료 검증(validation)을 한다.
    if ($this->Property->save($this->data['Property']))
    {
    // 메시지를 표시(flash)하거나 리다이렉트한다.
    $this->flash('Your information has been saved.',
    '/properties/view/'.$this->data['Property']['id'], 2);
    }
    // 만일 잘못된 필드가 존재하거나 저장이 실패한 경우 이 형식이 표시된다
    }
    }

save 동작이 조건문 안에 위치하는 것에 주의한다. 모델에 데이타를 저장하려고 하는 경우, Cake 는 지정 되어 있는 룰에 기초하여 자동적으로 데이타가 올바른지 체크한다. 자료 검증과 관련된 상세 정보는 "12. 자료 검증(validation)"을 참조한다. save() 에 자료 검증을 할 필요가 없는 경우에는 save($data, false) 를 사용한다.

기타 유용한 함수 :

  • del
    • string $id
    • boolean $cascade

$id 로 지정되었거나, 현재의 id 의 모델 데이타를 삭제한다.

이 모델에 다른 모델이 연관되어 있고, 연관(association)배열에 의존(dependent)키가 SET 되어 있는 경우, $cascade 가 true 로 설정되었 때, 이 메소드는 관련되는 모델 데이터도 삭제한다.

성공하면, true 를 반환한다.

  • saveField
    • string $name
    • string $value

하나의 필드 값을 저장한다.

  • getLastInsertId

가장 최근에 작성된 레코드의 ID 를 반환한다.

6.2.5. 모델 콜백#

모델의 동작 전후에 로직을 넣어을 때, 몇 모델에 콜백이 추가되었다. 애플리케이션 내에서 이 기능을 사용하려면 지정된 파라미터를 사용하고 Cake 의 모델의 이러한 함수를 오버라이드(override)한다.

  • beforeFind
    • string $conditions

beforeFind() 콜백은, 검색(find)동작이 시작되기 전에 실행된다. 검색 전에 실행하고 싶은 로직은 여기에 배치하도록 한다. 모델의 이 메소드를 오버라이드할 때에, 검색도 실행하고 싶으면 true 를, 검색을 중지하고 싶으면 false 를 반환하면 된다.

  • afterFind
    • array $results

검색(find)동작에 그 결과를 수정하고 싶은 경우나, 검색 후에 뭔가를 하고 싶은 경우에는 이 콜백을 사용한다. 이 함수의 파라미터는 모델의 검색 결과를 반환한다. 수정한 결과를 return value 로서 반환하면 된다.

  • beforeValidate

자료 검증(validation)이 걸리기 전에 데이터를 수정하고 싶은 경우에는 이 콜백을 사용한다. Model::invalidate() 를 사용하고 추가 검증이나, 더 복잡한 검증 룰을 추가하는 것도 가능하다. 이 때에는, $this->data 에 의한 모델 데이터로 접근 가능하다. 또 이 함수는 true 를 반환해야한다. true 를 반환하지 않으면 save() 의 실행이 중지된다.

  • beforeSave

저장 직전의 로직은 이 함수에 두면 된다. 모델 데이터의 자료 검증이 끝난 후 곧바로, (자료 검증에 save 호출이 중지된 경우에는, 이 콜백도 실행되지 않는다.) 데이터가 저장 되기 전에 이 함수가 실행된다. 이 함수도 저장 동작을 계속하고 싶은 경우에는 true 를 반환해야 한다. 중지하고 싶은 경우에는 false 를 반환한다.

beforeSave 의 사용 방법의 일례로, 특정 데이터베이스 엔진용 시간 데이터 포맷에 유용하다 :

  1. // HTML 헬퍼로 작성한 date/time :
    // 이 코드는 view 에 있다.

    $html->dayOptionTag('Event/start');
    $html->monthOptionTag('Event/start');
    $html->yearOptionTag('Event/start');
    $html->hourOptionTag('Event/start');
    $html->minuteOptionTag('Event/start');

  1. // 모델 콜백 함수는 날짜 데이터를 연결해 모두 저장한다
    // 이 코드는 그 이벤트 모델에 있다 :

    function beforeSave()
    {
    $this->data['Event']['start'] = $this->_getDate('Event', 'start');

    return true;
    }

    function _getDate($model, $field)
    {
    return date('Y-m-d H:i:s', mktime(
    intval($this->data[$model][$field . '_hour']),
    intval($this->data[$model][$field . '_min']),
    null,
    intval($this->data[$model][$field . '_month']),
    intval($this->data[$model][$field . '_day']),
    intval($this->data[$model][$field . '_year'])));
    }

  • afterSave

저장 후에 실행하고 싶은 로직을 이 콜백에 둔다.

  • beforeDelete

삭제 전에 실행하고 싶은 로직을 이 콜백에 둔다. 이 함수는 계속해서 삭재하고 싶은 경우에은 true 를 반환하면 된다. 중지하고자 할 경우 false 를 반환하면 된다.

  • afterDelete

삭제 후에 실행하고 싶은 로직을 이 콜백에 둔다.

6.3. 모델의 변수#

모델을 작성할 때, 특별한 몇가지 변수를 설정하는 것으로 Cake 의 기능을 이용할 수 있다.

$primaryKey

모델이 데이터베이스의 테이블에 관련되어 있는 경우, 테이블의 primary key 명이 'id' 가 아닌 경우, 이 변수를 사용하여 어느 필드가 primary key 인지를 Cake 에 알린다.

$recursive

find() 와 findAll() 의 동작중에, 관련되어 있는 모델의 데이터를 몇 레벨까지 불러(fetch) 올 것인지를 설정한다.

Groups (그룹) 이 있고, 이 그룹에 Users (유저) 가 있고, 각 유저가 많은 Articles (게시물) 을 갖고 있다고 가정하자.

테이블 6.1. Model::recursive 옵션

$recursive = 0 | Cake 는 Group 데이터를 불러 온다.
$recursive = 1 | Cake 는 Group 과 관련된 Users 를 불러 온다.
$recursive = 2 | Cake 는 Group 과 관련된 Users 와 그 User 애 관련된 Articels 를 불러온다

$transactional

이 모델로 트랜잭션 가능한지 아닌지 Cake 에 지시합니다 (begin/commit/rollback). boolean 값으로 설정한다. 트랜잭션을 지원하고 있는 데이터베이스만 사용할 수 있다.

$useTable

사용하고 싶은 테이블이 모델명의 복수형이 되어 있지 않은 경우 (그리고, 테이블명을 변경하기 싫은 경우), 이 변수로 사용하고 싶은데 테이블명을 설정한다.

$validate

이 모델에 전달받은 데이터를 발리 자료 검증하는 데 사용되는 배열이다. 상세한 것은 "자료 검증" 장을 참조한다.

$useDbConfig

데이터베이스의 설정은, /app/config/database.php 에서 설정하는 것을 기억는가? 그것을 바꾸기 위해서 이 변수를 사용한다. 작성한 데이터베이스 설정 파일에 있는 사용 가능한 데이타베이스 접속명을 쓴다. 기본값은, 'default' 이다.

6.4. 연관 (association)#

6.4.1. 소개#

CakePHP 대단히 강력한 기능중 하나는 모델에 의한 관계 매핑(relational mapping) 이다. CakePHP 에서는, 테이블의 관계를 연관(association)을 통해 다룬다. 연관은 논리적으로 관련되어 있는 유닛을 연결하는 접착제이다.

CakePHP 에는 네종류의 연관이 있다

  1. hasOne

  2. hasMany

  3. belongsTo

  4. hasAndBelongsToMany

모델간의 연관이 정의되면, Cake 에서는 작업하고 있는 모델에 관계된 모델에서 자동적으로 데이터를 가져 온다. 예를 들면, Post (투고) 모델이 Author (저자) 모델에 hasMany 를 사용해 연관이 설정 되고 있으며, 컨트롤러에 $this->Post->findAll() 로 불러내는 것으로, Post 레코드, 또 관련되어 있 Author 레코드의 데이터를 가져 온다.

자료 검증을 사용하려면, CakePHP 의 네이밍 규약(부록 "Cake 규약" 참조)에 따르는 것이 가장 좋다. CakePHP 의 네이밍 규약을 사용하는 경우, 스캐폴딩으로 애플리케이션의 데이타를 시각화하는 것이 가능하다. 스캐폴딩에서는, 모델간의 연관을 검출해 이용하기 때문이다. Cake 의 네이밍 규약에 따르지 않는 방법으로도 모델의 연관을 커스터마이징하는 것이 가능하다. 그러나, 그 팁에 관해서는 나중에 설명한다. 지금은 네이밍 규약에 따른 방법만 얘기한다. 외부키(foreign keys), 모델명, 테이블명에 관한 네이밍 규약을 염두에 둬야 한다.

다음에 설명하는 것은, 몇 가지의 요소의 이름에 관하여, Cake 가 기대하는 사항들의 복습이다. : (네이밍에 관한 상세한 정보는 부록 "Cake 규약" 참조)

  • 외부키 : [모델명의 단수형]_id. 예를 들어, "authors" 테이블내의 외부키가 Post 일 경우, Author 에 속한 것은 "post_id" 가 된다.
  • 테이블명 : [객체명의 복수형]. 블로그의 포스트(post)와 작성자(author)의 정보를 저장하고 싶기 때문에, 테이블 명은 각각, "posts" 와, "authors" 가 된다.
  • 모델명 : [낙타표기법의 테이블 단수형]. "posts" 테이블에 대한 모델은 "Post", 그리고 "authors" 테이블에는 "Author".

CakePHP 의 스캐폴딩에서는, 연관(association)된 컬럼의 정렬이 같다고 예상한다. 따라서, 만일 Article 이 다른 3개의 모델(Author, Editor, Publisher)에 속해 있는 경우(belongsTo), author_id, editor_id, publisher_id 라는 3개의 키가 필요하게 된다. 스캐폴딩에서는, 테이블내의 연관도 같은 정렬이라고 예상한다 (첫번째 Author, 두번째 Editor, 마지막에 Publisher).

연관이 어떻게 동작하는지를 설명하기 위해, 계속해서 블로그 애플리케이션을 예로 든다. 블로그용으로 간단한 유저 관리 시스템을 만든다고 하자. 이때 당연히 Users 의 기록을 보관하고 싶을 것이다. 그런데, 또 각각의 유저가 연관된 하나의 Profile (프로필) 을 갖게 하고 싶다 (User hasOne Profile, 유저는 프로필을 "하나 갖는다"). 게다가, Users 는 Comment 를 만들 수 있고, 그들 자신과 관련지을 수 있다 (User hasMany Comments, 유저는 코멘트를 "여러개 갖는다"). 일단 유저 시스템이 작동하게 되면, Posts 를 hasAndBelongsToMany 관계를 사용하는 Tag 객체와 관계되도록 한다 ( Post hasAndBelongsToMany Tags, "포스트는 여러 태그를 갖고, 또 태그에 속한다").

6.4.2. hasOne 정의하기와 질의하기#

이 연관을 설정하기 위해, 이미 User 와 Profile 모델은 작성되어 있어야 한다. 이 두개 사이에 hasOne 연관을 정의하려면, 모델에 하나의 배열을 추가하고 Cake 에 관련 방법을 전달한다.

다음과 같이하면 된다.

예 6.4. /app/models/user.php hasOne

  1. <?php
    class User extends AppModel
    {
    var $name = 'User';
    var $hasOne = array('Profile' =>
    array('className' => 'Profile',
    'conditions' => '',
    'order' => '',
    'dependent' => true,
    'foreignKey' => 'user_id'
    )
    );
    }
    ?>

$hasOne 배열은 User 모델과 Profile 모델의 연관을 Cake 가 조립하는데 사용된다. 배열의 각 키에 의해 연관을 더욱 상세하게 설정할 수 있다 :

  1. className (필수): 연관짓고 싶은 모델의 클래스명

    위 예제에서는, 'Profile' 모델의 클래스명을 지정하고 있다.

  2. conditions: 관계를 정의한 SQL 조건의 일부

    필요하면 녹색의 헤더가 붙은 프로필만 관련 짓도록 Cake 에 명령할때, 다음과 같이 키를 설정하면 가능하다 : "Profile.header_color = 'green'".

  3. order: 연관된 모델의 데이터 정렬

    연관된 모델을 특정 순서로 정렬하고 싶은 경우, 이 키로 SQL 의 order 방법으로 값을 설정한다, 예를 들면 : "Profile.name ASC".

  4. dependent: true 로 설정하면, 이 모델의 데이터 삭제시에 연관되어 있는 모델 측의 데이터도 삭제된다.

    예를 들면, 프로필 "Cool Blue" 가 "Bob" 에 연관되어 있는 경우, "Bob" 라는 유저를 삭제하고, 프로필 "Cool Blue" 도 삭제된다.

  5. foreignKey: 연관되어 있는 모델을 가리키고 있는 외부키의 이름

    Cake 의 네이밍 규약에 따라서 없는 데이터베이스를 사용하고 있는 경우에 여기서 지정할 수 있다.

이제, User 모델로 find() 또는 findAll() 을 호출하면, 연관되어 있는 Profile 모델도 함께 볼 수 있다.

  1. $user = $this->User->read(null, '25');
    print_r($user);

    // 출력:

    Array
    (
    [User] => Array
    (
    [id] => 25
    [first_name] => John
    [last_name] => Anderson
    [username] => psychic
    [password] => c4k3roxx
    )

    [Profile] => Array
    (
    [id] => 4
    [name] => Cool Blue
    [header_color] => aquamarine
    [user_id] = 25
    )
    )

6.4.3. belongsTo 정의하기와 질의하기#

이제 User 는 Profile 을 볼 수 있다. 이번에는 Profile 이 User 를 보이도록 한다. Cake 에서는 belongsTo 연관을 사용하는 것으로 가능하게 된다. Profile 모델 내에서 다음과 같이 한다 :

예 6.5. /app/models/profile.php belongsTo

  1. <?php
    class Profile extends AppModel
    {
    var $name = 'Profile';
    var $belongsTo = array('User' =>
    array('className' => 'User',
    'conditions' => '',
    'order' => '',
    'foreignKey' => 'user_id'
    )
    );
    }
    ?>

$belongsTo 배열은, Cake 가 User 모델과 Profile 모델 사이에 연관을 만들 때 사용한다. 배열 가각의 키에 연관을 더욱 상세하게 설정할 수 있다 :

  1. className (필수): 연관짓고 싶은 모델의 클래스명

    위 예제에서는, 'User' 모델의 클래스명을 지정하고 있다.

  2. conditions: 관계를 정의한 SQL 조건의 일부

    active 상태의 User 만을 연관 짓도록 Cake 에 명령할때, 다음과 같이 키를 설정하면 가능하다 : "User.active = '1'" 등.

  3. order: 연관된 모델의 데이터 정렬

    연관된 모델을 특정 순서로 정렬하고 싶은 경우, 이 키로 SQL 의 order 방법으로 값을 설정한다, 예를 들면 : "User.last_name ASC".

  4. foreignKey: 연관되어 있는 모델을 가리키고 있는 외부키의 이름

    Cake 의 네이밍 규약에 따라서 없는 데이터베이스를 사용하고 있는 경우에 여기서 지정할 수 있다.

이제, Profile 모델로 find() 또는 findAll() 을 호출하면, 연관되어 있는 User 모델도 함께 볼 수 이다 :

  1. $profile = $this->Profile->read(null, '4');
    print_r($profile);

    // 출력:

    Array
    (

    [Profile] => Array
    (
    [id] => 4
    [name] => Cool Blue
    [header_color] => aquamarine
    [user_id] = 25
    )

    [User] => Array
    (
    [id] => 25
    [first_name] => John
    [last_name] => Anderson
    [username] => psychic
    [password] => c4k3roxx
    )
    )

6.4.4. hasMany 정의 및 질의하기#

User 모델과 Profile 모델이 연관되고, 올바르게 작동하게 되었기 때문에, User 레코드를 Comment 레코드와 연관시켜 보도록 한다. 이것은, User 모델을 다음과 같이 하는 것으로 가능하다 :

예 6.6. /app/models/user.php hasMany

  1. <?php
    class User extends AppModel
    {
    var $name = 'User';
    var $hasMany = array('Comment' =>
    array('className' => 'Comment',
    'conditions' => 'Comment.moderated = 1',
    'order' => 'Comment.created DESC',
    'limit' => '5',
    'foreignKey' => 'user_id',
    'dependent' => true,
    'exclusive' => false,
    'finderQuery' => ''
    )
    );

    // 방금전에 정의했던 hasOne 의 관계..
    var $hasOne = array('Profile' =>
    array('className' => 'Profile',
    'conditions' => '',
    'order' => '',
    'dependent' => true,
    'foreignKey' => 'user_id'
    )
    );
    }
    ?>

$hasMany 배열은, Cake 가 User 모델과 Comment 모델 사이에 연관을 만들 때 사용한다. 배열 가각의 키에 연관을 더욱 상세하게 설정할 수 있다 :

  1. className (필수): 연관짓고 싶은 모델의 클래스명

    위 예제에서는, 'Comment' 모델의 클래스명을 지정하고 있다.

  2. conditions: 관계를 정의한 SQL 조건의 일부

    조정된(moderated) Comment 만을 연관 짓도록 Cake 에 명령할때, 다음과 같이 키를 설정하면 가능하다 : "Comment.moderated = 1" 등.

  3. order: 연관된 모델의 데이터 정렬

    연관된 모델을 특정 순서로 정렬하고 싶은 경우, 이 키로 SQL 의 order 방법으로 값을 설정한다, 예를 들면 : "Comment.created DESC" 등.

  4. limit: Cake 가 불러오는 관련 모델의 데이터의 최대 갯수

    위 예에서는, 유저의 Comment 를 전부 불러오지 않고 5개만 불러온다.

  5. foreignKey: 연관되어 있는 모델을 가리키고 있는 외부키의 이름

    Cake 의 네이밍 규약에 따라서 없는 데이터베이스를 사용하고 있는 경우에 여기서 지정할 수 있다.

  6. dependent: true 로 설정하면, 이 모델의 데이터 삭제시에 연관되어 있는 모델 측의 데이터도 삭제된다.

    예를 들면, 프로필 "Cool Blue" 가 "Bob" 에 연관되어 있는 경우, "Bob" 라는 유저를 삭제하고, 프로필 "Cool Blue" 도 삭제된다.

  7. exclusive: true 로 설정하면, 연관된 모든 객체가 하나의 SQL 구문의로 삭제된다. beforeDelete 콜백은 실행되지 않는다.

    빠르게 작동하기 때문에 간단한 연관에서 활용할 수 있다.

  8. finderQuery: 연관을 불러오기 위해 완전한 SQL 구문을 지정한다.

    여러 테이블에 의존하는 복잡한 연관의 경우에 활용할 수 있다. 만일 Cake 의 자동 연관을 사용할 수 없을 경우에, 이것으로 커스터마이징이 가능하다.

이제, User 모델로 find() 또는 findAll() 을 호출하면, 연관되어 있는 Comment 모델도 함께 볼 수 있다 :

  1. $user = $this->User->read(null, '25');
    print_r($user);

    //output:

    Array
    (
    [User] => Array
    (
    [id] => 25
    [first_name] => John
    [last_name] => Anderson
    [username] => psychic
    [password] => c4k3roxx
    )

    [Profile] => Array
    (
    [id] => 4
    [name] => Cool Blue
    [header_color] => aquamarine
    [user_id] = 25
    )

    [Comment] => Array
    (
    [0] => Array
    (
    [id] => 247
    [user_id] => 25
    [body] => The hasMany assocation is nice to have.
    )

    [1] => Array
    (
    [id] => 256
    [user_id] => 25
    [body] => The hasMany assocation is really nice to have.
    )

    [2] => Array
    (
    [id] => 269
    [user_id] => 25
    [body] => The hasMany assocation is really, really nice to have.
    )

    [3] => Array
    (
    [id] => 285
    [user_id] => 25
    [body] => The hasMany assocation is extremely nice to have.
    )

    [4] => Array
    (
    [id] => 286
    [user_id] => 25
    [body] => The hasMany assocation is super nice to have.
    )

    )
    )

프로세스를 문서에 설명하는 것은 하지 않고 있지만, "Comment belongsTo User" 연관도 정의하고, 상호 모델로부터 상대가 보이도록 하는 것은 좋은 생각이다. 스캐폴딩을 사용하려 했을 때, 상호 모델로부터 연관을 정의하지 않는 것은 가끔 흔한 실수가 되기도 한다.

6.4.5. hasAndBelongToMany 정의 및 질의하기#

간단한 연관에 관해서는 이것으로 마스터 가능하다. 다음으로는, 마지막 연관 : hasAndBelongsToMany (또는 HABTM) 으로 이동하자. 이것은 간단히 이해되지는 않겠지만, 가장 유용한 것중 하나이다. HABTM 연관은, 두개의 테이블이 있고, 그 두개를 조인 테이블로 연결되어 때 유용하다. 조인 테이블은 다른 것과 관련하고 있는 개별적인 열의 정보를 갖는다.

hasMany 와 hasAndBelongsToMany 의 차이는, hasMany 의 경우, 관련되어 있는 모델의 데이타를 공유할 수 없다는 것이다. 만일 User hasMany Comments (User 가 여러 Comment 를 갖고 있다) 라면, Comment 와 관련되어 있는 것은, User *뿐*이다. HABTM 는, 관련된 모델 데이타를 공유할 수 있다. 이것은 다음 경우에 매우 탁월하다 : Post 모델을 Tag 모델에 연관시킬 때. Tag 는 Post 에 belongs to(속할) 의 관계이지만, 소모해 버리는 것은 바람직하지 않다, Tag 는 계속해서 다른 Post 부터도 연관시키고 싶기 때문이다.

그러기 위해서는, 이 연관를 위해서 테이블을 바르게 설정할 필요가 있다. 물론, Tag 모델을 위한 "tags" 테이블, 그리고 Post 모델을 위한 "posts" 테이블이 필요합니다. 그리고, 연관을 위해서, 새로 조인 테이블을 생성할 필요도 있다. HABTM 조인 테이블 네이밍 규약은 [복수형 모델명1]_[복수형 모델명2] 이다. 모델명은 알파벳 순서대로 정렬한다 :

예 6.7. HABTM 조인 테이블 : 샘플 모델과 조인 테이블명

  1. Posts 와 Tags: posts_tags

  2. Monkeys 와 ceCubes: ice_cubes_monkeys

  3. Categories 와 Articles: articles_categories

HABTM 조인 테이블은 연결된 모델에 관해 최소 두개의 외부키로 구성되어 있다. 이 예에서는, "post_id" 와 "tag_id" 만이 필요하다

Post HABTM Tags 의 SQL 덤프 예이다 :

  1. --
    -- Table structure for table `posts`
    --

    CREATE TABLE `posts` (
    `id` int(10) unsigned NOT NULL auto_increment,
    `user_id` int(10) default NULL,
    `title` varchar(50) default NULL,
    `body` text,
    `created` datetime default NULL,
    `modified` datetime default NULL,
    `status` tinyint(1) NOT NULL default '0',
    PRIMARY KEY (`id`)
    ) TYPE=MyISAM;

    -- --------------------------------------------------------

    --
    -- Table structure for table `posts_tags`
    --

    CREATE TABLE `posts_tags` (
    `post_id` int(10) unsigned NOT NULL default '0',
    `tag_id` int(10) unsigned NOT NULL default '0',
    PRIMARY KEY (`post_id`,`tag_id`)
    ) TYPE=MyISAM;

    -- --------------------------------------------------------

    --
    -- Table structure for table `tags`
    --

    CREATE TABLE `tags` (
    `id` int(10) unsigned NOT NULL auto_increment,
    `tag` varchar(100) default NULL,
    PRIMARY KEY (`id`)
    ) TYPE=MyISAM;

테이블의 설정이 가능하면 Post 모델의 연관을 설정한다

예 6.8. /app/models/post.php hasAndBelongsToMany

  1. <?php
    class Post extends AppModel
    {
    var $name = 'Post';
    var $hasAndBelongsToMany = array('Tag' =>
    array('className' => 'Tag',
    'joinTable' => 'posts_tags',
    'foreignKey' => 'post_id',
    'associationForeignKey'=> 'tag_id',
    'conditions' => '',
    'order' => '',
    'limit' => '',
    'unique' => true,
    'finderQuery' => '',
    'deleteQuery' => '',
    )
    );
    }
    ?>

$hasAndBelongsToMany 배열은, Cake 가 Post 모델과 Tag 모델의 사이에 연관을 만들 때 사용한다. 배열 가각의 키에 연관을 더욱 상세하게 설정할 수 있다 :

  1. className (필수): 연관짓고 싶은 모델의 클래스명

    위 예제에서는, 'Tag' 모델의 클래스명을 지정하고 있다.

  2. joinTable: Cake 의 네이밍 규약에 따라서 없는 데이터베이스의 경우는 여기에서 설정한다. [복수형 모델1]_[복수형 모델2] 의 사전적인 순서이 되어 있지 않은 경우에는, 테이블명은 여기에서 지정한다.

  3. foreignKey: 현재의 모델을 가리키는 조인 테이블 내의 외부 키의 이름.

    Cake 의 네이밍 규약에 따라서 없는 데이터베이스의 경우는 여기에서 설정한다.

  4. associationForeignKey: 연관되어 있는 모델을 가리키는 외부 키의 이름.

  5. conditions: 관계를 정의한 SQL 조건의 일부

    입증된(approved) Tag 만을 연관 짓도록 Cake 에 명령할때, 다음과 같이 키를 설정하면 가능하다 : "Tag.approved = 1" 등

  6. order: 연관된 모델의 데이터 정렬

    연관된 모델을 특정 순서로 정렬하고 싶은 경우, 이 키로 SQL 의 order 방법으로 값을 설정한다, 예를 들면 : "Tag.tag DESC DESC" 등.

  7. limit: Cake 가 불러오는 관련 모델의 데이터의 최대 갯수

    불러올 연관된 Tags 의 갯수를 한정할 때 사용한다.

  8. unique: true 로 설정하면, 액세스와 쿼리 메소드에서, 관련된 객체가 중복되는 경우에는 무시한다.

    기본적으로, 관계가 별개로 되어 있는 경우에는, true 로 설정한다. 그렇다면, "Awesomeness" 이라는 tag는, Post 의 "Cake Model Associations" 에 한 번만 분배되고, 결과 배열 중에은, 한 번밖에 나타나지 않는다.

  9. finderQuery: 연관을 불러오기 위해 완전한 SQL 구문을 지정한다.

    여러 테이블에 의존하는 복잡한 연관의 경우에 활용할 수 있다. 만일 Cake 의 자동 연관을 사용할 수 없을 경우에, 이것으로 커스터마이징이 가능하다.

  10. deleteQuery: HABTM 모델간의 연관 데이터를 없애는 완전한 SQL 구문이다.

    Cake 의 삭제 방법이 바람직하지 않거나, 설정이 커스터마이징 되어 있는 경우에, 자신의 쿼리를 여기에 설정하여 삭제 동작 방법을 변경할 수 있다.

이제, Post 모델로 find() 또는 findAll() 을 호출하면, 연관되어 있는 Tag 모델도 함께 볼 수 있다 :

  1. $post = $this->Post->read(null, '2');
    print_r($post);

    //output:

    Array
    (
    [Post] => Array
    (
    [id] => 2
    [user_id] => 25
    [title] => Cake Model Associations
    [body] => Time saving, easy, and powerful.
    [created] => 2006-04-15 09:33:24
    [modified] => 2006-04-15 09:33:24
    [status] => 1
    )

    [Tag] => Array
    (
    [0] => Array
    (
    [id] => 247
    [tag] => CakePHP
    )

    [1] => Array
    (
    [id] => 256
    [tag] => Powerful Software
    )
    )
    )

6.4.6. 관련 데이터 저장#

관련된 모델로 작업할 때 기억해야할 중요한 주의사항 하나는, 모델 데이터의 저장은 언제나 대응하는 케이크 모델에 의해 행해져야 한다는 것이다. 만약 새로운 Post 하나와 이 Post 에 관련된 Comment 들을 저장한다면 저장 작업동안 Post 와 Comment 모두를 사용할 것이다.

만일 관련된 모델이 어느쪽도 아직 존재하지 않는다면 (예를 들어, 새로운 Post 와 관련된 Comment 를 동시에 저장하고 싶다), 먼저 주가되거나 부모의 모델을 먼저 저장할 필요가 있을 것이다. 이것이 어떻게 작동하는지 아이딩어를 얻으려면, 새로운 Post 와 관련된 Comment 의 저장을 다루는 PostsController 내의 액션을 갖고 있다고 상상하자. 아래 예로 든 액션은 하나의 Post 와 하나의 Comment 를 작성했다고 가정한다.

예 6.4.5 /app/controllers/posts_controller.php (일부)

    1. function add()
      {
      if (!empty($this->data))
      {
      //Post 데이터를 저장할 수 있다:
      //이것은 $this->data['Post'] 속에 있을 것이다

      $this->Post->save($this->data);

      //자, Comment 데이터를 저장할 필요가 있다.
      //하지만 먼저, 방금 저장한 Post 의 ID 를 알아 내야 한다...

      $post_id = $this->Post->getLastInsertId();

      //이제 데이터를 저장하고 comment 를 저장하기 위한 이 정보를 추가한다.

      $this->data['Comment']['post_id'] = $post_id;

      //Post hasMany Comments 관련 때문에
      //Post 모델을 통해 Comment 모델에 접근할 수 있다 :

      $this->Post->Comment->save($this->data);

      }
      }

    그러나, 만약, 부모 모델이 시스템에 이미 존재할 경우 (예를 들어, 존재하는 Post 에 Comment 를 추가한다), 저장하기 전에 부모모델의 ID 를 알 필요가 있다. URL 파라미터 , 또는 폼의 hidden 요소로 ID 를 넘겨 받을 수 있다...

    예 /app/controllers/posts_controller.php (일부)

    1. //이곳에서 URL 파라미터가 어떻게 사용되는지 볼 수 있다...
      function addComment($post_id)
      {
      if (!empty($this->data))
      {
      //$post_id 를 더욱 안전하게 만들어 싶었을 지 모른다,
      //하지만 이 작동 예제에서는 이것으로 족하다..

      $this->data['Comment']['post_id'] = $post_id;

      //Post hasMany Comments 관련 때문에
      //Post 모델을 통해 Comment 모델에 접근할 수 있다 :

      $this->Post->Comment->save($this->data);
      }
      }

    만약 ID 가 폼의 hidden 요소로 넘겨졌다면, 필드 (만일 html 헬퍼를 사용하고 있다면) 의 네이밍을 하고 싶을 수 있다. 그러면 적절한 곳에 다음과 같이 넘겨진 데이터를 넣으면 끝이다 :

    1. 만약 Post 의 ID 가 $post['Post']['id'] 에 있다면...

      <?php echo $html->hidden('Comment/post_id', array('value' => $post['Post']['id'])); ?>

    이렇게 되면, 부모 Post 모델의 ID 는 $this->data['Comment']['post_id'] 로 접근할 수 있고, 간단한 $this->Post->Comment->save($this->data) 의 호출 준비가 완전히 끝난다.

    이같은 기본적인 테크닉은 다수의 자식 모델을 저장할때, 단지 루프 (또한 Model::create() 를 사용하는 모델정보를 알고 있을 것을 기억하도록 한다) 내의 save() 의 호출을 넣는 것만으로 작동할것이다.

    요약하면, 만일 관련된 데이터를 저장하고 있다면 (belongsTo, hasOne, hasMany 관련), 요점은 부모 모델의 ID 를 얻는 것과 그 ID 를 자식 모델에 저장하는 것이다.

    6.4.7. hasAndBelongsToMany 의 저장#

    hasOne, belongsTo, hasMany 로 연관되는 모델을 저장하는 것은 대단히 간단하다. 관련되는 모델의 ID 와, 외부 키의 필드를 설정하면 된다. 그 후 모델의 save() 메소드를 호출하면, 연결된 모든 것들이 바르게 조합된다.

    hasAndBelongsToMany 는 좀더 막연하지만, 가능한 간단하게 해 보자. 예를 보는 동안, Tags 는 Posts 에 연관되어 있는 폼을 작성할 필요가 있다. posts 를 작성하는 폼을 작성하고, 이미 있는 Tags 의 목록과 연관시켜 본다.

    실제로는 새로운 태그를 작성하고 그 자리에서 포스트와 연관짓게 하고 싶을지도 모른다. 그러나 이번에는 간단하게 하기위해, 어떻게 연관 짓고 불로내는 지 정도만 나타낸다.

    Cake 에서 자신의 모델을 저장하고 싶은 경우, (HtmlHelper 를 사용하고 있으며) 태그명은 '모델/필드_이름' 과 같이 된다. 포스트를 작성하는 폼을 우선 만든다:

    예 6.9. posts 작성을 위한 /app/views/posts/add.thtml 형식

    1. <h1>Write a New Post</h1>
    2. <table>
    3. <tr>
    4. <td>Title:</td>
    5. <td><?php echo $html->input('Post/title')?></td>
    6. </tr>
    7. <tr>
    8. <td>Body:<td>
    9. <td><?php echo $html->textarea('Post/body')?></td>
    10. </tr>
    11. <tr>
    12. <td colspan="2">
    13. <?php echo $html->hidden('Post/user_id', array('value'=>$this->controller->Session->read('User.id')))?>
    14. <?php echo $html->hidden('Post/status' , array('value'=>'0'))?>
    15. <?php echo $html->submit('Save Post')?>
    16. </td>
    17. </tr>
    18. </table>

    이 폼은 Post 레코드를 작성할 수 있게 한다. 이번에는 하나 이상의 태그를 Post 에 지속시킬 수 있게 한다 :

    예 6.10. /app/views/posts/add.thtml (tag 연관 코드를 추가)

    1. <h1>Write a New Post</h1>
      <table>
      <tr>
      <td>Title:</td>
      <td><?php echo $html->input('Post/title')?></td>
      </tr>
      <tr>
      <td>Body:</td>
      <td><?php echo $html->textarea('Post/body')?></td>
      </tr>
      <tr>
      <td>Related Tags:</td>
      <td><?php echo $html->selectTag('Tag/Tag', $tags, null, array('multiple' => 'multiple')) ?>
      </td>
      </tr>
      <tr>
      <td colspan="2">
      <?php echo $html->hidden('Post/user_id', array('value'=>$this->controller->Session->read('User.id')))?>
      <?php echo $html->hidden('Post/status' , array('value'=>'0'))?>
      <?php echo $html->submit('Save Post')?>
      </td>
      </tr>
      </table>

    새로운 Post 와 게다가 관련된 Tags 를 저장하기 위해 컨트롤러에 $this->Post->save() 를 호출하려면, 필드명이 폼의 "Tag/Tag" 내에 없어선 안된다. 전송하는 데이터의 타입이 단일의 ID 나, 링크한 레코드의 ID 의 배열이어야 한다. 여기에는 multiple select 를 사용한 것이고, Tag/Tag 에 전송되는 데이타는 ID 의 배열이 된다.

    6.4.8. 애플리이션 작동중 bindModel() 과 unbindModel() 이용하여 관련 변경하기#

    애플리케이션을 구축할 때 이따금 예외적인 상황을 위해 모델의 관련을 변경하고 싶을 수 있다. 모델 파일의 관련 설정이 너무 많은 (또는 충분하지 않은) 정보를 주고 있다면, 다음의 find 작업전, 묶기 (bind) 와 풀기 (unbind) 이 두개의 모델 함수를 이용할 수 있다.

    어떻게 bindModel() 과 unbindModel() 이 작동하는지 보기 위해 몇개의 모델을 설정해보자. 다음과 같이 두개의 모델로 시작할 것이다 :

    예 6.14. leader.php 와 follower.php

    1. <?php
      class Leader extends AppModel
      {
      var $name = 'Leader';
      var $hasMany = array(
      'Follower' => array(
      'className' => 'Follower',
      'order' => 'Follower.rank'
      )
      );
      }
      ?>
      <?php
      class Follower extends AppModel
      {
      var $name = 'Follower';
      }
      ?>

    자, LeadersController, 에서 Leader (지도자) 모델의 find() 메쏘드를 이용하여 Follower (추종자) 와 관련된 Leader 를 불러 올 수 있다. 위에서 볼 수 있듯이, Leader 모델의 이 관련 배열은 "Leader hasMany Followers" 를 정의한다. 시험삼아 unbindModel() 을 이용하여 이 관련을 제거해보자.

    예 6.15. leaders_controller.php (일부)

    1. function someAction()
      {
      //Leaders 와, 관련된 Followers 를 불러온다.
    2. $this->Leader->findAll();

      //hasMany 관련을 없애보자...
      $this->Leader->unbindModel(array('hasMany' => array('Follower')));

      //자 find 함수를 이용하면 Follower 없이 Leader 를 불러올 것이다.
    3. $this->Leader->findAll();

      //주의 : unbindModel 은 오직 바로 다음의 find 함수에만 유효하다.
      //추가적인 find 함수 호출은 설정된 관련 정보를 이용하게된다.

      //이미 unbindModel() 후에 findAll() 을 사용하였기 때문에,
    4. //Leaders 와, 관련된 Followers 를 다시 불러온다...
      $this->Leader->findAll();
      }

    unbindModel() 함수는 다른 관련 설정과 유사하게 작동한다 : 관련 타입의 모델 클래스 네임을 바꾼다. unbindModel() 의 기본적 사용예는 :

    예 6.16. 일반적인 unbindModel() 예

    1. $this->Model->unbindModel(array('associationType' => array('associatedModelClassName')));

    자 이제 애플리케이션 작동중에 성공적으로 관련을 제거했다. 이젠 추가를 해보자. 아직 '주의 (Principle)' 가 없는 지도자 (Leader) 는 어떤 '주의'와 관련될 필요가 있다. Principle 모델 파일은 var $name 구문을 제외하고 텅 비어 있다. 애플리케이션 작동중에 Leader 에 어떤 Principle 을 관련지어 보자 (오직 다음 find 함수 호출을 위해서다) :

    예 6.17. leaders_controller.php (일부)

    1. funciton anotherAction()
      {
      //leader.php 모델 파일에는 Leader 에 Principle 이 hasMany 되어 있지 않다.
      //find 하면 Leader 만을 불러온다.
      $this->Leader->findAll();

      //bindModel() 를 사용하여 Principle 에 새로운 관련을 추가하자 :
      $this->Leader->bindModel(
      array('hasMany' => array(
      'Principle' => array(
      'className' => 'Principle'
      )
      )
      )
      );

      //자 이제 정확하게 관련지어 졌다.
    2. //하나의 find 함수를 사용하여 Principle 과 관련지어진 Leader 를 불러 올 수 있다:
      $this->Leader->findAll();
      }

    bindModel() 함수는 새로운 관련을 만들기 위해 조작된다. 하지만 애플리케이션 작동중에 설정된 관련 정보의 정렬 정보 또는 다른 파라미터를 변경할 때 유용한다.

    자 해보자. bindModel 의 기본적인 사용예는 일반적인 관련 배열 내부의 배열 (만들고자 하는 배열의 타입 뒤에 지정된 키 값) 을 캡슐화하는 것이다 :

    예 6.18. 일반적인 bindModel() 예

    1. $this->Model->bindModel(
      array('associationName' => array(
      'associatedModelClassName' => array(
      // 일반적인 관련 키값은 여기에...
      )
      )
      )
      );

    애플리케이션 작동중에 모델을 bind 하려면 테이블들은 정확하게 key 가 걸려 있어야 한다 (또는 관련 배열 속성의 설정) 는 점을 주의한다.


07. 컨트롤러 (Controllers)

  1. 7. 컨트롤러 (Controllers)
    1. 7.1. 컨트롤러란?
    2. 7.2. 컨트롤러 함수
      1. 7.2.1. 뷰와의 교류
      2. 7.2.2. 유저 리다이렉트
      3. 7.2.3. 컨트롤러 콜백
      4. 7.2.4. 기타 유용한 함수
    3. 7.3. 컨트롤러의 변수
    4. 7.4. 컨트롤러 파라미터

7. 컨트롤러 (Controllers)#

7.1. 컨트롤러란?#

컨트롤러는 애플리케이션의 특정 부분의 로직을 관리하기 위해 사용된다. 가장 일반적으로 단일 모델의 로직 관리로 사용한다. 예를 들면, 비디오 샵의 재고관리 사이트를 구축할 경우, VideoController (비디오 컨트롤러)와 RentalController (렌탈 컨트롤러) 로, 각각 비디오와 렌탈의 관리를 할 수 있다. Cake 에서 컨트롤러명은 항상 복수형이다.

애플리케이션의 컨트롤러는, Cake 의 AppController 클래스를 확장 (extend) 한 클래스이다. AppController 는 코어 라이브러리의 Controller 클래스를 확장하고 있다. 컨트롤러 내에서는 액션을 필요한 것만 포함시킬 수 있다. 액션은 뷰를 표시하기위해 웹 애플리케이션내에 사용되는 함수이다.

AppController 클래스는, /app/app_controller.php 에서 정의할 수 있고, 여러 컨트롤러에 공유하는 메소드를 포함한다. 그 AppController 자체도 표준 Cake 라이브러리에 포함되어 있는 Controller 클래스를 확장한 것이다.

액션은 컨트롤러의 하나의 기능이다. 들어온 페이지의 리퀘스트가 라우트를 설정하고 있는 경우, 디스패처에 의해 자동적으로 실행된다. 비디오 관리 예제로 되돌아 가, VideoController 는 view(), rent(), search() 액션이 필요할 것이다. 컨트롤러는 /app/controllers/videos_controller.php 내에 있으며, 아래와 같은 액션을 포함하고 있다 :

  1. class VideosController extends AppController
    {
    function view($id)
    {
    //액션의 로직은 여기에...
    }

    function rent($customer_id, $video_id)
    {
    //액션의 로직은 여기...
    }

    function search($query)
    {
    //액션의 로직은 여기...
    }
    }

이러한 액션에는, 아래와 같은 URL 예로 접속할 수 있다:

  1. http://www.example.com/videos/view/253
    http://www.example.com/videos/rent/5124/0-235253
    http://www.example.com/videos/search/hudsucker+proxy

이 페이지들은 어떻게 보여지는가? 각각의 액션에 대해 뷰를 정의해야한다 - 이에 관한 내용은 다음 장을 보면 되겠지만, 계속해서 이 장을 보기 바란다. 계속되는 섹션에서는 Cake 의 컨트롤러를 어떻게 활용하는지 보여준다. 특히, 데이터를 뷰에 어떻게 전달하는지, 유저를 어떻게 리다이렉트 하는지 등에 대해서 배운다.

7.2. 컨트롤러 함수#

이 섹션에서는 Cake 의 컨트롤러에서 자주 사용하는 함수에 대해서 다루지만, 전체 레퍼런스를 보기 위해선 http://api.cakephp.org 를 이용해야 하는 것을 주의한다.

7.2.1. 뷰와의 교류#
  • set
    • string $var
    • mixed $value

이 함수는 컨트롤러에서 뷰로 데이터를 전달하는 주요한 방법이다. 하나의 값, 배열 전체등, 어떤 것도 전달해 줄 수 있다. set() 으로 한번 설정하면, 뷰안에서 변수를 사용할 수 있게 된다. 컨트롤러에 set('color', 'blue') 를 실행하면, 뷰내에서 $color 변수를 사용할 수 있다.

  • validateErrors

저장에 실패하면 에러메시지를 반환한다.

  • validate

모델의 검증 룰에 기초하여, 모델의 데이터를 검증한니다. 검증(validation)에 관해서는, "12. Data Validation" 장 를 참조하기 바란다.

  • render
    • string $action
    • string $layout
    • string $file

컨트롤러의 액션 마지막에 render() 가 자동 호출되고, 액션명에 기초해 뷰가 자동 렌더링되기 때문에, 이 함수를 쓸 일은 그다지 많지 않을 것이다. 다른 방식으로, 컨트롤러 로직의 어떤 시점에서라도 뷰를 렌더링하기 위해 이 함수를 호출할 수 있다.

7.2.2. 유저 리다이렉트#
  • redirect
    • string $url

이 함수로 이동할 장소를 지정할 수 있다. Cake 의 내부 URL 이나, 완전한 URL (http://...) 을 지정할 수 있다.

  • flash
    • string $message
    • string $url
    • int $pause

이 함수는, $message 를 $pause 초동안 flash 레이아웃 ( app/views/layouts/flash.thtml 에서 찾을 수 있다. ) 속에서 보여주고, 유저가 지정한 $url 로 리다이렉트한다.

Cake 의 redirect() 와 flash() 함수는 exit() 호출을 포함하지 않는다. 만약 redirect() 또는 flash() 한 후 애플리케이션을 멈추고 싶을 경우, 그 후에 즉각 exit() 호출을 직접 해야 한다. exit() 보다 다른 값을 리턴받고 싶을 수 있는데, 그것은 그 상황에 따라 다르다. (예를 들어, 실행을 위한 콜백이 필요할 경우)

7.2.3. 컨트롤러 콜백#

Cake 의 컨트롤러에는 몇가지 콜백이 있으며, 컨트롤러의 중요한 함수 전후에 로직을 삽입할 수 있다. 이 기능을 사용하려면 컨트롤러 내에서 다음 함수들을 정의하면 된다.

  • beforeFilter

컨트롤러 액션 전에 매번 호출된다. 살아있는 세션의 체크, 역할 체크 등에 활용할 수 있다.

  • afterFilter

컨트롤러 액션 후에 매번 호출된다.

  • beforeRender

컨트롤러의 로직이 끝나고 뷰가 렌더링되기 직전에 호출된다.

7.2.4. 기타 유용한 함수#

이 것들은 Cake 의 객체 클래스의 함수지만, 컨트롤러내에서도 사용할 수 있다.

  • requestAction
    • string $url
    • array $extra

이 함수는 어느 곳에서든 컨트롤러의 액션을 호출할 수 있고, 렌더링된 뷰를 반환한다. $url 은 Cake URL (/controllername/actionname/params) 로 지정한다. $extra 배열 중에 'return' 키가 포함되어 있는 경우, 컨트롤러 액션을 위해서 AutoRender 가 자동으로 true 로 설정된다.

requestAction 을 사용하면 다른 컨트롤러 액션으로부터 데이터를 취득하거나, 컨트롤러부터 완전히 렌더링된 뷰를 취득할 수 있다.

먼저, 데이터는 컨트롤러에서 간단한 방법으로 얻을 수 있다. 데이터를 취득하기 원하는 뷰에서 requestAction를 사용하기만 하면 된다.

  1. // 간단한 컨트롤러

    class UsersController extends AppController
    {
    function getUserList()
    {
    return $this->User->findAll();
    }
    }

이 시스템의 유저 목록을 표시하는 간단한 테이블을 작성해야 한다. 다른 컨트롤러에 같은 코드를 반복하려면, requestAction() 을 사용하여 UsersController::getUserList() 로부터 데이터를 취득할 수 있다.

  1. class ProductsController extends AppController
    {
    function showUserProducts()
    {
    $this->set('users', $this->requestAction('/users/getUserList'));

    // 뷰의 $users 변수에는,
    // UsersController::getUserList() 로부터의 데이타가 들어간다
    }
    }

애플리케이션에서 고정적이지 않은 요소 (element) 가 여러번 필요할 경우, requestAction() 을 이용해 뷰 속에 주입 (inject) 가능하다. 단지 데이터를 UsersController::getUserList 로 받는 것 뿐만 아니라, 그 액션 안에서 렌더링된 뷰 (테이블 형태가 되어 있는) 를 취득하고 싶다고 하자. 이렇게 하면 코드의 반복없이 해결된다.

  1. class ProgramsController extends AppController
    {
    function viewAll()
    {
    $this->set('userTable', $this->requestAction('/users/getUserList', array('return')));

    // 이제, 이 액션의 뷰 내에서 $userTable 를 불러내고
    // /users/getUserList 에서 이용할 수 있는 렌더링된 뷰를 echo 할 수 있다.
    }
    }

requestAction() 을 사용하여 호출된 액션은 빈 레이아웃을 사용하여 렌더링되는 것에 주의한다. 이에 의해 레이아웃내에 레이아웃이 표시되는 위험을 걱정하지 않아도 된다.

requestAction() 함수는 AJAX 를 사용할 때에도 유용하다. AJAX 업데이트 중이나 전에, 작은 뷰 요소를 출력할 수 있다.

  • log
    • string $message
    • int $type = LOG_ERROR

웹 애플리케이션내에서 일어나는 여러가지 이벤트를 이 함수를 이용해 로깅 할 수 있다. 로그는 Cake 의 /tmp 디렉토리 내에 있다.

$type 이 PHP 상수 LOG_DEBUG 와 같은 경우, 디버그 메시지가 로그에 기록된다. 그외 타입은 에러메시지가 기록된다.

  1. // 컨트롤러 내부에서 log() 로 접속을 기록할 수 있다:

    $this->log('경고! 경고!');

    //접속 로그 :

    06-03-28 08:06:22 Error: 경고! 경고!

    $this->log("{$_SESSION['user']} 님이 로그인했습니다.", LOG_DEBUG);

    //접속 로그 :

    06-03-28 08:06:22 Debug: 바비님이 로그인했습니다.

  • postConditions
    • array $data

$this->data 에 전달해줄 수 있는 메소드로, 모델의 conditions 배열을 다시 반환한다.

예를 들어, person 의 검색 폼이 있다고 하자:

  1. // app/views/people/search.thtml:

    <?php echo $html->input('Person/last_name'); ?>

이 요소를 폼에 전송하면, $this->data 배열에는 다음과 입력된다:

  1. Array
    (
    [Person] => Array
    (
    [last_name] => Anderson
    )
    )

이 때에, postConditions() 을 사용하여 데이터를 모델내에 사용할 수 있는 형태로 만든다:

  1. // app/controllers/people_controller.php:

    $conditions = $this->postConditions($this->data);

    // 다음과 같은 배열을 생성한다:

    Array
    (
    [Person.last_name] => Anderson
    )

    // 이것은 모델의 find 조작에 사용할 수 있다:

    $this->Person->findAll($conditions);

7.3. 컨트롤러의 변수#

컨트롤러내의 몇가지 특별한 변수를 조작하면, Cake 의 몇가지의 추가 기능을 이용할 수 있다:

$name

PHP 4 는 현재 클래스명을 낙타표기법으로 반환해주지 않는다. 이 문제가 생겼을 경우, 이 변수에 바른 낙타표기법의 이름을 설정한다.

$uses

컨트롤러에 하나 보다 많은 여러 모델을 사용하는가? 예를 들면, FragglesController 는 자동으로 $this->Fraggle 를 로드하지만, $this->Smurf 와 같이 액세스하고 싶은 경우 컨트롤러에 다음과 같이 입력한다 :

  1. var $uses = array('Fraggle','Smurf');

지금까지 자동으로 사용 할 수 있었던 Fraggle 모델또한 $uses 배열에 포함시켜야 하는 것을 주의한다.

$helpers

이 변수를 사용하면, 뷰에 사용하는 헬퍼가 컨트롤러에 로드된다. HTML 헬퍼는 자동으로 로드되지만, 이 변수로 다른 것도 지정할 수 있다:

  1. var $helpers = array('Html','Ajax','Javascript');

이 변수를 사용하는 경우, Html 헬퍼도 $helpers 배열에 포함시켜야 한다. 보통은 디폴트로 사용할 수 있지만, $helpers 에 포함시키지 않으면 뷰에서 에러 메시지가 출력된다.

$layout

이 컨트롤러에 사용하고 싶은 레이아웃의 이름을 이 변수에 지정한다.

$autoRender

false 로 설정하면 자동 렌더링을 멈춘다.

$beforeFilter

액션이 호출될 때마다 (또는, 액션 코드가 실행되는 전에) 어떤 코드를 실행하고 싶은 경우 $beforeFilter 를 사용한다. 이 기능은 접근 제어 (AC) 에 대단히 편리하다 - 액션이 일어나기 전에 유저의 권한을 체크할 수 있다. 실행하고 싶은 컨트롤러의 액션을 배열을 사용하여 이 변수에 설정한다:

  1. class ProductsController extends AppController
    {
    var $beforeFilter = array('checkAccess');

    function checkAccess()
    {
    //유저의 ID를 체크하여 액세스하는 로직은 여기에....
    }

    function index()
    {
    //이 액션이 호출되면, checkAccess() 가 처음 호출된다.
    }
    }

$components

$helpers 와 $uses 와 같이, 필요한 컴포넌트를 불러 올 때 사용할 수 있다 :

  1. var $components = array('acl');

7.4. 컨트롤러 파라미터#

컨트롤러의 파라미터는 Cake 컨트롤러의 $this->params 로 액세스가능하다. 이 변수는, 컨트롤러에 데이타를 입력하고, 현재의 리퀘스트에 관한 정보로 액세스할 수 있게 한다. $this->params 의 가장 일반적인 사용 방법은, POST 또는 GET 방식에 의해 컨트롤러에 넘겨받은 정보로 액세스 한다

$this->data

는, HTML 헬퍼의 폼에 의해 컨트롤러에 전송된 POST 데이타를 다룰 때 사용한다.

  1. // 폼의 작성을 위해 HTML Helper 가 사용된다.

    $html->input('User/first_name');

    // HTML 이 표시되면 다음과 같다 :

    <input name="data[User][first_name]" value="" type="text" />

    // POST 로 전송되면
    // $this->data['User']['first_name'] 안에 나타난다

    Array
    (
    [data] => Array
    (
    [User] => Array
    (
    [username] => mrrogers
    [password] => myn3ighb0r
    [first_name] => Mister
    [last_name] => Rogers
    )

    )
    )

$this->params['form']

모든 폼의 POST 데이터가 여기에 저장된다. $ _FILES 에 있는 정보도 포함된다.

$this->params['bare']

현재의 레이아웃이 노출(bare)이라면, '1' 이 입력된다. 아니면, '0' 이 입력된다.

$this->params['ajax']

현재의 레이아웃이 ajax 라면, '1' 이 입력된다. 아니면, '0' 이 입력된다.

$this->params['controller']

리퀘스트를 다루는 현재의 컨트롤러명이 들어 간다. 예를 들면, URL /posts/view/1 이 호출된 경우, $this->params['controller'] 는 "posts" 가 된다.

$this->params['action']

리퀘스트를 다루는 현재의 액션명이 들어 간다. 예를 들면, URL /posts/view/1 이 호출된 경우, $ this->params['action'] 은, "view" 가 된다.

$this->params['pass']

현재 리퀘스트에 넘겨 받은 GET 쿼리가 들어 간다. 예를 들면, URL /posts/view/?var1=3&var2=4 가 호출된 경우, $this->params['pass'] 는 "?var1=3&var2=4" 가 된다.

$this->params['url']

리퀘스트된 현재의 URL이, 취득한 키-값(key-value pairs)과 함께 들어 간다. 예를 들면, URL /posts/view/?var1=3&var2=4 가 호출된 경우, $this->params['url'] 는 다음과 같다:

  1. [url] => Array
    (
    [url] => posts/view
    [var1] => 3
    [var2] => 4
    )


08. 뷰 (Views)

  1. 8. 뷰 (Views)
    1. 8.1. 뷰
      1. 8.1.1. 레이아웃 (layout)
      2. 8.1.2. 엘러먼드 (element)

8. 뷰 (Views)#

8.1. 뷰#

뷰란, 페이지의 템플릿을 말한다. 일반적으로, 액션에 기초해 이름지어진다. 예를 들면, PostsController::add() 에 대한 뷰는 /app/views/posts/add.thtml 이 된다. Cake 의 뷰는 꽤 간단한 PHP 파일이기 때문에 그 속에 PHP 코드를 넣을 수 있다. 대부분의 뷰 파일에는 HTML 이 포함되고 있으나, XML 이나 image 등 어떠한 데이타의 집합이 될 수도 있다.

뷰의 템플릿 파일 내에서, 일치하는 모델로 부터의 데이터를 사용할 수 있다. 이 데이터는 $data 라는 배열로 전달받게 된다. 컨트롤러의 set() 을 사용해 뷰에 전달받은 모든 데이터는 뷰에서 사용할 수 있다.

HTML 헬퍼는 기본값으로 모든 뷰 내에서 이용할 수 있고, 뷰 내에서 무엇보다도 일반적으로 사용되는 헬퍼이다. 폼 작성, 스크립트나 미디어 삽입, 데이타 검증의 연결와 보조에 이용할 수 있다. HTML 헬퍼에 관해서는, "헬퍼" 장의 섹션 1 을 참조한다.

뷰 내에서 이용할 수 있는 대부분의 함수는 헬퍼에 의해 준비된다. Cake 에는, 매우 좋은 헬퍼의 세트가 준비되어 있다. ( "10. 헬퍼" 장에서 다뤄진다 ) 또 사용자가 만든 헬퍼도 포함시킬 수 있다. 뷰에는 많은 로직을 포함시키지 않아야 하기 때문에, 뷰 클래스 속의 public 함수는 조금밖에 없다. 그 중 하나가 요긴한 함수인, renderElement() 이다. 이것은, 섹션 1.2. 에서 다룬다.

8.1.1. 레이아웃 (layout)#

레이아웃이란, 뷰의 주위의 부분을 둘러싸는 테두리 모든 코드를 포함한다. 모든 뷰에 표시하게 하고 싶은 것을 레이아웃에 포함시키면 된다.

레이아웃 파일은, /app/views/layouts 에 둘 수 있다. Cake 의 기본 레이아웃에서는, /app/views/layouts/default.thtml 에 새로운 레이아웃 파일을 두는 것으로 오버라이드 가능하다. 새로운 기본 레이아웃이 작성되면, 페이지가 렌더링되었을 때 컨트롤러가 작성한 뷰의 코드가 기본 레이아웃 내에 배치된다.

레이아웃을 작성하면 Cake 에 컨트롤러가 작성한 뷰의 코드를 어디에 두는지 지정해야 한다. 레이아웃 속에 $content_for_layout 이 위치하도록 한다. (옵션으로 $title_for_layout 도 지정할 수 있다. ) 다음는 기본 레이아웃의 예이다:

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

  2. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

  3. <html xmlns="http://www.w3.org/1999/xhtml">

  4. <head>

  5. <title><?php echo $title_for_layout?></title>

  6. <link rel="shortcut icon" href="favicon.ico" type="image/x-icon">

  7. </head>

  8. <body>

  9. <!-- 만일, 모든 뷰에 메뉴등을 보여주려면, 여기에 포함시킨다 -->

  10. <div id="header">

  11. <div id="menu">...</div>

  12. </div>

    <!-- 여기에 뷰에 표시될 내용이 들어 간다 -->
    <?php echo $content_for_layout ?>

    <!-- 각 페이지에 나타날 푸터를 추가한다 -->
    <div id="footer">...</div>

    </body>
    </html>

레이아웃에 타이틀을 설정하려면, 컨트롤러에 $pageTitle 컨트롤러 변수를 사용하는 것이 가장 편하다.

  1. class UsersController extends AppController
    {
    function viewActive()
    {
    $this->pageTitle = 'View Active Users';
    }
    }

Cake 로 만든 사이트에서 원하는 레이아웃을 여러 개 만들 수 있다. 그것을 app/views/layouts 디렉토리로 넣는다. 컨트롤러의 액션 내에 $layout 변수나, setLayout() 함수를 사용해 바꿀 수 있다.

예를 들면, 사이트에 작은 광고 배너 공간의 섹션을 포함시키고 싶을 경우, 작은 광고 공간이 있는 레이아웃를 작성한다. 그것을 모든 컨트롤러 액션의 레이아웃으로 지정하는 경우, 다음 코드와 같다:

  1. var $layout = 'default_small_ad';

8.1.2. 엘러먼드 (element)#

많은 애플리케이션은 페이지에서 페이지로 이동할때 마다 보여주는 코드 블럭을 가지고 있으며, 가끔은 레이아웃에 다른 곳에 배치하기도 한다. Cake 는 재이용할 필요가 있는 사이트의 반복 부분을 도와 준다. 이 재이용 가능한 부분을, 엘러먼트(element)라고 부른다. 광고, 도움말 박스, 네비게이션 컨트롤, 기타 메뉴, 콜아웃 등은 Cake 에서 엘리먼트로서 장착된다. 엘러먼트는 기본적으로 다른 뷰에 포함시킬 수 있으며, 이것을 미니-뷰(mini-view)라 한다.

엘러먼트는, /app/views/elements/ 폴더에 넣으며, .thtml 라는 파일 확장자가 붙는다.

엘러먼트는 기본적으로 어떤 데이터로도 접근 불가능하다. 데이터로 접근 가능하도록 하려면, 파라미터로 이름을 붙이고 배열 형태로 넣다.

예 8.1. 파라미터 없이 엘러먼트 호출

  1. <?php echo $this->renderElement('helpbox'); ?>

예 8.2. 데이터 배열를 통한 엘러먼트 호출

  1. <?php echo

  2. $this->renderElement('helpbox', array("helptext" => "오, 이 문자는 엄청나게 도움된다"));

    ?>

엘러먼트 파일에서는 전달받은 배열 키명을 변수명으로 사용할 수 있다 (컨트롤러의 set() 을 뷰에 비교해 동작이 매우 비슷하다). 위의 예에서는, /app/views/elements/helpbox.thtml 파일은, $helptext 변수로 사용할 수 있다. 물론, 엘러먼트에 배열을 전달하는 것이 유용하다.

엘러먼트를 사용하면 뷰를 읽기 쉽게 하고, 반복적으로 표시되는 요소를 당신의 파일 안에 넣을 수 있다. 또한, 엘러먼트는 웹 사이트내에 재이용 가능한 내용 일부을 만들 수 있게 해준다.


09. 컴포넌트 (Components)

  1. 9. 컴포넌트 (Components)
    1. 9.1. 프레젠테이션 (presentation)
    2. 9.2. 작성 방법
    3. 9.3. 컴포넌트 공개하기

9. 컴포넌트 (Components)#

9.1. 프레젠테이션 (presentation)#

컴포넌트는 특정한 상황에서 컨트롤러를 보조하기 위해 사용한다. 특별한 추가 기능은 Cake 의 핵심 라이브러리를 확장하는 것이 아니라 컴포넌트 속에 넣어는다.

olle 이란 사람이 IRC 채널에 말했다 : 컴포넌트라는 것은 공유할 수 있는 작은 "controllerette (역자주: ~ette 는 모조, 대용품의 뜻이 있다)" 이다. 이것은 좋은 정의라 생각한다. 실제로 주요 목표는 재이용성이다. 컨트롤러에는 컴포넌트가 있고, 뷰에는 헬퍼가 있다. 이 둘의 큰 차이는 '컴포넌트가 비즈니스 로직을 캡슐화하는 것에 반해, 헬퍼는, 프레젠테이션 로직을 캡슐화한다'는 것이다. 사실 이 점은 매우 중요하며 재이용성을 실현하고자 하는 새로운 Baker 에게 혼란을 준다 : 나는 X 라는 것에 도전하고 있다, 이것은 컴포넌트인가? 헬퍼인가? 대답은 대단히 간단하다. X는 무엇을 하는 것인가? 비즈니스 로직? 그렇지 않으면 프레젠테이션 로직? 어쩌면 둘 다? 만일 그것이 비즈니스 로직이라면, 컴포넌트 양쪽다 다. 만일 그것이 프레젠테이션 로직이라면, 헬퍼가 된다. 둘 다라면 ...뭐, 그것은 컴포넌트와 헬퍼 양쪽 다이다. 후반부의 예로 인증 시스템이 나온다. 로그인, 로그 웃, 접근 제한, 유저의 조작에 관한 권한 테스트(액션: edit, add, del .. or a url)를 추가하고 싶다면, 그것은 비즈니스 로직이다. 따라서 이 인증 시스템은 컴포넌트로 해야 한다. 그렇지만, 유저가 로그인 했을 때 메인 메뉴에 몇가지 항목을 늘리고 싶다면, 이것은 프레젠테이션 로직이다.

9.2. 작성 방법#

컴포넌트를 작성하려면, 파일을 app/controllers/components/ 디렉토리에 추가한다.

만일, foo.php 를 만들었다면, 파일 중에, 파일명에 대응하는 클래스를 정의해야 한다 (파일명에, 'Component' 를 덧붙인다.).이 경우 다음과 같이 작성할 수 있을 것이다 :

예 9.1. 간단한 컴포넌트

  1. class FooComponent extends Object

  2. {

  3. var $someVar = null;

  4. var $controller = true;

  6. function startup(&$controller)

  7. {

  8. // 이 메소드는, 이 컴포넌트가 로딩되었을때 컨트롤러에 참조를 갖는다.

  9. // 컨트롤러의 초기화를 여기서 한다.

  10. }

  12. function doFoo()

  13. {

  14. $this->someVar = 'foo';

  15. }

  16. }

컴포넌트를 사용하려면, 콘트롤러를 정의할 때 아래의 코드를 추가해야 한다 :

  1. var $components = array('Foo');

콘트롤러내에서 아래처럼 사용할 수 있다:

  1. $this->Foo->doFoo();

컴포넌트는, 컴포넌트를 로딩했던 컨트롤러에 startup() 메소드에 접근 가능하다. 이 메소드는, Controller::beforeFilter() 의 직후에 호출된다. 이것에 의해 beforeFilter 메소드에 Component의 속성을 설정할 수 있게 된다. Component는, startup() 메소드에 기초해 동작한다. 컴포넌트의 내부에서 모델을 이용하려면, 아래와 같이 새로운 인스턴스를 작성할 수 있다:

  1. $foo =& new Foo();

컴포넌트 안에서, 다른 컴포넌트를 사용하는 것도 가하다. 사용하고 싶은 컴포넌트를 정의하기만 하면 된다. 아래의 예에서는 세션 컴포넌트를 정의하고 있다.

  1. var $components = array('Session');

9.3. 컴포넌트 공개하기#

컴포넌트가 다른 사람에게도 도움이 된다고 생각하면, CakeForge 에 추가해 주기 바란다. 커뮤니티에 도움이 되는 컴포는트는, 핵심 라이브러리 배포에 포함될 수 있다.

또한 스니펫 아카이브(snippet archive) 에서도 유저가 보낸 컴포넌트가 등록된다.


10. 헬퍼 (Helpers)

  1. 10. 헬퍼 (Helpers)
    1. 10.1. 헬퍼
      1. 10.1.1. HTML
        1. 10.1.1.1. 소개
        2. 10.1.1.2. 잘 포맷된 요소(Well-Formatted elements) 삽입하기
        3. 10.1.1.3. 폼과 자료 검증
      2. 10.1.2. AJAX
      3. 10.1.3. 자바스크립트
      4. 10.1.4. 숫자
      5. 10.1.5. 문자
      6. 10.1.6. 시간
    2. 10.2. 자신의 헬퍼 작성하기
      1. 10.2.1. Cake 헬퍼 클래스 확장 (extend)
      2. 10.2.2. 다른 헬퍼의 포함
      3. 10.2.3. 커스텀 헬퍼 사용
      4. 10.2.4. 기여하기

10. 헬퍼 (Helpers)#

10.1. 헬퍼#

헬퍼는 편리한 방법으로 데이터를 가공하고 표현하기 위해 뷰에서 일반적으로 필요한 함수를 제공하는 것이다

10.1.1. HTML#
10.1.1.1. 소개#

HTML 헬퍼는, 단조로운 일을 줄이고, 좀 더 빠르게 개발할 수 있도록 하는 Cake 의 방식의 하나다. HTML 헬퍼에게는 두가지 주요 목표가 있다. 그것은, 자주 사용하는 HTML 코드를 삽입할 수 있도록 하는 것, 그리고 웹 폼을 빠르고 간단히 작성할 수 있도록 하는 것이다. 계속되는 섹션에서, 헬퍼의 주요 함수에 대해서 설명하지만, 최종적 레퍼런스로, http://api.cakephp.org 를 읽어야 하는 것을 꼭 기억하라.

HTML 헬퍼내의 함수가 사용하고 있는 HTML 태그의 정의는, tags.ini.php 라는 파일에 있다. Cake 의 코어 설정으로 tags.ini.php 가 있지만, 변경을 더하고 싶은 경우는, /cake/config/tags.ini.php 의 복사본를 만들고, 자신의 /app/config/ 폴더에 넣는다. HTML 헬퍼는 리퀘스트된 태그의 생성에 이 파일의 태그 정의를 사용한다. tags.ini.php 파일을 변경하면 사이트 전체가 변경이 되기 때문에, HTML 헬퍼에 뷰의 코드를 작성하면 편리하다.

부가적으로, 코어의 config 파일( /app/config/core.php ) 내에, AUTO_OUTPUT 이 true 로 설정되면, 헬퍼는 값을 반환하는 것은 아니고, 자동적으로 태그를 출력하게 된다. 이것은, short tag (<?= ?>) 의 사용이나 많은 echo() 호출 등을 뷰 코드에 넣기 싫어 하는 이들을 위해서 구현되었다. $return 파라미터를 사용하면, 코어의 config 의 설정을 강제적으로 오브라이드할 수 있다. AUTO_OUTPUT 의 설정으로 관계되어 없이, HTML 헬퍼가 HTML 코드를 반환하도록 하고 싶은 경우, $return 을 true 로 설정한다.

또한, HTML 헬퍼의 기능에는, $htmlAttributes 파라미터가 포함되고 있다. 이것은, 태그에 추가 속성을 더 할 수 있도록 하는 것이다. 예를 들면, class 속성을 추가하고 싶은 태그가 있는 경우, 그것을 $htmlAttribute 값을 사용해 아래와 같이 지정할 수 있다.

  1. array('class'=>'someClass')

10.1.1.2. 잘 포맷된 요소 (Well-Formatted elements) 삽입하기#

HTML 코드 속에 잘 포맷 되었고, 여러 번 반복하는 요소를 Cake 를 사용해 삽입하고 싶은 경우, HTML 헬퍼가 그 작업에 적격이다. 이 헬퍼 중에는, 미디어, 테이블을 만드는 데 도움이 되는 함수나, PHP 의 배열에 기초하고, ul 요소의 리스트를 만드는 guiListTree 등도 있다.

  • charset
    • string $charset
    • boolean $return

이것은, charset 의 meta 태그를 생성할 때 사용한다.

  • css
    • string $path
    • string $rel = 'stylesheet'
    • array $htmlAttributes
    • boolean $return = false

CSS 스타일 시트에의 링크를 만듭니다. $rel 파라미터에, 태그의 rel=값 을 지정할 수 있다.

  • image
    • string $path
    • array $htmlAttributes
    • boolean $return = false

image 태그를 표시합니다. 이 함수로 리턴된 코드는, link() 함수의 입력으로서 사용할 수 있기 때문에, 링크된 이미지를 자동 생성할 수 있다.

  • link
    • string $title
    • string $url
    • array $htmlAttributes
    • string $confirmMessage = false
    • boolean $escapeTitle = true
    • boolean $return = false

뷰에 링크를 작성할때 이 함수를 사용해 한다. 링크를 클릭 했을 때 한 번, 자바스크립트로 확인 메세지를 표시시킬 경우, $confirmMessage 를 이용한다. 예를 들면, 어떤 오브젝트를 삭제하는 링크이면, 링크 앞의 액션을 실행하기 전에 "정말로 삭제하시겠습니까?" 의 메세지를 나타낼 수 있다. 만일, $title 변수에 전달되는 데이터를 HTML 헬퍼가 escape 하도록 하고 싶은 경우에는, $escapeTitle 를 true 로 설정하면 된다.

  • tableHeaders
    • array $names
    • array $tr_options
    • array $th_options

포맷된 테이블을 작성할 때 사용한다.

  • tableCells
    • array $data
    • array $odd_tr_options
    • array $even_tr_options

포맷된 테이블의 셀을 작성할 때 사용한다.

  • guiListTree
    • array $data
    • array $htmlAttributes
    • string $bodyKey = 'body'
    • string $childrenKey = 'children'
    • boolean $return = false

배열에서 ul 요소의 리스트 트리를 생성한다.

10.1.1.3. 폼과 자료 검증#

HTML 헬퍼는, 뷰 내에 폼의 코드를 신속하게 만들 때에 대단한 빛을 발한다. 폼 태그를 전부 생성하고, 에러가 일어난 경우에는 자동적으로 값을 채워 되돌리고, 에러 메세지를 해당하는 장소에 표시한다. 이해하기 쉽도록, 짧은 예를 보도록 하자. 애플리케이션에 Note 모델이 있고, Note 의 오브젝트(데이터)를 추가, 편집하기 위한 컨트롤러 로직과 뷰를 작성한다고 하자. NotesController 에서, edit 액션은 다음과 같이 될 것이다 :

예10.1. NotesController 의 Edit 액션 내부

  1. function edit($id)
    {
    //먼저 폼데이터가 이 액션에 의해
    //    전송되었는지 아닌지를 체크한다.
    if (!empty($this->data['Note']))
    {
    //여기서 폼데이터의 검증을 한다. (12장 참조)
    //그리고 저장한다.
    if ($this->Note->save($this->data['Note']))
    {
    //저장이 잘 되었을 경우,
    //유저를 적당한 장소로 이동시킨다.
    $this->flash('Your information has been saved.', '/notes/edit/' . $id);
    exit();
    }
    else
    {

    //해당하는 필드 위치에 에러 메시지를 생성한다.
    //save 가 작용하기 때문에, 실제로는 필요하지 않지만, 이것은 예이다.
    //    save 하지 않을 경우, $this->Note->validates($this->data['Note']); 를 호출하도록 한다.
    //그리고 아래 메소드를 사용하여,     tagErrorMsg() 헬퍼 메소드에 값을 출력한다.
    $this->validateErrors($this->Note);

    //그리고 edit view 코드를 출력한다.
    $this->render();
    }
    }

    // 아직 폼 데이터를 아무것도 전송 받지 않았을 경우, 편집하고 싶은 note 를 받고
    // 그 정보를 뷰에 전달한다
    $this->set('note', $this->Note->find("id = $id"));
    $this->render();
    }

컨트롤러를 설정해 보았기에, 뷰 코드( app/views/notes/edit.thtml 에 있다. )를 보자. Note 모델은 지금의 시점에는 꽤 간단하고, id 와 송신자의 id, 본문(body)밖에 없다. 이 뷰 코드는, Note 데이타를 표시하고, 유저가 새로운 값을 입력하고, 모델에 데이타를 저장할 수 있게 한다.

HTML 헬퍼는 기본적으로 모든 뷰에서 사용할 수 있고, $html 로 액세스한다.

특히, 폼의 중요한 부분의 테이블을 보자 :

예10.2. Edit view의 코드 (edit.thtml) 샘플

  1. <!-- 이 태그는 폼태그를 작성한다 -->

    <?php echo $html->formTag('/notes/edit/' . $html->tagValue('Note/id')?>

    <table cellpadding="10" cellspacing="0">
    <tr>
    <td align="right">Body: </td>
    <td>

    <!--     여기에 HTML 헬퍼를 사용하여 text area 태그,
    컨트롤러에 의해 작성된 $note 변수,
    편집하고 있는 데이터 등을 표시한다 -->
    <?php echo
    $html->textarea('Note/body', array('cols'=>'60', 'rows'=>'10'));
    ?>
    <?php echo $html->tagErrorMsg('Note/body',
    '이 노트에 내용을 입력해 주십시오.') ?>
    </td>
    </tr>
    <tr>
    <td></td>
    <td>

    <!-- 또한 HTML helper 로     테이블 안에
    hidden 태그를 넣을 수 있다 -->

    <?php echo $html->hidden('Note/id')?>
    <?php echo $html->hidden('note/submitter_id', $this->controller->Session->read('User.id'))?>
    </td>
    </tr>
    </table>

    <!-- 끝으로, 전속 버튼 -->
    <?php echo $html->submit()?>

    </form>

태그를 생성하는 함수의 대부분으로는, ( tagErrorMsg 도 포함시킨다) $fieldName 를 지정해야 한다. 이 $fieldName 에 의해 Cake 는 전송 받은 데이터를 이해하고, 데이터의 저장, 검증을 올바르게 할 수 있다. $fieldName 파라미터에 폼으로 넘겨받는 문자열은, "모델명/필드명" 형식이다. Note 에 이번에 title 필드를 추가하고 싶다면, 뷰에 아래와 같은 코드를 더한다:

  1. <?php echo $html->input('Note/title') ?>
  2. <?php echo $html->tagErrorMsg('Note/title', '이 노트에 제목을 입력해 주십시오.')?>

tagErrorMsg() 함수에 표시되는 에러 메세지는, CSS 로 스타일링하기 쉽도록, <div class="error_message"></div> 로 둘러 쌓여 있다.

HTML 헬퍼를 생성할 수 있는 폼 태그에는 다음과 같다 (대부분 직접적인 이름으로 되어 있다).:

  • submit
    • string $buttonCaption
    • array $htmlAttributes
    • boolean $return = false

  • password
    • string $fieldName
    • array $htmlAttributes
    • boolean $return = false

  • textarea
    • string $fieldName
    • array $htmlAttributes
    • boolean $return = false

  • checkbox
    • string $fieldName
    • array $htmlAttributes
    • boolean $return = false

  • file
    • string $fieldName
    • array $htmlAttributes
    • boolean $return = false

  • hidden
    • string $fieldName
    • array $htmlAttributes
    • boolean $return = false

  • input
    • string $fieldName
    • array $htmlAttributes
    • boolean $return = false

  • radio
    • string $fieldName
    • array $options
    • array $inbetween
    • array $htmlAttributes
    • boolean $return = false

  • tagErrorMsg
    • string $fieldName
    • string $message

HTML 헬퍼에게는, 날짜에 관한 옵션 태그 생성을 구하는 함수 세트가 있다. $tagName 파라미터는, $fieldName 파라미터와 같은 방법으로 다룬다. 이 날짜 옵션 태그에 관련된 필드명을 지정한다. 데이터가 처리되면, 콘트롤러 내에서는 필드명이 끝나는 날짜의 일부분의 이름(year,month,day...)이 연결된 것으로 보인다. 일례로서, Note 가 deadline 이라는 날짜 필드를 갖고 있고, dayOptionTag 의 $tagName 파라미터가 'note/deadline' 로 설정 되어 있으면, 폼이 컨트롤러 액션에 전송되었을 때 날짜 데이터가 $params 변수에 나타난다:

  1. $this->data['Note']['deadline_day']

현재의 데이터베이스 설정에 따라, 이 정보를 시간의 데이터로서 연결해 맞출 수 있다. 이 코드는 데이터를 저장하기 직전에 위치하고, $data 배열 중에 저장되며 그 정보가 모델에 저장된다.

예10.3. 모델을 저장하기 전, time 데이터를 연결한다 ( NotesController 에서 발췌)

  1. function edit($id)
    {
    //    먼저 폼데이터가 이 액션에 의해 전송되었는지 아닌지를 체크한다.
    if (!empty($this->data['Note']))
    {

    //저장용으로 시간 데이터를 연결...
    $this->data['Note']['deadline'] =
    $this->data['Note']['deadline_year'] . "-" .
    $this->data['Note']['deadline_month'] . "-" .
    $this->data['Note']['deadline_day'];

    //여기에 폼 데이터를 검증    (10장 참조)하고 저장한다.
    if ($this->Note->save($this->data['Note']))
    {

    ...

  1. dayOptionTag ($tagName, $value=null, $selected=null, $optionAttr=null)

  2. yearOptionTag ($tagName, $value=null, $minYear=null, $maxYear=null, $selected=null, $optionAttr=null)

  3. monthOptionTag ($tagName, $value=null, $selected=null, $optionAttr=null)

  4. hourOptionTag ($tagName, $value=null, $format24Hours=false, $selected=null, $optionAttr=null)

  5. minuteOptionTag ($tagName, $value=null, $selected=null, $optionAttr=null)

  6. meridianOptionTag ($tagName, $value=null, $selected=null, $optionAttr=null)

  7. dateTimeOptionTag ($tagName, $dateFormat= 'DMY', $timeFormat= '12', $selected=null, $optionAttr=null)

10.1.2. AJAX#

Cake 의 ajax 헬퍼는, Ajax 의 조작과 클라이언트 사이드의 효과를 위해서, 굉장이 유명한 Prototype 과 script.aculo.us 라이브러리를 이용한다. 이 헬퍼를 사용하려면, http://script.aculo.us 에서 최신 버전의 자바스크립트 라이브러리를 다운 받아, /app/webroot/js/ 에 넣는다. 또 ajax 헬퍼를 사용할 예정인 모든 뷰에, 이러한 라이브러리를 include 한다.

이 헬퍼의 대부분의 함수에는, 특별한 $options 배열이 파라미터로서 필요하다. 이 배열은 ajax 조작에 관한 여러가지 사항을 지정할때 사용한다. 지정할 수 있는 여러가지 값에는 다음과 같다.

예 10.4. AjaxHelper $options Keys

  1. /* General Options */

    $options['url'] //     호출하기 원하는 액션의 URL.

    $options['frequency'] //     remoteTimer() 또는
    // observeField() 가 체크를 실행하기까지의 초단위 시간.

    $options['update'] //     Ajax 조작 결과를
    // 업데이트하고 싶은 요소의 DOM ID.

    $options['with'] //     Ajax 의 폼 전송에
    // serialize 하고, 전송하는 폼 요소의 DOM ID.

    $options['type'] //     'asynchronous' (기본) 또는, 'synchronous'
    // 옵션 선택.

    /*     콜백:XMLHttpReques 프로세스 동안
    여러번 실행되는 JS 코드 */

    $options['loading'] //     브라우저에 원격 문서의 데이터가
    // 로딩중일때 실행되는 JS 코드.

    $options['loaded'] //     브라우저가 원격 문서에 로딩을
    // 마쳤을 때 실행되는 JS 코드.

    $options['interactive'] //     로딩이 마치지 않아도, 사용자가 원격 문서와
    // 송수신 가능할 때 실행되는 JS 코드.

    $options['complete'] //     XMLHttpRequest 가 완료되었을때
    // 호출되는 JS 코드.

    $options['confirm'] //     XMLHttpRequest 액션이 시작되기 전에
    // cconfirm 대화상자에 표시되는 텍스트.

    $options['condition'] //     XMLHttpRequest 를 초기화하기 전에 충족되어야 하는
    // JS 조건

    $options['before'] //     리퀘스트가 초기화되기 전에 호출되는 JS 코드.

    $options['after'] //     리퀘스트가 초기화되고,
    // 'loading' 이 시작되는 직전에 호출되는 JS code.

다음은 Cake 에서 Ajax 를 빠르고 간단하게 작성하기 위한 헬퍼 함수들이다 :

  • link
    • string $title
    • string $href
    • array $options
    • boolean $confirm
    • boolean $escapeTitle

링크를 텍스트 $title 를 표시한다. $options['url'] 에 있는 원격문서를 로딩하고 $options['update'] 로 지정된 DOM 요소를 업데이트한다. 이 함수에 콜백을 사용할 수 있다.

  • remoteFunction
    • array $options

이 함수는 원격 호출이 필요한 자바스크립트를 만든다. 주로, linkToRemote 의 헬퍼로 사용된다. 커스터마이징한 스크립트를 생성하는 것은 아니면, 이 함수는 자주 사용하지 않는다.

  • remoteTimer
    • array $options

정기적으로 $options['url'] 로 지정된 액션을 $options['frequency'] (기본값은 10)초마다 호출한다. 보통, 리모트 호출의 결과로, 특정 div ($options['update'] 으로 지정)을 업데이트할 때 사용한다. 이 함수에서는 콜백을 사용할 수 있다.

  • form
    • string $action
    • string $type
    • array $options

일반적으로 리로드가 필요한 POST 전송 대신, XMLHttpRequest 를 사용하여 $action 에 지정된 액션을 백그라운드에 전송하는 폼 태그를 반환한다. 이 폼으로 전송된 데이터는, 보통 폼 데이타와 같이 행동한다 ($ this->param['form'] 로 이용할 수 있다). $options['update'] 로 지정된 DOM 요소가 원격 문서의 결과로 업데이트 된다. 이 함수는 콜백을 사용할 수 있다.

  • observeField
    • string $field_id
    • array $options

$field_id 로 지정된 DOM ID 의 필드를 ($options['frequency'] 초마다) 관찰하고, 내용이 변경되었을 때 $options['url'] 의 액션을 실행한다. ID $options['update'] 또는 $options['with'] 로 폼 요소를 지정하고, DOM 요소를 업데이트하는 것도 가능하다. 이 함수는 콜백을 사용할 수 있다.

  • observeForm
    • string $form_id
    • array $options

observeField() 와 같이 작동한다. 이 함수는 지정된 폼의 모든 요소 외에는 관찰하지 않는다.

  • autoComplete
    • string $field
    • string $url
    • array $options

ID $field 를 자동 완성이 붙은 텍스트 필드로 표시한다. $url 의 액션은, 자동 완성의 용어 리스트를 리턴해야 한다 : 기본적으로 액션은 자동 완성의 용어를 ul 요소 (<ul></ul>) 의 리스트 항목에서 내는 것이 필요합니다. 블로그에 작성한 제목을 불러내는 자동 완성 필드를 작성하고 싶은 경우, 컨트롤러의 액션은 다음과 같다:

  1. function autocomplete ()

  2. {

  3. $this->set('posts',

  4. $this->Post->findAll(

  5. "subject LIKE '{$this->data['Post']['subject']}'")

  6. );

  7. $this->layout = "ajax";

  8. }

위 autocomplete() 액션에 대한 뷰는 이와 같다 :

  1. <ul>
    <?php foreach($posts as $post): ?>
    <li><?php echo $post['Post']['subject']; ?></li>
    <?php endforeach; ?>
    </ul>

뷰내의 실제 자동 완성 필드는 아래와 같다:

  1. <form action="/users/index" method="POST">
    <?php echo $ajax->autoComplete('Post/subject', '/posts/autoComplete')?>
    <?php echo $html->submit('View Post')?>
    </form>

autoComplete() 함수는 텍스트 필드와 액션이 작성한 자동 완성 용어 리스트를 표시하기 위해 사용하는 몇가지의 div 의 표시에 이 정보를 사용한다. 또 아래와 같이 하여 뷰의 스타일도 설정할 수 있다 :

  1. <style type="text/css">

    div.auto_complete {
    position :absolute;
    width :250px;
    background-color :white;
    border :1px solid #888;
    margin :0px;
    padding :0px;
    }

    li.selected { background-color: #ffb; }

    </style>

  • drag
    • string $id
    • array $options

ID 가 $id 인 DOM 요소를 드래그할 수 있게 만든다. $options 을 사용하면, 기타 추가 사항도 지정할 수 있다.

  1. // (버전 번호는, script.aculo.us 의 버전이다. )

    $options['handle'] // (v1.0) 임베디드 핸들에 의해서만 드래그할 수 있도록
    // 할 것인지 설정한다. 값은 반드시
    // 요소의 레퍼런스나 요소의 ID 여야한다.

    $options['handle'] // (V1.5) 앞서 말한 대로, 값은 CSS 의 클래스치를 나타내는 문자열이다.
    // 최초의 아들(child)/손자(grandchild)/등이다.
    // 이 CSS 클래스 값을 가진 요소내의 요소는, 핸들로서 이용된다.

    $options['revert'] // V1.0) true 로 설정하면, 드래그가 끝났을 때
    // 요소(element)는 원래의 위치를 반환한다.

    $options['revert'] // (V1.5) Revert 도 임의의 함수 레퍼런스가 될 수 있고,
    // 드래그가 끝났을 때 호출된다.

    $options['constraint'] // horizontal 또는 vertical 로 설정되면,
    // 드래그 방향이 횡방향 또는
    // 종방향으로 제한된다.

  • drop
    • string $id
    • array $options

ID $id 로 지정된 DOM 요소를 드롭할 수 있게 한다. $options 를 사용해 지정할 수 있는 몇가지 추가 사항이 있다.

  1. $options['accept'] // CSS 클래스를 나타낸 문자열 또는 자바스크립트의 배열을 accept 에 지정한다.
    // 드롭 가능한 것은, 드래그할 수 있도록 되어 있다.
    // CSS 클래스를 하나 이상 갖고 있는 것만을 받아들인다.

    $options['containment'] // 드롭 가능한 요소는, 지정된 요소(또는 요소 ID)로 제한되어 있는 경우,
    // 그 드래그할 수 있는 요소만을 받아들인다.
    // 단일 요소 또는 요소의 JS 배열일 수 있다.

    $options['overlap'] // horizontal 또는 vertical 로 설정될 때,
    // 지정된 방향으로 50% 이상 겹친 경우에,
    // 드롭 가능한 것은 드래그할 수 있는 요소에 대해서만 반응한다.

  • dropRemote
    • string $id
    • array $options
    • array $ajaxOptions

드래그할 수 있는 요소가 드롭되었을 때 XMLHttpRequest 를 시작하는 드롭 대상을 작성하기위해 사용된다. $options 은 drop() 과 같고, $ajaxOptions 은 link() 와 같다.

  • sortable
    • string $id
    • array $options

float 오브젝트의 그룹 또는 리스트 (DOM요소 ID $id 로 지정) 를 정렬할 수 있게 한다. $options 배열을 다음과 같은 정렬 방법을 설정할 수 있다:

  1. $options['tag'] // 정렬 가능한 태그 (컨테이너(container) 의 자식 요소(child element)) 의 종류를 설정한다.
    // UL 과 OL 컨테이너용으로, 이것은
    // LI 이다. 자식 태그 (child tag) 의 기타의 정렬용으로
    // 태그의 종류를 지정할 필요가 있다.
    // 기본값은 'li' 이다.

    $options['only'] // 자식 요소의 선택을,
  2. // 지정된 CSS 클래스를 포함하는 요소만을 포함도록, 더 제한한다.
    // (또는, 문자열의 배열을 설정한 경우는, 어떤 클래스상이라도. )

    $options['overlap'] // vertical(기본값) 또는 horizontal 이다.
    // floating sortables 또는 horizontal lists 는 horizontal 을 선택한다.
    // vertical list 는 vertical 이어야 한다.

    $options['constraint'] // 드래그할 수 있는 요소의 움직임을
    // vertical'(종방향) 또는 'horizontal'(횡방향) 으로 제한한다.

    $options['containment'] // sortable 사이에서 드래그와 드롭을 가능하게 한다..
    // (콘테이너의)요소의 배열 또는 요소의 id 의 배열을 잡는다.

    $options['handle'] // 작성된 드래그 가능 요소가 핸들을 사용하도록 한다.
    // drag() 의 handle 옵션을 참조하라.

  • editor
    • string $id
    • string $url
    • array $options

첫번째 파라미터로서 지정된 DOM id 의 요소를 사용하고, in-place ajax 에디터를 작성한다. 구현되면, mouseOver 로 하이라이트 되고, 클릭하면 단일의 텍스트 입력 필드로 변한다. 두번째 파라미터는, 편집한 데이타가 보내져야 하는 URL 입니다. 액션도 갱신된 요소의 내용을 반하라. in-place 에디터의 추가 옵션은, Script.aculo.us wiki 에서 참조한다.

10.1.3. 자바스크립트#

자바스크립트 헬퍼는 자바스크립트에 관한 태그와 데이타를 잘 포맷 된 형태로 출력할 수 있도록 개발자를 도와준다.

  • codeBlock
    • string $string

$script 를 자바스크립트의 <script> 태그로 둘러싼 뒤 반환한다.

  • link
    • string $url

$url 로 참조되는 스크립트를 자바스크립트 인클루드 태그로 반환한다..

  • linkOut
    • string $ur

link()와 같지만, 인클루드 태그는 $url 로 참조되는 스크립트와 같은 도메인이 아니라고 가정한다.

  • escapeScript
    • string $script

자바스크립트의 코드 부분 처리를 위해 캐리지 리턴(CR), 싱글 쿼트( ' ), 더블 쿼트( " ) 를 escape 한다.

  • event
    • string $object
    • string $event
    • string $observer
    • boolean $useCapture

Prototype 라이브러리를 사용하여 요소에 이벤트를 덧붙인다.

  • cacheEvents

event() 로 작성된 자바스크립트 이벤트를 캐시한다.

  • writeEvents

cacheEvents() 로 캐시된 캐시 이벤트를 기록한다.

  • includeScript
    • string $script

10.1.4. 숫자#

숫자 헬퍼는 뷰내에서 숫자에 관한 데이터를 포맷하기 위한 몇가지 아주 좋은 함수가 포함되어 있다.

  • precision
    • mixed $number
    • int $precision = 3

$precision 로 지정된 레벨의 정밀도로 포맷된 $number 를 반환한다.

  • toReadableSize
    • int $sizeInBytes

$size 로 지정한 Byte 를 사람이 읽을 수 있게 하여 반환한다. 기본적으로 몇 Byte 인지 입력하면 사람이 읽을 수 있는 적당한 값 KB, MB, GB, TB 로 반환한다.

  • toPercentage
    • mixed $number
    • int $precision = 2

입력된 숫자를 $precision 로 지정된 범위의 퍼센티지(%)로 포맷하여 반환한다.

10.1.5. 문자#

문자 헬퍼는, 개발자가 텍스트를 브라우저에 출력할 때 필요한 메소드를 제공한다.

  • highlight
    • string $text
    • string $highlighter = '<span class="highlight">\1</span>'

$text 속에 포함된 $phrase 를 모두 $highlighter 로 지정한 태그로 둘러싼 뒤 반환한다.

  • stripLinks
    • string $text

HTML 링크 (<a href= ...) 을 제외하고 $text 를 반환한다.

  • autoLinkUrls
    • string $text
    • array $htmlOptions

대응하는 <a> 태그로 둘러싸인 URL 로 $text 를 반환한다.

  • autoLinkEmails
    • string $text
    • array $htmlOptions

대응하는 <a> 태그로 둘러싸인 이메일 주소로 $text 를 반환한다.

  • autoLink
    • string $text
    • array $htmlOptions

대응하는 <a> 태그로 둘러싸인 URL 과 이메일 주소로 $text 를 반환한다.

  • truncate
    • string $text
    • int $length
    • string $ending = '...'

$text 를 최초의 $length 길이의 문자로 반환한다. 문자 끝에 $ending (기본값은 '...' )을 붙는다.

  • excerpt
    • string $text
    • string $phrase
    • int $radius = 100
    • string $ending = '...'

$text 로부터 $radius 로 정해진 각각의 위치에 있는 다수의 문자로 $phrase 를 잡아내 인용을 추출한다.

Extracts an excerpt from the $text, grabbing the $phrase with a number of characters on each side determined by $radius.

  • autoLinkEmails
    • string $text
    • boolean $allowHtml = false

텍스트를 HTML로 파싱한다, 구문은 조금 다르나, Textile 이나 RedCloth 를 닮았다.

10.1.6. 시간#

시간 헬퍼는 유닉스 타임스탬프나 datetime 문자열을 좀 더 이해하기 쉬운 형태로 브라우저에 출력하기 위해, 개발자가 필요로 하는 것을 제공한다.

모든 함수에 대해, 날짜는, 유효한 PHP datetime 문자열이나 유닉스 타임스탬프 중 어떤 것을 지정해도 된다.

  • fromString
    • string $dateString

유닉스 타임스탬프 또는 유효한 strtotime() 날짜 문자 둘중하나를 입력받아 유닉스 타임스탬프를 반환한다.

  • nice
    • string $dateString
    • boolean $return = false

포맷된 날짜 문자열을 갚습니다. "D, M jS Y, H:i" 나, 'Mon, Jan 1st 2005, 12:00' 의 형태로 포맷된 날짜다.

  • niceShort
    • string $dateString
    • boolean $return = false

날짜의 문자열을, nice() 와 같은 형태로 포맷하지만, 문자열이 오늘이 경우에는 "Today, 12:00" 과 같이 출력하고, 어제이면, "Yesterday, 12:00" 과 같이 출력합니다.

  • isToday
    • string $dateString

지정된 datetime 문자열이 오늘이면 true 를 반환한다.

  • daysAsSql
    • string $begin
    • string $end
    • string $fieldName
    • boolean $return = false

두 개의 날짜 사이를 전부 검색하는 SQL 문자열의 부분을 반환한다.

  • dayAsSql
    • string $dateString
    • string $fieldName
    • boolean $return = false

같은 날 두 개의 시각간 레코드를 모두 검색하는 SQL 문자열의 일부를 반환한다.

  • isThisYear
    • string $dateString
    • boolean $return = false

입력받은 datetime 문자열이 올해면, ture 를 반환한다.

  • wasYesterday
    • string $dateString
    • boolean $return = false

입력받은 datetime 문자열이 어제이면, ture 를 반환한다.

  • isTomorrow
    • string $dateString
    • boolean $return = false

입력받은 datetime 문자열이 내일이면, ture 를 반환한다.

  • toUnix
    • string $dateString
    • boolean $return = false

텍스트에 기술된 datetime 을 유닉스 타임스탬프로 반환한다. PHP strtotime() 함수의 래퍼다.

  • toAtom
    • string $dateString
    • boolean $return = false

Atom RSS 피드용 날짜 포맷을 반환한다.

  • toRSS
    • string $dateString
    • boolean $return = false

RSS 피드용 날짜 포맷을 반환한다.

  • timeAgoInWords
    • string $dateString
    • boolean $return = false

현재의 시간과 입력 받은 datetime 의 차에 기초하여 상대적인 날짜 또는 포맷된 날짜를 반환한다. $datetime 은 MySQL datetime 과 같이, strtotime 으로 파싱 가능한 포맷이어야 한다.

  • relativeTime
    • string $dateString
    • boolean $return = false

timeAgoInWords() 와 같이 작동하지만, 미래의 타임스탬프도 출력할 수 있다. ("Yesterday, 10:33", "Today, 9:42", "Tomorrow, 4:34". )

  • relativeTime
    • string $timeInterval
    • string $dateString
    • boolean $return = false

지정된 datetime 이 지정된 interval 의 사이에 속하면 true 를 반환하고, 아니면 false 를 반환한다. 시간의 간격은 숫자 또는 '6 hours', '2 days' 등과 같이 지정한다.

10.2. 자신의 헬퍼 작성하기#

뷰 코드에 도움이 좀 필요한 적 있는가? 만일 뷰로직의 특정한 코드가 계속해서 반복되는 것을 발견했다면, 자기 자신만의 뷰 헬퍼를 만들 수 있다.

10.2.1. Cake 헬퍼 클래스 확장(extend)#

애플리케이션 중에서, CSS로 스타일을 조절한 링크를 출력하는 헬퍼를 작성한다고 하자. Cake 의 기존 헬퍼 구조로 맞추기 때문에, /app/views/helpers 속에 새로운 클래스를 작성해야 한다. LinkHelper 라는 이름으로 한다. 실제의 PHP 클래스 파일은, 이와 같다:

예 10.5. /app/views/helpers/link.php

  1. class LinkHelper extends Helper
    {
    function makeEdit($title, $url)
    {
    //     링크를 조정하는 로직은 여기에...
    }
    }

활용할 수 있는 Cake 의 헬퍼 클래스의 몇가지 함수가 있다:

  • output
    • string $string
    • boolean $return = false

AUTO_OUTPUT ( /app/config/core.php 를 참조)에 기초하여, 출력하는지, 문자열로 반환하는지를 결정한다. 뷰에 되돌리고 싶은 데이터가 있는 경우에는 이 함수를 사용한다.

  • loadConfig

애플리케이션으로, 현재의 코어 정의와 태그 정의를 반환한다.

링크의 타이틀과, URL 을 output() 으로 조정한 뒤 뷰에 리턴해 보자.

예 10.6. /app/views/helpers/link.php (로직추가)

  1. class LinkHelper extends Helper
    {
    function makeEdit($title, $url)
    {
    //     헬퍼의 출력 함수를 사용하고, 조절한 데이터를
    // 뷰에 반환한다:

    return $this->output("<div class=\"editOuter\"><a href=\"$url\" class=\"edit\">$title</a></div>");
    }
    }

10.2.2. 다른 헬퍼의 포함#

다른 헬퍼의 기존 기능을 이용하고 싶은 경우가 있다. 이 경우는 컨트롤러에서의 경우와 같은 서식에, 사용하고 싶은 헬퍼를 $helpers 배열에 지정할 수 있다.

예10.7. /app/views/helpers/link.php (다른 헬퍼 사용)

  1. class LinkHelper extends Helper
    {

    var $helpers = array('Html');

    function makeEdit($title, $url)
    {
    //     HTML 헬퍼를 사용해
    // 조정한 데이터를 출력한다:

    $link = $this->Html->link($title, $url, array('class' => 'edit'));

    return $this->output("<div class=\"editOuter\">$link</div>");
    }
    }

10.2.3. 커스텀 헬퍼 사용#

헬퍼를 작성하고, /app/views/helpers/ 에 넣으면, 컨트롤러내에서는 $helpers 라는 특별한 변수에 그것을 include 할 수 있게 된다.

  1. class ThingsController
    {
    var $helpers = array('Html', 'Link');
    }

어떤 부분에 HTML 헬퍼를 사용할 예정이면, 잊지 않고 포함시켜야 한다. 네이밍 규약은 모델과 같다.

  1. LinkHelper = 클래스명

  2. link = 헬퍼 배열내에서의 키

  3. link.php = /app/views/helpers 내의 PHP 파일명.

10.2.4. 기여하기#

Cake 에 당신의 코드를 제공하는 것도 고려해 주기 바란다 - trac 시스템이나 메일링 리스트의 개발자게 알릴 수 있고, CakeForge 로 새로운 프로젝트를 열고, 새로운 헬퍼를 다른 사람에게 배포할 수 있다.


11. Cake 전역 상수와 함수 (Cake's Global Constants And Functions)

  1. 11. Cake 전역 상수와 전역 함수 (Cake's Global Constants And Functions)
    1. 11.1. 전역 함수
    2. 11.2. CakePHP 코어에 정의된 상수
  2. 11.3. CakePHP 경로 상수
    1. 11.4. CakePHP Webroot 설정 경로

11. Cake 전역 상수와 전역 함수 (Cake's Global Constants And Functions)#

Cake 는 애플리케이션을 구축할 때 전역(global)으로 사용가능한 유용한 정수와 함수를 몇가지 설명한다.

11.1. 전역 함수#

다음은 Cake 의 전역 함수이다. 이 함수 대부분은 긴 이름을 가진 PHP 함수의 편리한 wrapper 이다. 그러나, 몇개(vendor() 나 uses() 등)는, 코드를 include 하거나, 기타 도움이 되는 기능을 구현하기위해 사용된다. 어쩌면 여러분은 계속해서 성가시게할 이런 작고 좋은 함수를 원하고 있을지 모른다.

  • config

Cake 의 코어 설정 파일을 로드한다. 성공하면, true 를 반환한다.

  • uses
    • string $lib1
    • string $lib2...

Cake 의 코어 라이브러리( cake/libs/ 에 존재한다. )의 로드할 때 사용한다. lib 의 파일명에서 '.php' 확장자를 뺀 것을 지정한다.

  1. uses('sanitize', 'security');

  • vendor
    • string $lib1
    • string $lib2...

/vendors 디렉토리로 있는 외부 라이브러리가 로드할 때 사용한다. lib 의 파일명에서 '.php' 확장자를 뺀 것을 지정한다.

  1. vendor('myWebService', 'nusoap');

  • debug
    • mixed $var
    • boolean $showHtml = false

애플리케이션의 DEBUG 레벨이 0 이 아니면, $var 가 출력된다. 만일 $showHTML 이 true 로 되어 있으면, 데이터는 browser-friendly 방법으로 표시된다.

  • a

랩(wrap)하는 함수를 호출할 수 있고, 파라미터의 배열을 반환한다.

  1. function someFunction()
    {
    echo print_r(a('foo', 'bar'));
    }

    someFunction();

    // 출력 결과 :

    array(
    [0] => 'foo',
    [1] => 'bar'
    )

  • aa

랩(wrap)하는 함수를 호출할 수 있고, 파라미터를 사용해 연상 배열(associative array)을 작성합니다.

  1. echo aa('a','b');
  2. // 출력 결과 :
  3. array(
  4. 'a' => 'b'
  5. )

  • e
    • string $text

echo() 의 사용하기 쉬운 래퍼(wrapper).

  • low

strtolower() 의 사용하기 쉬운 래퍼.

  • up

strtoupper() 의 사용하기 쉬운 래퍼.

  • r
    • string $search
    • string $replace
    • string $subject

str_replace() 의 사용하기 쉬운 래퍼.

  • pr
    • mixed $data

아래와 같이 사용하기 쉬운 함수이다.

  1. echo "<pre>" . print_r($data) . "</pre>";

DEBUG 가 0 이 아닐 경우에 정보를 출력한다.

  • am
    • array $array1
    • array $array2...

파라미터에 지정된 배열을 병합(merge)해 반환한다.

  • env
    • string $key

액세스 가능한 소스로부터 환경 변수를 얻는다. $_SERVER 나 $_ENV 를 사용할 수 없는 경우의 예비로 사용할 수 있다.

또 이 함수는 PHP_SELF 와 DOCUMENT_ROOT 를 지원하고 있지 않는 서버에, 그 동작을 에뮬레이트한다. 풀 에뮬레이션 래퍼이기 때문에, (특히 코드를 배포하는 경우 등에는) $_SERVER 나 getenv() 대신에 평소에 env() 를 사용하는 것도 좋은 생각이다.

  • cache
    • string $path
    • string $expires
    • string $target = 'cache'

$data 의 데이타를, $path 로 지정된 경로내의 /app/tmp 에 캐시로서 기록한다. $expires 로 지정한 유효 시간은, 유요한 strtotime() 문자열이어야 한다. 캐시 데이터의 $target 은, 'cache' 또는 'public' 이다.

  • clearCache
    • string $search
    • string $path = 'views'
    • string $ext

캐시 디렉토리에 있는 파일의 삭제, 또는 캐시 디렉토리의 캐시의 내용을 클리어할때 사용한다.

$search 가 단일의 문자일때, 일치한는 캐시 디렉토리나 파일명이 캐시에서 삭제된다. $search 파라미터는 클리어해야 하는 파일/디렉토리 이름의 배열을 전달해줄 수 있다. 만일 빈값이면, /app/tmp/cache/views 의 모든 파일이 클리어된다.

$path 파라미터에, /tmp/cache 내 어떤 디렉토리를 클리어하는지를 지정할 수 있다. 기본값은, 'views' 이다.

$ext 파라미터는, 클리어하고 싶은 파일의 확장자를 지정할때 사용한다.

  • stripslashes_deep
    • array $array

배열 모든 값에 대해, 재귀적으로 stripslashes 를 실행한다.

  • countdim
    • array $array

지정된 배열의 차원수(dimensions)를 반환한다.

  • fileExistsInPath
    • string $file

지정된 파일명에 현재의 include 경로를 검색합니다. 발견된 경우에는 그 경로를 반환한다. 발견되지 않은 경우에는 false 를 반환한다.

  • convertSlash
    • string $string

forward slashe('/') 를 underscore('_') 로 변환하고, 문자열에 있는 맨앞과 맨뒤의 '_' 를 삭제한다.

11.2. CakePHP 코어에 정의된 상수#

ACL_CLASSNAME: CakePHP 의 ACL 의 실행과 관리를 현재 맡고 있는 클래스의 이름. 이 정수에, 써드 파티의 클래스를 통합해 배치할 수 있다.

ACL_FILENAME: 클래스 ACL_CLASSNAME 의 들어가 있는 파일명.

AUTO_SESSION: false 로 설정하면, 애플리케이션의 리퀘스트가 있어도 session_start() 가 자동적으로 호출되지 않는다.

CACHE_CHECK: false 로 설정하면, 애플리케이션 전체에 뷰의 캐싱이 OFF 된다.

CAKE_SECURITY: CAKE_SESSION_TIMEOUT 에 기초하여, 애플리케이션의 세션 보안의 레벨이 정해진다. 설정할 수 있는 값은, 'low', 'medium', 'high' 이다. 설정에 따라, CAKE_SESSION_TIMEOUT 이 다음과 같이 늘어난다:

  1. low: 300

  2. medium: 100

  3. high: 10

CAKE_SESSION_COOKIE: 애플리케이션의 세션 쿠키명.

CAKE_SESSION_SAVE: 'php', 'file', 'database' 중 하나로 설정할 수 있다.

  1. php: Cake 는 PHP 의 디폴트 세션 핸들링을 사용한다 (보통, php.ini 에 정의됨).

  2. file: 세션 데이터는, /tmp 에 저장, 관리된다.

  3. database: Cake 의 데이터베이스 세션 핸들링이 사용된다 (상세한 정보는, "16. Cake 세션 컴포넌트" 장 참조 ).

CAKE_SESSION_STRING: 세션관리에 랜덤 문자열이 사용된다.

CAKE_SESSION_TABLE: 세션 데이터를 저장하는 테이블명. ( CAKE_SESSION_SAVE == 'database' 인 경우) 데이타베이스 접속용 기본 접두사(prefix)가 설정 되어 있는 경우에는, 그것을 포함시켜선 안된다.

CAKE_SESSION_TIMEOUT: 세션 타임 아웃까지의 초수. 이 값은, CAKE_SECURITY 를 곱한 값이 된다.

COMPRESS_CSS: true 로 설정하면, CSS 스타일 시트를 압축해 출력한다. 이것을 사용하려면 웹 서버에서 /var/cache 디렉토리를 쓰기 가능하게 설정해야 한다. 이것을 사용하려면, 스타일 시트를 /ccss (/css 보다 낫다)나, Controller::cssTag() 를 사용하여 참조한다.

DEBUG: CakePHP 애플리케이션이 표시하는 에러 보고 레벨과 디버그 출력 레벨을 정한다. 0 과 3 사이.

  1. 0: 프로덕션(공개) 모드. 에러는 출력되지 않고 디버그 메세지도 표시되지 않는다.

  2. 1: 개발 모드. Warning(경고)과 에러, 디버그 메세지를 표시한다.

  3. 2: 1 과 같지만, 추가로 SQL 출력이 있다.

  4. 3: 2 와 같지만, 추가로 현재의 오브젝트(보통 컨트롤러)의 전체 덤프도 표시된다.

LOG_ERROR: 에러 상수. 에러 로깅과 디버깅을 구별할 때 사용한다. 현재 PHP 는 LOG_DEBUG 를 지원하고 있다.

MAX_MD5SIZE: md5() 해쉬를 이 값에 기초해 실행합니다. 최대치(바이트)에 지정합니다.

WEBSERVICES: true 로 설정하면, Cake 에 내장된 webservices 기능이 ON 된다.

11.3. CakePHP 경로 상수#

APP: 애플리케이션 디렉토리 경로.

APP_DIR: 현재 애플리케이션의 app 디렉토리명.

APP_PATH: 애플리케이션의 app 디렉토리 절대 경로.

CACHE: 캐시 파일 디렉토리 경로.

CAKE: 애플리케이션의 cake 디렉토리 경로.

COMPONENTS: 애플리케이션의 컴포넌트들의 디렉토리 경로.

CONFIGS: 설정 파일의 디렉토리 경로.

CONTROLLER_TESTS: 컨트롤러의 테스트 디렉토리 경로.

CONTROLLERS: 애플리케이션의 컨트롤러 경로.

CSS: CSS 파일 디렉토리 경로.

ELEMENTS: 엘러먼트들의 디렉토리 경로.

HELPER_TESTS: 헬퍼 테스트 디렉토리 경로.

HELPERS: 헬퍼들의 디렉토리 경로.

INFLECTIONS: 인플렌션들의 디렉토리 경로. (보통, configuration directory 안).

JS: 자바스크립트 파일 디렉토리 경로.

LAYOUTS: 레이아웃들의 디렉토리 경로.

LIB_TESTS: Cake Library 테스트 디렉토리 경로.

LIBS: Cake libs 디렉토리 경로.

LOGS: 로그 디렉토리 경로.

MODEL_TESTS: 모델 테스트 디렉토리 경로.

MODELS: 모델들의 디렉토리 경로.

SCRIPTS: Cake scripts 디렉토리 경로.

TESTS: tests 디렉토리 경로. (model 이나 controller 의 부모 등. test 디렉토리)

TMP: tmp 디렉토리 경로.
VENDORS: 벤더들의 디렉토리 경로.

VIEWS: 뷰들의 디렉토리 경로.

11.4. CakePHP Webroot 설정 경로#

CORE_PATH: Cake 의 핵심 라이브러리 경로.

WWW_ROOT: 애플리케이션의 webroot 디렉토리 경로. (보통, /cake/ )

CAKE_CORE_INCLUDE_PATH: Cake 핵심 라이브러리 경로.

ROOT: CakePHP 의 베이스가 되는 index.php 의 디렉토리명.

WEBROOT_DIR: 애플리케이션의 webroot 디렉토리명.


12. 자료 검증 (Data Validation)

  1. 12. 자료 검증 (Data Validation)
    1. 12.1. 자료 검증

12. 자료 검증 (Data Validation)#

12.1. 자료 검증#

검증(validation)룰을 커스터마이징하여 작성하면 비밀번호는 8글자만, 유저명은 영문만 등과 같이 모델내의 데이터가, 애플리케이션의 비즈니스 룰에 확실히 적합하도록 만들 수 있다.

자료 검증의 첫번째 단계는, 검증룰을 모델내에 작성하는 것부터 시작된다. 그것에는, 모델 정의에 있는 Model::validate 배열을 사용한다. 예를 들면:

예 12.1. /app/models/user.php

  1. <?php
  2. class User extends AppModel
  3. {
  4. var $name = 'User';
  5. var $validate = array(
  6. 'login' => '/[a-z0-9\_\-]{3,}$/i',
  7. 'password' => VALID_NOT_EMPTY,
  8. 'email' => VALID_EMAIL,
  9. 'born' => VALID_NUMBER
  10. );
  11. }
  12. ?>

검증은, 펄(Perl) 호환 정규 표현을 사용해 정의한다. 몇가지는 /libs/validators.php 에 이미 정의되어 있다 :

  • VALID_NOT_EMPTY
  • VALID_NUMBER
  • VALID_EMAIL
  • VALID_YEAR

모델의 정의 ($validate 배열) 속에 검증이 있으면, 저장 작업중 (Model::save() 메소드) 에 그것을 해석하고 체크한다. 데이터를 직접적으로 검증하고 싶은 경우에는 Model::validates() (데이터가 바르지 않으면 false 를 반환) 와 Model::invalidFields() (에러 메세지의 배열을 반환) 를 사용한다.

그러나, 그 데이터는 일반적으로 컨트롤러 코드내에 암시적(정의 없이 사용)이다. 다음 예는 어떻게 폼의 데이터를 다루는지 나타낸다 :

예 12.2. /app/models/blog_controller.php 의 폼을 다루는 액션

  1. <?php
    class BlogController extends AppController {

    var $uses = array('Post');

    function add ()
    {
    if (empty($this->data))
    {
    $this->render();
    }
    else
    {
    if($this->Post->save($this->data))
    {
    //ok, 바른 내용이다.
    }
    else
    {
    //    Danger, Will Robinson. 검증 에러.
    $this->set('errorMessage', 'Please correct errors below.');
    $this->render();
    }
    }
    }
    }
    ?>

* 역자주 : Danger, Will Robinson. : 60년대 미국 TV 시리즈에서 유래한 파생어, 단순한 위험 경고로 생각하면 된다

이 액션으로 사용하는 뷰는 다음과 같다 :

예 12.3. /app/views/blog/add.thtml 의 add 폼의 view

  1. <h2>Add post to blog</h2>
    <form action="<?php echo $html->url('/blog/add')?>" method="post">
    <div class="blog_add">
    <p>Title:
    <?php echo $html->input('Post/title', array('size'=>'40'))?>
    <?php echo $html->tagErrorMsg('Post/title', '제목은 필수사항입니다.')?>
    </p>
    <p>Body
    <?php echo $html->textarea('Post/body') ?>
    <?php echo $html->tagErrorMsg('Post/body', '내용은 필수사항입니다.')?>
    </p>
    <p><?=$html->submit('입력')?></p>
    </div>
    </form>

Controller::validates($model[, $model...]) 는 모델에 추가된 커스텀 검증의 체크로 사용된다. Controller::validationErrors() 메소드는, 모델내에 확인된 에러 메세지를 모두 반환한다. 그것은 뷰내에서 tagErrorMsg() 에 의해 표시된다.

Cake 의 정규 표현에 기초하는 검증 외에 독자적으로 커스터마이징한 검증을 실행하고 싶은 경우에는, 모델의 invalidate() 함수를 이용하고, 잘못된 필드에 flag 를 둘 수 있다. 예를 들어, 시스템에 이미 존재하는 이름을, 유저가 지정한 경우에 에러를 표시한다고 하자. 이 경우에는 Cake 가 정규 표현을 사용할 수 없다. 따라서, 커스텀 검증을 이용해 Cake 의 일반적 폼 체크 프로세스를 호출, 필드에 flag 를 둔다.

컨트롤러는 다음과 같은 형태로 가능하다 :

  1. <?php
  2. class UsersController extends AppController
  3. {
  4. function create()
  5. {
  6. //폼 데이터가 전송되었는지 체크한다.
  7. if (!empty($this->data['User']))
  8. {
  9. //username 필드의 유저명이 존재하는지 확인.
  10. $user = $this->User->findByUsername($this->data['User']['username']);
  11. //HTML helper의 에러 메시지를 trigger하기 위해 필드를 Invalidate(무효로)한다
  12. if (!empty($user['User']['username']))
  13. {
  14. $this->User->invalidate('username');//tagErrorMsg('User/username') 가 들어 간다.
  15. }
  16. //일반적 데이터로 저장한다. 필드가 부적합하면 실행되지 않는다.
  17. if($this->User->save($this->data))
  18. {
  19. $this->redirect('/users/index/saved');
  20. }
  21. else
  22. {
  23. $this->render();
  24. }
  25. }
  26. }
  27. }
  28. ?>


13. 플러그인 (Plugins)

  1. 13. 플러그인 (Plugins)
    1. 13.1. 플러그인의 작성
    2. 13.2. 플러그인 컨트롤러
    3. 13.3. 플러그인 모델
    4. 13.4. 플러그인 뷰
    5. 13.5. 플러그인 사용하기

13. 플러그인 (Plugins)#

CakePHP 는 일련의 컨트롤러, 모델, 뷰의 편성을 설정하고, 다른 사람이 자신의 CakePHP 애플리케이션으로 사용 가능한, 팩키징된 애플리케이션 플러그인의 배포가 가능하도록 하고 있다. 당신의 애플리케이션 속에는, 유저 관리 모듈이나, 간단한 블로그, 웹 서비스의 모듈을 포함하고 있는가? CakePHP 의 플러그인으로 패키지화하면, 다른 애플리케이션에 쉽게 넣을 수 있게 된다.

플러그인과 그것이 설치된 애플리케이션은 애플리케이션의 설정(데이터베이스 접속 등)에 연결 되어 있다. 만일 설정이 다른 경우에는, 그 작은 공간안에서 작동하고, 별개의 애플리케이션과 같이 실행된다.

13.1. 플러그인의 작성#

실례로 피자 주문 플러그인을 작성해 보자. 무엇이 CakePHP 애플리케이션을 더 유용하게 해줄까? 먼저, 플러그인 파일을 /app/plugins 폴더내에 넣어야 한다. 모든 플러그인 파일을 두기 위한 폴더 이름은 중요하며 많은 곳에서 사용되기 때문에 현명하게 선택해야 한다. 이 플러그인은 'pizza' 라고 한다. 설정이 끝나면 다음과 같이 된다 :

예 13.1. 피자 주문 파일 시스템의 레이아웃

  1. /app
    /plugins
    /pizza
    /controllers <- 플러그인 컨트롤러는 여기에
    /models <- 플러그인 모델은 여기에
    /views <- 플러그인 뷰는 여기에
    /pizza_app_controller.php <- 플러그인의 AppController, 플러그인 뒤에 이름 붙인다
    /pizza_app_model.php <- 플러그인의 AppModel, 플러그인 뒤에 이름 붙인다

일반적인 애플리케이션에서는, AppController 와 AppModel 을 반드시 정의할 필요는 없으나, 플러그인에서는 반드시 정의해야 한다. 플러그인을 만드는 것은 플러그인이 작동되기 전에 필요하다. 이러한 특별한 클래스는 플러그인에 기초하여 네이밍하며, 부모(parent) 애플리케이션의 AppController 와 AppModel 을 extend 하고 있다. 다음과 같다:

예 13.2. 피자 플러그인 AppController: /app/plugins/pizza_app_controller.php

  1. <?php

  2. class PizzaAppController extends AppController

  3. {

  4. //...

  5. }

  6. ?>

예 13.3. 피자 플러그 인 AppModel: /app/plugins/pizza_app_model.php

  1. <?php

    class PizzaAppModel extends AppModel
    {
    //...
    }

    ?>

이러한 특별한 클래스를 정의하여 잊고 있으면, CakePHP 에서는 수정될 때까지 "Missing Controller" 에러가 발생한다.

13.2. 플러그인 컨트롤러#

피자 플러그인의 컨트롤러는, /app/plugins/pizza/controllers 에 들어 간다. 기록해야할 중요 사안은 피자의 주문(Orders)이기 때문에, 이 플러그인에는 OrdersController 가 필요하다.

필수 사항은 아니지만, 부모 애플리케이션과의 네임스페이스의 충돌을 피하기 위해 플러그인 컨트롤러의 이름은 부모 애플리케이션인 것과는 다른 이름을 붙이는 것을 권장한다. 부모 애플리케이션이 UsersController 이나 OrderController 또는, ProductController 를 가질 수 있다는 것은 잘못된 것이 아니다 : 따라서, 컨트롤러명을 새롭게 만들거나 플러그인의 이름을 앞에 넣어(prepend) 만드는 것도 좋다 (이 경우, PizzaOrdersController).

그래서, 새로운 PizzaOrdersController 를 /app/plugins/pizza/controllers 에 넣는다. 내용은 다음과 같다:

예 13.4. /app/plugins/pizza/controllers/pizza_orders_controller.php

  1. <?php

    class PizzaOrdersController extends PizzaAppController
    {
    var $name = 'PizzaOrders';

    function index()
    {
    //...
    }

    function placeOrder()
    {
    //...
    }
    }

    ?>

이 컨트롤러가 부모 애플리케이션의 AppController 가 아니라, 플러그 인의 AppController (PizzaAppController) 를 extend 하고 있는 점을 주의한다.

13.3. 플러그인 모델#

플러그인을 위한 모델은, /app/plugins/pizza/models 에 저장된다. 이 플러그인을 위해 PizzaOrdersController 를 이미 정의했기에, 이번엔 그 컨트롤러에 사용되는 모델을 작성하고, PizzaOrders 라고 한다. (PizzaOrders 라는 클래스명은 네이밍이 계획에 따른 것이고, 중복되지 않기 때문에 이대로 한다. )

예 13.5. /app/plugins/pizza/models/pizza_order.php

  1. <?php

    class PizzaOrder extends PizzaAppModel
    {
    var $name = 'PizzaOrder';
    }

    ?>

이것 또한, AppModel 이 아니라 PizzaAppModel 을 extend 하고 있는 점을 주의한다.

13.4. 플러그인 뷰#

뷰는 일반적 애플리케이션의 뷰와 똑같이 움직인다. /app/plugins/[plugin]/views 폴더와 같이 바른 경로에 저장한다. 피자 주문 플러그인에는, 적어도 PizzaOrdersController::index() 액션에 대한 뷰가 필요하다. 바로 그것을 포함시켜 보자:

예 13.6. /app/plugins/pizza/views/pizza_orders/index.thtml

  1. <h1>Order A Pizza</h1>
    <p>Nothing goes better with Cake than a good pizza!</p>
    <!-- An order form of some sort might go here....-->

13.5. 플러그인 사용하기#

이것으로 모든 작성이 끝났고 배포 준비가 되었다. (readme 나 sql 파일 등과 같은 몇가지 추가 파일도 권장한다.)

플러그인을 /app/plugins 에 설치하면, /플러그인명/컨트롤러명/액션 이라는 URL 로 접속할 수 있다. 이 피자 주문 플러그인의 예에선, /pizza/pizzaOrders 의 PizzaOrdersController 로 액세스한다.

CakePHP 애플리케이션의 플러그 인을 사용하기 위한 마지막 Tip 몇 가지 :

  1. [플러그인]AppController 와 [플러그인]AppModel 이 없을 경우, 플러그인 컨트롤러에 액세스하면, missing Controller 에러가 발생한다.
  2. 플러그인명의 기본 컨트롤러를 작성할 수 있고, /[플러그 인]/액션으로 액세스 가능하다. 예를 들어, 'users' 라는 플러그인에 UsersController 이라는 컨트롤러가 있는 경우, [플러그 인]/controllers 폴더에 AddController 이라는 플러그인이 존재하지 않으면 /users/add 로 액세스 가능하다.
  3. 플러그인은 기본값으로 /app/views/layouts 폴더의 레이아웃을 사용한다.
  4. 다음을 사용하여 컨트롤러에서 inter-plugin 할 수 있다

  1. $this->requestAction('/plugin/controller/action');

5 . requestAction 을 사용하는 경우, 컨트롤러명과 모델명은 가능한 모두 다른 이름이 되어야 한다. 그렇지 않으면, PHP의 "redefined class ..."(...클래스가 재선언되었다) 라는 에러가 날 수 있다.

이 장의 준비에 대해 Felix Geisendorfer (the_undefined) 에게 많은 감사를 드린다.


14. 접근 제어 목록 (Access Control List)

  1. 14. 접근 제어 목록 (Access Control List)
    1. 14.1. ACL 작동의 이해
    2. 14.2. 권한(permission)의 정의: Cake 의 INI-based ACL
    3. 14.3. 권한(permission) 의 정의: Cake 의 데이터베이스 ACL
      1. 14.3.1. 시작하기
      2. 14.3.2. 접근 요청 객체(Access Request Objects/AROs) 와 접근 제어 객체(Access Control Objects/ACOs)
    4. 14.3.3. 권한 할당
    5. 14.4. 권한 체크: ACL 컴포넌트

14. 접근 제어 목록 (Access Control List)#

14.1. ACL 작동의 이해#

가장 중요한 것으로, 강력한 애플리케이션에는 어떠한 접근 제어가 있어야 하다. 접근 제어 목록은 fine-grained (정리되어야) 하며, 게다가 유지보수와 관리가 용이한 애플리케이션의 권한을 관리하는 방법이다. 접근 제어 목록(ACL)은 주로 다음 두개의 사항을 다룬다 : 물건을 필요로 하는 동작, 그리고 필요한 물건 그 자체. ACL 의 용어로서, 접근(access)을 필요로 하는 동작 (대게, 각 유저) 을 접근 요청 객체(access request object, ARO)라고 부른다. 시스템 중에 필요하게 되는 것 (대게, 액션이나 데이터) 은, 접근 제어 객체( access control object, ACO)라고 부른다. 이러한 대상이 객체라고 불리는 것은 요청 객체가 유저나 인물이 아닌 작업이기 때문이다. 때때로, 애플리케이션외 부분의 로직을 개시할 수 있는 바와 같이, 특정 Cake 컨트롤러만이 접근할 수 있도록 제한하고 싶을 수 있다. ACO 로는 컨트롤러 액션, 웹 서비스, 여러분의 할머니의 온라인 일기로 접속하는 회선등, 컨트롤하고 싶은 것이면 어떤 것이든 될 수 있다.

약어를 정리하면 : ACL 은 ARO 가 언제 ACO 에 접근 가능한지를 결정하기 위해 사용한다.

이해하기 쉽도록 실례를 들겠다. 어드벤쳐 게임을 위해 컴퓨터 시스템이 사용되어 있다고 생각해 보자. 그룹의 리더는 탐험에 앞서, 그룹의 멤버가 충분한 개안 정보와 보안를 확보하고 싶다고 한다. ARO는 다음을 포함한다 :

  1. Gandalf (간달프)
    Aragorn (아라곤)
    Bilbo (빌보)
    Frodo (프로도)
    Gollum (골룸)
    Legolas (레골라스)
    Gimli (김리)
    Pippin (피핀)
    Merry (메리)

이들은 시스템으로 사물(ACO)을 요청하게 될 엔티티들이다. ACL 은 유저를 인증하는 시스템이 * 아니다 * 라는 점을 특히 주의한다. 유저가 시스템에 들어올 때, 유저 정보를 저장하는 수단과 유저의 ID를 확인할 수 있는 시스템은 별로 만들어 놓아야 한다. 유저가 누구인지를 확인하면, 그때가 ACL 의 차례다. 그럼 다시 탐험을 떠나자.

간달프가 다음에 해야할 것은 아이템들의 초기 목록(또는 ACO, 시스템이 그것을 다룰 것이다)을 만드는 것이다. 리스트는 다음과 같다 :

  1. Weapons (무기)
    The One Ring (절대반지)
    Salted Pork (절인돼지고기)
    Diplomacy (외교술)
    Ale (에일맥주)

전통적으로, 시스템은 매트릭스와 같은 것을 사용해 관리된다. 그 매트릭스는 기본 유저 세트와 객체와 관련된 권한을 나타내는 것이다. 만일 이 정보가 하나의 표에 저장된다면 다음과 같다. X 는 접근 거부, O 서는 접근 허가를 나타내고 있다.

  1. Weapons The One Ring Salted Pork Diplomacy Ale
    Gandalf X X O O O
    Aragorn O X O O O
    Bilbo X X X X O
    Frodo X O X X O
    Gollum X X O X X
    Legolas O X O O O
    Gimli O X O X X
    Pippin X X X O O
    Merry X X X X O

얼핏 보면, 이러한 형태의 시스템은 잘 돌아 가고 있는 것 같다. 보안을 위해서 분배(프로도만 절대반지에 접근 가능하게 하는 등. )할 수 있고, 사고가 일어나지 않도록 보호(호빗족은 절인돼지고기에 접근 불가능하도록 하는 등.) 되어 있다. 이것으로 충분히 정리 되어 있고, 읽기 쉬어 보이지만, 정말일까?

이와 같은 작은 시스템이면, 매트릭스에 의한 설정은 잘 작동할 것이다. 그러나, 시스템이 커져 가고, 리소스(ACO)나 유저(ARO)가 상당한 크기가 되는 경우, 테이블은 빨라지기보다 커지게 된다. 수백이라고 하는 진영의 접근을 제어하는, 예를 들어, 부대마다 관리하려고 하는 경우를 상상해 보자. 매트릭스의 다른 하나의 결점은, 유저의 집단을 논리적으로 그룹을 나누거나, 논리적인 그룹 분배에 기초해 있는 유저 그룹의 권한을 정리해 변경하거나 불가능한 점이다. 예를 들면 전투가 끝났을 때, 에일맥주와 절인돼지고기에 자동으로 접근할 수 있도록 가능하면 좋을지도 모른다. 이것을 개별 유저마다 실행하는 것은 지루하고 에러가 생기기 쉬운 작업이다. 하지만 모든 호빗에 허가를 정리해 주면 간단하다.

ACL 은 일반적으로 트리구조로 구현된다. 보통, ARO 의 트리와 ACO 의 트리가 존재한다. 각 객체를 트리에 정리하면 세세하게 권한을 설정할 수 있지만, 전체적으로도 확실히 파악하면서 유지보수가 가능하다. 영리한 리더로서 간달프는 ACL 을 새로운 시스템으로 채용하기로 하고, 자신의 객체를 다음과 같은 라인으로 정리하기 시작했다:

  1. 반지원정대 :
    전사(Warriors)
    아라곤
    레골라스
    김리
    마법사(Wizards)
    간달프
    호빗족(Hobbits)
    프로도
    빌모
    메리
    피핀
    객원(Vistors)
    골룸

그룹을 이와 같은 방법으로 구조화하는 것에 따라, 트리에 대해 접근 제어를 정의하고 그것들의 권한을 어떤 자식에게 적용할 수 있다. 기본 권한은 모든 액세스를 거부한다. 트리를 더듬어 보며 권한을 선택하고 적용한다. 마지막으로 (여러분이 의아해 하는 ACO에 대해) 적용한 권한이 계속해서 유지할 권한이 된다. 따라서, ARO 트리를 사용하여 간달프는 몇개의 권한을 건다 :

  1. 반지원정대 : [거부: 전체]
    전사 [허용 : 무기, 에일맥주, 비상식량, 절인돼지고기]
    아라곤
    레골라스
    김리
    마법사 [허용 : 절인돼지고기, 외교력, 에일맥주]
    간달프
    호빗족 [허용 : 에일맥주]
    프로도
    빌보
    메리
    피핀
    객원 [허용 : 절인돼지고기]
    골룸

피핀이 에일맥주에 접근 가능한지를 확인하기 위해 ACL 을 사용하는 경우, 우선 피핀이 트리의 어떤 경로에 있는지를 찾는다. 반지원정대->호빗족->피핀 이다. 그리고 각각의 포인트로 있는 다른 권한을 확인하고, 피핀과 에일맥주가 관련되어 있는 가장 가까운 권한을 사용한다.

  • 반지원정대 = 에일맥주 거부, 따라서 거부. (모든 ACO거부로 설정 되어 있으므로 )
  • 호빗족 = 에일맥주 허용, 따라서 허가.
  • 피핀 = ?; 에일맥주에 특정한 정보가 발견되지 않기 때문에, 허용이 되다.
  • 최종 결과: 에일맥주 허용.

트리에 의해 보다 좋은 컨트롤을 위한 조정을 할 수 있다. ARO 그룹의 변경은 매우 간단하다:

  1. 반지원정대 : [거부: 전체]
  2. 전사 [허용 : 무기, 에일맥주, 비상식량, 절인돼지고기]
    아라곤 [허용 : 외교력]
    레골라스
    김리
    마법사 [허용 : 절인돼지고기, 외교력, 에일맥주]
    간달프
    호빗족 [허용 : 에일맥주]
    프로도 [허용 : 절대반지]
    빌보
    메리 [거부 : 에일맥주]
    피핀 [허용 : 외교력]
    객원 [허용 : 절인돼지고기]
    골룸

아라곤 ARO 는 전사 ARO 그룹의 멤버 권한과 같이 관리되고 있지만, 또 특별한 케이스가 필요하면 조정을 가할 수 있다. 역시, 기본 권한은 '거부'이고, 트리의 줄기 부분을 내려가 '허용' 한다. 메리가 에일맥주에 접근 가능하도록 하려면, 반지원정대->호빗족->메리의 경로를 찾아, 에일맥주에 관련된 권한을 설정한다:

  1. 반지원정대 = 거부 (전체 거부로 설정되었으므로), 에일맥주 거부.

  2. 호빗족 = 허용: 에일맥주이기 때문에, 에일맥주 허용.

  3. 메리 = 거부 : 에일맥주이기 때문에, 에일맥주 거부.

  4. 최종 결과 : 에일맥주 거부.

14.2. 권한 (permission) 의 정의: Cake 의 INI-based ACL#

Cake 의 초기 ACL 구현은, Cake 의 인스톨시에 저장된 INI 파일에 기초한다. 이것도 편리하고 안정성이 있지만, 데이터베이스에 의한 ACL 솔루션을 사용하는 것을 권장한다. ACO 와 ARO 를 신속하게 작성할 수 있기 때문이다. 이 ACL 은 간단한 애플리케이션이나, 어떤 이유로 데이터베이스를 사용할 수 없는 경우 등에 사용할 수 있다.

ARO/ACO 권한은 /app/config/acl.ini.php 에 지정된다. 액세스의 설정 방법의 설명은 acl.ini.php 에 있다 :

  1. ; acl.ini.php - Cake ACL Configuration

  2. ; ---------------------------------------------------------------------

  3. ; Use this file to specify user permissions.

  4. ; aco = access control object (something in your application)

  5. ; aro = access request object (something requesting access)

  6. ;

  7. ; User records are added as follows:

  8. ;

  9. ; [uid]

  10. ; groups = group1, group2, group3

  11. ; allow = aco1, aco2, aco3

  12. ; deny = aco4, aco5, aco6

  13. ;

  14. ; Group records are added in a similar manner:

  15. ;

  16. ; [gid]

  17. ; allow = aco1, aco2, aco3

  18. ; deny = aco4, aco5, aco6

  19. ;

  20. ; The allow, deny, and groups sections are all optional.

  21. ; NOTE: groups names *cannot* ever be the same as usernames!

INI 파일을 사용하여 유저(ARO), 그들이 속해 있는 그룹, 또 개개인의 권한을 설정할 수 있다. 또 권한과 함께 그룹을 작성할 수 있다. 이 INI 파일을 사용하여 Cake 의 ACL 컴포넌트에 권한을 체크하는 방법은, 섹션 11.4 를 참조한다.

14.3. 권한 (permission) 의 정의: Cake 의 데이터베이스 ACL#

14.3.1. 시작하기#

기본 ACL 권한의 구현은 데이터베이스로 데이터가 저장된다. 데이터베이스 ACL 또는 dbACL 은 일련의 코어 모델과 코맨드 라인 스크립트로 구성되어 있고, Cake 의 설치시에 들어가 있다. Cake 가 사용하는 모델은 데이터베이스와 주고 받고, ACL 트리의 node 의 저장, 읽기에 사용된다. 코맨드 라인 스크립트는, 초기 설정과 트리의 인터랙트에 사용할 수 있다.

시작하기 전에, /app/config/database.php 가 존재하며 바르게 설정되어 있는 지 확인하라. Cake 의 설치 후 그것을 확인하는 가장 간단한 방법은 설치한 한 최상위 디렉토리를 웹 브라우저로 여는 것이다. 바르게 설정했을 경우, 메인 페이지에 "Your database configuration file is present.(데이터베이스 설정 파일이 존재합니다.)", 그리고 "Cake is able to connect to the database.(Cake 서는 데이타베이스로 접속 가능합니다. )" 가 표시될 것이다. 상세한 것은, 섹션 4.1에 있는 데이터베이스 설정을 참조한다.

다음으로, ACL 코맨드 라인 스크립트에 ACL 정보를 데이터베이스로 보존할 수 있도록 초기화 한다. /cake/scripts/acl.php 라는 스크립트로 할 수 있다. 아래의 명령을 ( /cake/scripts/ 디렉토리에서) 실행하고, ACL 용 데이터베이스를 초기화 한다 :

예 14.1. acl.php 를 사용한 데이터베이스 초기화

  1. $ php acl.php initdb

    Initializing Database...
    Creating access control objects table (acos)...
    Creating access request objects table (acos)...
    Creating relationships table (aros_acos)...

    Done.

이 시점에, 프로젝트의 데이터베이스로 새로운 테이블이 작성 되어 있는지를 확인할 수 있다. Cake 가 어떻게 해서 트리 정보를 테이블에 저장하고 있는 것인지에 관심이 있는 사람은, "modified dabase tree traversal" 에 대한 책을 보기 바란다. 기본적으로 그것은 노드(node)와, 트리 상의 위치를 저장한다. aco 와 aro 테이블은, 각각의 트리에 대한 노드를 보존하고, aros_acos 테이블은 ARO 와 액세스 가능한 ACO 를 묶기 위해서 사용된다.

이로서, ARO 와 ACO 트리를 작성할 수 있게 되었다.

14.3.2. 접근 요청 객체 (Access Request Objects/AROs) 와 접근 제어 객체 (Access Control Objects/ACOs)#

AROs/ACOs 를 참조하려면 두가지 방법이 있다. 하나는 수치 ID 를 붙이는 방법이다. 보통은 속해 있는 테이블의 primary key 가 된다. 다른 방법으로는 문자열에 alias 를 붙이는 방법이다. 이 두가지는 함께 사용할 수 없다.

새로운 ARO 를 작성하려면, Aro Cake 모델로 정의 되어 있는 메소드를 사용한다. Aro 클래스의 create() 메소드는 3개의 파라미터($link_id, $parent_id, $alias)를 갖는다. 이 메소드는 새로운 ACL 객체를 parent_id 로 지정된 그 부모에 작성한다. 만일 $parent_id 가 null 로 전달 받았을 때는 루트 오브젝트를 작성한다. $link_id 는 현재의 유저 오브젝트를 Cake 의 ACL 구조로 묶을 수 있다. alias 파라미터에 의해 오브젝트를 non-integer ID 로 다룰 수 있게 된다.

ACOs 와 AROs 를 작성하기 전에, 그것들의 클래스를 먼저작성할 필요가 있다. 가장 간단한 방법은 Cake 의 ACL 컴포넌트를 $component 배열을 사용해 컨트롤러에 포함시키는 것이다.

  1. var $components = array('Acl');

그 다음 작업으로서, 오브젝트를 작성하는 예를 보자. 다음 코드를 컨트롤러의 액션 어딘가에 넣는다:

  1. $aro = new Aro();

    // 우선, AROs 를 설정합니다.
    // 초기에는, 이러한 오브젝트에는 부모가 존재하지 않는다.

    $aro->create( 1, null, 'Bob Marley' );
    $aro->create( 2, null, 'Jimi Hendrix');
    $aro->create( 3, null, 'George Washington');
    $aro->create( 4, null, 'Abraham Lincoln');

    // 이제, 이런 유저를 정리하기 위한 그룹을 작성한다.
    // 이 시스템내에서 유저와 묶여저 있지 않기 때문에,
    // 오브젝트의 ID 가 0 이 되는 것을 주의하라.

    $aro->create(0, null, 'Presidents');
    $aro->create(0, null, 'Artists');

    //이제, AROs 를 각각의 그룹에 묶는다 :

    $aro->setParent('Presidents', 'George Washington');
    $aro->setParent('Presidents', 'Abraham Lincoln');
    $aro->setParent('Artists', 'Jimi Hendrix');
    $aro->setParent('Artists', 'Bob Marley');

    //정리해서, ARO 를 작성하려면 다음과 같이 한다 :
    $aro = new Aro();
    $aro->create($user_id, $parent_id, $alias);

AROs 는 코맨드 라인 스크립트에서 다음과 같이 하여 작성할 수 있다 : $acl.php create aro <link_id> <parent_id> <alias>.

ACO 도 같은 방법으로 작성할 수 있다.

  1. $aco = new Aco();

    //액세스 콘트롤 오브젝트를 몇개 작성한다:
    $aco->create(1, null, 'Electric Guitar');
    $aco->create(2, null, 'United States Army');
    $aco->create(3, null, 'Fans');

    // setParent() 를 사용하여, 이러한 오브젝트를 위한 그룹을
    // 작성할 수 있다. 그러나, 이 예에서는 넘어간다.
    // for this particular example

    // 그래서, ACO 를 하나 만든다 :
    $aco = new Aco();
    $aco->create($id, $parent, $alias);

상응하는 코맨드 라인 스크립트의 코맨드는 다음과 같다: $acl.php create aco <link_id> <parent_id> <alias>.

14.3.3. 권한 할당#

ACOs 와 AROs 를 작성하고 나면, 마지막으로 이 두 그룹에 권한을 분배한다. 이것은 Cake 의 코어 Acl 컴포넌트를 사용해 이뤄진다. 이 예제를 통해 보자:

  1. // 먼저, 컨트롤러를 통해
    // Cake 의 ACL Component로 액세스 할 필요 있다 :

    class SomethingsController extends AppController
    {
    // 이것을 AppController 내에 쓰는 방법도 있다.
    // 그러나, 이곳에서도 좋을 것이다.

    var $components = array('Acl');

    // 기억하라 : ACL 은 정보가 없으면 평소에 거부(deny)한다
    // 뭔가에 대해 체크가 있으면 그것은 거부된다.
    // ARO 가 ACO 에 액세스할 수 있도록 허가해 하자.

    function someAction()
    {
    //허가(ALLOW)

    // 여기에, ACO 에 대한 ARO 의 풀 액세스를 허가한다.
    $this->Acl->allow('Jimi Hendrix', 'Electric Guitar');
    $this->Acl->allow('Bob Marley', 'Electric Guitar');

    // 그룹에 대해 허가를 분배할 수 있다. 기억하고 있는가?
    $this->Acl->Allow('Presidents', 'United States Army');

    // allow() 메소드에는, $ action 이라는 3번째의 파라미터가 있다.
    // 이 파라미터를 사용하면, 부분적인 액세스를 지정할 수 있다.
    // $action 에는, create, read, update, delete 를 설정할 수 있다.
    // action 가 지정되어 있지 않으면, 풀 액세스라고 간주한다.

    // 보라. 손대지 말고, 아저씨:
    $this->Acl->allow('George Washington', 'Electric Guitar', 'read');
    $this->Acl->allow('Abraham Lincoln', 'Electric Guitar', 'read');

    //거부(DENY)

    //거부하는 작업도 같은 방법이다:

    //그의 임기가 끝나면...
    $this->Acl->deny('Abraham Lincoln', 'United States Army');

    }
    }

이 특수한 컨트롤러는 특히 편리하다 할 수 없지만, 주요 목적은 처리 동작 방법을 설명 하는 것이다. Acl 컴포넌트는 유저 관리 컨트롤러와 함께 사용하는 것이 가장 좋은 사용 방법이다. 시스템 중에 유저가 작성되면, 그의 ARO 가 작성되고 트리의 바른 위치에 배치된다. 그리고 ID 에 기초하여 특정 ACO 또는 ACO 그룹에 권한이 분배된다.

권한은 Cake 에 포함되어 있는 코맨드 라인 스크립트를 사용해 분배할 수도 있다. 구문은 모델 함수와 유사하며, $php acl.php help 를 실행하여 확인할 수 있다.

14.4. 권한 체크: ACL 컴포넌트#

Cake 의 ACL 의 사용에 있어서 권한의 체크가 가장 간단하다. Acl 컴포넌트의 하나의 메소드, check() 를 사용하는 것 뿐이다. 애플리케이션 중에서는, AppController 의 액션 안에 넣고 ACL 의 체크를 실행하는 것이 좋다. 여기에 넣을 경우 Acl 컴포넌트로 접근 가능하고 애플리케이션 전체에서 권한을 체크할 수 있다. 구현된 예는 다음과 같다:

  1. class AppController extends Controller
    {
    // 컴포넌트를 갖는다.
    var $components = array('Acl');

    function checkAccess($aco)
    {
    // 이 컴포넌트를 사용해 액세스를 체크한다.
    $access = $this->Acl->check($this->Session->read('user_alias'), $aco, $action = "*");

    //액세스 거부
    if ($access === false)
    {
    echo "액세스가 거부되었습니다";
    exit;
    }
    //액세스 허가
    else
    {
    echo "액세스가 허가되었습니다";
    exit;
    }
    }

기본적으로, AppController 의 Acl 컴포넌트 를 사용할 수 있게 하면, 모든 컨트롤러부터 보이게 되어 사용할 수 있다.

  1. // 기본형식은 다음과 같다:
  2. $this->Acl->Check($aro, $aco, $action = '*');


15. 데이터 새니타이제이션 (Data Sanitization)

  1. 15. 데이터 새니타이제이션 (Data Sanitization)
    1. 15.1 새니타이즈 사용하기
    2. 15.2. SQL 과 HTML 를 위한 데이타 안전하게 하기

15. 데이터 새니타이제이션 (Data Sanitization)#

15.1 새니타이즈 사용하기#

Cake 에는 새니타이즈 기능이 있다. 악의가 있는 공격을 갖은 유저의 송신 데이터나, 다른 바람직하지 않은 데이터를 없애는 클래스이다. 새니타이즈는 핵심 라이브러리 중 하나이기 때문에, 코드 내의 어디에서든 사용할 수 있지만, 컨트롤러나 모델내에서 사용하는 것이 가장 좋다.

  1. // 먼저 핵심 라이브러리를 인클루드한다 :

    uses('sanitize');

    // 다음 새로운 Sanitize 객체를 작성한다 :

    $mrClean = new Sanitize();

    // 여기에서, Sanitize 를 이용해 데이터를 새니타이즈하는 것이 가능하다.
    // (메소드의 설명은 다음섹션에 이어진다)

15.2. SQL 과 HTML 을 위한 데이타 안전하게 하기#

이 섹션에서는 새니타이즈의 몇가지 함수 사용 방법에 대해서 설명한다.

  • paranoid
    • string $string
    • array $allowedChars

이 함수는 대상이 되는 $string 중에서, 일반적인 영문, 숫자외에는 strip 한다. 그러나, $allowed 배열을을 지정하여, 특정 문자를 예외 처리할 수도 있다.

  1. $badString = ";:<script><html>< // >@@#";
  2. echo $mrClean->paranoid($badString);
  3. // 출력결과: scripthtml
  4. echo $mrClean->paranoid($badString, array(' ', '@'));
  5. // 출력결과: scripthtml @@

이 메소드는 유저가 송신한 데이타를 기존 HTML 레이아웃내에 표시하기 위해 도와준다. 레이아웃이 유저에 의해 깨지거나, 블로그의 코멘트나, 포럼의 포스트중에 이미지나 스크립트를 삽입하는 것을 불가능하도록 하고 싶을 때 특히 유용하다. $remove 옵션이 true 가 되어 있으면, 모든 HTML 은 HTML entity로 대신하여, 삭제된다.

  1. $badString = '<font size="99" color="#FF0000">HEY</font><script>...</script>';

    echo $mrClean->html($badString);

    // 출력결과: &lt;font size=&quot;99&quot; color=&quot;#FF0000&quot;&gt;HEY&lt;/font&gt;&lt;script&gt;...&lt;/script&gt;

    echo $mrClean->html($badString, true);

    // 출력결과: font size=99 color=#FF0000 HEY fontscript...script

  • sql
    • string $string

시스템의 현재의 magic_quotes_gpc 설정에 기초하여, SQL 구문을 슬래시를 붙혀 escape 한다.

  • cleanArray
    • array @$dirtyArray

이 함수는, 배열 전체 (예를 들어 $this->params['form'] 등) 을 조작할 때 주로 사용된다, 다목적 클리너이다. 이 함수는 하나의 배열을 클린한다. 배열은 레퍼런스로 전달 받기 때문에, 무엇도 반환하지 않는다. 다음과 같은 클리닝 조작을 (재귀적으로) 배열 각 요소에 대해 행할 수 있다 :

  1. Odd space (0xCA 를 포함) 는 일반적 스페이스로 바꾼다.

  2. HTML 은 대응하는 HTML entitiy 로 치환한다 (\n 를 <br> 로 변환하는 것 포함).

  3. 특수문자를 더블 체크하고 SQL의 보안을 높이기 위해 캐리지 리턴 (CR) 을 없앤다.

  4. SQL 에 슬래시를 추가한다 (위에 설명한 SQL 함수를 호출한다).

  5. 유저가 입력한 백슬래시를 안전한 (trusted) 백슬러시로 바꾼다.


16. 세션 컴포넌트(The Cake Session Component)

  1. 16. 세션 컴포넌트 (The Cake Session Component)
    1. 16.1. 세션 저장 옵션
    2. 16.2. Cake 세션 컴포넌트 사용하기

16. 세션 컴포넌트 (The Cake Session Component)#

16.1. 세션 저장 옵션#

Cake 에는 현재 세션 데이터의 보존 방법으로 3가지가 제공되고 있다: Cake 를 인스톨 한 디렉토리내의 임시 파일, PHP 의 기본값을 사용하는 것, 그리고, 데이터베이스로 나열하는 방법이다. 기본값으로, Cake 는 PHP 의 기본 설정을 사용하게 되어 있다. 임시 파일이나 데이터베이스를 사용도록 변경하려면, /app/config/core.php 에 있는 코어 설정 파일을 수정한다. CAKE_SESSION_SAVE 정수를 애플리케이션의 필요에 따라, 'cake', 'php', 'database' 중 하나로 고친다.

예 16.1. core.php 세션 설정

  1. /**
    * CakePHP 데이터베이스 또는 파일로, 3가지 방법으로 세션을 저장한다.
    * 사용하고 싶은 방법으로 설정한다.
    * 원하는 저장 핸들러를 사용하고 싶을 경우,pp/config/이름.php 으로 작성한다.
    * 이름으로 file 이나 database 를 "사용해선 안된다".
    * 아래의 이름을 일부 사용하기 바란다.
    *
    * cake 로 설정하면 /cakedistro/tmp 디렉토리에 저장된다
    * php 로 설정하면 php 기본 설정을 사용한다
    * database 로 설정하면 데이터베이스를 사용한다
    *
    *
    */
    define('CAKE_SESSION_SAVE', 'php');

데이터베이스에 세션 저장시, 데이타베이스 테이블을 작성해야 한다. 그 테이블의 스키마는, /app/config/sql/sessions.sql 에 있다.

16.2. Cake 세션 컴포넌트 사용하기#

Cake 의 세션 컴포넌트는 세션 정보를 주고 받기 위해 사용한다. 이 중에는 세션에 대한 기본적인 읽고 쓰기가 포함되고 있지만, 에러 메세지와 수령 메세지("데이타가 저장되었습니다.")를 위해서 세션을 이용하는 기능등도 포함되고 있다. 세션 컴포넌트는 기본적으로 모든 Cake 의 컨트롤러내에서 사용할 수 있다.

자주 사용하는 함수에는 다음과 같은 것이 있다:

  • check
    • string $name

$name 으로 지정한 키가 세션내에 설정 되어 있는지를 체크한다.

  • del
    • string $name
  • delete
    • string $name

$name 으로 지정된 세션을 삭제한다.

  • error

CakeSession 컴포넌트가 생성한 마지막 에러메시지를 반환한다. 주로 디버깅에 사용한다.

  • flash
    • string $key = 'flash'

setFlash() 에 의해 세션으로 설정된 마지막 에러메시지를 반환한다. $key가 설정되어 있으면,그 키에 저장된 마지막 메시지를 반환한다.

  • read
    • string $name

$name 으로 지정된 세션 변수를 반환한다.

  • renew

새로운 세션 ID 를 작성하고, 현재의 활성화된 세션을 갱신합니다. 이전의 세션은 삭제되고, 지난 세션 데이터는 새로운 세션에 전달된다.

  • setFlash
    • string $flashMessage
    • string $layout = 'default'
    • array $params
    • string $key = 'flash'

$flashMessage 로 지정된 메세지를 세션에 입력한다. (그 다음, flash() 로 출력된다. )

$layout 이 'default' 로 설정되면, 메세지는 '<div class="message">' .$ flashMessage.'</div>' 로 저장된다. $default 가 '' 로 설정 되어 있으면, 메세지는 전달받은 대로 저장된다. 다른 값을 전달 받은 경우, 메세지는 $layout 에 지정된 Cake 의 뷰 중에 저장된다.

Params 는 차후의 사용을 위해서 이 함수내에 포함되어 있지만, 상세한 것은 다시 체크바란다.

$key 변수에 flash 메시지를 특정 키에 보존할 수 있다. 키에 기초한 flash 메세지를 불러 오는 동작에 관한 것은 flash() 를 참조한다.

  • valid

세션에 valid 되어 있으면 true 를 반환한다. read() 동작으로 세션에 접근하기 전에 valid 인지 아닌지 체크하는 것으로, 유용하다.

  • write
    • string $name
    • mixed $value

$name 과 $value 로 지정된 변수를 활성화된 세션에 입력한다.


17. 리퀘스트 핸들러 컴포넌트 (The Request Handler Component)

  1. 17. 리퀘스트 핸들러 컴포넌트 (The Request Handler Component)
    1. 17.1. 소개
    2. 17.2. 클라이언트/리퀘스트 정보 얻기
    3. 17.3. 데이터 삭제(Stripping)
      1. 17.4. 기타 유용한 함수

17. 리퀘스트 핸들러 컴포넌트 (The Request Handler Component)#

17.1. 소개#

리퀘스트 핸들러 컴포넌트는 들어온 HTTP 리퀘스트에 관한 정보를 Cake 가 산출하기 위해서 사용한다. AJAX 리퀘스트에 관하여 좀 더 상세한 정보를 컨트롤러에 취득하거나, 원격 클라이언트의 IP 주소나 리퀘스트 타입을 취득하거나, 출력으로부터 바람직하지 않은 데이터를 제거(strip)할 때 사용할 수 있다. 리퀘스트 핸들러 컴포넌트를 사용하려면, 컨트롤러의 $component 배열에 이것을 지정한다.

  1. class ThingsController extends AppController

  2. {

  3. var $components = array('RequestHandler');

  4. // ...

  5. }

17.2. 클라이언트/리퀘스트 정보 얻기#

바로 보자 :

  • accepts
    • string $type

$type 에 기준하여 클라이언트가 접수한 content-types 를 반환한다. null 이나 아무것도 전달되지 않았을 경우, 클라이언트가 접수한 content-types 를 배열에 반환한다. 문자열을 전달 받은 경우, content-type map(setContent() 참조)에 대해 $type 을 검사하고, 클라이언트가 그 종류를 접수하는 경우에는 true 를 반환한다. $type 이 배열인 경우, 각 문자열을 개별로 평가된다. accepts() 는, 그 중 하나가 수락된 content-type 과 일치할 경우에 true 를 반환한다.

예를 들면 :

  1. class PostsController extends AppController
    {
    var $components = array('RequestHandler');

    function beforeFilter ()
    {
    if ($this->RequestHandler->accepts('html'))
    {
    // 클라이언트가 HTML (text/html) 응답을 수락하는 경우에만 코드를 실행한다.
    }
    elseif ($this->RequestHandler->accepts('rss'))
    {
    // RSS-only 코드 실행
    }
    elseif ($this->RequestHandler->accepts('atom'))
    {
    // Atom-only 코드 실행
    }
    elseif ($this->RequestHandler->accepts('xml'))
    {
    // XML-only 코드 실행
    }

    if ($this->RequestHandler->accepts(array('xml', 'rss', 'atom')))
    {
    // 클라이언트가 다음 중 아무것이나 수락하면 실행 : XML, RSS or Atom
    }
    }
    }

  • getAjaxVersion

Prototype JS 라이브러리를 사용하고 있는 경우, AJAX 리퀘스트로 설정 되어 있는 special header 를 fetch 가능하다. 이 함수는, 사용하고 있는 Prototype 의 버젼을 반환한다.

  • getClientIP

리모트 클라이언트의 IP 주소를 반환한다.

  • getReferrer

리퀘스트가 시작된 서버의 이름을 반환한다.

  • isAjax

현재의 리퀘스트가 XMLHttpRequest 이면 true 를 반환한다

  • isAtom

클라이언트가 Atom feed content (application/atom+xml)를 수락하면 true 를 반환한다.

  • isDelete ( ) ;

현재의 리퀘스트가 DELETE 에 의한 것이면 true 를 반환한다.

  • isGet ( ) ;

현재의 리퀘스트가 GET 에 의한 것이면 true 를 반환한다.

  • isMobile ( )

user agent 문자열이 모바일 웹브라우저와 일치한 경우에 true 를 반환한다.

  • isPost

현재의 리퀘스트가 POST 에 의한 것이면 true 를 반환한다.

  • isPut

현재의 리퀘스트가 PUT 에 의한 것이면 true 를 반환한다.

  • isRss

클라이언트가 RSS feed content (application/rss+xml) 를 수락하면 true 를 반환한다.

  • isXml

클라이언트가 XML content (application/xml or text/xml) 를 수락하면 true 를 반환한다.

  • setContent
    • string $name
    • string $type

accepts() 와 prefers() 가 사용한 content-type alias mapping 을 추가한다. $name 은 mapping 명(문자열)에, $type 은 문자열, 또는 문자열로 이루어지는 배열로, 각각 MIME type 이다. 미리 설정 되어 있는 것은 다음과 같다.

  1. // Name => Type
    'js' => 'text/javascript',
    'css' => 'text/css',
    'html' => 'text/html',
    'form' => 'application/x-www-form-urlencoded',
    'file' => 'multipart/form-data',
    'xhtml' => array('application/xhtml+xml', 'application/xhtml', 'text/xhtml'),
    'xml' => array('application/xml', 'text/xml'),
    'rss' => 'application/rss+xml',
    'atom' => 'application/atom+xml'

17.3. 데이터 삭제 (Stripping)#

때때로, 리퀘스트나 출력에서부터, 데이터를 삭제하고 싶을 때가 있다. 이런 동작을 하고 싶은 경우에는, 다음의 리퀘스트 핸들러 컴포넌드에 있는 함수를 사용한다.

  • stripAll
    • string $str

$str 의 여백, 이미지, 스크립트를 삭제한다. (stripWhitespace(), stripImages(), stripScripts() 를 사용).

  • stripImages
  • string $str

$str 의 HTML embedded images 를 모두 삭제한다.

  • stripScripts
  • string $str

$str 의 <script> 과 <style> 관련 태그를 삭제한다.

  • stripTags
    • string $str
    • string $tag1
    • string $tag2...

$str 의 $tag1, $tag2 등에 지정된 태그를 삭제한다.

  1. $someString = '<font color="#FF0000"><bold>Foo</bold></font> <em>Bar</em>';

    echo $this->RequestHandler->stripTags($someString, 'font', 'bold');

    // 출력 결과 : Foo <em>Bar</em>

  • stripWhiteSpace
    • string $str

$str 의 공백을 삭제한다.

17.4. 기타 유용한 함수#

리퀘스트 핸들러 컴포넌트는 애플리케이션으로 AJAX 리퀘스트를 포함시킬 때에 특히 유용하다. setAjax() 함수는, AJAX 리퀘스트를 자동적으로 검출하기위해 사용되고, 리퀘스트에 대해 컨트롤러의 레이아웃을 AJAX 레이아웃으로 설정한다. 이것은 AJAX 의 뷰로서 이용할 수 있는 작은 뷰의 모듈을 작성할 수 있는 이점이 있다.

  1. // list.thtml

  2. <ul>

  3. <? foreach ($things as $thing):?>

  4. <li><?php echo $thing;?></li>

  5. <?endforeach;?>

  6. </ul>

  7. //-------------------------------------------------------------

  8. //The list action of my ThingsController:

  9. function list()

  10. {

  11. $this->RequestHandler->setAjax($this);

  12. $this->set('things', $this->Thing->findAll());

  13. }

일반적으로 브라우저 리퀘스트가 /things/list 에 이루어지면, 순서 없는 리스트(Unordered List)는, 애플리케이션의 기본 레이아웃으로 표시된다. 만일 URL 이 AJAX 동작 하나로서 리퀘스트 된 경우에는, 목록은 자동적으로 노출된 AJAX 레이아웃으로 표시된다.


18. 보안 컴포넌트 (The Security Component)

  1. 18. 보안 컴포넌트 (The Security Component)
    1. 18.1. 소개
    2. 18.2. 컨트롤러 액션의 보호
    3. 18.3. 부정한 리퀘스트의 처리
    4. 18.4. 고급 리퀘스트 인증

18. 보안 컴포넌트 (The Security Component)#

18.1. 소개#

보안 컴포넌트는 컨트롤러 액션을 악의가 있는 리퀘스트나 실수의 리퀘스트부터 보호한다. 이 컴포넌트는 어떤 액션을 리퀘스트 가능한지라고 하는 조건을 설정할 수 있고, 옵션으로서 필요조건에 이루어져 있지 않은 리퀘스트를 어떻게 다루는지도 설정할 수 있다. 이 보안 컴포넌트를 사용할 때에도, 컨트롤러의 $components 배열 중에, 'Security' 를 포함시키는 것을 잊지 않길 바란다.

18.2. 컨트롤러 액션의 보호#

보안 컴포넌트는 컨트롤러의 액션의 액세스를 제한하기 위한 두개의 주요한 메소드가 있다 :

  • requirePost
    • string $action1
    • string $action2
    • string $action3...

지정한 액션을 실행하기 위해서는 POST 방식으로 리퀘스트 해야 한다.

  • requireAuth
    • string $action1
    • string $action2
    • string $action3...

유저의 세션에 기록되어 있는 인증키와 포스트 방식 데이타로 포함되는 인증 키를 체크하는 것으로, 리퀘스트가 애플리케이션내에서 행해진 것을 확인한다. 두가지가 일치하면, 액션의 실행이 허가된다. 그러나, 유연성을 가지게 하기 위해서, 이 체크는 폼 데이터가 실제로 POST 되지 않으면 실행되지 않는다. 액션이 일반적인 GET 방식으로 불려진 경우, requireAuth() 는 아무것도 하지 않는다. 보안을 최대한으로 높이기 위해서는 완전하게 보호하고 싶은 액션에 대해 requirePost() 와 requireAuth() 를 사용한다. 인증 키의 생성 방법과 그 최종 상태에 대해서, 섹션 4 에 더 상세한 설명이 있다.

그럼 먼저, 간단한 예를 보자 :

  1. class ThingsController extends AppController
  2. {
  3. var $components = array('Security');
  4. function beforeFilter()
  5. {
  6. $this->Security->requirePost('delete');
  7. }
  8. function delete($id)
  9. {
  10. //이것은 HTTP POST 리퀘스트에 의해 액션이 호출된 경우에만 발생한다.
  11. $this->Thing->del($id);
  12. }
  13. }

여기에서, 보안 컴포넌트에게 'delete' 액션에는 POST 리퀘스트가 필요함을 전달하고 있다. 일반적으로, beforeFilter() 메소드에 보안컴포넌트(또는 다른 컴포넌트 대부분)에 무엇을 행해야 하는지를 전달한다. 그것들은 액션이 호출되기 직전 beforeFilter() 가 호출되었을 때 실행된다.

필요한 정보는 이 정도다. 브라우저에 URL을 입력해 액션을 호출하고, 무엇이 일어나는지를 조사해 보자.

18.3. 부정한 리퀘스트의 처리#

만일, 정의한 보안상의 필요조건과 리퀘스트가 적합하지 않는 경우에는 무슨 일이 생긴것일까? 기본적으로, 리퀘스트는 black-holed 이다. 즉, 클라이언트에는 404 헤더가 송신되고, 애플리케이션은 즉각 exit 한다. 그러나, 보안 컴포넌트에는 blackHoleCallback 속성이 있고 있고, 컨트롤러에 정의되어 있는 커스텀 콜백 함수명을 설정할 수 있다.

단지 404 헤더를 내 보내 아무것도 하지 않는 것은 아니고, 이 속성에 의해 리퀘스트의 추가 검사, 다른 곳으로 리다이렉트, 부정 클라이언트의 IP 주소의 로그 기록 등을 할 수 있다. 그러나, 커스텀 콜백의 사용을 선택한 경우, 리퀘스트가 부정했던 경우에 애플리케이션을 종료시키는 것은 개발자의 책임이 된다. 작성한 콜백이 true 를 반환하면, 보안 컴포넌트는 잇따르고, 기타 정의된 필요조건에 대해서 리퀘스트를 검사한다. true 가 아니면 않으면, 검사를 마치고 애플리케이션은 금지되어 있지 않은 처리를 속행한다.

18.4. 고급 리퀘스트 인증#

requireAuth() 메소드에 의해 있는 액션으로 어떻게 하고, 또 어딘가 접근 가능한지 매우 상세한 지정이 가능하다. 그러나, 사용상의 특정 결정이 있고, 이 인증 메소드의 동작을 이해하면, 더 확실할 것이다. 앞서 말한 대로, requireAuth() 는 POST 데이터에 포함되는 인증 키와 유저의 세션 데이터에 포함되는 인증 키를 비교하는 것에 따라 동작한다. 따라서, 보안 컴포넌는 리퀘스트를 받는 컨트롤러와 리퀘스트를 작성하는 컨트롤러의 양쪽에 포함되어 있어야 한다.

예를 들면, CommentsController 에 있는 액션에 POST 하는 뷰가 PostsController 에 있는 경우, 보안 컴포넌트는 CommentsController (리퀘스트를 받고, 액션을 보호한다) 와 PostsController (리퀘스트가 작성된다)의 양쪽에 모두 포함시켜 놓아야 한다.

보안 컴포넌트가 로딩되면, 액션의 보호에 이용되어 있지 않은 경우에도, 다음의 작업이 실행된다 : 우선, 코어의 Security 클래스를 사용해 인증 키를 생성한다. 그리고, 그 키를 세션에 써 넣다. 동시에 유효기한과 추가 정보도 입력한다 (유효기한은, /app/config/core.php 에 있는 세션 보안 설정에 의해 결정된다). 다음, 컨트롤러에 인증 키가 설정되고, 차후에 참조될 수 있도록 한다.

그 후, 뷰 파일내에 $ html->formTag() 를 사용해 생성된 폼 태그에는 반드시 인증 키를 포함한 hidden input 필드를 추가한다. 이와 같이 하고, 형식이 POST 라면, 보안 컴포넌트는 세션내의 값과 리퀘스트의 마지막 새 세션의 값을 비교할 수 있다. 그 후, 인증 키는 재생성되고, 다음 리퀘스트를 위해서 세션이 갱신된다.


19. 뷰 캐싱 (View Caching)

  1. 19. 뷰 캐싱 (View Caching)
    1. 19.1. 개요
    2. 19.2. 동작 해설
      1. 19.2.1. 캐시를 작동시킨다
      2. 19.2.2. $cacheAction 컨트롤러 변수
      3. 19.2.3. 뷰의 내용에 지시 기록하기
      4. 19.2.4. 캐시 삭제
    3. 19.3. 주의사항

19. 뷰 캐싱 (View Caching)#

19.1. 개요#

0.10.9.2378_final 부터, Cake 는 뷰 캐싱(풀 페이지 캐싱 또한)을 지원한다. 이것은 농담이 아니다. 레이아웃과 뷰의 캐시가 가능하게 되었다. 또 캐시 메카니즘에 무시하기 위해 뷰 파트에 지시를 쓸 수 있다. 현명하게 사용하면 애플리케이션의속도를 상당한 수준까지 올릴 수 있다.

URL 을 리퀘스트하면, Cake 는 우선 리퀘스트 된 URL 이 이미 캐시되어 있는지를 확인한다. 캐시되어 있는 경우에는, dispatcher 를 우회하고 이미 표시되었던 페이지의 캐시를 반환한다. 만일 그 페이지가 캐시되어 있지 않으면 Cake 는 평소대로 작동한다.

Cake 의 캐시 기능을 ON 으로 하면, Cake 는 일반적으로 조작에 출력한 것을 다음의 유저를 위해서 보관한다. 다음에 그 페이지가 리퀘스트되면, Cake 는 그것을 캐시로부터 꺼낸다. 꽤 훌륭하다. 그 동작을 좀 더 알아 보자.

19.2. 동작 해설#

19.2.1. 캐시를 작동시킨다#

기본값으로, 뷰 캐생은 미작동으로 되어 있다. 작동시키려면, 우선 CACHE_CHECK 의 값을 false 에서 true 로 변경해야 한다. ( /app/config/core.php 내에 있다. ):

예 19.1. /app/config/core.php (일부)

  1. define ('CACHE_CHECK', true);

이 줄에, Cake 에 뷰 캐싱을 할 수 있도록 지시한다.

캐시를 하고 싶은 뷰에 대응하는 컨트롤러 중, 캐시 헬퍼(Cache helper)를 헬퍼(helpers) 배열 속에 추가한다.

예 19.2.

  1. var $helpers = array('Cache');

다음, 무엇을 캐시할 것인지 지정한다.

19.2.2. $cacheAction 컨트롤러 변수#

이 섹션은 Cake 에 무엇을 캐시할 것인지 지시하는 방법을 설명한다. 이것은 $cacheAction 이라 불리는 컨트롤러 변수를 설정하는 것으로 실행한다. $cacheAction 변수에 캐시 하고 싶은 액션, 그리고 데이터 중에 캐시를 유지하는 시간('초'로 지정)을 포함하는 배열로 설정한다. 시간의 값은 strtotime() 으로 변환할 수 있는 문자열 ( '1 day' 나 '60 seconds') 로 좋다.

ProductsController 가 있고 그 안에 몇가지 캐시하고 싶은 것이 있다고 하자. 다음 예는 $cacheAction 으로 Cake 에 컨트롤러 액션이 있는 부분을 캐시하도록 지정하는 방법이다.

19.3. $cacheAction 예

  2. //가장 많은 방문이 있는 프로덕트 페이지를 6시간 캐시한다
    var $cacheAction = array(
    'view/23/' => 21600,
    'view/48/' => 21600
    );

    //    모든 액션을 캐시한다. 이 예에서는, 프로덕트 리스트를 1일 유지한다:
    var $cacheAction = array('recalled/' => 86400);

    //원할 경우 모든 액션을     캐시하도록
    //    strtotime() 으로 변환할 수 있는 문자열로 캐시 시간을 설정할 수 있다:
    var $cacheAction = "1 hour";

    //또한 다음을 사용하여 액션의 캐싱을 정의하는 것도 가능하다 : $this->cacheAction = array()...

19.2.3. 뷰의 내용에 지시 기록하기#

캐시하길 원치 않는 뷰의 일부분이 있을 수 있다. 신제품을 두드러지게 하는 듯한 요소 등이 있는 경우, Cake 에 그 작은 부분 이외만 캐시하도록 지시하고 싶을 것이다.

뷰내에서 캐시길 원치 않은 내용을 <cake:nocache> </cake:nocache> 태그로 둘러싸게되면, 캐시 엔진이 건너 뛰게 된다.

예 19.4. <cake:nocache> 예

  1. <h1> New Products! </h1>
    <cake:nocache>
    <ul>
    <?php foreach($newProducts as $product): ?>
    <li>$product['name']</li>
    <?endforeach;?>
    </ul>
    </cake:nocache>

19.2.4. 캐시 삭제#

먼저, 데이터베이스의 변경이 있는 경우, Cake 는 자동적으로 캐시를 삭제한다는 점을 알아 둬야 한다. 예를 들어, 뷰가 Post 모델의 정보를 사용하고 있다면, Post 에 대해, INSERT, UPDATE, DELETE 가 실행되면, Cake 는 그 뷰의 캐시를 삭제한다.

그러나, 특정 캐시 파일을 직접 삭제하고 싶은 경우가 생길 수 있다. Cake 는 이를 위해 clearCache 함수가 존재하며 전역으로 작동한다 :

예 19.5. <cake:nocache> 예

  1. //컨트롤러명에 대한 모든 캐시된 페이지를 삭제.
    clearCache('controller');

    //컨트롤러 액션명에 대한 모든 캐시된 페이지를 삭제.
    clearCache('controller_action/');

    //컨트롤러_액션에 대한 모든 캐시된 페이지를 삭제.
    //주의 : 여러파라미터를 지정할 수 있다.
    clearCache('controller_action_params');

    //배열을 사용해 여러 캐시를 동시에 삭제할 수 있다.
    clearCache(array('controller_action_params','controller2_action_params));

19.3. 주의사항#

뷰캐시에 관하여 다음과 같이 몇가지 기억할 사항있다 :

  • 캐시를 작동시키려면 /app/config/core.php 내에 있는 CACHE_CHECK 를 true 에 설정한다.

  • 캐시를 하고 싶은 뷰에 대응하는 컨트롤러에 캐시 헬퍼를 helper 배열에 추가한다.

  • 특정 URL 을 캐시하려면, 컨트롤러내의 $cacheAction 을 사용한다.

  • 뷰의 특정 부분의 캐시를 중지시키려면 <cake:nocache> </cake:nocache> 태그로 둘러싼다.

  • Cake 는 데이터베이스의 변경이 생기면, 자동적으로 그 특정 캐시를 삭제한다.

  • 수동으로 캐시를 삭제할 때는, clearCache() 함수를 사용한다.