Page 1
1.1
1.2
1.2.1
1.2.2
1.3
1.3.1
1.3.2
1.3.3
1.4
1.4.1
1.4.2
1.4.3
1.4.4
1.5
1.5.1
1.5.2
1.5.3
1.5.4
1.5.5
1.5.6
1.5.7
1.5.8
1.6
1.6.1
1.6.2
1.6.3
1.6.4
1.6.5
1.6.6
1.6.7
1.6.8
1.7
1.7.1
1.7.2
1.7.3
1.7.4
1.7.5
1.7.6
1.7.7
차례
Introduction
시작하기
설치하기
업데이트
사이트설정
config디렉토리
.env
디버깅
XE구조
디렉토리구조
컴포넌트
플러그인
서비스
기본사용법
라이프사이클
라우팅(routing)
컨트롤러(Controllers)
요청(Request)
응답(Response)
프리젠터(Presenter)
뷰(View)
템플릿(BladeTemplate)
플러그인제작하기
플러그인개발시작하기
플러그인구조
버전관리(install,update)
사이트관리페이지추가
컴포넌트추가
개인설정페이지추가
회원가입인증/입력폼추가
다국어지원
서비스
캡챠(captcha)
설정(config)
문서(document)
키생성기(keygen)
메뉴/모듈(menu,module)
이벤트/인터셉션(event/interception)
파일/스토리지(file,storage)
1
Page 2
1.7.8
1.7.9
1.7.10
1.7.11
1.7.12
1.7.13
1.7.14
1.7.15
1.7.16
1.7.17
1.7.18
1.7.19
1.7.20
1.7.21
1.7.22
1.7.23
1.7.24
1.7.25
1.7.26
1.7.27
1.7.28
1.8
1.8.1
1.8.2
1.8.3
1.8.4
1.8.5
1.8.6
1.8.7
1.8.8
1.8.9
1.8.10
1.9
1.9.1
회원/인증(user/auth)
모바일(mobile)
권한(permission)
카테고리(category)
데이터베이스(database)
메일(email)
프론트앤드(frontend/assets)
이미지처리(Image,media)
세션(Session)
헬퍼(helpers)
쿠키(cookie)
UI오브젝트/폼빌더
카운터(counter)
휴지통(trash)
유효성검사(validation)
오류처리
...
테마(theme)
스킨(skin)
위젯(widget)
위젯박스(widgetbox)
컴포넌트제작가이드
테마
스킨
위젯
모듈
UI오브젝트
토글메뉴
다이나믹필드
다이나믹필드스킨
에디터
에디터툴
콘솔명령
...
2
Page 3
Xpressengine매뉴얼
이문서는Xpressengine을사용하거나,Xpressengine(이하XE)의플러그인을제작하고자하는개발자들을위한매뉴얼입니다.
이문서는아래와같은순서를가지고있습니다.
시작하기
우선XE를설치하는방법을설명합니다.XE를설치하기위한서버요구사항을알아보고리눅스와윈도우환경에서다양한방법으
로설치하는방법을안내합니다.
사이트설정
XE를설치한후,사이트의다양한기본설정을관리하는방법을알아봅니다.또,개발할때필요한디버깅환경을설정해보고디버
깅하는방법에대해알아봅니다.
XE구조
XE를구성하는여러디렉토리에대해알아보고,XE에서중요한개념인컴포넌트,플러그인,서비스에대하여설명합니다.
기본사용법
대부분의웹어플리케이션은브라우저로부터요청(Request)을받고,이를처리한후응답(Response)을반환하는라이프사이클을
가지고있습니다.라이프사이클에서는XE는어떤과정을거쳐요청을처리한후응답을보내는지에대해설명하고,라이프사이클
을거치면서만나는여러구성요소에대하여자세히설명합니다.
플러그인
XE를확장하는유일한방법인플러그인을제작하는방법과플러그인의구조에대하여설명합니다.
서비스
XE플러그인을개발할때,개발자는XE에서제공하는서비스(여러가지기능과라이브러리)를로드해서사용해야합니다.이장에
서는XE에서제공하는다양한서비스를사용하는방법에대하여안내합니다.
컴포넌트제작가이드
XE의여러가지컴포넌트(테마,스킨,모듈등)를제작하는방법에대하여자세히설명합니다.
현재누락된매뉴얼
laravelconfig사용법
플러그인캐싱안내
widgetwidgetboxtheme-widgetbox
Introduction
3
Page 4
설치하기
서버요구사항
XE를설치하기위해서는아래의요구사항이만족되어야합니다.
웹서버(apache,nginx등)PHP5.5.9이상
OpenSSLPHPExtensionPDOPHPExtensionMbstringPHPExtensionTokenizerPHPExtensionGDPHPExtensionFileInfoPHPExtension
MariaDBorMySQL5.1이상
터미널접속환경
디스크300M이상의여유공간
500M이상권장
알려진문제점
Cafe2410G광아우토반FullSSD웹호스팅서비스에서UTF-8(PHP7.0,mariadb-10.0.x)옵션만지원합니다.10G광아우
토반FullSSD의다른옵션을신청했을경우XE3는정상동작하지않을수있습니다.닷홈무제한웹호스팅서비스에서PHP7.0만지원합니다.그외의버전은지원하지않습니다.고도호스팅리눅스웹호스팅서비스에서UTF8(Mysql5.5)PHP7.0에테스트었으며Apache설정이슈로정상동작되지않습니다.문제해결을위해서배포되는.htaccess파일의하단에아래내용을추가해야합니다.
php_valuedefault_charsetUTF-8
<IfModulemod_url.c>
CheckURLOn
ServerEncodingUTF-8
</IfModule>
인스톨러를이용한설치
Linux
터미널에서아래와같이명령어를실행합니다.
$php-r"copy('http://start.xpressengine.io/download/installer','installer');"&&phpinstallerinstall
NOTE:일부호스팅환경에서작동이안될수있습니다.작동이안될경우,아래명령어를실행해보시기바랍니다.
$php-ddisplay_errors=1-derror_reporting=-1-dallow_url_fopen=1-r"copy('http://start.xpressengine.io/do
wnload/installer','installer');"&&phpinstallerinstall
Window
Git설치
터미널환경을위해Git을설치합니다.Git(준비중)다운로드및설치를참고하세요
Git-Bash를실행하고아래와같이명령어를실행합니다.
설치하기
4
Page 5
$php-r"copy('http://start.xpressengine.io/download/installer','installer');"&&phpinstallerinstall
위명령어를실행하면설치가시작됩니다.안내에따라설치정보를입력하십시오.
Git을이용한설치
Git을사용하면업데이트및현재개발중인코드를손쉽게적용할수있습니다.코어버전업데이트할때FTP없이Git을통해업데이트할수있습니다.
Github저장소파일을이용해설치합니다
$gitclonehttps://github.com/xpressengine/xpressengine.git
$cdxpressengine
$composerinstall
...
$phpartisanxe:install
...
위명령어를실행하면설치가시작됩니다.안내에따라설치정보를입력하십시오.
설치정보입력
1.데이터베이스,사이트정보입력
인스톨러캡쳐이미지(database,site정보입력/엔터)인스톨러는Database에필요한테이블을생성하고,기본적인설정파일을생성합니다.이작업은시간이오래걸릴수있습니다.
Host[localhost]:Database주소.기본localhostPort[3306]:Databaseprot.기본3306Databasename:DatabasenameUserId[root]:Databaseuserid.기본rootPassword[]:Databaseuserpasswordsiteurl[http://mysite.com]:홈페이지주소입력.
하위디렉토리에설치할경우하위디렉토리까지입력해야합니다.
Timezone[Asia/Seoul]:타임존정보를입력합니다.기본Asia/Seoul타임존에서원하는지역의시간대를입력하세요.
locale[]:언어를입력합니다.영어,한국어두가지언어를지원합니다.다른언어의설치는인스톨후에언어팩을업로드해서사용가능합니다.RC버전에서지원할예정입니다.
2.관리자정보입력
인스톨러캡쳐이미지(관리자정보입력)
Email:관리자이메일
Name[admin]:관리자이름.기본adminPassword:관리자비밀번호
Passwordagain:관리자비밀번호확인
3.디렉토리권한및서버정보수집동의
인스톨러캡쳐이미지(설정)
./storagedirectorypermission[0707]:/storage디렉토리권한설정.기본0707
./bootstrap/cachedirectorypermission[0707]:/bootstrap/cache디렉토리권한설정.기본0707
설치하기
5
Page 6
Doyouagreetocollectyoursystemenvironmentalinformation?[yes]:서버환경정보수집동의.기본yes더나은서비스제공을위해설치된서버의환경을수집하고있습니다.서버,웹서버,PHP,Database등의정보를수집
합니다.
설정파일을이용한설치
설정파일을사용하면더욱쉽게설치할수있습니다.설치하기전에아래와같이커맨드를실행하여설정파일을생성합니다.
$php-r"copy('http://start.xpressengine.io/download/installer','installer');"&&phpinstallermake
xe_install_config.yaml파일이생성됩니다.파일을열고설치정보를입력하세요.설치커맨드를실행합니다.--config및--no-interact옵션을사용하십시오.
$phpinstallerinstall--config=.xe_install_config.yaml--no-interact
설치옵션
--config=<configfile>설정파일을지정합니다.--no-interact대화형입력을사용하지않고설정파일의정보를사용하여자동으로설치합니다.이옵션을--config옵션과같이
사용해야합니다.--install-dir설치경로를지정합니다.지정하지않을경우현재디렉토리에설치합니다.
웹인스톨러로설치하기
Composer나Console접속을어려워하는사용자를위해웹브라우져에서설치할수있도록웹인스톨러를제공합니다.
웹인스톨러로설치후사이트운영에서권한관련문제가발생할수있습니다.
설치동영상
이해를돕기위해설치영상을마련했습니다.바로가기
FileZila
FTP는FileZila를사용해서설명합니다.FileZila는무료로사용이가능한프로그램입니다.다운로드
설치파일다운로드
http://start.xpressengine.io/download/latest.zip을다운로드합니다.다운로드받은zip파일의압축을풀고서버에업로드합니다.(약100MB)
디렉토리권한설정
웹서버가파일을쓸수있도록권한을설정합니다.
권한설정할때하위디렉터리로이동,모든파일과디렉터리에적용을반드시체크해주세요.
1. bootstrap/cache디렉토리권한설정
2. config/production디렉토리권한설정
3. plugins디렉토리권한설정
4. storage디렉토리권한설정
5. vendor디렉토리권한설정
6. composer.lock파일권한설정
설치하기
6
Page 7
웹인스톨러실행
설치할사이트에접속하면인스톨화면으로이동됩니다.
만약하위디렉토리에설치할경우는해당디렉토리로접속해주세요.
알려진문제점
FTP의파일업로드오류
파일업로드및디렉토리설정을완료하고웹인스톨러접근할때오류가발생하는경우가있습니다.이문제는FTP파일업로드중누락된있어발생할수있는문제입니다.해결하기위해서FTP로다시업로드해야합니다.동일조건을업로드할경우비슷한오류가계속해서발생할수잇으므
로중복파일건너뛰기옵션으로업로드해보는걸권장합니다.이미지
웹서버타임아웃
서버성능에따라웹서버타임아웃설정에의해설치에실패할가능성이있습니다.이문제는웹서버설정을변경해야
하는아주복잡한문제입니다.
윈도우개발환경
영상보기
설치하기
7
Page 8
업데이트
XE업데이트는웹페이지에서업데이트하는방법과,콘솔에서명령어를사용하는방법이있습니다.두가지방법모두미리FTP나git등으로XE코어의소스코드를최신코드로다운로드받아덮어씌운다음진행할수있습니다.
최신버전다운로드
우선XE최신버전을다운로드받은후,압축을풀어서XE가설치된디렉토리에덮어씌웁니다.
만약최신버전바로이전의버전을설치하여사용하고계셨다면,최신버전에서변경된파일만덮어씌우셔도됩니다.변경된파일은
XE3의Github릴리즈페이지에서다운로드받을수있습니다.변경된파일모음은각릴리즈마다changed.xxx.zip의형식으로첨부되어있습니다.
주의!만약,XE소스코드나플러그인소스코드를수정했다면미리백업해두시길바랍니다.
웹페이지에서업데이트적용
'사이트관리>플러그인&업데이트>XE3업데이트'페이지에서업데이트할수있습니다.
만약실제서비스서버와별도로개발서버(또는스테이지서버)를운영하고있다면,개발서버에서는'composerupdate실행안함'항목을체크해제한상태에서업데이트하십시오.composerupdate가함께실행되면서/vendor디렉토리에설치된의존라이
브러리들도최신상태로업데이트됩니다.
개발서버에서문제없이업데이트가됐다면,개발서버와서비스서버의소스코드를동기화한다음,서비스서버에서다시업데이
트를실행하십시오.이때에는'composerupdate실행안함'항목을체크한다음업데이트를실행하시기바랍니다.
콘솔에서명령어를사용하여업데이트적용
아래와같이콘솔커맨드를사용하여업데이트된소스코드를적용할수도있습니다.
phpartisanxe:update--skip-download
실제구동화면입니다.
업데이트
8
Page 9
$phpartisanxe:update--skip-download
UpdateXpressengine.
Updateversioninformation:
3.0.0-beta->3.0.0-beta.2
UpdatetheXpressenginever.3.0.0-beta.Thereisamaximummoistureisapplied.
Doyouwanttoupdate?(yes/no)[no]:
>yes
Runthecomposerupdate.Thereisamaximummoistureisapplied.
----------------------------------------------------------------
composerupdate
...
...
RunthemigrationoftheXpressengine.
--------------------------------------
updatingcategory..[skipped]
updatingconfig..[skipped]
updatingcounter..[skipped]
updatingdocument..[skipped]
updatingdynamicField..[skipped]
updatingeditor..[skipped]
updatingmedia..[skipped]
updatingmenu..[skipped]
updatingpermission..[skipped]
updatingplugin..[skipped]
updatingrouting..[skipped]
updatingsettings..[skipped]
updatingsite..[skipped]
updatingskin..[skipped]
updatingstorage..[skipped]
updatingtag..[skipped]
updatingtemporary..[skipped]
updatingtheme..[skipped]
updatingtranslation..[success]
updatingupdate..[skipped]
updatinguser..[skipped]
[OK]UpdatetheXpressenginetover.3.0.0-beta.2
skip-composer옵션
XE업데이트커맨드는내부적으로composerupdate명령을실행하여/vendor디렉토리에설치된의존라이브러리들도최신상태로업데이트합니다.--skip-composer옵션을사용하면composerupdate를실행하지않습니다.
만약실제서비스서버와별도로개발서버(또는스테이지서버)를운영하고있다면,개발서버에서는이옵션을사용하지않고업데
이트합니다.
개발서버에서문제없이업데이트가됐다면,개발서버와서비스서버의소스코드를동기화한다음,서비스서버에서다시업데이
트를실행하십시오.이때에는만약실제서비스서버와별도로개발서버(또는스테이지서버)를운영하고있다면,--skip-composer옵션을사용하여중복으로composerupdate를실행하지않도록할수있습니다.
설치된플러그인의업데이트
XE3코어를업데이트할때,설치된플러그인들은같이업데이트되지않습니다.업데이트를완료한후에는사이트관리자>플러그인
>플러그인목록에서플러그인들도업데이트하시기바랍니다.
업데이트
9
Page 12
config디렉토리
conifg디렉토리에는XE에서필요한다양한설정정보가포함된파일들이위치합니다.XE는사용자들이XE코어업데이트로부
터자유로워질수있도록Cascading방식의config확장기능을지원합니다.
Cascadingconfig
이것은사용자의XE에지정된환경변수에따라서로다른설정을사용할수있도록하는방법중하나입니다.이방식은지정된환경
변수와같은이름의config디렉토리내하위디렉토리에정의된설정값을사용하도록하고있습니다.
config/
├──production
│├──app.php
│├──database.php
│└──mail.php
├──app.php
├──auth.php
├──database.php
...
└──xe.php
config디렉토리는위와같은구조를가지고있습니다.같은이름을가지는config/app.php와config/production/app.php파일을열어보면아래와같은내용을확인할수있습니다.
//inconfig/app.php
return[
'debug'=>env('APP_DEBUG',false),
...
//inconfig/production/app.php
return[
'debug'=>false,
...
config/app.php파일에서는env함수를사용하여debug설정을하고있습니다.환경변수APP_DEBUG가정의되어있는경우
해당값을,그렇지않은경우false가지정되도록되어있습니다.그리고config/production/app.php파일에서는debug를false로설정하고있습니다.
이상황에서만약사용자의환경이production이고config/production/app.php의debug값을true로지정했다면,XE는config/app.php의설정을무시하고true를debug설정으로사용합니다.
환경변수의지정
환경변수는XE루트디렉토리에위치하고있는.env파일에서지정할수있습니다.
//in.envfile
APP_ENV=production
만약사용자가local개발환경에대한설정을별도로지정하여사용하고싶은경우,.env파일의APP_ENV항목에local로지정하고,config디렉토리내에local디렉토리를생성하여원하는설정을해당디렉토리에위치시키면됩니다.
XE를처음설치할때,환경변수는production으로지정됩니다.
.env에대한내용은다음문서에서자세히확인하실수있습니다.
configcaching
config디렉토리
12
Page 13
config파일에는env,storage_path와같은함수들을사용하여값을지정하고있습니다.이는config로부터값을얻고자할때,해당함수가실행되어야함을의미합니다.그래서XE에서는좀더빠르게설정값을불러올수있도록cache파일을생성할수있는커맨드를제공하고있습니다.
$phpartisanconfig:cache
콘솔에서위커맨드를실행하면config디렉토리내에설정된모든값이cache파일로생성되어집니다.
이미생성된configcache파일을제거하고싶은경우아래커맨드를통해제거할수있습니다.
$phpartisanconfig:clear
config디렉토리
13
Page 14
.env
.env파일은웹애플리케이션이위치한곳이어떤환경인지를지정하고현재환경에서사용되어질환경변수들이작성된파일입니
다.
.env파일은다음과같이작성되어집니다.
APP_ENV=local
APP_KEY=SomeRandomString
APP_DEBUG=true
APP_LOG_LEVEL=debug
APP_URL=http://localhost
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
...
위내용에서APP_ENV는현재환경을가리키는항목이고기타나머지는config에서사용되어질설정값들입니다.
환경변수추가하기
.env에서정의되는환경변수는php파일내에env함수를사용하여작성되어져야합니다.
'mysql'=>[
'driver'=>'mysql',
'host'=>env('DB_HOST','localhost'),
'database'=>env('DB_DATABASE','forge'),
'username'=>env('DB_USERNAME','forge'),
'password'=>env('DB_PASSWORD',''),
'port'=>env('DB_PORT','3306'),
'charset'=>'utf8',
'collation'=>'utf8_unicode_ci',
'prefix'=>'xe_',
'strict'=>false,
],
위설정에있는host,database,username,password,port는.env파일에작성되어값을지정할수있지만,나머지
charset,prefix등은.env파일을통해지정할수없습니다.
만약기존에존재하는설정이외에추가로설정이추가되고,이설정들이.env파일에의해값이지정되길원한다면,해당설정값을
env함수를이용해작성하면됩니다.
'new_setting'=>env('NEW_SETTING','');
그리고.env파일에해당값을지정해주면됩니다.
NEW_SETTING=MySetting
XE는설치시.env파일이존재하지않습니다.현재시스템의환경을지정하거나환경변수를추가하고자하는경우웹애플
리케이션root위치에.env파일을생성하여사용할수있습니다.
.env
14
Page 15
디버깅
XE를운영하거나개발하는과정에서오류가발생하는경우,발생한오류에대한자세한정보를필요로합니다.
디버깅모드활성화
XE를디버깅모드로설정하면오류가발생한경우,브라우저에오류의자세한정보가바로출력됩니다.디버그모드를활성화하기
위해서는config/production/app.php파일의debug값을true로설정해야합니다.
현재환경에맞는config파일을열어값을변경합니다.
//inconfig/production/app.php
...
'debug'=>true,
...
.env파일에debug모드를지정할수도있습니다.
//inconfig/production/app.php
...
'debug'=>env('APP_DEBUG',true)
...
위와같이config/production/app.php파일을설정하고,XE의루트디렉토리의.env파일에는아래와같이작성합니다.
#in.envfile
APP_DEBUG=true
주의!실제서비스가운영되는환경에서는디버깅모드를활성화하지마십시오.디버깅모드를활성화해놓으면,일반사용자도오류가발생할경우,브라우저에서오류의자세한내용을볼수있으므로,보안상문제가될수있습니다.
변수값확인하기(dump)디버깅을위해특정시점에서변수의값이나함수의반환값등을확인할필요가있습니다.XE는변수의값을브라우저에곧바로출력하거나로그파일에기록해놓을수있는기능을제공합니다.
브라우저에출력하기
XE는dump와dd함수를제공하고있습니다.dump는php의내장함수인var_dump와유사하지만브라우저상에서좀더보기좋은형태로표현해줍니다.dd는dump&die의약어로dump처리후라이프사이클을중단시킵니다.
dump($var);
dd($var);
여러가지값을동시에확인하고자한다면해당변수들을인자로나열하면됩니다.
dump($foo,$bar,$baz);
dd($foo,$bar,$baz);
로그파일에기록하기
디버깅
15
Page 16
Log파사드를이용해로그파일에내용을기록할수있습니다.로그파일의위치는storage/log/laravel.log입니다.
$var='a';
$bar='b';
Log::info($var.''.$bar);//[0000-00-0000:00:00]production.INFO:ab
log파일에는사용자의호출에의한기록이외에도장애시발생한오류정보등이기록되어있습니다.
디버깅
16
Page 17
디렉토리구조
루트디렉토리에서중요한항목만나열해보았습니다.XE는라라벨프레임워크의구조를기반으로하여커스터마이징되어있습니
다.
├──app/
├──assets/
├──bootstrap/
├──config/
├──core/
├──migrations/
├──plugins/
├──resources/
├──storage/
├──vendor/
├──web_installer/
├──artisan
├──composer.json
├──composer.user.json.example
└──index.php
루트디렉토리
appXE를구성하는컨트롤러나커맨드와같은어플리케이션레벨에해당하는코드가들어있습니다.
coreXE의핵심적인서비스및컴포넌트관리기능을위한클래스파일의모음입니다.
migrationsXE를설치하거나업데이트할때,실행되는코드가들어있습니다.
configXE의설정을저장하는파일들이들어있습니다.
assets웹페이지에서필요한stylesheet,javascript나이미지파일과같은assets파일들이들어있습니다.
plugins서드파티에서작성한플러그인이설치되는디렉토리입니다.
resourcesXE에서필요한템플릿파일이나언어파일이들어있습니다.
storage이디렉토리에는XE가실행되면서생성되는동적인파일들이저장되는위치입니다.회원프로필사진이나게시판첨부파일,그외
캐시파일등이이디렉토리에생성됩니다.
디렉토리구조
17
Page 18
vendor
composer를통해설치되는외부라이브러리들이설치되는디렉토리입니다.
web_installer
XE를웹을통해설치할때필요한파일들이들어있습니다.
artisan
터미널에서XE의명령을실행할때사용하는파일입니다.
composer.json
XE는PHP패키지관리툴인composer를사용합니다.이파일은composer툴에서사용하는설정파일입니다.
composer.user.json.example
XE에서는composer.json파일을직접수정하는것을금지합니다.composer.json파일은XE의소스코드에포함된파일이므로
XE를업데이트할때덮어씌워집니다.composer.json을수정하는대신composer.user.json파일을사용하여패키지를관리할수있습니다.composer.user.json.example파일이름을composer.user.json로바꿔서사용하시면됩니다.
index.php
웹브라우저를통해사이트에접근할때,항상실행되는PHP파일입니다.
디렉토리구조
18
Page 19
컴포넌트
컴포넌트는플러그인을통해등록할수있는XE의구성요소를통틀어지칭하는용어입니다.XE에는여러가지타입의컴포넌트가
있으며테마,스킨,에디터,위젯등이대표적입니다.각타입의컴포넌트들간에별다른공통점은없습니다.오직플러그인개발자
에의해등록됨으로써XE에추가될수있다는점이유일한공통점입니다.
플러그인개발자들은컴포넌트를제작하여XE에등록할수있습니다.만약자신만의컴포넌트를만들어사용하고싶다면먼저플러그인을하나생성한다음플러그인에컴포넌트를추가해넣습니다.그다음,플러그인을활성화하면자신이제작한컴포넌트를
XE에서사용할수있습니다.
예를들어새로운테마를만들어서자신의사이트에적용하고싶다면,먼저빈플러그인을하나생성합니다.그다음플러그인안에
서테마컴포넌트를만듭니다.만약테마와함께게시판스킨과위젯도만들어서사이트에적용하고싶다면,이두가지컴포넌트도
같은플러그인에추가합니다.플러그인을활성화하면플러그인에포함된컴포넌트들을XE에서모두사용할수있습니다.
XE1에서는각컴포넌트를따로XE에추가해야했습니다.XE3에서는하나의플러그인에컴포넌트를모두포함하여한번에
추가할수있습니다.배포할때에도마찬가지로각컴포넌트를따로배포하지않고,플러그인하나에담아서배포하면됩니
다.
컴포넌트인터페이스
모든컴포넌트는Xpressengine\Plugin\ComponentInterface인터페이스를구현한클래스형태여야합니다.이인터페이스는컴포
넌트를XE에등록할때필요한규칙을한정해주는역할을합니다.
컴포넌트타입
XE는기본적으로10개타입의컴포넌트를가지고있습니다.각타입의컴포넌트는모두
\Xpressengine\Plugin\ComponentInterface를구현한추상클래스를정의하고있습니다.여러분이어떤타입의컴포넌트를제작하
고싶다면,그타입의추상클래스를상속받는클래스를작성하면됩니다.
만약,테마컴포넌트를제작하려고한다면테마타입의추상클래스인\Xpressengine\Theme\AbstractTheme를상속받는클래스를
작성하십시오.
\Xpressengine\Plugin\ComponentInterface
├──테마-\Xpressengine\Theme\AbstractTheme
├──모듈-\Xpressengine\Module\AbstractModule
├──스킨-\Xpressengine\Skin\AbstractSkin
├──다이나믹필드-\Xpressengine\DynamicField\AbstractType
├──다이나믹필드스킨-\Xpressengine\DynamicField\AbstractSkin
├──토글메뉴-\Xpressengine\ToggleMenu\AbstractToggleMenu
├──UI오브젝트-\Xpressengine\UIObject\AbstractUIObject
├──위젯-\Xpressengine\Widget\AbstractWidget
├──에디터-\Xpressengine\Editor\AbstractEditor
└──에디터툴-\Xpressengine\Editor\AbstractTool
플러그인개발자는10개의타입이외에플러그인에서필요한타입을직접생성하여사용할수도있습니다.플러그인에서필요한컴포넌트타입을정의해놓으면,다른플러그인개발자들이정의한타입의컴포넌트를제작하여등록할수있습니다.
11개의XE의기본컴포넌트타입에대해간략히설명해보겠습니다.
테마
테마는웹사이트의디자인과웹페이지의전체적인레이아웃을결정합니다.
컴포넌트
19
Page 20
모든웹사이트는요청받은URL에해당하는메인콘텐츠를출력합니다.예를들어특정포스트를출력하는웹페이지라면,포스트의
제목,내용,댓글목록이메인콘텐츠에해당됩니다.XE는출력할메인콘텐츠를작성한후웹브라우저로보내기전에지정된테마에
게메인콘텐츠를전달합니다.테마는웹사이트에서공통적으로필요한헤더,푸터,사이드바영역과전달받은메인콘텐츠영역을
조립하여,완성된웹페이지를만듭니다.이때테마는웹페이지에디자인을결정짓는마크업을생성하고,스타일시트를로드합니다.
테마컴포넌트의제작법은이문서를참조하세요.
스킨
스킨의목적은출력되는웹브라우저에서출력되는모든html을교체가능하도록만들기위함입니다.가령,게시판플러그인은글목록보기,글보기등의페이지를제공합니다.만약게시판플러그인에스킨을적용하지않았다면,게시판플러그인의제작자가처음
제작한페이지디자인을바꿀수없습니다.하지만게시판에스킨시스템이적용되어있다면사이트관리자는다른개발자가제작한
게시판스킨을선택하고,사용할수있습니다.자신이만든플러그인의디자인을사이트관리자가교체할수있도록하려면스킨시스템을적용하십시오.
스킨컴포넌트의제작법은이문서를참조하세요.
다이나믹필드
다이나믹필드는사용자가관리자에서입력필드를추가해사용할수있도록해줍니다.
이는코어의패키지나개별플러그인별로지원여부를결정하며회원패키지,Board플러그인에서지원하고있습니다.다이나믹필드를이용해회원가입할때나이를입력받거나게시물을작성할때주소를입력받을수있습니다.
다이나믹필드스킨
다이나믹필드스킨은다이나믹필드를출력할때사용하는스킨입니다.
라디오버튼을제공하는다이나믹필드를사용할때테마나스킨에따라스타일을변경할수있도록합니다.
UI오브젝트
XE에서는화면에자주출력되는UI요소들이있습니다.텍스트인풋박스나셀렉트박스(select)와같은기본적인폼요소들이있고,테마선택기나메뉴선택기,또는권한설정UI와같이XE에서만사용되는특별한요소들도있습니다.UI오브젝트는이렇게자주
사용되는UI요소를개발자들이쉽게출력할수있는방법을제공합니다.
직접UI요소의마크업을작성하는대신,uio()함수를사용하여UI오브젝트를출력하십시오.선택된UI오브젝트는알아서html마크업을생성하여출력하고,스타일시트와스크립트파일도자동으로로드합니다.
위젯
위젯은다양한컨텐츠를사이트의어느곳에서나반복적으로출력할수있는방법을제공합니다.위젯은출력할컨텐츠에대한설정
정보를사용자로부터입력받고,입력받은설정정보를이용하여컨텐츠를생성합니다.그다음,미리정해진템플릿을통해컨텐츠
를출력합니다.사용자는화면에위젯을추가하기위해위젯생성UI를사용할수있습니다.위젯생성UI에추가할위젯의종류와
스킨을선택하고,위젯의설정정보를입력하면XML형식의위젯코드가생성됩니다.이위젯코드를테마나페이지의원하는위치에
삽입하면,XE는최종적으로위젯코드를HTML로변환하여화면에출력합니다.
모듈
...
에디터
...
에디터툴
컴포넌트
20
Page 21
...
토글메뉴
...
컴포넌트
21
Page 22
플러그인
플러그인은여러분이XE를확장할수있는유일한방법입니다.XE에새로운기능을추가하거나기본기능을변경하려고할때,또는테마나스킨과같은컴포넌트를XE에추가하고싶을때,플러그인을사용하십시오.
XE는오픈소스프로그램으로자유롭게코어소스코드를수정하여사용할수있지만,업데이트되는소스코드를계속적용하려면소스코드의수정을피해야합니다.대신플러그인을사용하면XE소스코드의수정을피할수있습니다.또한플러그인을다른XE사용자들과공유할수도있습니다.
각플러그인은고유한이름을가지는하나의디렉토리로구성되며,/plugins디렉토리에등록됩니다.
번들플러그인
XE는사용자들이자주사용할만한플러그인을XE에포함하여배포하고있습니다.
alicegoogle_analyticsorientatorpageboardcommentckeditorclaimexternal_pagesocial_loginnews_client
플러그인상태
각플러그인은활성화또는비활성화상태를가집니다.어떤플러그인을/plugins디렉토리에추가한다고해도처음에는비활성
화상태이기때문에바로작동되지않습니다.플러그인을활성화(activate)시켜야비로소플러그인이작동합니다.플러그인을활성
화시키려면사이트관리자>플러그인>플러그인목록에서원하는플러그인을활성화시키십시오.
개발모드플러그인
XE자료실을통해설치하지않은플러그인을개발모드플러그인이라고합니다.
사이트관리자는XE자료실을통해다른개발자들이배포한플러그인을다운로드받을수있습니다.물론,다른개발자의플러그인
소스코드를/plugins에직접추가하여사용할수도있으며,여러분이직접생성한플러그인을추가하여사용할수도있습니다.
XE자료실을통해설치하지않은플러그인은터미널에서반드시플러그인디렉토리로이동후composerupdate명령을실행해주
어야합니다.이명령을실행하면,플러그인디렉토리에vendor디렉토리가생성됩니다.따라서개발모드플러그인은vendor디렉토리를가집니다.
직접설치한플러그인에서composerupdate를실행하지않으면autoload가등록되지않아제대로작동하지않을수있습니
다.또XE에서는자료실을통해설치한플러그인으로인식하여오작동을일으킬수있습니다.
플러그인
22
Page 23
서비스
소개
XE가앞서설명한라이프사이클에따라요청을처리하고응답하는과정을수행하는동안,우리는매우다양한작업을실행해야합니다.때로는데이터베이스에서자료를검색하거나자료를저장하기도하고,이메일을전송하거나세션이나쿠키를다뤄야할때도
있습니다.개발자들이이런작업을수행하는코드를모두직접구현한다면매우힘든개발이될것입니다.
XE는개발자들이원하는기능을쉽게구현할수있도록많은서비스를제공합니다.아래목록은라라벨이기본으로제공하는프레
임워크레벨의주요서비스목록입니다.
AuthCacheConfigConsoleContainerCookieDatabaseEncryptionEventsFilesystemHashingLogMailPaginationQueueRedisRoutingSessionTranslationValidationView
XE는라라벨이제공하는위서비스이외에도문서(document),파일(storage),회원(user)관리와같은CMS어플리케이션레벨의
서비스를많이제공합니다.XE에서제공하는서비스은아래목록과같습니다.
CaptchaCategoryConfigCounterDatabaseDocumentDynamicFieldEditorFrontendInterceptionKeygenMediaMenuModulePermissionPlugin
서비스
23
Page 24
PresenterRegisterRoutingSeoSettingsSiteSkinStorageTagTemporaryThemeToggleMenuTranslationTrashUIObjectUserWidget
각각의서비스가제공하는기능과용도를충분히알고있다면훨씬편하게플러그인을개발할수있습니다.본매뉴얼의서비스카테고리에서각서비스의기능과사용법을자세히안내하고있습니다.
서비스로드하기
파사드를사용하여로드하기
내코드에서필요한서비스를로드하는방법으로XE는파사드를제공합니다.파사드는어떤서비스를대신하는프록시역할하며,Static클래스형식으로사용할수있습니다.
//User서비스사용
$user=\XeUser::find($userId);
위예제에서XeUser는파사드입니다.이파사드를통해User서비스를사용할수있습니다.위코드와같이파사드의find메소
드를호출하면실제로는\Xpressengine\User\UserHandler클래스의인스턴스의find메소드가호출됩니다.
서비스컨테이너를사용하여로드하기
파사드를사용하는대신,서비스컨테이너로부터직접서비스를로드할수있습니다.XE에서제공하는모든서비스는'서비스컨테
이너'에등록됩니다.내코드에서어떤서비스를로드해야할때,서비스컨테이너에게그서비스를달라고요청하면,서비스컨테이
너는요청받은서비스를반환해줍니다.보통app()함수나App파사드를사용하여서비스컨테이너로부터필요한서비스를로드합니다.
//User서비스로드
$userHandler=app('xe.user');
//or
$userHandler=\App::make('xe.user');
//User서비스사용
$user=$userHandler->find($userId);
보통서비스는특정클래스의싱글톤인스턴스입니다.그클래스는개발자가서비스를이용할때편하게사용할수있는메소드(인터페이스)를가지고있습니다.
사실,서비스컨테이너는서비스관리에국한된역할을하는것은아닙니다.정확히말하면,서비스컨테이너는다양한클래스의의존성을관리하는강력한도구입니다.서비스컨테이너에대하여더알고싶다면,이문서를참고하세요.
서비스
24
Page 26
라이프사이클
여러분이XE를사용하거나XE의플러그인을개발하려고한다면,XE사용법,플러그인제작법,그리고XE에서제공하는여러가지
서비스의사용법을숙지하는것만으로도충분히많은것을할수있습니다.하지만여러분이좀더고도의기능을필요로하는플러
그인을만들거나XE를제대로사용하고싶다면,XE가어떤방식과구조로요청을처리하는지에대한전체적인흐름을알고있어야
합니다.전체적인흐름을알고있지않다면결국한계에도달할것이고,전체적인흐름을알기위해노력하는시점이올것입니다.
XE의라이프사이클은크게두가지경우로나눌수있습니다.사용자들의웹브라우저로부터http요청을받았을때,이를처리하고
응답하는일반적인경우와,사이트관리자가ssh와같은콘솔에접근하여php명령을실행시킬경우가있습니다.이문서에서는더일반적으로생각할수있는http요청을처리하는경우에대하여살펴보겠습니다.
아래다이어그램은XE의요청처리흐름을개략적으로보여줍니다.
index.php사용자의웹브라우저로부터http전송요청이들어올경우,XE는항상index.php파일을실행시킵니다.index.php파일이그리
많은코드를가지고있는것은아닙니다.
가장먼저index.php는composer를통해생성된autoload파일을로드합니다.autoload파일을로드함으로써XE는php파일을
include하지않고자동으로로드할수있게됩니다.
그다음으로서비스컨테이너를생성합니다.서비스컨테이너는생성되자마자주요서비스인라우팅(routing)서비스와이벤트
(events)서비스를등록합니다.
세번째로는http요청을처리하기위한Http커널을생성합니다.그리고현재http요청에대한정보를가지는Request인스턴스를
생성합니다.
마지막으로Request인스턴스를커널에게전달하여http요청의본격적인처리를시작합니다.
Http커널
Http커널의주목적은단순하게Request를처리하고,브라우저로돌려줄응답(HttpResponse)를만드는것입니다.
Http커널은Illuminate\Foundation\Http\Kernel를상속받고있으며,생성된다음에는Request를처리할준비,즉부팅
(bootstrapping)을합니다.
Http커널은부팅과정에서에러처리,로그설정,어플리케이션의실행환경의검사등실제로요청이처리되기전에수행해야되는
작업들을합니다.
또,Http커널은부팅과정에서데이터베이스,문서,회원과같은XE에서제공하는대부분의서비스를앞서생성된서비스컨테이너
에등록합니다.
라이프사이클
26
Page 27
NOTE:XE에서활성화되어있는플러그인들이부팅되는시점은서비스들이등록된바로다음입니다.
Http미들웨어
Http커널은Request처리할준비가완료되면,커널에등록된미들웨어들에게Request를전달합니다.Request는미들웨어들을
거친후컨트롤러에게전달됩니다.
Http미들웨어는글로벌미들웨어(globalmiddleware)와라우트미들웨어(routemiddleware)로구분할수있습니다.글로벌미들
웨어는모든요청에대해항상작동하는미들웨어이며,라우트미들웨어는특정라우트에등록된미들웨어입니다.Request에해당
하는라우트가선택되면그라우트에등록된미들웨어들이작동합니다.
미들웨어에대한자세한정보는라라벨문서를참고하시기바랍니다.
라우터
미들웨어를통과한Request는라우터에게전달됩니다.라우터는전달받은Request를전담하여처리할컨트롤러가누군지찾고,찾은컨트롤러를호출합니다.
컨트롤러
각각의Request는모두XE가해주길바라는정확한목적을가지고있습니다.예를들어,어떤요청은게시판글출력이목적일수도있고,또어떤요청은현재사용자를로그아웃처리해달라는게목적일수도있습니다.컨트롤러는Request의주목적을처리하
는역할을합니다.XE에는매우많은컨트롤러가이미포함돼있고,플러그인을통해서도많이추가될수있습니다.
컨트롤러는Request를처리하기위해여러가지서비스나모델그리고헬퍼함수들을가져다사용합니다.
프리젠터
컨트롤러가Request를처리하고나면Response를생성하기위해프리젠터를호출합니다.이때만약화면출력이필요하다면화면출력에필요한데이터와출력할때사용할스킨정보를프리젠터에게전달합니다.
프리젠터는컨트롤러가전달한데이터와스킨정보,그리고지정된테마정보등을조합해서완성된html문서를조립합니다.이렇게
완성된html문서는Response인스턴스가되어다시미들웨어를거친후브라우저로전송됩니다.
프리젠터는html타입의Response뿐만아니라,ajax요청에대한응답과같이json방식의Response도처리합니다.
라이프사이클
27
Page 28
라우팅(routing)라우터는Request의URI를판단하여Request를처리할담당컨트롤러를찾는역할을합니다.이장에서는라우트를정의하는
방법에대하여설명합니다.
기본적인라우팅
이미XE에는매우많은라우트가app/Http/routes.php파일안에정의되어있습니다.플러그인을개발할때에는각플러그인클래
스의boot메소드에라우트정의코드를작성하십시오.플러그인이boot될때라우트가등록됩니다.
가장기본적인라우트는URI와Closure하나로지정할수있습니다.
기본적인라우트
라우트를등록할때에는Route파사드를사용합니다.
//GETHttp메소드로요청될경우'HelloWorld'를화면에출력
Route::get('/',function()
{
return'HelloWorld';
});
Route::post('foo/bar',function()
{
return'HelloWorld';
});
Route::put('foo/bar',function()
{
//
});
Route::delete('foo/bar',function()
{
//
});
Closure대신컨트롤러를사용할수도있습니다.
Route::get('user/profile','UserController@showProfile');
여러HTTP메소드에라우트등록하기
Route::match(['get','post'],'/',function()
{
return'HelloWorld';
});
any메소드를사용하면모든http메소드에응답하는라우트를등록할수도있습니다.
Route::any('foo',function()
{
return'HelloWorld';
});
라우트에등록된URL을생성하려면url헬퍼함수를사용하면됩니다:
라우팅(routing)
28
Page 29
$url=url('foo');
라우트파라미터
라우트에서요청된URI세그먼트를얻을수있습니다:
기본적인라우트파라미터
Route::get('user/{id}',function($id)
{
return'User'.$id;
});
주의:라우트파라미터는-문자를포함하면안됩니다.(_)를사용하십시오.
선택적인라우트파라미터
Route::get('user/{name?}',function($name=null)
{
return$name;
});
Route::get('user/{name?}',function($name='John')
{
return$name;
});
name파라미터는옵션입니다.name파라미터가URL에포함되어있지않아도위라우트가작동됩니다.
정규표현식으로파라미터제약하기
Route::get('user/{name}',function($name)
{
//
})
->where('name','[A-Za-z]+');
Route::get('user/{id}',function($id)
{
//
})
->where('id','[0-9]+');
Route::get('user/{id}/{name}',function($id,$name)
{
//
})
->where(['id'=>'[0-9]+','name'=>'[a-z]+']);
이름이지정된라우트
이름이지정된라우트는지정된라우트에대한URL을생성하거나Redirect를할때편리함을제공합니다.as배열키를통해라우트에이름을지정할수있습니다.
라우팅(routing)
29
Page 30
Route::get('user/profile',['as'=>'profile',function()
{
//
}]);
컨트롤러액션에대해서도라우트이름을지정할수있습니다.
Route::get('user/profile',[
'as'=>'profile','uses'=>'UserController@showProfile'
]);
이제URL을생성하거나Redirect를하는데라우트이름을사용할수있습니다.
$url=route('profile');
$redirect=redirect()->route('profile');
라우트가파라미터를가지고있다면,route함수의두번째인자로파라미터를전달할수있습니다.주어진파라미터는자동으로
URL에추가됩니다.
Route::get('user/{id}/profile',['as'=>'profile',function($id){
//
}]);
$url=route('profile',['id'=>1]);
라우트그룹
때때로많은라우트들이URL세그먼트,미들웨어,네임스페이스등과같은공통의요구사항을공유하고자하는경우가있습니다.이러한옵션들을모든라우트에개별로각각지정하는대신에라우트그룹을통해서다수의라우트에속성을지정할수가있습니다.
속성값들을공유하는것은Route::group메소드의첫번째인자로배열을지정하면됩니다.
라우트미들웨어
라우트그룹에지정하는배열의middleware값에미들웨어의목록을정의함으로써그룹내의모든라우트에미들웨어가적용됩니
다.라우트미들웨어는배열에정의된순서대로실행될것입니다:
주의!라우트미들웨어는Http커널의미들웨어와는다른별개의기능입니다.
Route::group(['middleware'=>['foo','bar']],function()
{
Route::get('/',function()
{
//HasFooAndBarMiddleware
});
Route::get('user/profile',function()
{
//HasFooAndBarMiddleware
});
});
네임스페이스
그룹의속성배열에namespace파라미터를사용하여가룹의모든컨트롤러에네임스페이스를지정할수있습니다:
라우팅(routing)
30
Page 31
Route::group(['namespace'=>'Admin'],function()
{
//ControllersWithinThe"App\Http\Controllers\Admin"Namespace
Route::group(['namespace'=>'User'],function()
{
//ControllersWithinThe"App\Http\Controllers\Admin\User"Namespace
});
});
참고:기본적으로RouteServiceProvider에서포함하고있는routes.php파일에는라우트컨트롤들을위해서네임스페이
스가지정되어있습니다.따라서App\Http\Controllers의전체네임스페이스를따로지정할필요는없습니다.
라우트접두어지정하기
라우트그룹의접두어(prefix)는그룹의속성배열에prefix옵션을사용하여지정합니다:
Route::group(['prefix'=>'admin'],function()
{
Route::get('users',function()
{
//MatchesThe"/admin/users"URL
});
});
또한,prefix파라미터를라우트들의공통파라미터로지정할수있습니다:
라우트prefix안에서URL파라미터등록하기
Route::group(['prefix'=>'accounts/{account_id}'],function()
{
Route::get('detail',function($account_id)
{
//
});
});
또한,지정된파라미터변수의제약사항을정의할수도있습니다:
Route::group([
'prefix'=>'accounts/{account_id}',
'where'=>['account_id'=>'[0-9]+'],
],function(){
//DefineRoutesHere
});
Fixed라우트
각플러그인들은자유롭게라우트를추가하여사용할수있습니다.하지만서로다른플러그인들이동일한규칙의라우트를등록하
면문제가발생할수있습니다.XE는플러그인간라우트의충돌을방지하기위하여각플러그인에게별도의라우트공간(url세그먼
트)을할당합니다.
각플러그인에게할당된라우트규칙을사용하려면Route::fixed메소드를사용하십시오.
라우팅(routing)
31
Page 32
Route::fixed(<plugin_id>,function(){
//DefineRoutesHere
Route::get('/',...);
});
Route::fixed메소드의첫번째파라미터는플러그인의아이디를지정하면됩니다.Route::fixed를사용할경우,접두어(prefix)가/plugin/<plugin_id>으로자동으로지정됩니다.따라서위의코드는아래의Route::group을사용한코드와동일합니다.
Route::group(['prefix'=>'plugin/<plugin_id>'],function(){
//DefineRoutesHere
Route::get('/',...);
});
하지만,접두어에자동으로추가되는plugin은사이트관리자가설정에서변경할수있는값이므로반드시Route::fixed를사용
하십시오.
CSRF보호하기
XE에서는크로스사이트요청위조(cross-siterequestforgeries)으로부터응용프로그램을쉽게보호할수있습니다.크로스사이
트요청위조는악의적인공격의하나이며인증받은사용자를대신하여허가받지않은명령을수행합니다.
XE는사용자별CSRF"토큰"을자동으로생성합니다.이토큰은인증된사용자가실제로XE에요청을보내고있는지식별하는데사용됩니다.
Form에CSRF토큰삽입하기
<inputtype="hidden"name="_token"value="<?phpechocsrf_token();?>">
다음처럼Blade템플릿에서사용할수있습니다.
<inputtype="hidden"name="_token"value="{{csrf_token()}}">
일일이수동으로POST,PUT또는DELETE요청에대한CSRF토큰을확인할필요가없습니다.XE가자동으로요청중인토큰을
세션에저장되어있는토큰과일치하는지확인할것입니다.
메소드Spoofing-속이기
HTMLform은실제로PUT,PATCH와DELETE액션을지원하지않습니다.따라서PUT,PATCH이나DELETE로지정된라우트
를호출하는HTMLform을정의한다면_method의숨겨진필드를지정해야합니다.
_method필드로보내진값은HTTP요청메소드를구분하는데사용됩니다.다음예를참조하십시오:
<formaction="/foo/bar"method="POST">
<inputtype="hidden"name="_method"value="PUT">
<inputtype="hidden"name="_token"value="<?phpechocsrf_token();?>">
</form>
라우팅(routing)
32
Page 33
Http컨트롤러(HttpControllers)플러그인은플러그인클래스의boot메소드를통해라우트를등록할수있습니다.라우트를등록할때,특정URL의요청을처리
할로직을클로저로작성하는대신,별도의컨트롤러클래스에작성할수있습니다.성격이비슷한요청을처리하기위한컨트롤러
클래스를정의하십시오.
컨트롤러는App\Http\Controllers\Controller클래스를상속받아작성하십시오.
기본컨트롤러
다음은기본적인컨트롤러클래스의예제입니다:
<?phpnamespaceApp\Http\Controllers;
useApp\Http\Controllers\Controller;
classUserControllerextendsController{
/**
*Showtheprofileforthegivenuser.
*
*@paramint$id
*@returnResponse
*/
publicfunctionshowProfile($id)
{
returnview('user.profile',['user'=>User::findOrFail($id)]);
}
}
다음과같이컨트롤러의액션에라우트를지정할수있습니다:
Route::get('user/{id}','App\Http\UserController@showProfile');
컨트롤러&네임스페이스
컨트롤러의네임스페이스를지정할때에는반드시전체네임스페이스를다써주어야합니다.
Route::get('foo','Photos\AdminController@method');
namespace를사용하면반복되는namespace를생략할수있습니다.
Route::group(['prefix'=>'photos','namespace'=>'Photos'],function()
{
Route::get('admin','AdminController@method');
});
이름이지정된컨트롤러라우트
클로저라우트와같이컨트롤러라우트에이름을지정할수있습니다.
Route::get('foo',['uses'=>'FooController@method','as'=>'name']);
컨트롤러액션의URL구하기
컨트롤러(Controllers)
33
Page 34
컨트롤러액션에대한URL을생성하기위해서action헬퍼함수를사용합니다:
$url=action('App\Http\Controllers\FooController@method');
단순히컨트롤러의전체네임스페이스의대신클래스명만으로URL을생성하고싶은경우에는root컨트롤러네임스페이스를URL제너레이터에등록하면됩니다:
URL::setRootControllerNamespace('App\Http\Controllers');
$url=action('FooController@method');
실행중인컨트롤러액션의이름을찾고자한다면currentRouteAction메소드를사용하면됩니다:
$action=Route::currentRouteAction();
컨트롤러미들웨어
미들웨어는다음과같이컨트롤러라우트에지정합니다.
Route::get('profile',[
'middleware'=>'auth',
'uses'=>'UserController@showProfile'
]);
덧붙여미들웨어를컨트롤러의생성자에서지정할수도있습니다.
classUserControllerextendsController{
/**
*InstantiateanewUserControllerinstance.
*/
publicfunction__construct()
{
$this->middleware('auth');
$this->middleware('log',['only'=>['fooAction','barAction']]);
$this->middleware('subscribed',['except'=>['fooAction','barAction']]);
}
}
묵시적컨트롤러
XE에서는한번의라우팅등록으로컨트롤러를통해모든액션들을처리할수있는손쉬운방법을제공합니다.먼저
Route::controller메소드를사용하여경로를지정합니다:
Route::controller('users','UserController');
controller메소드는두개의인자를넘겨받도록되어있습니다.첫번째인자는컨트롤러로제어할URI이고,두번째는컨트롤
러의클래스명을의미합니다.이어서해당하는HTTP메소드이름을접두어로(get,post..)사용하는형태로컨트롤러의메소드를
추가합니다:
컨트롤러(Controllers)
34
Page 35
classUserControllerextendsController{
publicfunctiongetIndex()
{
//
}
publicfunctionpostProfile()
{
//
}
publicfunctionanyLogin()
{
//
}
}
위의경우에컨트롤러의index메소드는usersURI에대한루트주소에대한결과를반환합니다.
만약컨트롤러의메소드가여러개의단어로구성되어진형태라면"-"을통해서접속할수있는URI를제공하게됩니다.예를들어,UserController에다음과같은액션이정의되었다면URI는users/admin-profile과같이구성됩니다:
publicfunctiongetAdminProfile(){}
라우트에이름지정하기
컨트롤러라우트에어떤“이름”을지정하고자한다면controller메소드의세번째인자를통해서지정할수있습니다:
Route::controller('users','UserController',[
'anyLogin'=>'user.login',
]);
의존성주입&컨트롤러
생성자주입
XE의서비스컨테이너는모든컨트롤러의의존성을해결하기위해서사용됩니다.그결과컨트롤러가필요로하는의존객체들에
대해서생성자에서타입힌트로지정할수있게됩니다:
컨트롤러(Controllers)
35
Page 36
<?phpnamespaceApp\Http\Controllers;
useIlluminate\Routing\Controller;
useApp\Repositories\UserRepository;
classUserControllerextendsController{
/**
*Theuserrepositoryinstance.
*/
protected$users;
/**
*Createanewcontrollerinstance.
*
*@paramUserRepository$users
*@returnvoid
*/
publicfunction__construct(UserRepository$users)
{
$this->users=$users;
}
}
서비스컨테이너가의존성을해결을할수있다면타입힌트에지정할수는있습니다.
메소드인젝션-주입
생성자주입과더불어컨트롤러의메소드에서도타입힌트를통한의존성주입을할수있습니다.예를들어,메소드에서Request인스턴스를타입힌트를통해서주입할수있습니다:
<?phpnamespaceApp\Http\Controllers;
useIlluminate\Http\Request;
useIlluminate\Routing\Controller;
classUserControllerextendsController{
/**
*Storeanewuser.
*
*@paramRequest$request
*@returnResponse
*/
publicfunctionstore(Request$request)
{
$name=$request->input('name');
//
}
}
컨트롤러메소드가라우트인자로부터입력값을받아야한다면간단하게의존성지정뒤에인자를지정하면됩니다:
컨트롤러(Controllers)
36
Page 37
<?phpnamespaceApp\Http\Controllers;
useIlluminate\Http\Request;
useIlluminate\Routing\Controller;
classUserControllerextendsController{
/**
*Updatethespecifieduser.
*
*@paramRequest$request
*@paramint$id
*@returnResponse
*/
publicfunctionupdate(Request$request,$id)
{
//
}
}
컨트롤러(Controllers)
37
Page 38
요청(Request)XE는웹브라우저로부터요청을받으면,제일먼저index.php가실행되고index.php는현재요청에대한정보를담고있는
Request인스턴스를생성합니다.이Request인스턴스는XE가실행되는동안매우많은곳에서로드되어현재요청에대한정보
를참조할수있도록합니다.
Request인스턴스획득하기
파사드를이용한방법
Request파사드는컨테이너와결합된현재의Request에엑세스할수있도록해줍니다.예를들면:
$name=Request::input('name');
만약특정네임스페이스아래에서Request파사드를사용하고자한다면클래스상단부분에useRequest;구문을추가해야된다
는것을기억하십시오.
의존성주입을통한방법
현재의의존성주입을통해서HTTPrequest을획득하기위해서는여러분의컨트롤러생성자나메소드에서타입힌트를지정해야
합니다.XE의request인스턴스는\Xpressengine\Http\Request클래스의인스턴스입니다.이클래스는
\Illuminate\Http\Request를상속받고있습니다.
현재의request의인스턴스는서비스컨테이너에의해서자동으로주입될것입니다:
<?phpnamespaceApp\Http\Controllers;
useXpressengine\Http\Request;
useIlluminate\Routing\Controller;
classUserControllerextendsController{
/**
*Storeanewuser.
*
*@paramRequest$request
*@returnResponse
*/
publicfunctionstore(Request$request)
{
$name=$request->input('name');
//
}
}
만약컨트롤러메소드에서라우트파라미터를입력값으로받아야한다면의존성을지정한뒤에라우트파라미터를나열하면됩니
다:
요청(Request)
38
Page 39
<?phpnamespaceApp\Http\Controllers;
useXpressengine\Http\Request;
useIlluminate\Routing\Controller;
classUserControllerextendsController{
/**
*Updatethespecifieduser.
*
*@paramRequest$request
*@paramint$id
*@returnResponse
*/
publicfunctionupdate(Request$request,$id)
{
//
}
}
입력값검색하기
입력값검색하기
간단한메소드를통해서Xpressengine\Http\Request인스턴스모든사용자입력값에엑세스할수있습니다.request에서어떤
HTTP메소드를사용했는지에대해서는걱정할필요없이모든HTTP메소드에대해서같은방법으로입력값에대해엑세스가가능합니다.
$name=$request->input('name');
입력값이없을때기본값가져오기
$name=$request->input('name','Sally');
입력값이존재하는지확인하기
if($request->has('name'))
{
//
}
전체입력값가져오기
$input=$request->all();
Request입력중에서몇개의값만가져오기
$input=$request->only('username','password');
$input=$request->except('credit_card');
입력폼에배열로값이전달된다면‘점’으로구분하여입력값에엑세스할수있습니다:
$input=$request->input('products.0.name');
요청(Request)
39
Page 40
이전입력
XE는현재request의입력값을다음request까지유지하는방법을제공합니다.예를들어,폼의입력값체크에서에러가발생하면
작성한값들을다시채워줘야할필요가있을수있습니다.
입력값들세션에저장하기
flash메소드는현재의입력들을세션에저장하여사용자가다음번에request를보내도사용가능하게만들어줍니다.
$request->flash();
몇개의입력값만세션에저장하기
$request->flashOnly('username','email');
$request->flashExcept('password');
플래쉬&리다이렉트
대부분이전페이지로리다이렉트하면서입력값을플래슁하기를원하는데,이경우리다이렉트와함께입력값플래싱을메소드
체이닝으로사용할수있습니다.
returnredirect('form')->withInput();
returnredirect('form')->withInput(Request::except('password'));
이전입력값검색하기
이전Request에대해저장된입력값을검색하기위해서는Request인스턴스의old메소드를사용하면됩니다.
$username=Request::old('username');
블레이드템플릿안에서지난입력값을보여주려면old헬퍼함수를사용하는것이보다편리합니다:
{{old('username')}}
파일처리
Request인스턴스의file메소드를사용하면사용자가업로드한파일을엑세스할수있습니다.file메소드에의해반환되는
값은Symfony\Component\HttpFoundation\File\UploadedFile클래스의인스턴스입니다.이인스턴스의다양한메소드를사용하여
업로드된파일에대한정보를참조할수있습니다.
업로드한파일가져오기
$file=$request->file('photo');
파일이업로드되었는지확인하기
요청(Request)
40
Page 41
if($request->hasFile('photo'))
{
//
}
업로드한파일이유효한지판단하기
if($request->file('photo')->isValid())
{
//
}
업로드한파일이동하기
$request->file('photo')->move($destinationPath);
$request->file('photo')->move($destinationPath,$fileName);
기타파일메소드
그밖에도다양한메소드들이UploadedFile인스턴스에준비되어있습니다.추가적인메소들에대한정보는API문서를참고하십
시오.
기타Request에대한정보
Request클래스는Symfony\Component\HttpFoundation\Request클래스를상속하고있으며어플리케이션을위한HTTPrequest을확인하는많은메소드를제공하고있습니다.다음은몇몇예시들입니다.
RequestURI가져오기
$uri=$request->path();
Request가AJAX요청인지확인
if($request->ajax())
{
//
}
Request메소드확인하기
$method=$request->method();
if($request->isMethod('post'))
{
//
}
현재request가패턴에일치하는지확인하기
요청(Request)
41
Page 42
if($request->is('admin/*'))
{
//
}
현재requestURL가져오기
$url=$request->url();
요청(Request)
42
Page 43
응답(Response)
Response사용의제한
XE의기본프레임워크인라라벨에서는대부분의라우트나컨트롤러액션에서Illuminate\Http\Response의인스턴스나뷰를반환
하도록합니다.
하지만XE는웹브라우저로html형식의응답을보낼때,스킨과테마를적용한후보내야합니다.특별한경우가아니라면컨트롤
러에서Illuminate\Http\Response인스턴스나뷰를직접반환(return)하지마십시오.대신,프리젠터를사용하여반환하십시오.반드시프리젠터를사용해야만테마와스킨이적용되고위젯또한정상적으로출력됩니다.
리다이렉트
일반적으로리다이렉트Response는Illuminate\Http\RedirectResponse클래스의인스턴스이며,사용자를다른URL로리다이
렉트하는데필요한적절한헤더를포함하고있습니다.
리다이렉트반환하기
RedirectResponse인스턴스를생성하는데는몇가지방법이있습니다.가장간단한방법은redirect헬퍼함수를사용하는것입
니다.테스트를진행할때리다이렉트Response를생성하는모킹(Mock)은일반적으로잘하지않기때문에,대부분의경우에헬퍼
함수를사용하게됩니다.
returnredirect('user/login');
리다이렉트에플래시데이터와함께반환하기
새로운URL로리다이렉트이동하고플래시데이터를세션에저장하는것은일반적으로동시에진행됩니다.따라서편의성을높이
기위해RedirectResponse인스턴스를생성하고동시에메소드체인을통해플래시데이터를세션에저장할수있습니다:
returnredirect('user/login')->with('message','LoginFailed');
이전URL로리다이렉트
예를들어,폼전송후에,사용자를이전URL로리다이렉트시키고자하는경우가있을수있습니다.이런경우에는back메소드
를사용하면됩니다:
returnredirect()->back();
returnredirect()->back()->withInput();
이름이지정된라우트로리다이렉트하기
전달인자없이redirect헬퍼함수를호출할때에는Illuminate\Routing\Redirector의인스턴스가반환됩니다.따라서
Redirector인스턴스의메소드를사용할수있습니다.예를들어,이름지지정된라우트로이동하는RedirectResponse를생성하
고자한다면route메소드를사용할수있습니다:
returnredirect()->route('login');
이름이지정된라우트로파라미터와함께리다이렉트하기
응답(Response)
43
Page 44
라우트에전달해야할파라미터가있다면route메소드의두번째인자로전달하면됩니다.
//ForaroutewiththefollowingURI:profile/{id}
returnredirect()->route('profile',[1]);
이름지지정된라우트로파라미터이름과함께리다이렉트하기
//ForaroutewiththefollowingURI:profile/{user}
returnredirect()->route('profile',['user'=>1]);
컨트롤러액션으로리다이렉트하기
이름이지정된라우트로이동하는RedirectResponse인스턴스를생성하는것과비슷하게컨트롤러액션으로리다이렉션할수있습니다.
returnredirect()->action('App\Http\Controllers\HomeController@index');
주의:URL:setRootControllerNamespace를통해서컨트롤러의루트네임스페이스가지정되었다면,전체네임스페이스를지정할필요가없습니다.
컨트롤러액션으로파라미터와함께리다이렉트하기
returnredirect()->action('App\Http\Controllers\UserController@profile',[1]);
컨트롤러액션으로파라미터이름과함께리다이렉트하기
returnredirect()->action('App\Http\Controllers\UserController@profile',['user'=>1]);
기타Responseresponse헬퍼함수를사용하여편리하게다른타입의response인스턴스를생성할수도있습니다.response헬퍼함수를인자
없이호출하게되면Illuminate\Contracts\Routing\ResponseFactorycontract를반환합니다.이contract는response를생성하기
위한다양한메소드를제공합니다.
JSONresponse생성하기
json메소드는헤더의Content-Type을자동으로application/json으로지정합니다:
returnresponse()->json(['name'=>'Abigail','state'=>'CA']);
JSONPResponse생성하기
returnresponse()->json(['name'=>'Abigail','state'=>'CA'])
->setCallback($request->input('callback'));
파일다운로드Response생성하기
응답(Response)
44
Page 45
returnresponse()->download($pathToFile);
returnresponse()->download($pathToFile,$name,$headers);
returnresponse()->download($pathToFile)->deleteFileAfterSend(true);
참고:파일다운로드를관리하는Symfony의HttpFoundation에서다운로드할파일의이름이ASCII파일이름임을필요로
하고있습니다.
응답(Response)
45
Page 46
프리젠터(Presenter)3.0.0-beta6변경사항
화면출력을담당하는renderer의명칭을presentable로변경하고이인터페이스를따르는구현체의이름을변경함
3.0.0-beta7변경사항
프리젠터의본래목적인html,api형식의유연한처리를위해서XePresenter::make()을변경합니다.이전에는make()가html에대한지원만처리하였지만이후부터make를사용할경우html,api모두를지원하도록변경합니다.
XE에서는웹브라우저로응답을보낼때뷰를대신해서프리젠터를사용합니다.
프리젠터는일반적인HTML응답뿐만아니라API요청에대한Json응답을하나의메소드로처리할수있습니다.프리젠터는요청
(Request)정보에포함된응답포멧을검사하고,데이터를해당포멧에맞게반환합니다.
XE설계과정에서,하나의컨트롤러를이용해서HTML형식과API형식(Json)의응답을모두를처리하여유지보수의비용을줄이
고자하는요구사항이있었습니다.
프리젠터는응답포멧에따라presetable한구현체를선택합니다.기본으로HtmlPresenter,JsonPresenter두개의구현체를포함하고있습니다.만약API요청의응답을Json이아닌XML형식으로받고싶다면,플러그인을통해presentable한XML구현체
를추가하고요청에서반환포멧을xml로하면됩니다.
프리젠터는XePresenter파사드를제공합니다.
HTML형식으로응답하기
Html응답을처리할경우,HtmlPresenter는테마,스킨을처리합니다.HtmlPresenter는테마핸들러에게테마컴포넌트를받아
처리합니다.스킨은XePresenter::setSkinTargetId()으로외부에서스킨타겟아이디를입력받아스킨핸들러를사용해설정된
스킨을사용합니다.
XE에서HTML형식으로응답할때에는,컨트롤러가생성한데이터를스킨을사용해HTML로변환합니다.또,컴파일된HTML에테마를적용하여반환합니다.프리젠터에서스킨을선택할수있도록응답할때어떤종류의스킨을사용해야하는지설정하고사용
해야합니다.
HTML형식만지원하려고싶을경우XePresenter::makeHtml()을사용합니다.
//inapp/Html/Controllers/ProfileController.php
//스킨타겟지정
XePresenter::setSkinTargetId('member/profile');
...
//HTML형식으로반환,'index'뷰를사용
returnXePresenter::makeHtml('index',compact('user','grant'));
스킨을사용하지않고,템플릿파일의뷰이름을바로지정할수도있습니다.
//inapp/Html/Controllers/SeoController.php
//뷰이름을직접지정
returnXePresenter::makeHtml('seo.setting');
스킨타겟아이디
스킨은특정컴포넌트나집단밑에하나의그룹을형성합니다.
프리젠터(Presenter)
46
Page 47
코어에서제공하는프로필컨트롤러는member/profile아이디를스킨타겟아이디로사용하고있습니다.플러그인으로프로필스킨을만드려고할때제작되는스킨컴포넌트는member/profile를대상아이디로컴포넌트아이디를생성해야하며스킨패키지는
이를기준으로스킨을설정할수있는관리자를제공합니다.
다른예로Board플러그의Board모듈컴포너트는module/board@board스킨타겟아이디를사용합니다.
스킨을사용하는컨트롤러는XePresenter::setSkinTargetId()에지정된스킨타겟아이디를설정하여
HtmlRenderer::render()가처리될때설정된스킨을찾아처리할수있도록합니다.
HtmlPresenter프리젠터에서Html응답처리할XePresenter::make()할경우기본으로/resources/views/를참고합니다.이것은
HtmlPresenter::renderSkin()에서스킨의타겟아이디가지정되지않았을경우뷰를직접사용하기때문입니다.
정상적인사용과정으로스킨의타겟아이디를프리젠터에게전달한경우HtmlPresenter는사용될스킨을찾아Renderable인터
페이스의render()를실행시키며스킨컴포넌트는뷰를사용해서블레이드파일을처리합니다.
전체프레임구성파일
resources/views/common/base.blade.php으로전체프레임을구성하며$content에테마를전달받아출력합니다.
HtmlPresenter::render()는SEO,스킨,테마순서로처리되고마지막에self::$commonHtmlWrapper으로감싸서반환합니다.self::$commonHtmlWrapper는app/Providers/PresenterServiceProvider.php에서config/xe.php의HtmlWrapper로설정합니
다.
base.blade.php는프론트엔드에등록된js,css등여러요소들을어떤위치에출력할지결정하고있습니다.
API형식으로응답하기
XE는json형식을지원하며JsonPresenter가사용됩니다.
//inapp/Html/Controllers/DynamicFieldController.php
...
//$list를json형식으로변환하여반환
returnXePresenter::makeApi(['list'=>$list]);
모든형식지원
HTML,API모든형식을지원하기위해서XePresenter::make()를사용합니다.
//insrc/Controllers/UserController.php
...
//응답형식에따라$data를json형식으로반환하거나,
//$data를index템플릿(스킨)에적용한후,HTML형식으로반환
returnXePresenter::make('index',$data);
XE의API를이용한개발케이스가많지않아API지원에대한부분은계속개선해야합니다.
프리젠터(Presenter)
47
Page 48
뷰(View)컨트롤러는Request를처리한다음다양한형태의Response를반환합니다.GET요청일경우대부분html형식으로
Response를반환할것이며,때로는json형식으로반환할것입니다.POST요청일경우에는대부분리다이렉트(redirect)형식으
로반환할것입니다.
뷰는컨트롤러가html형식의Response를만들때사용됩니다.컨트롤러는html형식의Response를반환하기위해우선필요한
데이터를생성합니다.그다음,생성된데이터를미리정의된템플릿에적용하여html형식의Response를만들어야합니다.이때
뷰에템플릿정보와데이터를전달하면뷰는이를html로생성해줍니다.
뷰는프리젠테이션로직을컨트롤러및어플리케이션로직과분리해주는역할을합니다.
뷰사용의제한
XE는웹브라우저로html형식의응답을보낼때,스킨과테마를적용한후보내야합니다.특별한경우가아니라면컨트롤러에서
뷰를직접반환(return)하지마십시오.대신프리젠터를사용하여반환하십시오.반드시프리젠터를사용해야만테마와스킨이적용
되고위젯또한정상적으로출력됩니다.
단,컨트롤러가아닌다른구성요소이거나,컨트롤러에서라도반환하는값이아니라면,html스트링을생성할때뷰를자유롭게사용하십시오.
기본사용법
간단한뷰는다음과같습니다:
//plugins/my_plugin/views/greeting.php
<html>
<body>
<h1>Hello,<?phpecho$name;?></h1>
</body>
</html>
뷰는다음과같이브라우저로보내집니다:
Route::get('/',function()
{
returnview('my_plugin::views.greeting',['name'=>'James']);
});
위와같이view헬퍼함수에전달하는첫번째인자는템플릿파일의이름이됩니다.두번째전달인자는템플릿에서사용하기위한데이터의배열입니다.
템플릿파일을지정할때,<플러그인아이디>::형태의전치사(prefix)를사용하면플러그인디렉토리를기준으로하는상대경로로파일경로를지정할수있습니다.또,점(.)을사용하여중첩된서브디렉토리에있는파일을지정할수도있습니다.
뷰에데이터전달하기
//Usingconventionalapproach
$view=view('greeting')->with('name','Victoria');
//UsingMagicMethods
$view=view('greeting')->withName('Victoria');
위예제의경우뷰에서는$name변수에Victoria라는값을확인할수있습니다.
뷰(View)
48
Page 49
필요한경우에view헬퍼함수에두번째인자로데이터배열을전달할수도있습니다:
$view=view('greetings',$data);
이러한방식으로정보를전달할때,$data는키/값으로구성된배열이어야합니다.뷰안에서여러분은{{$key}}와같이각각의
키에해당하는값에엑세스할수있습니다.($data['$key']는존재한다고가정합니다.)
파일패스로부터view반환
필요하다면절대경로를기반으로뷰를생성할수도있습니다:
returnview()->file($pathToFile,$data);
뷰(View)
49
Page 50
템플릿(BladeTemplate)
소개
XE에서제공하고있는블레이드는간결하고강력한템플릿엔진입니다.다른인기있는PHP템플릿엔진들과는다르게,블레이드
는여러분이순수한PHP코드를뷰에사용하는것을제한하지않습니다.모든블레이드뷰는순수한PHP코드로컴파일된후캐싱
되고,파일이수정되지않을때까지캐싱된파일을사용합니다.이는블레이드가본질적으로성능상의부담이없음을의미합니다.블레이드뷰파일은확장자로.blade.php를사용합니다.
템플릿의상속
레이아웃정의하기
블레이드를사용할때의주된장점두가지는템플릿상속과섹션입니다.시작하기전에간단한예제를살펴보겠습니다.첫번째로,우리는"master"페이지레이아웃을보겠습니다.대부분의웹사이트는여러페이지에걸쳐동일한레이아웃을사용하기때문에,이레이아웃을하나의블레이드뷰로정의하는것이편합니다.
<!--Storedinresources/views/layouts/master.blade.php-->
<html>
<head>
<title>AppName-@yield('title')</title>
</head>
<body>
@section('sidebar')
Thisisthemastersidebar.
@show
<divclass="container">
@yield('content')
</div>
</body>
</html>
보는바와같이,이파일은일반적인HTML마크업을가지고있습니다.그런데@section과@yield지시어에주목해주십시오.@section지시어는이름에서도알수있듯이컨텐츠의섹션을정의하고있고.반대로@yield지시어는주어진섹션의컨텐츠를
출력하고있습니다.
이제레이아웃은정의했고,이레이아웃을상속받을자식페이지를정의해보겠습니다.
레이아웃확장하기
자식페이지를정의할때,@extends지시어를사용하여자식페이지에서"상속"받을레이아웃을지정할수있습니다.레이아웃을
상속(@extends)받는뷰들은@section지시어를사용해서그레이아웃의섹션에들어갈컨텐츠를주입해야합니다.앞의예에서
보았듯이,자식페이지에서주입한섹션의컨텐츠는레이아웃의@yield부분에출력될것입니다.
템플릿(BladeTemplate)
50
Page 51
<!--Storedinresources/views/child.blade.php-->
@extends('layouts.master')
@section('title','PageTitle')
@section('sidebar')
@@parent
<p>Thisisappendedtothemastersidebar.</p>
@endsection
@section('content')
<p>Thisismybodycontent.</p>
@endsection
Inthisexample,thesidebarsectionisutilizingthe@@parentdirectivetoappend(ratherthanoverwriting)contenttothelayout'ssidebar.The@@parentdirectivewillbereplacedbythecontentofthelayoutwhentheviewisrendered.
위의예에서,sidebar섹션은레이아웃의사이드바에컨텐츠를붙이기위해@@parent지시어를사용하고있습니다.@@parent지시어는뷰가렌더링될때,그레이아웃의sidebar섹션이가지고있는컨텐츠로대체될것입니다.
Ofcourse,justlikeplainPHPviews,Bladeviewsmaybereturnedfromroutesusingtheglobalviewhelperfunction:
블레이드템플릿은순수한PHP뷰와마찬가지로view헬퍼를사용하여곧바로반환될수있습니다.
Route::get('blade',function(){
returnview('child');
});
데이터출력하기
YoumaydisplaydatapassedtoyourBladeviewsbywrappingthevariablein"curly"braces.Forexample,giventhefollowingroute:
중괄호(culrybrace)로변수를감싸면,블레이드뷰로전달된데이터를출력할수있습니다.예를들어,주어진라우트가있을때:
Route::get('greeting',function(){
returnview('welcome',['name'=>'Samantha']);
});
name변수의값을다음과같이출력할수있습니다:
Hello,{{$name}}.
뷰로전달된변수들의값을출력하는것에별다른제한은없습니다.PHP함수의결과값을출력할수도있습니다.블레이드의데이
터출력구문안에는어떤PHP코드도들어갈수있습니다.
ThecurrentUNIXtimestampis{{time()}}.
주의: ̀ 구문은자동으로PHP의htmlentities`함수로감싼후출력합니다.XSS공격을방어하기위함입니다.
블레이드&자바스크립트프레임워크
SincemanyJavaScriptframeworksalsouse"curly"bracestoindicateagivenexpressionshouldbedisplayedinthebrowser,youmayusethe@symboltoinformtheBladerenderingengineanexpressionshouldremainuntouched.Forexample:
템플릿(BladeTemplate)
51
Page 52
많은자바스크립트프레임웍에서도브라우저에출력되어야할데이터를표시하기위해중괄호("curly"braces)를사용하고있습니
다.이럴경우,@기호를사용하면블레이드랜더링엔진은이구문을변환하지않고그대로출력할것입니다.예를들어:
<h1>Laravel</h1>
Hello,@{{name}}.
위의예에서@기호는블레이드에의해서제거될것이고,나머지{{name}}구문은블레이드엔진에의해해석되지않고그대
로남아있게되어,자바스크립트프레임워크에의해변환되어집니다.
데이터가존재하는지확인후출력하기
Sometimesyoumaywishtoechoavariable,butyouaren'tsureifthevariablehasbeenset.WecanexpressthisinverbosePHPcodelikeso:
가끔변수를출력할때,그변수가실제로존재하는지확신하지못할때도있습니다.이때다음과같이PHP코드를사용할수있습
니다.
{{isset($name)?$name:'Default'}}
이렇게3항연산자를사용하는대신,블레이드는다음과같은편리한구문을제공합니다:
{{$nameor'Default'}}
위의예에서,만약$name변수가존재하면그값이출력될것이고,존재하지않는다면대신Default라는문자가출력될것입니
다.
이스케이프하지않고데이터출력하기
Bydefault,Blade{{}}statementsareautomaticallysentthroughPHP'shtmlentitiesfunctiontopreventXSSattacks.Ifyoudonotwantyourdatatobeescaped,youmayusethefollowingsyntax:
기본적으로,{{}}구문은XSS공격을방어하기위해자동으로PHP의htmlentities함수를실행후반환합니다.만약데이터를
htmlentities함수를거치지않은채출력하고싶다면,다음과같은구문을사용하십시오.
Hello,{!!$name!!}.
주의:사용자로부터입력된내용을표시할때에는escape에대한매우세심한주의가필요합니다.컨텐츠의HTML엔티티
를escape하기위해항상이중중괄호표기법을사용하십시오.
제어문
블레이드는템플릿상속및데이터출력과더불어,조건문이나반복문과같이일반적인PHP제어문의실행을위해편리하고간결
한구문을제공합니다.이구문들은매우깔끔하고간단하면서도,대응되는PHP제어문들과비슷한모습을띄고있습니다.
If문
if문은@if,@elseif,@else와@endif지시어를사용하여구성할수있습니다.이지시어들은각각대응되는PHP구문과
동일하게작동합니다.
템플릿(BladeTemplate)
52
Page 53
@if(count($records)===1)
Ihaveonerecord!
@elseif(count($records)>1)
Ihavemultiplerecords!
@else
Idon'thaveanyrecords!
@endif
편의성을위해,블레이드는@unless지시어를제공합니다.
@unless(Auth::check())
Youarenotsignedin.
@endunless
반복문
Inadditiontoconditionalstatements,BladeprovidessimpledirectivesforworkingwithPHP'ssupportedloopstructures.Again,eachofthesedirectivesfunctionsidenticallytotheirPHPcounterparts:
조건문과더불어,블레이드는PHP가지원하는반복문기능을하는간단한지시어를제공합니다.다시한번말하지만,각각의지시
어들은대응하는PHP구문과동일한기능을합니다.
@for($i=0;$i<10;$i++)
Thecurrentvalueis{{$i}}
@endfor
@foreach($usersas$user)
<p>Thisisuser{{$user->id}}</p>
@endforeach
@forelse($usersas$user)
<li>{{$user->name}}</li>
@empty
<p>Nousers</p>
@endforelse
@while(true)
<p>I'mloopingforever.</p>
@endwhile
서브뷰포함하기
@include지시어는손쉽게블레이드뷰를현재의뷰에포함(include)시킬수있도록도와줍니다.부모뷰에서사용할수있는모든
변수는포함된서브뷰에서도사용할수있습니다.
<div>
@include('shared.errors')
<form>
<!--FormContents-->
</form>
</div>
Eventhoughtheincludedviewwillinheritalldataavailableintheparentview,youmayalsopassanarrayofextradatatotheincludedview:
서브뷰는부모뷰의모든데이터를상속받겠지만,그외에더많은데이터를배열형식으로전달할수있습니다.
@include('view.name',['some'=>'data'])
템플릿(BladeTemplate)
53
Page 54
주의:블레이드뷰안에서__DIR__과__FILE__과같은상수를사용하지마십시오.블레이드뷰는캐싱된뷰의위치를참조하기때문입니다.
컬렉션을뷰에서렌더링하기
블레이드의@each지시어를사용하면반복문(loop)과include구문을한줄로합칠수있습니다.
@each('view.name',$jobs,'job')
첫번째인자는배열이나컬렉션의각요소를렌더링하기위한서브뷰의이름입니다.두번째인자는반복처리하는배열이나컬렉션
이며세번째인수는뷰에서의반복값이대입되는변수의이름입니다.예를들어jobs배열을반복처리하려면보통서브뷰에서
각과제를job변수로접근해야할것입니다.
Youmayalsopassafourthargumenttothe@eachdirective.Thisargumentdeterminestheviewthatwillberenderedifthegivenarrayisempty.
또한@each지시어로네번째인수를전달할수도있습니다.이인자는특정배열이비었을경우렌더링될뷰를결정합니다.
@each('view.name',$jobs,'job','view.empty')
주석
Bladealsoallowsyoutodefinecommentsinyourviews.However,unlikeHTMLcomments,BladecommentsarenotincludedintheHTMLreturnedbyyourapplication:
블레이드는또한뷰에주석을정의할수있습니다.하지만HTML주석과는다르게,블레이드주석은브라우저로전송되는HTML에포함되어있지않습니다:
{{--ThiscommentwillnotbepresentintherenderedHTML--}}
서비스주입하기
The@injectdirectivemaybeusedtoretrieveaservicefromtheLaravelservicecontainer.Thefirstargumentpassedto@injectisthenameofthevariabletheservicewillbeplacedinto,whilethesecondargumentistheclass/interfacenameoftheserviceyouwishtoresolve:
@inject지시어는서비스컨테이너로부터서비스를조회하는데에사용될수있습니다.@inject에전달된첫번째인자는서비스
가할당될변수의이름이며두번째인자는주입하려는서비스의클래스/인터페이스의이름입니다:
@inject('metrics','App\Services\MetricsService')
<div>
MonthlyRevenue:{{$metrics->monthlyRevenue()}}.
</div>
템플릿(BladeTemplate)
54
Page 56
플러그인개발시작하기
플러그인생성커맨드
처음플러그인개발을시작할때부딪히는난관은플러그인에필요한기본적인디렉토리와파일들을직접생성하는것입니다.만약
my_plugin이라는플러그인을하나만들기시작한다면,우선plugins디렉토리에my_plugin이라는이름의디렉토리를만들고,그안에plugin.php,composer.json파일을만드는것부터시작해야합니다.이러한수고를줄이기위하여XE는플러그인생성
커맨드를제공합니다.
플러그인생성커맨드를사용해서만든플러그인은웹페이지를출력하는기본적인기능을샘플로포함하고있습니다.원치않을경우샘플웹페이지출력기능을삭제하시고,플러그인개발을시작하시기바랍니다.
터미널에서아래와같이명령어를실행하십시오.
$phpartisanmake:plugin<name><namespace><title>
name파라메터는플러그인의고유id입니다.플러그인의디렉토리이름으로도사용됩니다.
namespace파라메터에는플러그인클래스의네임스페이스를지정합니다.지정한네임스페이스는플러그인클래스뿐만아니라플러그인내에존재하는모든PHP클래스의네임스페이스로도사용됩니다.이네임스페이스는다른개발자가작성한클래스와클래
스명이동일할때서로구분하기위해사용됩니다.다른사람과중복되지않는자신만의고유한네임스페이스를지정하십시오.가능
하면자신의이름이나소속회사명을사용하시길권장합니다.
예를들어,본인의이름이'SungbumHong'이고'foo'플러그인이라면SungbumHong\XePlugins\Foo또는SungbumHong\Foo를네임
스페이스로사용하십시오.또다른플러그인'bar'가있다면,bar플러그인에는SungbumHong\XePlugins\Bar를네임스페이스로사용
할수있습니다.
XE의다양한기능(서비스)사용하기
플러그인을개발하려면플러그인의목적에따라XE에서회원이나문서,이벤트,세션등의매우많은기능을사용해야합니다.가령
로그인한회원의정보를얻거나,작성된문서의목록을가져올때,사이트관리페이지의메뉴를추가하기위해서도XE에서제공하
는기능을사용해야합니다.본매뉴얼의'서비스'섹션에서는플러그인개발시도움이될수있는많은기능(서비스)에대한사용법
을제공하고있습니다.참고하시기바랍니다.
플러그인개발시작하기
56
Page 57
플러그인구조
XE플러그인은하나의디렉토리로구성되며,디렉토리안에는플러그인에필요한파일들이포함돼있습니다.아래의파일목록은
각플러그인이포함해야할필수파일들입니다.
plugins/
└──myplugin/
├──composer.json
└──plugin.php
플러그인을생성할때에는직접모든파일을작성하지마시고,플러그인생성명령(phpartisanmake:plugin)을사용하시
기바랍니다.몇가지설정만입력해주면플러그인에필요한파일을자동으로작성해주고,권장하는디렉토리구조를생성해
줍니다.
composer.jsonXE플러그인은하나의composer패키지이기도합니다.다른플러그인이나라이브러리패키지에대한의존성처리,오토로드
(autoload)와같은composer의장점을활용하기위해,XE에서는composer를사용하여플러그인을관리합니다.composer.json파일은composer가패키지의정보를담을때사용하는파일입니다.또한XE는XE가자체적으로필요로하는플러그인정보를담기위해이파일을같이사용합니다.
아래코드는alice번들플러그인의composer.json파일입니다.
{
"name":"xpressengine-plugin/alice",
"description":"ThisPackageisXpressenginePlugin-AliceTheme.",
"keywords":["xpressengine","plugin","theme","alice"],
"license":"LGPL-2.1",
"version":"0.9.0",
"type":"xpressengine-plugin",
"authors":[{
"name":"xpressengine"
}],
"extra":{
"xpressengine":{
"title":"AliceTheme",
"screenshots":[
"screenshots/main.png",
"screenshots/sub.png",
"screenshots/site.png"
],
"icon":"icon.png",
"component":{
"theme/alice@alice":{
"class":"Xpressengine\\Plugins\\Alice\\Theme\\Alice",
"name":"Alice",
"description":"TheFirstThemeforXpressEngine3",
"screenshot":"/plugins/alice/screenshots/main.png",
}
}
}
},
"autoload":{
"psr-4":{
"Xpressengine\\Plugins\\Alice\\":"src/"
}
}
}
플러그인구조
57
Page 58
composer.json파일은json형식으로정보를담고있습니다.대부분composer가필요로하는정보들입니다.여러분이눈여겨보아야할정보는extra>xpressengine에기록된정보입니다.extra>xpressengine에는이플러그인의제목(title),스크린샷목록(screenshots),아이콘(icon),그리고이플러그인에서등록하는XE컴포넌트에대한정보(component)를담고있습니다.
plugin.php
composer.json파일이플러그인에대한정보를담고있는파일이라면,plugin.php파일은플러그인의실제작동을하는데필요
한코드가기술되는파일입니다.
XE는플러그인이설치(install),업데이트(update),활성화(activate),비활성화(deactivate),삭제(uninstall),부트(boot)될때,이파일에기술된코드를실행하여플러그인이필요로하는작업을할수있도록합니다.
plugin.php에는반드시하나의PHP클래스가기술되어야합니다.이플러그인클래스는반드시
\Xpressengine\Plugin\AbstractPlugin클래스를extends해야합니다.
가장간단한형태의plugin.php파일입니다.
<?php
namespaceMyPlugin\Sample;
useXpressengine\Plugin\AbstractPlugin;
classPluginextendsAbstractPlugin
{
publicfunctionboot()
{
//implementcode
}
}
플러그인클래스는아래의메소드를구현할수있습니다.
메소드 설명
activate 플러그인이활성화될때필요한코드를작성하십시오.
boot 플러그인이부트될때필요한코드를작성하십시오.
checkInstalled 플러그인의설치여부를체크한결과를반환하십시오.이메소드가false를반환하면플러그인이활성화될때install메소드가실행됩니다.
install 데이터베이스테이블생성코드와같이플러그인을설치할때필요한코드를작성하십시오.
checkUpdated 플러그인의업데이트여부를체크한결과를반환하십시오.이메소드가false를반환하면플러그인이활성화될때update메소드가실행됩니다.
update 플러그인이변경되었고,데이터베이스테이블의컬럼추가와같은작업이필요하다면,이메소드에필요한코드를작성하십시오.
deactivate 플러그인이비활성화될때필요한코드를작성하십시오.
uninstall 플러그인이삭제될때필요한코드를작성하십시오.
getSettingsURI 만약플러그인이'플러그인설정'페이지를가진다면,이메소드에서페이지의url을반환하십시오.플러그인관리페이지에서반환한링크가노출됩니다.
권장하는플러그인구조
플러그인은위에서설명한두개의파일(plugin.php,composer.json)만으로도구현이가능합니다.하지만규모가큰플러그인을
제작할때에는더욱더많은파일을필요로합니다.또,많은파일을제대로관리하기위해서는디렉토리구조를잘설계해야합니다.XE는플러그인의구조를아래와같이권장하고있습니다.
플러그인구조
58
Page 59
plugins/
└──myplugin
├──assets/
├──screenshots/
├──src/
├──views/
├──icon.png
├──composer.json
└──plugin.php
assets디렉토리는stylesheet,script,이미지파일과같은asset파일들을담는디렉토리입니다.웹브라우저에서직접적으로요청하는파일들을이디렉토리에넣으십시오.
src디렉토리는PHP클래스나함수와같이php로작성된파일들을담는디렉토리입니다.컨트롤러및컴포넌트파일들을이디렉토리에넣으십시오.
views디렉토리는템플릿파일을담는디렉토리입니다.XE에서는blade템플릿엔진을사용합니다.blade템플릿으로작성된
PHP파일,또는순수한PHP작성된템플릿파일을이디렉토리에넣으십시오.
screenshots디렉토리에는플러그인의스크린샷이미지를넣으십시오.
icon.png와같이플러그인의아이콘이미지파일은플러그인디렉토리에넣으십시오.파일명과확장자는다를수있습니다.
스크린샷이미지나아이콘이미지는composer.json의extra>xpressengine에파일경로를지정해주어야합니다.
플러그인구조
59
Page 60
플러그인버전관리
플러그인은처음설치될때,필요에따라플러그인에서사용할데이터베이스테이블을생성하거나,설정을저장하기도하고,필요한
파일을미리생성해놓기도합니다.이런작업들은플러그인이실행될때마다매번필요한작업이아니라처음설치될때단한번만
실행돼야하는작업입니다.플러그인이업데이트되었을경우에도마찬가지입니다.플러그인에새로운기능이플러그인에추가되었
다면데이터베이스테이블을변경해야할수도있습니다.
이렇게플러그인이설치되고업데이트될때,또는플러그인이삭제될때실행되어야하는코드는plugin.php파일의플러그인클래스에작성하십시오.
플러그인설치과정
플러그인클래스는설치와관련된두개의메소드를가지고있습니다.checkInstalled와install메소드입니다.
<?php
namespaceMyPlugin;
useXpressengine\Plugin\AbstractPlugin;
useSchema;
classPluginextendsAbstractPlugin
{
publicfunctioncheckInstalled($installedVersion=null)
{
//플러그인이설치된상태인지체크하는코드를작성합니다.
}
publicfunctioninstall()
{
//플러그인이설치될때필요한코드를작성합니다.
}
}
checkInstalled메소드는플러그인이활성화될때마다호출됩니다.만약이메소드가true를반환하면XE는이플러그인이이미XE에설치된상태로간주하고곧바로플러그인을활성화시킵니다.반대로이메소드가false를반환하면XE는이플러그인이
아직XE에설치가안된상태라고판단합니다.
만약필요한테이블이생성되어있는지검사하고싶다면아래와같이작성하면됩니다.
publicfunctioncheckInstalled($installedVersion=null)
{
//테이블이존재하는지검사,없으면false를반환
returnSchema::hasTable('table_name');
}
XE는checkInstalled메소드의리턴값이false일경우,install메소드를호출합니다.
publicfunctioninstall()
{
//플러그인이설치될때필요한코드를작성합니다.
Schema::create('table_name',function($table){
$table->engine='InnoDB';
$table->increments('id');
$table->string('name',200);
});
}
버전관리(install,update)
60
Page 61
플러그인업데이트과정
플러그인클래스는업데이트와관련된두개의메소드를가지고있습니다.checkUpdated와update메소드입니다.두메소드는앞서설명한checkInstalled와install메소드와비슷한작동과정을가집니다.
<?php
namespaceMyPlugin;
useXpressengine\Plugin\AbstractPlugin;
useSchema;
classPluginextendsAbstractPlugin
{
publicfunctioncheckUpdated($installedVersion=null)
{
//최신버전이적용된상태인지체크합니다.
}
publicfunctionupdate()
{
//플러그인의최신버전을적용하기위한코드를작성합니다.
}
}
checkUpdated메소드는현재XE에적용된플러그인의버전을파라메터로받습니다.
버전관리(install,update)
61
Page 62
사이트관리페이지추가하기
XE는사이트관리자또는관리등급을가진회원만접근할수있는'사이트관리영역'을가지고있습니다.사이트관리영역은사이
트관리에필요한다양한'사이트관리페이지'들로구성되어있습니다.사이트관리영역은일반적으로http://<도메인
>/settings로접근할수있습니다.
플러그인도사이트관리영역에플러그인을위한관리페이지를추가할수있습니다.
사이트관리페이지를추가할때에는크게세가지작업이필요합니다.
페이지(라우트)등록
사이트관리메뉴등록및페이지와연결
사이트관리권한등록및페이지와연결
세가지작업에대하여자세히살펴보겠습니다.
페이지등록(라우트등록)관리페이지를등록하는것은결국라우트를등록하는것을뜻합니다.관리페이지를위한라우트를등록하는방법은일반웹페이지
의라우트를등록하는방법과크게다르지않습니다.다만,Route::group메소드대신Route::settings메소드를사용하면됩니
다.
Route::settings($uri,function(){
Route::get('/',...);
Route::post('/',...);
});
사이트관리영역에속하는모든페이지의URL은모두첫번째세그먼트로settings를가지게됩니다.라우트를등록하는코드는
플러그인클래스의boot메소드에등록하십시오.
<?php
//plugins/my_plugin/plugin.php
namespaceMyPlugin;
useXpressengine\Plugin\AbstractPlugin;
useRoute;
usePresenter;
classPluginextendsAbstractPlugin
{
publicfunctionboot()
{
//사이트관리페이지추가
//http://<domain>/settings/my_pluginurl로접근가능
Route::settings(static::getId(),function(){
Route::get('/',function(){
returnPresenter::make(static::view('views.settings');
}
});
}
}
사이트관리페이지들이출력될때에는사이트관리영역용테마를적용한후출력돼야합니다.Presenter를사용하여결과를반환하십시오.자동으로사이트관리영역용테마가적용됩니다.
메뉴등록
사이트관리페이지추가
62
Page 63
사이트관리영역의화면좌측에는관리메뉴트리가출력됩니다.플러그인은이트리에메뉴를추가할수있습니다.XeRegister파사드의push메소드를사용하여settings/menu에메뉴정보를등록합니다.
\XeRegister::push('settings/menu',$menuId,$menuInfo);
아래코드는사이트관리메뉴의컨텐츠메뉴하위에서브메뉴로게시판메뉴를추가하는예제입니다.
\XeRegister::push('settings/menu','contents.board',[
'title'=>'게시판',
'display'=>true,
'description'=>'',
'ordering'=>2000,
]);
첫번째파라미터는항상settings/menu를지정하십시오.
두번째파라미터는메뉴의아이디인동시에메뉴의부모메뉴를지정하는역할을합니다.부모메뉴의아이디와현재메뉴의아이디
를점(.)을사용하여연결해주십시오.위예제의경우contents메뉴하위에board메뉴를추가합니다.
세번째파라미터는메뉴의상세정보를담은배열입니다.
title은메뉴가출력될때사용하는메뉴의이름입니다.
display가true이면메뉴가메뉴트리에출력됩니다.false의경우메뉴트리에출력되지않습니다.예를들어회원정보
수정과같은메뉴는메뉴트리에는출력되지않는숨김메뉴입니다.사이트관리페이지의상단에는빵조각(breadcrumb)으로
현재관리페이지의위치를표시해주는데,이때숨김메뉴를등록해놓으면유용합니다.
description은메뉴에대한설명입니다.사이트관리페이지의상단에출력됩니다.
ordering은메뉴가출력되는순서를지정할때사용됩니다.같은레벨의메뉴들이출력될때ordering이작은메뉴부터출력됩니다.
페이지에메뉴연결하기
사이트관리페이지(라우트)를등록할때,페이지에해당하는메뉴를지정할수있습니다.아래코드는회원추가페이지에메뉴를
지정하는예제입니다.
//member.create등록
\XeRegister::push('settings/menu','user.create',[
'title'=>'새회원추가',
'description'=>'신규회원을추가합니다.',
'display'=>false,
'ordering'=>200
]);
//settings_menu지정
Route::settings('user',function(){
Route::get('create',[
'as'=>'settings.member.create',
'uses'=>'Member\Settings\UserController@create',
'settings_menu'=>'user.create'
]);
});
라우트를등록할때,두번째파라미터배열의settings_menu필드에메뉴아이디를지정하면됩니다.위예제의경우
member.create메뉴를라우트와연결하고있습니다.
관리권한
사이트관리페이지추가
63
Page 64
기본적으로사이트의최고관리자(super)는모든관리페이지에접근할수있습니다.하지만,최고관리자가아닌관리자(manager)등급의회원이더라도선택적으로사이트관리페이지에접근할수있도록지정할수있습니다.
권한등록
권한을등록하는방법은앞서설명한메뉴등록방법과유사합니다.XeRegister파사드의push메소드를사용합니다.
\XeRegister::push('settings/permission',$permissionId,$permissionInfo);
아래코드는'회원생성'이라는권한을등록하는예제입니다.
\XeRegister::push('settings/permission','user.create',[
'title'=>'회원생성',
'tab'=>'회원관리'
]);
첫번째파라미터는항상settings/permission을지정하십시오.
두번째파라미터는권한의아이디입니다.
세번째파라미터는권한의상세정보를담은배열입니다.
title은권한의이름입니다.권한관리페이지에서권한을표시할때사용됩니다.
tab은권한이속할그룹을이름을지정합니다.권한관리페이지에서는동일한tab을가진권한들을그룹지어출력합니다.
위와같이권한을등록해놓으면,'사이트관리>설정>관리페이지권한설정'페이지에등록한권한이표시됩니다.사이트관리자
는이페이지에서특정사용자에게권한을부여할수있습니다.
페이지에권한지정하기
등록한권한을관리페이지(라우트)에연결하는과정도사이트관리메뉴를지정했던방식과유사합니다.settings_permission필드에권한의아이디를지정하십시오.
Route::settings('user',function(){
Route::get('create',[
'as'=>'settings.member.create',
'uses'=>'Member\Settings\UserController@create',
'settings_menu'=>'user.create',
'settings_permission'=>'user.create'
]);
});
위예제에서는'회원생성'권한을회원추가페이지에지정하고있습니다.
어떤회원이회원추가페이지에접근할경우,XE는먼저회원추가페이지에지정된권한을찾습니다.위의경우회원추가
(user.create)권한을찾게됩니다.그다음XE는로그인한회원에권한이부여되어있는지검사합니다.만약권한이부여되어있지않은회원이라면오류를출력하게됩니다.
사이트관리페이지추가
64
Page 65
컴포넌트등록
XE는다양한타입의컴포넌트가있습니다.여러분이컴포넌트를가지는플러그인개발을시작한다면,먼저뼈대만갖춘컴포넌트
를작성하고,플러그인을통해작성한컴포넌트를XE에등록해야합니다.타입이다른컴포넌트라도XE에등록하는방법은모두
동일합니다.
컴포넌트아이디
각각의컴포넌트는모두고유의아이디를가지고있어야합니다.XE는컴포넌트의아이디를통해등록된컴포넌트를효과적으로
관리합니다.또,컴포넌트아이디는컴포넌트타입을구분짓는역할도병행합니다.각컴포넌트타입마다아이디를지정하는규칙이
아래와같이정해져있습니다.
컴포넌트타입
아이디규칙 예제
테마
theme/<plugin_name>@<pure_id> theme/alice@alice
모듈
module/<plugin_name>@<pure_id> module/myplugin@board
위젯
widget/<plugin_name>@<pure_id> widget/myplugin@content
스킨
<skin_target_id>/skin/<plugin_name>@<pure_id>
user/profile/skin/social_login@default
module/myplugin@board/skin/board@gallery
widget/xpressengine@content/skin/myplugin@content
UI오브젝트
uiobject/<plugin_name>@<pure_id> uiobject/myplugin@formSelect
다이나믹필드
FieldType/<plugin_name>@<pure_id> FieldType/myplugin@Text
다이나믹필드스킨
<dynamic_field_id>/FieldSkin/<plugin_name>@<pure_id> FieldType/myplugin@Text/FieldSkin/fooplugin@TextDefault
에디터
... ...
에디터툴
... ...
컴포넌트추가
65
Page 66
composer.json을사용하여등록하기
플러그인의필수구성파일인composer.json을통해컴포넌트를등록할수있습니다.
composer.json항목의extra>xpressengine>component항목에아래의형식으로컴포넌트정보를기입합니다.
"component":{
"<컴포넌트아이디>":{
"class":"<컴포넌트클래스명>",
"name":"<컴포넌트제목>",
"description":"<컴포넌트설명>",
"screenshot":"<스크린샷경로>",
}
}
<컴포넌트아이디>에는앞서설명한컴포넌트아이디를적습니다.
class에는컴포넌트클래스의풀네임을적습니다.컴포넌트클래스는반드시autoload를통해로드가능한위치이어야합니다.
name에는컴포넌트의제목을적습니다.
description에는컴포넌트에대한간략한설명을적습니다.
screenshot에는컴포넌트의스크린샷이미지경로를적습니다.스크린샷이미지는이미플러그인디렉토리안에저장되어있어야
합니다.
Alice플러그인의composer.json파일예제입니다.Alice플러그인은하나의테마컴포넌트를등록하고있습니다.
{
"name":"xpressengine-plugin/alice",
"description":"ThisPackageisXpressenginePlugin-AliceTheme.",
...
"extra":{
"xpressengine":{
"title":"AliceTheme",
"icon":"icon.png",
"component":{
"theme/alice@alice":{
"class":"Xpressengine\\Plugins\\Alice\\Theme\\Alice",
"name":"Alice",
"description":"TheFirstThemeforXpressEngine3",
"screenshot":"/plugins/alice/screenshots/main.png",
}
}
}
},
...
}
plugin.php를사용하여등록하기
component.json을사용하지않고,PHP코드를사용하여컴포넌트를등록할수도있습니다.만약컴포넌트가항상등록될필요가
없다면,즉,특정조건에의해동적으로등록하고싶다면이방법을사용할수있습니다.
활성화된플러그인는XE가실행될때마다그플러그인의플러그인클래스(plugin.php파일에존재)의boot메소드가실행됩니
다.boot메소드에서플러그인이가지고있는컴포넌트를등록시킬수있습니다.아래코드를사용하십시오.
컴포넌트추가
66
Page 67
//myplugin/plugin.php
...
publicfucntionboot()
{
//컴포넌트의클래스명을파라미터로전달
XePlugin::addComponent($componentClass);
}
...
component.json을사용하지않고직접컴포넌트를등록하는경우,컴포넌트클래스는반드시컴포넌트아이디를직접지정하고있어야합니다.아래와같이$id프로퍼티를직접지정하십시오.
publicstatic$id='component_type/my@component';
컴포넌트추가
67
Page 68
개인설정페이지추가하기
개인설정페이지란
XE에서는로그인한회원이자신의회원정보를변경할수있는페이지를제공합니다.이페이지에서는여러개의섹션으로구성됩니
다.'기본설정'섹션에서는사용자의이메일,이름,비밀번호와같은기본정보를수정할수있습니다.
서드파티플러그인은이개인설정페이지에섹션을추가할수있습니다.예를들어소셜로그인플러그인에서는로그인한회원과연동된SNS계정의정보를출력하고,SNS계정에연결할수있는섹션을추가합니다.
섹션을추가하면탭목록에추가된섹션의제목이추가되며,이를클릭할경우페이지에선택한섹션이출력됩니다.
개인설정섹션추가하기
개인설정섹션을추가하려면XeRegister파사드(또는app('xe.register'))의push메소드를사용하여
user/settings/section에섹션정보를등록합니다.
소셜로그인플러그인에서는아래와같이설정섹션을추가하고있습니다.
app('xe.register')->push(
'user/settings/section',
'social_login@section',
[
'title'=>'소셜로그인설정',
'content'=>function($member)use($plugin){
return$plugin->getMemberSettingSection($member);
}
]
);
첫번째파라미터는항상user/settings/section로지정하십시오.
두번째파라미터는섹션의고유아이디입니다.고유한아이디를가질수있도록플러그인아이디를포함하여지정하십시오.
세번째파라미터는html을반환하는클로저형식으로작성하십시오.사용자가섹션을선택할때이클로저가반환하는html이페이
지에그대로출력됩니다.이클로저는현재회원정보를파라메터로전달받습니다.
개인설정섹션이출력된다음,사용자로부터입력을받아처리할내용이있을경우,별도의라우트와컨트롤러를작성하셔야합니
다.
개인설정페이지추가
68
Page 69
회원가입인증및입력폼추가하기
XE3에서의회원가입과정
일반적인사이트의회원가입과정을생각해보면,회원가입페이지에서회원정보를입력받고가입버튼을누르면회원정보가서버에
저장되면서회원가입이완료되는과정일것입니다.하지만최근에는소셜로그인이나이메일인증,본인인증과같이별도의인증과
정을거쳐야회원가입을할수있는사이트가많이있습니다.
XE는이와같이인증단계를회원가입과정에추가할수있도록구조화되어있습니다.
뿐만아니라,XE는사용자로부터별도의정보를입력받기위한입력폼을추가할수있도록구조화되어있기때문에,서드파티플러
그인은회원가입시기본적으로요구하는정보이외에별도의졍보를사용자로부터입력받을수도있습니다.
회원가입인증페이지
XE3에서는회원가입을원하는사용자들이회원정보입력페이지에접근하기전에별도의인증과정을거칠수있도록인증페이지
를추가할수있습니다.이인증페이지에서인증을통과해야회원정보입력페이지에접근할수있습니다.그예로회원가입시이메
일인증을사용하도록사이트관리자가설정하면이메일인증을위한페이지가먼저출력되고,이메일인증을통과해야회원가입이
가능합니다.
XE3에서기본적으로제공하는이메일인증외에도서드파티플러그인은다양한인증방식을제공할수있습니다.예를들어소셜로
그인플러그인을사용하면소셜로그인인증을추가할수있습니다.사용자들은이메일인증이나소셜로그인인증과같이제공되는
인증방식중하나를선택하여회원가입을할수있습니다.
XE3에서회원가입페이지의주소는도메인/auth/register입니다.
물론제공되는인증방식이하나도없다면,사용자들은인증페이지를거치지않고곧바로회원정보입력페이지를볼수있으며,인증절차없이회원가입이가능합니다.
사용자들은인증페이지거친후에회원정보입력페이지를볼수있습니다.회원정보입력페이지에서는사용자의기본정보(이메
일,이름,비밀번호등)를입력하게됩니다.이때인증페이지에서미리입력한정보를사용하고싶을수도있습니다.만약이메일인증을거친후라면,미리획득한이메일정보를이메일입력폼에미리설정해놓을수도있습니다.이또한이벤트(인터셉션)와자바
스크립트를사용하면가능합니다.
회원가입인증방식추가하기
회원가입인증방식을추가하려면XE에등록해야합니다.XeRegister파사드의push메소드를사용하여
user/register/guard에인증방식을추가합니다.
예를들어,회원가입인증페이지에이메일인증과정을추가하고싶다면,이메일주소를입력받는폼을아래코드와같이등록합니
다.
app('xe.register')->push('user/register/guard','email-confirm',function(){
$formHtml=$this->getEmailConfirmForm();
return$formHtml;
});
첫번째파라미터는항상user/register/guard로지정하십시오.
두번째파라미터는인증방식의고유아이디입니다.고유한아이디를가질수있도록플러그인아이디를포함하여지정하십시오.
세번째파라미터는html을반환하는클로저형식으로작성하십시오.이클로저가반환하는html은인증페이지에그대로출력됩니
다.이메일인증의경우html은아래와같이작성될수있습니다.
회원가입인증/입력폼추가
69
Page 70
<h3>이메일인증으로회원가입하기</h3>
<divclass="confirmEmail">
<formaction="{{url('auth/confirm')}}"method="post">
{{csrf_field()}}
<label>이메일</label>
<inputtype="text"name="email">
<buttontype="submit">인증메일전송</button>
</form>
</div>
회원가입인증처리
인증페이지로부터인증요청이서버로전송되었을경우,서버에서는인증이메일을전송하고인증과관련된정보를서버에저장하
는작업을해야합니다.소셜로그인이라면소셜로그인을제공하는사이트의oAuth페이지로부터인증을받고인증받은정보를서버에저장하는단계를거쳐야합니다.
위이메일인증의예에서는사용자가입력한이메일정보를url('auth/confirm')으로전송합니다.서버에서는이요청을받은다음이메일을전송하고,전송정보를서버에저장합니다.요청을받는라우트는플러그인이직접등록해야합니다.
회원가입토큰
인증요청을받은플러그인은인증처리를할때,'회원가입토큰'을발급해야합니다.이토큰은현재사용자가정상적인인증과정
을거친사용자라는것을증명하며,인증과관련된정보를토큰에저장합니다.토큰에저장한정보는차후회원가입을처리할때사용할수있습니다.
또,인증처리의마지막에는사용자를다시회원가입페이지로리다이렉트시켜주어야합니다.이때,발급한토큰의아이디를파라
미터로추가하여리다이렉트해야합니다.XE는회원가입페이지를출력할때토큰유무를판단하여토큰이없을경우인증페이지
를,토큰이있을경우회원정보입력페이지를출력합니다.아래코드는이메일인증을처리하는컨트롤러의코드입니다.
회원가입인증/입력폼추가
70
Page 71
publicfunctionpostRegisterConfirm(Request$request)
{
$this->validate($request,[
'email'=>'required|email'
]);
$this->addEmailRegister();
$email=$request->get('email');
try{
$this->handler->validateEmail($email);
}catch(\Exception$e){
thrownewHttpException(400,'이미등록된이메일입니다.');
}
if($mail=$this->handler->pendingEmails()->findByAddress($email)){
$this->emailBroker->sendEmailForConfirmation($mail,'emails.register');
}else{
\DB::beginTransaction();
try{
$mailData=['address'=>$email,'userId'=>app('xe.keygen')->generate()];
$user=newUser();
$user->id=$mailData['userId'];
$mail=$this->handler->pendingEmails()->create($user,$mailData);
$this->emailBroker->sendEmailForConfirmation($mail,'emails.register');
}catch(\Exception$e){
\DB::rollBack();
throw$e;
}
\DB::commit();
}
$token=XeUser::storeRegisterToken('email',['email'=>$email,'userId'=>$mail->userId]);
returnredirect()->route('auth.register',['token'=>$token['id']])->with(['alert'=>['type'=>'success','messa
ge'=>'인증이메일이전송되었습니다.']]);
}
위코드를보면,
1.우선인증페이지에서사용자로부터입력받은이메일을검사합니다.2.인증이메일을전송합니다.3.인증정보를XeUser::storeRegisterToken를사용하여서버에저장합니다.4.회원가입페이지로리다이렉트합니다.이때token정보를파라미터로추가합니다.실제리다이렉트되는주소는도메
인/auth/register?token=XXXX와같은형식이됩니다.
이메일인증의경우,인증이메일을전송하고곧바로회원정보입력페이지로리다이렉트됩니다.사용자는이메일로전송받은인증
코드를회원정보입력페이지에입력해야회원가입이정상적으로완료됩니다.
회원정보입력페이지
인증을거친사용자는회원정보입력페이지에접근할수있습니다.회원정보입력페이지는위에서설명한바와같이도메
인/auth/register?token=XXXX형식으로인증토큰을포함하고있습니다.
회원정보입력페이지는기본적인회원정보를입력받는폼과개인정보및사이트이용약관의동의를받는폼을가지고있습니다.플러그인은기본적으로제공하는폼이외에도별도의입력폼(또는UI)를추가할수있습니다.이메일인증기능에서는이메일인증
코드를입력받는폼을추가할수있고,캡차기능에서는캡차UI를추가할수있습니다.
회원정보입력폼추가하기
XeRegister파사드의push메소드를사용하여user/register/form에입력폼을추가합니다.
예를들어,이메일인증코드입력폼을추가하고싶다면아래코드와같이등록할수있습니다.
회원가입인증/입력폼추가
71
Page 72
app('xe.register')->push(
'user/register/form',
'email',
function($token){
//guard가'email'이아니면skip
if($token->guard!=='email'){
returnnull;
}
//이메일입력폼에인증시사용된이메일지정
app('xe.frontend')->html('email.setter')->content("<script>$('input[name=email]').attr('readonly'
,'readonly').val('{$token->email}');</script>")->load();
//이메일인증코드입력폼출력
returnview('register.forms.confirm',compact('token'));
}
);
첫번째파라미터는항상user/register/form으로지정하십시오.
두번째파라미터는인증방식의고유아이디입니다.고유한아이디를가질수있도록플러그인아이디를포함하여지정하십시오.
세번째파라미터는html을반환하는클로저형식으로작성하십시오.이클로저가반환하는html은그대로페이지에출력됩니다.이메일인증의경우html은아래와같이작성될수있습니다.클로저는하나의파라미터를가집니다.이파라미터는인증토큰의정보
를담고있습니다.만약인증토큰이유효하지않다면Exception을발생시키는방법으로사용자가회원정보입력페이지에접근하
는것을막을수있습니다.
<h4>이메일인증코드확인</h4>
<div>
<p>{{$token->email}}로인증이메일이전송되었습니다.회원가입을계속하시려면인증이메일에명시된인증코드를입력하시거나,이메
일에서[회원가입계속하기]링크를클릭하세요.</p>
<inputtype="text"name="code">
</div>
만약form이출력되는순서를지정하고싶을수있습니다.예를들어이메일인증폼은제일상단에나와야합니다.이럴때에는
intercept를사용하여XeUser@getRegisterForms가실행되는시점을가로챈다음폼의순서를바꿔주시기바랍니다.XeUser@getRegisterForms는등록된회원가입폼목록을반환하는메소드입니다.
아래코드는프로모션이메일수신동의를받는체크박스를특정위치에추가하는코드입니다.
intercept('XeUser@getRegisterForms','mailing@form',function($target,$token){
$forms=$target($token);
$idx=array_search('agreements',array_keys($forms))+1;
$updated=[];
foreach($formsas$id=>$form){
$updated[$id]=$form;
if($id==='agreements'){
$updated['mailing@agreement']=function($token){
return'<divclass="xe-input-group">
<labelclass="xe-label">
<inputtype="checkbox"name="agree_mailing"value="on">
<spanclass="xe-input-helper"></span>
<spanclass="xe-label-text">
이벤트및프로모션이메일을수신합니다.
</span>
</label>
</div>';
};
}
}
return$updated;
});
회원가입인증/입력폼추가
72
Page 73
회원정보저장과정
사용자가회원정보입력페이지에서정보를입력하고가입버튼을누르면서버에서는회원가입처리를합니다.이과정에서플러그
인은사용자가정확하게입력을했는지,정상적인인증을거친사용자인지검사할수있습니다.
intercept를사용하여XeUser@validateForCreate가실행되는시점을가로챈다음,올바른입력정보와토큰인지검사하십시오.
아래코드는이메일인증코드가정상적인지검사하는코드입니다.
intercept('XeUser@validateForCreate','register.email.validator',function($target,$data,$token=null){
if($token->guard==='email'){
$code=array_get($data,'code');
//인증코드가올바른지검사,올바르지않다면예외가발생됨.
$email=$this->checkPendingEmail($token->email,$code);
$data['id']=$email->userId;
}
return$target($data,$token);
});
XeUser@validateForCreate는입력된정보를사용하여회원을생성할때항상거치는유효성검사절차입니다.
만약회원정보입력페이지에별도의입력폼을추가하여입력받은정보가있다면,아래와같은코드로저장할수있습니다.
intercept('XeUser@create','mailing@create',function($target,$data,$token=null){
$agree=array_get($data,'agree_mailing');
$user=$target($data,$token);
if($agree==='on'){
app('mailing::handler')->agree($user->id);
}
return$user;
});
XeUser@create는회원이생성될때항상호출되는메소드입니다.위코드는회원정보입력페이지에추가한프로모션이메일수신동의여부(agree_mailing)를저장하기위한코드입니다.
회원가입인증/입력폼추가
73
Page 77
설정(config)XE에서제공하는config는laravel에서제공되는config와는다르게데이터베이스에정보를담으며계층을가지고상위정보를참조합니다.이때각계층은"."(dot)으로구분합니다.
등록
config는해당설정을나타내는이름과그에매칭되는배열을통해등록됩니다.
XeConfig::add('foo',['var1'=>'a','var2'=>'b']);
XeConfig::add('foo.bar',['var1'=>'A']);
XeConfig::set('foo.baz',['var2'=>'B']);
add는새로운설정정보를등록하는메서드입니다.만약같은이름의설정이이미등록되어져있는경우Exception이발생하게됩니다.
set은기존설정의존재유무와상관없이값을등록합니다.set의경우기존에같은이름의설정이존재하는경우메서드호출
시전달된배열의키에해당하는값만을갱신해줍니다.
XeConfig::add('foo',['var1'=>'a','var2'=>'b']);
XeConfig::set('foo',['var1'=>'A']);
echoXeConfig::getVal('foo.var1');//A
echoXeConfig::getVal('foo.var2');//b
설정을등록하는방법은add,set이외에도put,modify,setVal이있습니다.
put은기존에같은이름을가지는설정이먼저등록되어있어야정상적으로동작합니다.또한set과는다르게기존의설정들을
메서드호출시전달된배열값으로모두대체합니다.
XeConfig::add('foo',['var1'=>'a','var2'=>'b']);
XeConfig::put('foo',['var1'=>'A']);
echoXeConfig::getVal('foo.var1');//A
echoXeConfig::getVal('foo.var2');//null
modify는config객체를통해수정하는기능입니다.
$config=XeConfig::get('foo');
$config->set('var1','A');
XeConfig::modify($config);
setVal은설정의특정항목의값만등록하는메서드입니다.
XeConfig::setVal('foo.var1','A');
조회
등록된설정을조회할때는특정설정키에해당하는값을받거나,config객체를반환받은후값을조회하는방법이있습니다.
XeConfig::add('foo',['var1'=>'a','var2'=>'b']);
echoXeConfig::getVal('foo.var1');//a
//또는
$config=XeConfig::get('foo');
echo$config->get('var1');//a
만약등록된설정이없는경우특정값을반환받고싶으면다음인자에값을전달하면됩니다.
설정(config)
77
Page 78
XeConfig::add('foo',['var1'=>'a','var2'=>'b']);
echoXeConfig::getVal('foo.var3','c');//c
//또는
$config=XeConfig::get('foo');
echo$config->get('var3','c');//c
계층조회
config는계층을가지고있고이를이용하여존재하지않는설정은부모에해당하는설정을조회하여결과를반환합니다.
XeConfig::add('foo',['var1'=>'a','var2'=>'b']);
XeConfig::add('foo.bar',['var1'=>'A']);
echoXeConfig::getVal('foo.bar.var2');//b
설정을조회할때두번째인자로default값을전달하는경우최상위설정에서도해당하는결과가없는경우에default값이반환되어집니다.
설정(config)
78
Page 79
문서(document)사이트에서생산되는게시판,블로그,댓글등다양한형태의컨텐츠를저장할저장소를제공합니다.DocumentHandler는Document,Revision모델을이용해서컨텐츠변경이력을관리할수있는기능을제공하고저장소의부하분산을위한Division(저장소분할)저장소를제공하합니다.이러한기능은instanceId에따라설정을추가해서관리할수있습니다.
또한Document모델은DynamicField를지원합니다.
DocumentHandler,InstanceManager,ConfigHandler는InterceptionProxy로만들어사용되므로인터셉트할수있습니다.(인터셉트하기)
기본사용법
XeDocument파사드로DocumentHandler를사용합니다.문서등록,수정,삭제는DocumentHandler를이용해서처리하고문서
조회는Document모델을직적사용합니다.단,인스턴스를생성해사용할경우Document모델에대한설정을모델에포함시키기
위해XeDocument::getModel($instnaceId)를사용하기를권장합니다.
모든문서가져오기
$doc=Document::all();
PrimaryKey를통해서하나의레코드가져오기
$doc=Document::find($documentId);
var_dump($doc->content);
인스턴스생성을통해등록된문서가져오기
메뉴를통해인스턴스가생성되었거나또는어떤방식으로든InstanceManager를이용해인스턴스를만들어설정을사용하는경우에는모델을직접사용하지말고DocumentHandler를통해획득한모델을사용하도록해야합니다.
$model=XeDocument::getModel($instanceId);
//10개의문서를가져옵니다.
$model->paginate(10);
이렇게획득한모델은설정에대한정보를포함하고있으며revision,division에대한처리및DatabaseProxy를이용하는
기능인DynamiField에대한처리가가능합니다.
문서등록
$params['instanceId']=$instanceId;
$params['title']='제목';
$params['content']='내용';
$inputs['userId']=Auth::user()->getId();
$inputs['writer']=Auth::user()->getDisplayName();
XeDocument::add($params);
문서등록은Document모델을직접사용하지않고DocumentHandler를통해서처리합니다.이는문서저장할때인터셉트할수있는포인트를제공하며Document가입력값검사및revision을처리합니다.
수정하기
문서(document)
79
Page 80
Document를통해문서를직접가져오는경우는아래와같이XeDocument::setModelConfig()를이용해서모델에설정을삽입해야
DocumentHandler에서설정에따른처리를이상없이수행할수있습니다.
$doc=Document::find($documentId);
$doc->title='제목수정';
//Document모델($doc)에설정삽입
XeDocument::setModelConfig($doc,$doc->instanceId);
XeDocument::put($doc);
일반적인상태에서문서를찾을때라이프사이클상에서instanceId를확보할수있다고가정한다면아래형식의코드를사용할
수있습니다.
$doc=XeDocument::getModel($instanceId)->find($documentId);
$doc->title='제목수정';
XeDocument::put($doc);
삭제하기
$doc=Document::find($documentId);
//Document모델($doc)에설정삽입
XeDocument::setModelConfig($doc,$doc->instanceId);
XeDocument::remove($doc);
//'937c2ec7'은인스턴스아이디
$doc=XeDocument::getModel($instanceId)->find($documentId);
XeDocument::remove($doc);
인스턴스
관리자>사이트메뉴에서게시판을만들때게시판플러그인은Document서비스를사용하기위해Document인스턴스를만듭니
다.Document를사용하기위한설정및그에필요한주변요소를생성해서실제사용가능한형태로만드는것입니다.
인스턴스생성
$params['revision']=false;
$params['division']=true;
XeDocument::createInstance($instanceId,$params);
Document에설정요소를$params로전달합니다.
인스턴스제거
XeDocument::destroyInstance($instanceId);
생성된인스턴스를제거하며인스턴스아이디로저장된문서를삭제합니다.이것은게시판삭제와같은처리를할때사용됩니다.
Document모델
데이터를저장할때사용하는이것은DynamicQuery를처리하기위해Config를등록하는기능과문서를처리할때유용한다양한
메소드를지원합니다.
문서(document)
80
Page 81
테이블부하분산
Document::setConfig()하는과정에서테이블부하분산처리를위해실제사용할테이블이름을변견할수있도록지원합니다.이기능은DocumentHandler::setModelConfig()할때처리됩니다.
다양한상태값
상태를표시하기위한다양한컬럼을갖고있습니다.
status:문서의상태를저장합니다.(휴지통,임시저장,비공개,공개,공지)approved:승인에대한설정을저장합니다.(거절됨,기다림,허용됨)published:발행에대한설정을저장합니다.(거절됨,기다림,예약발행,발행됨)display:보여지는상태에대한설정을저장합니다.(숨김,비밀,보여짐)
이상태값들을복합적으로사용하여플러그인자체적으로다양한방식의상태를표현할수있습니다.그중에일반적으로사용되는
보여짐,숨김,발행등다양한형태의상태값들을제안하기위한메소드들이추가되어있습니다.
각설정은숫자로정의되어있으며between쿼리를이용해서더다양한설정을사용할수있기를바라고있습니다.서드파티플러
그인에서Document모델을확장하는ExtendDocument모델을만들고상태를추가하여더다양한형태의문서상태처리방법이
제공될수있기를바랍니다.
format문서를출력할때어떤형태의포멧으로처리해야할지알려주기위해서글저장할때format을등록하도록하고있습니다.이것은
스마트에디터를사용하거나혹은마크다운으로작성된문서를구분하고저장된내용을출력할때어떤방식으로처리해야할지결정할때유용하게사용될것입니다.현재의포멧은HTML형식만정의하고있습니다.
reply
//상위글
$doc=XeDocument::getModel($instanceId)->find($documentId);
//parentId등록
$params['parentId']=$doc->id;
$params['instanceId']='937c2ec7';
$params['title']='제목';
$params['content']='내용';
$inputs['userId']=Auth::user()->getId();
$inputs['writer']=Auth::user()->getDisplayName();
XeDocument::add($params);
답글처리를위해Document::setReply()를제공합니다.어떤문서의하위로글을작성하려고한다면해당모델에parentId를놓고저장하면됩니다.Document::setReply()의실행은/app/Providers/DocumentServiceProvider.php의boot()에서
Illuminate\Database\Eloquent\Model의creating이벤트리스너를등록해서자동으로처리됩니다.
Division지원
테이블분리기능을처리하기위해Document모델은문서등록,수정,삭제할때설정값을확인하고추가적인처리를진행합니다.Illuminate\Database\Eloquent\Model에서제공하는performInsert(),performUpdate(),performDeleteOnModel()기능을이용해데이터베이스에처리될때추가적인코드를처리합니다.
관계
Document모델은User(회원)에대한관계만제공합니다.더많은릴레이션은제공하지않습니다.더많은모델의릴레이션을사용
하기위해서는게시판플러그인의Board모델과같이Document모델을확장해서사용하는것을권장합니다.
문서(document)
81
Page 83
키생성기(keygen)XE는단순하고유추하기쉬운기존의데이터베이스를이용한auto_increment를벗어나,유일성을보장하면서유추하기어려운
키를생성할수있는기능을제공합니다.키의생성은UUID방식으로ramsey/uuid를이용해사용자가간편하게고유키를얻을수있도록해줍니다.
설정
키생성기의설정은config/xe.php의uid항목에있습니다.
'uid'=>[
'version'=>4,
'namespace'=>'xe'
]
version은사용할UUID의버전을말합니다.1,3,4,5네가지값중하나를입력할수있고,기본은4버전을사용합니다.namespace는3,5버전에서사용될네임스페이스를가리킵니다.
각버전의차이는위키등에서확인하실수있습니다.
사용
키의생성은generate메서드를통해이루어집니다.
$keygen=app('xe.keygen');
$id=$keygen->generate();
생성된키는xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx와같은형식을가집니다.
모델
XE에서는Illuminate\Database\Eloquent\Model을상속한DynamicModel모델을이용하는경우키생성기를통해고유키를제공
합니다.키생성기에서제공하는키를사용하고자한다면해당모델의incrementnig을비활성해야합니다.
useXpressengine\Database\Eloquent\DynamicModel;
classMyModelextendsDynamicModel
{
public$incrementing=false;
...
}
키생성기(keygen)
83
Page 84
메뉴/모듈(menu,module)
메뉴/모듈(menu,module)
84
Page 85
이벤트및인터셉션
플러그인개발자가플러그인에서필요한기능을구현하기위해서는XE가실행되는여러시점에끼어들어XE의행동을바꾸거나
추가적인행동을할수있어야합니다.
예를들어,사이트에새로운회원이가입할때,가입한회원에게가입축하메일을보내는기능을플러그인으로만들수있습니다.이기능을구현하려면XE가회원가입을처리할때,플러그인이끼어들어메일을전송하는코드를실행할수있어야합니다.
이러한'끼어들기'를일반적으로'hook'또는'event'라고칭합니다.XE에서는라라벨에서기본적으로제공하는'event'방식과XE에서새롭게제공하는'interception'방식으로'끼어들기'를지원합니다.
이벤트
이벤트는옵저버패턴으로구현됩니다.특정이벤트에대하여리스너를등록해놓으면,이벤트가발생했을때,등록한리스너가차례대로실행됩니다.
이벤트리스너등록
이벤트가발생했을때실행될리스너는Event파사드나Illuminate\Contracts\Events\Dispatchercontract의구현을이용해등록할수있습니다
/**
*Registeranyothereventsforyourplugin
*
*@param\Illuminate\Contracts\Events\Dispatcher$events
*@returnvoid
*/
publicfunctionboot(DispatcherContract$events)
{
$events->listen('event.name',function($foo,$bar){
//
});
}
//or
publicfunctionboot()
{
\Event::listen('event.name',function($foo,$bar){
//
});
}
이벤트리스너를클래스로정의
클로저형식의이벤트리스너대신클래스형식으로이벤트리스너를정의할수있습니다.
Event::listen('auth.login','LoginHandler');
기본적으로이벤트가발생했을경우,이벤트리스너클래스의handle메소드가호출됩니다.
classLoginHandler{
publicfunctionhandle($data)
{
//
}
}
이벤트/인터셉션(event/interception)
85
Page 86
handle메소드대신다른메소드를사용할수도있습니다.리스너등록시메소드명을아래와같이기입하십시오.
Event::listen('auth.login','LoginHandler@onLogin');
와일드카드이벤트리스너
*를와일드카드로사용하여리스너를등록하면동일한리스너에서여러개의이벤트에대응할수있습니다.와일드카드리스너
는전체이벤트데이터배열을하나의인자로받아들입니다:
$events->listen('event.*',function(array$data){
//
});
리스너우선순위지정
리스너의우선순위를지정할수있습니다.우선순위가높은리스너가먼저실행되며,동일한우선순위일경우,먼저등록된리스너
가먼저실행됩니다.
Event::listen('auth.login','LoginHandler',10);
Event::listen('auth.login','OtherHandler',5);
이벤트발생시키기
이벤트를발생시키기려면Event파사드의fire메소드를사용하십시오.
$args=[...];
Event::fire('event.name',$args)
이벤트전달중단하기
경우에따라서이벤트가다른리스너에게전달되는것을중단하기를원할수도있습니다.이러한경우에는리스너가false를반환하면됩니다.
Event::listen('auth.login',function($event)
{
//Handletheevent...
returnfalse;
});
XE이벤트목록
아래목록은XE에서제공하는다양한코어레벨의이벤트의목록입니다.플러그인의boot메소드에서아래이벤트가발생할때실행할리스너를등록하여쓸수있습니다.
이벤트/인터셉션(event/interception)
86
Page 87
Event Parameter(s)
auth.attempt $credentials,$remember,$login
auth.login $user,$remember
auth.logout $user
cache.missed $key
cache.hit $key,$value
cache.write $key,$value,$minutes
cache.delete $key
connection.{name}.beginTransaction $connection
connection.{name}.committed $connection
connection.{name}.rollingBack $connection
illuminate.query $query,$bindings,$time,$connectionName
illuminate.queue.after $connection,$job,$data
illuminate.queue.failed $connection,$job,$data
illuminate.queue.stopping null
mailer.sending $message
router.matched $route,$request
composing:{viewname} $view
creating:{viewname} $view
xe.editor.option.building $editor
xe.editor.option.builded $editor
xe.editor.render' $editor
xe.editor.compile' $editor
인터셉션
이벤트방식을사용하는경우,특정시점에이벤트를발생시키기위해이벤트를fire하는코드를일일이작성해주어야합니다.만약
게시판플러그인에서게시판에글이처음등록될때,또는수정,삭제될때마다다른플러그인에서'끼어들기'를할수있도록해주
려면게시글의등록,수정,삭제에대한이벤트를fire하는코드를3군데에작성해주어야합니다.이벤트방식대신인터셉션을사용
하면이벤트를fire하는코드를매번작성하는번거로움을줄여줄수있습니다.
XE3는인터셉션을구현하기위하여AOP를사용하였습니다.AOP는AspectOrientedProgramming의약어입니다.
어드바이저(리스너)등록
인터셉션방식에서어드바이저는이벤트방식의리스너와비슷합니다.만약사이트에회원이가입할때,메일전송코드가실행되도
록하려면intercept함수를사용하여어드바이저를등록할수있습니다.
이벤트/인터셉션(event/interception)
87
Page 88
//가입축하메일보내기등록
intercept('XeUser@create','welcome_mail::send_mail',function($createUser,array$data)use($mailer){
//회원가입코드를실행
$member=$createUser($data);
//메일전송
$mailer->sendWelcomeMail($member->email,$member->getDisplayName);
//회원가입처리결과반환
return$member;
});
intercept함수의원형은아래와같습니다.
intercept($pointCut,$name,Closure$advice)
첫번째파라메터$pointCut은이벤트서비스의이벤트명과동일한역할을합니다.즉,'끼어들기'를할대상메소드를칭하며,위의
예에서는XeUser@create에해당합니다.이는XeUser의create메소드를뜻합니다.
AOP에서'끼어들기'를하는주체를어드바이저(Advisor)라고합니다.이벤트서비스의리스너와같은개념입니다.두번째파라메터
인$name은이어드바이저의'이름'입니다.원하는이름을직접지정하십시오.단이이름은다른어드바이저의이름과중복되지않아야합니다.중복을회피하기위하여해당플러그인의아이디(디렉토리명)를이름의접두사로사용하십시오.welcome_mail이플러그인아이디에해당합니다.::을사용하여접두사를연결하십시오.
세번째파라메터$advice'끼어들기'를한후실행될코드입니다.클로저형식으로입력해야합니다.이클로저내부에서는반드시
타겟메소드(회원가입메소드)를호출해주어야합니다.타겟메소드는클로저의첫번째파라메터($createUser)를사용하여호출
할수있습니다.타겟메소드를호출하기전이나후에원하는코드를추가하여실행시킬수있습니다.위의예에서는회원가입처리
를한후에해당회원에게메일을전송하는코드가추가되어있습니다.
이클로저에대해자세히설명하면,이클로저의첫번째파라메터는타겟메소드입니다.위의예에서$createUser가이에해당합
니다.클로저내부에서항상이타겟메소드를호출해주어야합니다.또,클로저는타겟메소드의리턴값을다시리턴해야합니다.물론필요에따라리턴값을변경해도됩니다.두번째파라메터부터는대상메소드가호출될때받은파라메터를그대로전달받습니
다.위의예에서는$data에해당하며,가입할회원의정보가담겨있습니다.타겟메소드에따라파라메터의수와내용이달라집니
다.
function($createUser,array$data){
//항상타겟메소드(첫번째파라메터)를호출해주어야한다.
$member=$createMember($data);
//항상타겟메소드의반환값을다시반환해야한다.
return$member;
}
어드바이저간의우선순위지정
다수의어드바이저가등록돼있다면,어드바이저간의실행순서가중요할수있습니다.intercept함수를호출시다른어드바이저
와의우선순위를지정할수있습니다.
//email_checker::check가실행된후실행.
intercept('XeUser@create',['welcome_mail::send_mail'=>'foo@bar'],$closure);
우선순위는두번째파라미터에배열형태로지정할수있습니다.위의intercept함수는배열의키이름인
welcome_mail::send_mail어드바이저를등록하고있습니다.동시에배열의값(value)을이용해여선행되어야하는어드바이저
foo@bar를지정하고있습니다.
위코드에서우선순위관계더욱더명시적으로작성할수있습니다.중첩된배열의키에'before'를사용하십시오.
이벤트/인터셉션(event/interception)
88
Page 89
//명시적으로before사용
intercept(
'XeUser@create',
['welcome_mail::send_mail'=>['before'=>'foo@bar'],
$closure
);
//다수의어드바이저와우선순위지정
intercept(
'XeUser@create',
['welcome_mail::send_mail'=>['before'=>['foo@bar','foo@baz']],
$closure
);
반대로after를사용하면,현재어드바이저보다나중에실행되어야할어드바이저를등록할수도있습니다.
//after사용
intercept(
'XeUser@create',
['welcome_mail::send_mail'=>['after'=>['foo@bar']],
$closure
);
인터셉션서비스는intercept함수를통해등록받은어드바이저들의우선순위를파악한후,순서대로호출해줍니다.어드바이저
들과타겟메소드는데코레이션패턴으로실행됩니다.이는미들웨어와거의동일한방식입니다.
프록시생성
위의회원가입예제에서보면,XeUser@create를포인트컷으로지정하고있습니다.위예제의코드는XeUser파사드
(\Xpressengine\User\UserHandler)의create메소드가실행될때,자동으로호출됩니다.
이렇게어떤클래스의메소드(public메소드만해당)가실행될때,자동으로등록된어드바이저들이호출되도록하려면,그클래스
프록시클래스를생성하고,프록시클래스를대신사용해야합니다.
프록시클래스는XeInterception파사드의proxy메소드를사용하여생성할수있습니다.
//프록시클래스생성
$proxyClass=XeInterception::proxy('\Xpressengine\User\UserHandler','XeUser');
//원본클래스대신프록시클래스사용
$userHandler=new$proxyClass();
위코드에서는원본클래스인\Xpressengine\User\UserHandler클래스의프록시클래스를생성합니다.그리고원본클래스대신
프록시클래스의인스턴스를생성하여사용합니다.마지막파라미터에는원본클래스의별칭(alias)를지정할수있습니다.UserHandler클래스는XeUser파사드를통해많이사용됩니다.위의경우파사드명을별칭으로등록했습니다.intercept함수
를사용할때,원본클래스명뿐만아니라별칭을사용할수도있습니다.
아래의두코드모두사용할수있습니다.
//별칭을사용
intercept('XeUser@create',...);
//원본클래스명을그대로사용
intercept('Xpressengine\User\UserHandler@create',...);
이벤트/인터셉션(event/interception)
89
Page 90
파일/스토리지(file,storage)XE의스토리지는라라벨이제공하는파일시스템을기반으로사용자의파일을관리할수있는기능을제공합니다.또한분산저장
기능을갖추어필요에따라여러개의파일저장소에파일들을나누어저장할수있습니다.
설정
스토리지와관련된설정은config/filesystems.php에있습니다.기본으로는local드라이버가지정되어소스코드가존재하는위치의storage디렉토리에파일이저장됩니다.
클라우드스토리지를파일저장소로사용하고자하는경우에는별도의패키지가설치되어야합니다.S3나Rackspace를사용하는
경우아래의패키지가컴포저를통해설치되어야합니다.
AmazonS3:league/flysystem-aws-s3-v3~1.0Racspace:league/flysystem-rackspace~1.0
분산저장
분산저장기능을사용하기위해서는설정을변경해야합니다.설정파일의division항목에enable값을true로설정하고,파일을저장할파일저장소명을작성해야합니다.
'division'=>[
'enable'=>true,
'disk'=>['local','s3','rackspace']
]
설정이변경되면스토리지패키지는파일이업로드될때마다자동으로각파일저장소로분산하여저장하게됩니다.
분산저장의대상이되는파일저장소는분산저장을설정하기이전에사용가능한값으로설정되어야합니다.
사용
업로드
파일을서버로업로드하고자할때는간단하게upload메서드만호출하면됩니다.
useXeStorage;
useXpressengine\Http\Request;
useApp\Http\Controllers\Controller;
classUserControllerextendsController
{
publicfunctionuploadFile(Request$request)
{
XeStorage::upload($request->file('attached'),'path/to/dir');
}
}
저장하는파일이서버에서특별한이름을가지게하고싶은경우이름을지정할수있습니다.
XeStorage::upload($request->file('attached'),'path/to/dir','custom_file_name');
그리고만약특정파일저장소에파일을저장하고자한다면해당파일저장소를지정할수있습니다.
XeStorage::upload($request->file('attached'),'path/to/dir',null,'s3');
단순업로드를통한저장이아닌편집등을거쳐생성된결과물을파일로저장하려는경우create메서드를사용하면됩니다.
파일/스토리지(file,storage)
90
Page 91
$content=file_get_content('path/to/file');
$content=/*편집처리(이미지섬네일생성등과같은)*/;
XeStorage::create($content,'path/to/dir','somefile');
다운로드
파일객체를download메서드를통해전달하세요.
$file=XeStorage::find($id);
XeStorage::download($file);
관계와재사용
XE스토리지는파일과파일을이용하는대상과의관계를필요로합니다.그래서업로드후관계를맺어주는작업을수행해야합니
다.
$file=XeStorage::upload($request->file('attached'),'path/to/dir');
XeStorage::bind($targetId,$file);
그리고이미업로드되어진파일은다른대상과도관계를맺을수있습니다.
$file=XeStorage::find($id);
XeStorage::bind($otherTargetId,$file);
만약파일을사용하던대상이더이상해당파일을사용하지않게된경우관계를끊을수있습니다.
$file=XeStorage::find($id);
XeStorage::unBind($targetId,$file);
이때어떤대상과도연결되어지지않게될경우해당파일을삭제할수있습니다.
XeStorage::unBind($targetId,$file,true);
여러개의파일을동시에처리하는경우sync메서드를사용하면파일을사용하는대상과파일들의관계를간편하게정의할수있습니다.
$fileIds=[];
foreach($uploadedFilesas$uploadedFile){
$file=XeStorage::upload($request->file('attached'),'path/to/dir');
$fileIds[]=$file->id;
}
XeStorage::sync($targetId,$fileIds);
위와같이관계가형성되면대상의아이디로대상에연결된파일들을조회하거나관계를끊을수있습니다.
//대상의파일목록조회
$files=XeStorage::fetchByFileable($targetId);
//대상의삭제등의처리시파일들의연결해제처리
XeStorage::unBindAll($targetId);
파일/스토리지(file,storage)
91
Page 92
__
회원및인증서비스
XE는회원정보를저장하고관리하는기능을제공합니다.일반적인회원관리기능으로는신규가입,회원정보수정,회원삭제와같은기능이있습니다.또,인증시도,로그인,로그아웃과같은인증관련기능도함께제공합니다.
회원관리기능을정확히사용하기위해서는회원및회원의부가정보들의데이터구조를정확히파악해야합니다.
데이터구조
회원
회원은사이트에서가입및인증의대상이되는개념으로,일반적으로한명의사람이라고생각할수있습니다.회원은사이트내에
서권한(permission)을부여받을수있는대상이될수있습니다.
XE는회원정보를\Xpressengine\User\Models\User엘로퀀트모델로표현하며user테이블에저장합니다.
계정
일반적인회원서비스에서는회원(user)과계정(account)을동일개념으로처리합니다.하지만XE는소셜로그인과같은외부계정
을통한가입/인증을유연하게적용하기위하여회원과계정의개념을분리했습니다.회원은기본계정이외에다수의외부계정을
가질수있으며,다수의계정중하나로회원을인증(authentication)하고로그인할수있습니다.
만약소셜로그인과같이외부사이트에서제공하는회원서비스를사용하는회원가입/로그인기능을구현하고싶다면,회원계정을
잘활용하시길바랍니다.
XE는계정정보를\Xpressengine\User\Models\UserAccount엘로퀀트모델로표현하며user_account테이블에저장합니다.
XE에서는회원의계정정보를등록(저장)하고조회,삭제하는기능만제공합니다.계정정보를이용한가입및인증은별도의
플러그인(social_login)을통해제공합니다.
이메일
한명의회원은다수의'승인된이메일'을소유할수있습니다.만약회원이다수의이메일을소유하고있다면,소유한이메일중하나
와비밀번호를사용하여로그인할수있습니다.
다수의이메일중하나는회원의대표이메일로지정됩니다.대표이메일은회원에게이메일을전송하거나외부에공개될때사용되
는이메일입니다.
XE는이메일정보를\Xpressengine\User\Models\UserAccount엘로퀀트모델로표현하며user_email테이블에저장합니다.
승인대기이메일
회원은하나의'승인대기이메일'을가질수있습니다.신규회원이가입할때입력한이메일은승인대기이메일로등록됩니다.승인
대기이메일은승인된이메일과별도의테이블에저장되며,이메일인증과정을거친후에승인된이메일로등록됩니다.
XE는승인대기이메일정보를\Xpressengine\User\Models\UserAccount엘로퀀트모델로표현하며user_pending_email테이블
에저장합니다.
사이트관리자가'이메일인증'기능을사용하도록설정했을경우,가입시등록한이메일승인대기이메일에등록됩니다.만약이메일인증기능을사용하지않는다면,가입과동시에일반이메일로등록됩니다.
회원등급
회원/인증(user/auth)
92
Page 93
한명의회원은3개의회원등급(최고관리자,관리자,일반회원)중하나를부여받습니다.최고관리자(super)는사이트내에서모든권한을가지는운영자입니다.관리자(manager)는일반적으로사이트를함께관리하는부운영자라고생각할수있습니다.일반회원
(member)는사이트에사용자인일반적인회원입니다.
회원등급은별도의테이블에저장되지않으며,회원(user)테이블의rating컬럼에저장됩니다.
그룹
회원그룹은회원을자유롭게그루핑할때사용합니다.사이트관리자는자유롭게회원그룹을생성할수있고,회원을다수의회원그
룹에소속시킬수있습니다.회원그룹은권한을부여받을수있는대상이될수있습니다.
XE는그룹정보를\Xpressengine\User\Models\UserGroup엘로퀀트모델로표현하며user_group테이블에저장합니다.또,회원
과회원이소속된그룹의관계는user_group_user테이블에저장됩니다.
회원관리
회원및부가정보(그룹,계정,이메일등)를조회하거나처리할때에는XeUser파사드를사용하십시오.XeUser파사드의실제구현은\Xpressengine\User\UserHandler에정의되어있습니다.
회원조회
회원아이디로회원을조회할때에는find메소드를사용하십시오.
$user=XeUser::find($id);
$username=$user->getDisplayName();
여러회원을조회할수도있습니다.
$ids=[1,2,3];
$users=XeUser::find($ids);
foreach($usersas$user){
...
}
다양하고복잡한조건(쿼리)으로회원을조회할수도있습니다.자세한사용법은라라벨문서를참조하십시오.
//displayName이'foo'인회원조회
$user=XeUser::where('displayName','foo')->first();
//displayName이foo로시작하는모든회원조회
$user=XeUser::where('displayName','like','%foo')->get();
신규회원추가
XeUser는복잡한회원생성과정을한번에처리해주는create메소드를제공합니다.create메소드는입력한신규회원정보에
대한유효성검사후신규회원을생성합니다.또한회원의계정(account),이메일(email)정보도자동으로추가되며,소속될그룹에
대한정보가전달되었을경우,그룹에추가시켜주기도합니다.
$data=[
'displayName'=>'foo',
'email'=>'[email protected] ',
//...
];
$newUser=XeUser::create($data);
회원/인증(user/auth)
93
Page 94
신규회원의계정(account)정보를같이등록할수도있습니다.
$data=[
'displayName'=>'foo',
'email'=>'[email protected] ',
//...
]
$data['account']=[
'provider'=>'facebook',
'token'=>'3DIfdkwwfdsie...',
'id'=>'idoffacebookuser',
'data'=>'...'
]
$newUser=XeUser::create($data);
신규회원이소속될회원그룹을지정할수도있습니다.
$data=[
'displayName'=>'foo',
'email'=>'[email protected] ',
'groupId'=>[21,23]//그룹아이디가21,23인그룹에회원을소속시킴
]
$newUser=XeUser::create($data);
만약,유효성검사를생략하거나부가정보(account,email,group)를등록을하지않고,순수하게회원정보만추가하고싶다면
XeUser파사드대신,UserRepository를사용하십시오.
UserRepository는XeUser파사드를사용하여로드할수있습니다.
//순수회원정보만등록
$data=[
'displayName'=>'foo',
'email'=>'[email protected] ',
'password'=>'...'
]
$newUser=XeUser::users()->create($data);
UserRepository의create메소드를곧바로사용할때,password를암호화하지않고바로저장합니다.먼저password필드를직접암호화하십시오.
회원정보수정
XeUser::update메소드를사용하면회원정보를변경할수있습니다.유효성검사및프로필이미지처리,소속그룹의변경도동시
에처리합니다.
$user=XeUser::find(20);
XeUser::update($user,['displayName'=>'bar']);
회원삭제
XeUser::leave메소드를사용하면회원을삭제(탈퇴)할수있습니다.삭제할회원의부가정보(account,email)도같이삭제됩니
다.
$ids=[12,23,34];
XeUser::leave($ids);//3명의회원을탈퇴시킴
회원/인증(user/auth)
94
Page 95
UserRepository의delete메소드를사용하여회원을삭제할수도있습니다.단,이메소드를사용하면삭제될회원의부가정보
는함께삭제되지않습니다.
$user=XeUser::find(12);
XeUser::users()->delete($user);
회원계정관리
계정조회
회원계정을조회할때에는UserAccountRepository를사용하십시오.UserAccountRepository는XeUser파사드를사용하여로드
할수있습니다.
$userAccountRepository=XeUser::accounts();
회원이소유한계정목록을조회할수있습니다.
//회원아이디로계정정보조회
$userId='123';
$accounts=XeUser::accounts()->findByUserId($accountId);
위코드는아래코드로대체할수도있습니다.
$user=XeUser::find('123');
$accounts=$user->accounts;
User의getAccountByProvider메소드를사용하면특정프로바이더(소셜로그인벤더)의계정을조회할수있습니다.
$user=XeUser::find('123');
$facebookAccount=$user->getAccountByProvider('facebook');
좀더복잡한조건으로계정을조회하고싶다면,query메소드를사용하십시오.
$query=XeUser::accounts()->query();
$account=$query->where('email','[email protected] ')->first();
계정생성
회원계정을생성할때에는XeUser파사드를사용할수있습니다.createAccount메소드를사용하십시오.
//기존회원에계정정보추가하기
$user=XeUser::find('123');
$accountData=[
'email'=>'[email protected] ',
'accountId'=>'facebookId',
'provider'=>'facebook',
'token'=>'39238432893,
'data'=>'...'
];
$newAccount=XeUser::createAccount($user,$accountData);
계정수정
회원계정을수정할때에도XeUser파사드를사용할수있습니다.updateAccount메소드를사용하십시오.
회원/인증(user/auth)
95
Page 96
$user=XeUser::find('123');
$facebookAccount=$user->getAccountByProvider('facebook');
XeUser::updateAccount($facebookAccount,[token=>'2197548']);
계정삭제
회원계정을수정할때에는XeUser::deleteAccount메소드를사용하십시오.
XeUser::deleteAccount($facebookAccount);
회원이메일관리
이메일조회
회원계정을조회할때에는UserEmailRepository를사용하십시오.UserEmailRepository는XeUser파사드를사용하여로드할수있습니다.
$userEmailRepository=XeUser::emails();
이메일주소로이메일정보를조회할수있습니다.
$email=XeUser::emails()->findByAddress('[email protected] ');
특정회원이소유한모든이메일을조회할수있습니다.
$userId='123';
$emails=XeUser::emails()->findByUserId($userId);
좀더복잡한조건으로이메일을조회하고싶다면,query메소드를사용하십시오.
//'foo@'로시작되는이메일검색
$query=XeUser::emails()->query();
$emails=$query->where('address','like','foo@%')->get();
승인대기이메일을조회할때에는UserEmailRepository대신'PendingEmailRepository'를사용하십시오.PendingEmailRepository는XeUser파사드를사용하여로드할수있습니다.UserEmailRepository와동일하게사용할수있습니
다.
$userEmailRepository=XeUser::pendingEmails();
이메일생성
회원이메일을생성할때에는XeUser파사드의createEmail메소드를사용하십시오.
$user=XeUser::find('123');
$emailData=[
'address'=>'[email protected] '
];
$email=XeUser::createEmail($user,$emailData,true);
createEmail메소드의마지막메소드는생성하는이메일의승인여부를지정합니다.true이면승인된이메일로저장되며,false이면승인대기이메일로저장됩니다.
회원/인증(user/auth)
96
Page 97
이메일수정
기등록된이메일(또는승인대기이메일)을수정할수있습니다.XeUser::updateEmail을사용하십시오.
$email->address='[email protected] ';
XeUser::updateEmail($email);
//or
XeUser::updateEmail($email,['address'=>'[email protected] ']);
이메일삭제
이메일(또는승인대기이메일)을삭제할수있습니다.XeUser::deleteEmail을사용하십시오.
XeUser::deleteEmail($email);
회원그룹관리
그룹조회
...
그룹생성
//그룹정보생성
$groupData=[
'name'=>'정회원',
'description'=>'기본회원',
];
$group=XeUser::createGroup($groupData);
그룹수정
XeUser::updateGroup($group,['name'=>'기본회원']);
그룹삭제
XeUser::deleteGroup($group);
인증
등록된회원은사이트에로그인할수있습니다.사이트에로그인할때XE는입력된회원정보에대한인증(authentication)과정을
거친후,세션(session)에로그인한회원의정보를등록합니다.로그아웃을할때에는반대로세션에등록했던회원정보를삭제합
니다.
Auth파사드를사용하면현재접속한회원의로그인,로그아웃을비롯한인증과관련된기능을수행할수있습니다.
현재로그인한사용자조회
현재로그인한사용자를조회할때에는user메소드를사용하십시오.
회원/인증(user/auth)
97
Page 98
$user=Auth::user();
또는request인스턴스를사용할수도있습니다.
$user=request()->user();
로그인한사용자의아이디(id)만조회할수도있습니다.
$loggedUserId=Auth::id();
로그인상태조회
check메소드를사용하면접속중인사용자가로그인을한상태인지조회할수있습니다.
if(Auth::check()){
//Theuserisloggedin...
}
로그인
등록된회원의인스턴스를획득하고있다면,login메소드를사용하여해당회원을로그인시킬수있습니다.
$user=XeUser::find('123');
Auth::login($user);
해당회원이현재와동일한환경(단말기의브라우저)에서다음에다시사이트에접속했을때,자동으로로그인처리를하는'로그인
유지'기능을사용할수있습니다.login메소드의두번째파라메터로true를사용하면,해당회원에게'로그인유지'기능을활성화시킵니다.
//Loginand"remember"thegivenuser...
Auth::login($user,true);
로그인시도
login메소드는회원정보의인증과정을거치지않고,회원인스턴스를통해바로로그인처리를합니다.만약회원의입력한정보
를인증한후로그인까지시도하고싶을때에는attempt메소드를사용하십시오.
attempt메소드를실행하면입력된정보가등록된회원정보와일치하는지검사합니다.회원정보가일치한다면해당회원을로그
인시킵니다.
if(Auth::attempt(['email'=>$email,'password'=>$password])){
//Authenticationpassed...
returnredirect()->intended('dashboard');
}
만약'로그인유지'기능을사용하고싶을때에는두번째파라메터로true를전달하십시오.
if(Auth::attempt(['email'=>$email,'password'=>$password],true)){
//Theuserisbeingremembered...
}
로그아웃
logout메소드를사용하십시오.
회원/인증(user/auth)
98
Page 99
Auth::logout();
회원/인증(user/auth)
99
Page 100
모바일(mobile)플러그인을개발할때,현재접근한기기가데스크탑인지모바일기기인지알아야할때가있습니다.XE는자체적으로접근한기기
의타입을판단하고,이를사용할수있도록인터페이스를제공합니다.
기기타입강제지정
XE는우선브라우저의user-agent정보를바탕으로현재접근한기기의타입이데스크탑인지모바일인지검사합니다.
만약사용자가접근한기기의타입을강제로지정하고싶다면,요청URL의쿼리스트링을사용해지정할수있습니다.요청하는
URL에?_m=1을추가하면XE가현재접속한기기를모바일기기로판단하도록강제지정할수있습니다.반대로?_m=0을추가
하면데스크탑기기로판단하도록강제지정됩니다.강제로지정한정보는쿠키에추가되어차후요청시에도당분간유지됩니다.
코드상에서기기타입을지정하고싶다면직접쿠키를저장하면됩니다.쿠키이름을mobile에값을'0'또는'1'로저장하십시오.
//현재접속기기를120분간모바일기기로강제지정,
\Cookie::queue('mobile','1',120);
현재접근한기기의타입검사
XE는현재접근한기기의타입은Request인스턴스를통해알수있습니다.
$isMobile=request()->isMobile();
isMobile메소드를사용하면,현재설정된기기의타입이모바일인지검사할수있습니다.만약접근한기기타입이강제로지정돼
있다면,강제로지정된타입을반환합니다.
강제로지정된타입과관계없이순수하게브라우저의user-agent정보만으로판단된기기타입을알고싶다면대신
isMobileByAgent메소드를사용하십시오.
$isMobile=request()->isMobileByAgent();
모바일(mobile)
100
Page 101
권한(permission)XE는관리자시스템을통한동적인권한검사를위해laravel의Authorization기능을확장하여제공하고있습니다.
권한검사유형
permission에서는권한을검사하기위해5가지유형의데이터를가지고있습니다.
RATING_TYPE:사용자의등급을나타냅니다.등급은GUEST,MEMBER,MANAGER,SUPER로구분합니다.GROUP_TYPE:권한을가지는그룹들을지정합니다.USER_TYPE:권한을가지는특정사용자들을지정합니다.EXCEPT_TYPE:권한을가지지않는특정사용자들을지정합니다.VGROUP_TYPE:권한을가지는가상그룹을지정합니다.
위다섯가지권한중EXCEPT_TYPE이가장우선됩니다.사용자가EXCEPT_TYPE에속하지않는경우나머지중단하나의유형에만
속하여도권한을가지는것으로판단합니다.
등록
권한을등록할때는Grant객체를이용합니다.Grant객체에검사하고자하는특정행위를나타내는키워드와각유형에맞는값들을지정하고,권한을구분하는대상의이름과함께등록하게됩니다.
$grant=newGrant();
$grant->set('show',Rating::MEMBER);
$grant->set('create',Grant::GROUP_TYPE,['{groupId}']);
$grant->set('create',Grant::EXCEPT_TYPE,['{userId}']);
app('xe.permission')->register('foo.bar',$grant);
하나의행위에해당하는다양한유형의데이터를배열을이용하여동시에설정할수있습니다.
$grant->set('create',[
Grant::RATING_TYPE=>Rating::MEMBER,
Grant::GROUP_TYPE=>['{groupId}'],
Grant::EXCEPT_TYPE=>['{userId}']
]);
폼
위와같은데이터를브라우저를통해전달받기위해uiobject가제공됩니다.
<div>
{!!uio('permission',$arguments)!!}
</div>
이때전달되는인자는배열로다음과같은구조를필요로합니다.
array(
'mode'=>'manual',//manualorinherit
'grant'=>[],//특정행위에해당하는Grant객체등록값
'title'=>'create'//특정행위를나타내는키워드
'groups'=>[]//전체그룹
);
uiobject는지정된행위를나타내는키워드와유형을조합하여input의이름을제공합니다.예를들어create라는행위에대한
회원등급의이름은createRating이됩니다.
권한(permission)
101
Page 102
권한등록을위한편의제공
관리자페이지로부터전달되는권한정보를형식에맞게정제하고등록하기에불편함을느낄수있습니다.그래서이런부분에서자유로워질수있도록클래스에삽입되어지는PermissionSupporttrait을제공합니다.
classSomeController
{
usePermissionSupport;
publicfunctiongetSetting()
{
$args=$this->getPermArguments('foo.bar',['create','show']);
...
}
publicfunctionpostSetting(Request$request)
{
$this->permissionRegister($request,'foo.bar',['create','show']);
...
}
...
}
권한검사
권한을검사할때는laravel의Authorization기능을이용합니다.첫번째인자값에행위에대한키워드를전달하고두번째인자에권한등록시사용한이름으로생성된Instance객체를전달합니다.
if(Gate::denies('create',newInstance('foo.bar'))){
//throwexception
}
권한(permission)
102
Page 103
카테고리(category)웹애플리케이션을구성하다보면컨텐츠를분류하고자할때가있습니다.이때필요한게카테고리기능입니다.
등록
카테고리를사용하기위해선먼저카테고리의그룹을생성해야합니다.그리고해당카테고리그룹에아이템들을등록시킵니다.
//신규카테고리그룹생성
$category=XeCategory::createCate();
//카테고리그룹에아이템추가
$item=XeCategory::createItem($category,['word'=>'foo']);
만약특정카테고리아이템의자식에해당하는아이템을등록하고자한다면,전달하는값에parentId항목에부모에해당하는아이디값을포함하면됩니다.
$item=XeCategory::createItem($category,['word'=>'foo','parentId'=>'{parentid}']);
트리
카테고리는트리구조를가지고있습니다.그래서특정카테고리아이템의부모또는자식에해당하는아이템들을조회할수있습니
다.
$item=XeCategory::items()->find($id);
//자식에해당하는아이템
$children=$item->getChildren();
//부모아이템
$parent=$item->getParent();
또한트리형태로아이템들을얻을수있습니다.
$category=XeCagetory::cates()->find($id);
$tree=$category->getTree();//카테고리전체트리
$item=XeCategory::items()->find($id);
$tree=$item->getDescendantTree();//특정아이템의자손들로이루어진트리
카테고리(category)
103
Page 104
데이터베이스(database)XE가자체적으로부하분산을처리할수있을까?어떻게하면회원과문서의데이터베이스사용을분리시킬수있을까?
이고민에서시작이었습니다.이미Laravel은훌륭한데이터베이스처리방식을제안하고있습니다.다만우리는앞서얘기한고민
을해결하기위해새로운데이터베이스를만들기보다우리가하고자하는일을해결할수있는기능을추가한전혀새롭지않은
(Laravel의데이터베이스와같은)데이터베이스를만들고싶었습니다.
기본적인사용방법은Laravel과같으며단지XeDB파사드를사용하면됩니다.
기본적인데이터베이스사용,쿼리빌더,마이그레이션의내용을숙지하시기바랍니다.
설정
config/xe.php의database에가상연결설정을등록합니다.
'database'=>[
'default'=>[
'slave'=>['default'],
],
'document'=>[
'slave'=>['default'],
],
'user'=>[
'slave'=>['default'],
],
],
XE에서는회원과문서의데이터베이스를분리해서사용할수있도록가상연결을분리해서사용하고있습니다.app/Providers/의UserServiceProvider.php,DocumentServiceProvider.php에서각각user,document커넥션을사용하는코드를확인할수있습니다.
가상연결
여러개의논리적인데이터베이스연결을사용하여다중커넥션사용을가능하도록하고서버에트래픽이증가할경우패키지별데이터베이스연결설정을변경하여부하분산을빠르게처리할수있도록합니다.또한하나의논리적인데이터베이스연결에여러개
의데이터베이스연결설정을할수있도록하고Query처리할때랜덤하게커넥션을사용하도록구현하여부하분산을처리합니다.
또한물리적으로다른여러개의커넥션에대해서트랜젝션을사용할수있습니다.여러개의커넥션에서각각발생하는트랜젝션을
관리하여하나의커넥션에서처리되는것과같이동작합니다.
다이나믹쿼리를이용해서ProxyManager에등록된Proxy들을사용할수있습니다.다이나믹쿼리는데이터베이스CRUD처리
시발생하여쿼리를조작할수있도록인터페이스를제공합니다.ProxyInterface의인터페이스를QueryBuilder에서각메소드에
서필요한인터페이스를사용합니다.
Proxy데이터베이스Proxy는쿼리실행실행에앞서쿼리를수정할수있도록인터페이스를지원합니다.DynamicQuery에서
ProxyManager를사용하도록해서inster(),update(),delete(),get(),first(),find(),paginate(),simplePaginate()이사용될때쿼리를수정합니다.DynamicField는Proxy기능을이용해서데이터베이스에쿼리할때설정에따라추가된확장필드의처리를위한쿼리를실행하도록구현했습니다.
Proxy사용
데이터베이스(database)
104
Page 105
$users=XeDB::dynamic('user')->get();
Proxy를사용하기위해서table()이아닌dynamic()메소드를사용합니다.dynamic()을사용해반환된DynamicQuery인스
턴스는Proxy를사용하기위한상태가됩니다.
실제데이터베이스연결설정은기존의Laravel설정방법과동일합니다.config/database.php에데이터베이스연결을수정하거
나추가하면됩니다.
트랜잭션
XeDB::beginTransaction();
...query...
XeDB::commit();//orXeDB::rollback();
XE3데이터베이스는여러개의연결에대해서트랜잭션을처리합니다.전체커넥션정보를참고하고있는TransactionHandler는트랜잭션이시작될때연결되어있는모든가상연결에트랙잭션을시작하고또한새로맺는연결도트랜잭션이시작되도록처리합
니다.
가상연결로처리되는하나이상의데이터베이스연결에대해서트랜젝션을처리합니다.DB1,DB2에insert처리할때물리적으
로다른두개의데이터베이스에트랜젝션이처리될수있도록지원합니다.
데이터베이스(database)
105
Page 106
메일(email)
설정
메일에대한설정파일은config/mail.php입니다.SMTP호스트,포트,인증및라이브러리를통해송신되는메세지들의글로벌
form주소를설정할수있는옵션들을제공합니다.원하는경우어떤SMTP서버라도사용할수있습니다.메일을보낼때PHP의mail함수를사용하려한다면설정파일에서driver를mail로변경하면됩니다.또한,sendmail드라이버도사용할수있습
니다.
API드라이버
또한,Mailgun과Mandrill의HTTPAPI드라이버를사용할수있습니다.HTTPAPI를사용하려면라라벨문서를참고하십시오.
로그드라이버
config/mail.php설정파일에서driver옵션을log로설정한다면실제로이메일을수신자에게보내지않고로그파일에기록
하게됩니다.이설정은주로로컬에서빠르게디버깅을해야하거나내용을확인하고자할때유용합니다.
기본사용법
Mail::send메소드를통해서이메일을보낼수있습니다:
Mail::send('emails.welcome',['key'=>'value'],function($message)
{
$message->to('[email protected] ','JohnSmith')->subject('Welcome!');
});
send메소드의첫번째인자로이메일의본문에사용되는뷰(템플릿파일)의이름을전달합니다.두번째는뷰로전달되는인자인
데대부분배열의형태로구성되며$key를통해서뷰에서사용됩니다.세번째로전달되는클로저는이메일메세지에대한다양한
옵션을지정하는데사용됩니다.
주의:$message라는이름의변수가항상이메일뷰에전달되고,인라인첨부를사용가능하게합니다.따라서message라는
이름의변수를메일뷰에서사용하는것은피하는게좋습니다.
만약이메일본문에서사용할템플릿디자인이준비되지않았다면,XE에서제공하는템플릿디자인을사용할수도있습니다.XE에서제공하는템플릿디자인은resources/views/emails/common.blade.php파일로제공됩니다.
이메일본문을담고있는blade파일에서emails.common을extends하십시오.
<!--plugins/my_plugin/views/emails/welcome.blade.php-->
@extends('emails.common')
@section('content')
이메일내용...
@endsection
HTML뷰에추가로플레인텍스트뷰를지정할수도있습니다:
Mail::send(['html.view','text.view'],$data,$callback);
또는html또는text키를사용하고한종류의뷰를지정할수있습니다:
메일(email)
106
Page 107
Mail::send(['text'=>'view'],$data,$callback);
이메일메세지에대해참조나첨부파일과같은다른옵션들을지정할수도있습니다:
Mail::send('emails.welcome',$data,function($message)
{
$message->from('[email protected] ','Laravel');
$message->to('[email protected] ')->cc('[email protected] ');
$message->attach($pathToFile);
});
이메일에첨부파일을추가하고자할때는파일의MIME타입또는첨부파일이표시되는이름을지정할수있습니다:
$message->attach($pathToFile,['as'=>$display,'mime'=>$mime]);
이메일을보내기위한뷰대신에간단한문자열을사용하고자한다면raw메소드를사용하면됩니다:
Mail::raw('Texttoe-mail',function($message)
{
$message->from('[email protected] ','Laravel');
$message->to('[email protected] ')->cc('[email protected] ');
});
주의:Mail::send클로저에전달되는메세지인스턴스는SwiftMailer메세지클래스를확장하므로이메일을작성하는데필요한클래스의메소드들를사용할수있습니다.
인라인첨부
이메일에인라인이미지를포함시키는것은번거로운일입니다.XE는이메일에이미지를첨부하고최적의CID를얻을수있는편리한방법을제공합니다.
이메일뷰에서이미지를첨부하는방법
<body>
Hereisanimage:
<imgsrc="<?phpecho$message->embed($pathToFile);?>">
</body>
이메일뷰에서Raw데이터를첨부하는방법
<body>
Hereisanimagefromrawdata:
<imgsrc="<?phpecho$message->embedData($data,$name);?>">
</body>
$message변수는항상Mail클래스에의해서뷰에전달된다는것에주의하십시오.
로컬개발환경에서의메일
메일(email)
107
Page 108
이메일을전송하는플러그인을개발하는경우에로컬또는개발환경이라면메세지전송을비활성화하는것이바람직할것입니다.이를위해서config/mail.php설정파일에pretend옵션을true로설정하거나Mail::pretend메소드를호출하면됩니다.메일러가pretend모드인경우에는이메일메세지는수신자에게송신되는대신에어플리케이션의로그파일에기록됩니다.
만약실제로이메일이어떻게보여지는지확인하고자한다면MailTrap과같은서비스를이용하는것도고려해보시기바랍니다.
메일(email)
108
Page 109
프론트앤드(frontend)브라우저에서어떤페이지를요청하면XE는보통모듈스킨,테마,그리고스킨과테마에서사용한위젯이나UI오브젝트에서생성
한html조각들을조합하여하나의html문서를만듭니다.그리고이html문서에는다양한스타일시트와스크립트파일이로드되고
meta태그같은태그들이html에추가됩니다.Frontend서비스는이렇게하나의html문서를출력할때필요한다양한태그를추가
해주고관리하는역할을합니다.
XE에서는요청을처리할때마다매우다양한플러그인이실행되며,각각의플러그인들은자신이필요한스크립트나스타일시트파일을로드하게됩니다.만약Frontend와같은관리주체가없다면,플러그인들에의해동일한스크립트파일이중복으로로드될수있습니다.
Frontend서비스는아래와같은역할을합니다.
html문서의타이틀을지정한다.body태그에특정class를추가한다.js파일을html특정영역(head또는body의상,하단)에로드한다
css파일을html에로드한다.이미다른컴포넌트에서로드된js,css파일을언로드(unload)한다.meta태그를html에로드한다.custom태그(자유로운형식의text)를html에추가한다.formvalidation을위한rule을지정한다.(브라우저에서script를통해실행되는validation)script에서사용할언어팩을로드한다.
위의역할을수행할때,Frontend서비스는여러곳에서로드된js파일이나css파일의중복을처리해줍니다.또,js파일이나css파일은로드되는순서도매우중요합니다.Frontend패키지는파일이로드되는순서를지정하는기능도제공합니다.
브라우저타이틀태그지정하기
title메소드를사용하십시오.
XeFrontend::title('브라우저타이틀입니다');
body태그의class지정하기
bodyClass메소드를사용하십시오.
//body에'profile'class지정
XeFrontend::bodyClass('profile');
js파일로드하기
기본사용법
js메소드를사용하여스크립트파일을로드할수있습니다.반드시마지막에는load메소드를사용해야합니다.
//xe.js파일을body의하단에로드함.
XeFrontend::js('assets/core/common/js/xe.js')->load();
이때,appendTo,prependTo메소드를사용하면,html상에스크립트파일이로드되는위치를지정할수있습니다.지정하지않을
경우<body>태그하단에로드합니다.
프론트앤드(frontend/assets)
109
Page 110
//xe.js파일을head의하단에로드함.
XeFrontend::js('assets/core/common/js/xe.js')->appendTo('head')->load();
//xe.js파일을body의상단에로드함.
XeFrontend::js('assets/core/common/js/xe.js')->prependTo('body')->load();
배열을사용하여다수의파일을동시에로드할수도있습니다.
XeFrontend::js([
'assets/vendor/jquery/jquery.min.js',
'assets/core/common/js/xe.js',
'plugin/board/assets/js/my.js'
)->load();
우선순위지정
만약스크립트파일을로드할때,반드시먼저로드돼야하는다른스크립트파일이있다면before메소드를사용하여지정할수있습니다.반대의경우,after메소드를사용하십시오.
//bootstrap.js이로드된이후에xe.js파일이로드되도록우선순위지정
XeFrontend::js('assets/core/common/js/xe.js')
->before('assets/vendor/bootstrap/js/bootstrap.js')
->appendTo('body')->load();
다수의파일을동시에로드한다면,배열에기입된순서대로우선순위가정해집니다.
//3파일의우선순위가자동으로지정됨
XeFrontend::js([
'assets/vendor/jquery/jquery.min.js',
'assets/core/common/js/xe.js',
'plugin/board/assets/js/my.js'
)->load();
주의!만약위의방법을사용하여명시적으로우선순위를지정하지않았다면,로드된파일들의우선순위를보장할수없습니
다.먼저로드된파일이라하더라도html상에서는나중에로드된파일보다늦게로드될수있습니다.
언로드
이미로드된스크립트파일이라도unload메소드를사용하여언로드할수있습니다.
//로드된xe.js파일을언로드함.
XeFrontend::js('assets/core/common/js/xe.js')->unload();
css파일로드하기
css파일도js스크립트파일과사용법이동일합니다.단appendTo,prependTo메소드를지원하지않습니다.
//xe.css파일을로드함.반드시bootstrap.css가로드된다음에로드되도록우선순위를지정
XeFrontend::js('assets/core/common/css/xe.css')->appendTo('body')->before('assets/vendor/bootstrap.css')->load();
meta태그추가
meta메소드를사용하여meta태그를지정할수있습니다.meta태그의attribute를지정하기위해name,charset,property,httpEquiv,content를지원합니다.
프론트앤드(frontend/assets)
110
Page 111
//등록하려는meta태그의별칭등록.
$alias='my.viewport';
XeFrontend::meta($alias)->name('viewport')
->content('width=device-width,initial-scale=1.0')->load();
alias는다른곳에서내가입력한meta태그를언로드할때key로사용합니다.
customhtml태그추가하기
html메소드를사용하면자유롭게원하는코드를추가할수있습니다.
$alias='myscript';
//script코드를`<body>`하단에추가
XeFrontend::html($alias)->content('
<script>
$(function(){
$('[data-toggle="tooltip"]').tooltip()
})
</script>
')->appendTo('body')->load();
unload,before
icon파일로드하기
icon메소드를사용할수있습니다.
XeFrontend::icon($iconUrl)->load();
프론트앤드(frontend/assets)
111
Page 112
이미지처리(Image,Media)XE에서는사용자가등록하는이미지와같은미디어파일들을제어하기위한Media패키지가있습니다.Media는Intervention/image에서제공하는기능을이용하여설정에서정의된내용에의해섬네일을어떤형태로생성할지결정하고사이
즈별로생성해줍니다.또한이미지외에도오디오,비디오파일들을간편하게표현해주기위한인터페이스를제공합니다.
설정
미디어의설정은config/xe.php파일의media항목에서지정합니다.섬네일설정은다음과같이작성되어있습니다.
'thumbnail'=>[
'disk'=>'local',
'path'=>'public/thumbnails',
'type'=>'fit',
'dimensions'=>[
'S'=>['width'=>200,'height'=>200,],
'M'=>['width'=>400,'height'=>400,],
'L'=>['width'=>800,'height'=>800,],
],
],
disk:생성된섬네일이미지가저장되어질파일저장소를지정합니다.파일저장소는config/filesystems.php에서정의합니
다.path:섬네일이미지가위치할경로를지정합니다.type:어떤형태로섬네일이미지를생성할지지정합니다.지정할수있는타입은다음과같습니다.
fit:지정된사이즈에꽉차고,넘치는영역은삭제
letter:지정된사이즈안에이미지가모두포함되면서기존비율과같은비율을가지는형태
widen:가로사이즈만을기준으로생성
heighten:세로사이즈만을기준으로생성
stretch:비율을무시하고지정된사이즈에꽉차게생성
spill:지정된사이즈에꽉차고,넘치는영역은삭제하지않는형태
dimensions:생성될섬네일이미지의가로,세로사이즈를지정합니다.
Media에서는비디오파일에서이미지를추출하기위한확장기능을제공합니다.
'videoExtensionDefault'=>'dummy',
'videoExtensions'=>[
'ffmpeg'=>[
'ffmpeg.binaries'=>'/usr/local/bin/ffmpeg',
'ffprobe.binaries'=>'/usr/local/bin/ffprobe',
'timeout'=>3600,
'ffmpeg.threads'=>4,
]
],
'videoSnapshotFromSec'=>10
기본으로지정된dummy는이미지추출작업을하지않는가상의확장기능입니다.비디오에서이미지를추출하도록하고싶다면
ffmpeg로변경하고필요한항목을설정해야합니다.ffmpeg를사용하기위해선서버에FFmpeg가설치되어있어야합니다.그리고컴포저를통해다음패키지가설치되어야합니다.
php-ffmpeg/php-ffmpeg~0.5
비디오확장기능은현재ffmpeg만지원합니다.
섬네일생성
이미지처리(Image,media)
112
Page 113
섬네일을생성하기위해서는우선File객채를미디어객체로변환해야합니다.is메서드를통해특정파일이미디어파일인지
확인하고,미디어파일인경우섬네일을생성하도록합니다.
useXeStorage;
useXeMedia;
useXpressengine\Http\Request;
useApp\Http\Controllers\Controller;
classUserControllerextendsController
{
publicfunctionuploadFile(Request$request)
{
$file=XeStorage::upload($request->file('attached'),'path/to/dir');
if(XeMedia::is($file)){
$media=XeMedia::make($file);
$thumbnails=XeMedia::createThumbnails($media);
}
}
}
특정페이지는섬네일이미지생성시별도의형태로하고자하는경우직접타입을지정할수있습니다.
$thumbnails=XeMedia::createThumbnails($media,'widen');
기존에생성된섬네일이미지는이미지핸들러를통해얻을수있습니다.
$thumbnails=XeMedia::images()->getThumbnails($media);
//특정섬네일을얻는경우
$thumbnail=XeMedia::images()->getThumbnail($media,'spill','S');
편집기능특별하게사용하기
XE에서제공하는이미지편집기능중에crop기능이있습니다.crop은가로,세로사이즈뿐만아니라좌표값이필요한기능이
므로기본적인섬네일생성방식에서제외되어있습니다.이기능을사용하기위해서는아래와같이조금특별하게코드가작성되어
야합니다.
useXeStorage;
useXpressengine\Media\Commands\CropCommand;
useXpressengine\Media\Coordinators\Position;
useXpressengine\Media\Coordinators\Dimension;
useXpressengine\Media\Thumbnailer;
useApp\Http\Controllers\Controller;
classUserControllerextendsController
{
publicfunctioneditImage($id)
{
$image=XeMedia::images()->find($id);
$cropCmd=newCropCommand();
$cropCmd->setPosition(newPosition(50,100));//시작좌표설정
$cropCmd->setDimension(newDimension(300,200));//시작좌표로부터가로,세로사이즈지정
$thumbnailer=newThumbnailer();
$content=$thumbnailer->setOrigin($image->getContent())->addCommand($cropCmd)->generate();
XeStorage::create($content,'path/to/dir','crop_file_name');
}
Thumbnailer는처리할명령을순서대로입력받을수있습니다.만약crop한이미지를letter타입으로크기를변환하려고한다면다음과같이할수있습니다.
이미지처리(Image,media)
113
Page 114
$letterCmd=newLetterCommand();
$letterCmd->setDimension(newDimension(100,100));
$content=$thumbnailer->setOrigin($image->getContent())->addCommand($cropCmd)->addCommand($letterCmd)->generate();
타입별접근
XE에서는3가지미디어타입을분리하여기능을제공하고있습니다.각타입의객체를획득하거나기능을사용하기위해서는해당
타입의핸들러를획득해야합니다.핸들러를획득하기위해서는각타입의타입키워드를단수또는복수형으로호출하는방식을이용해야합니다.
//imagehandler
XeMedia::image();
XeMedia::images();
//videohandler
XeMedia::video();
XeMedia::videos();
//audiohandler
XeMedia::audio();
XeMedia::audios();
extend메서드를이용하여추가된미디어핸들러도같은방식으로사용할수있습니다.
미디어표현
미디어의객체는간편하게화면에표현할수있도록인터페이스를제공합니다.이것은사전에정의된html태그를반환합니다.
<div>
{{$image->render()}}
</div>
<div>
{{$audio->render()}}
</div>
<div>
{{$video->render()}}
</div>
기본으로제공하는html태그를사용하지않길원할수도있습니다.이땐단순히파일의url을반환받을수있으므로별도의태그를
직접작성하여해결할수있습니다.
<objectdata="{{$video->url()}}"width="300"height="200">
<paramname="autoplay"value="true">
<paramname="showcontrols"value="true">
</object>
이미지처리(Image,media)
114
Page 115
세션(Session)
설정
HTTP기반의어플리케이션은상태를저장할수없기때문에,HTTP요청들에관계없이사용자의정보를저장하기위해서세션이사용됩니다.XE는세션을사용함에있어간결하고통일된API를제공합니다.세션시스템으로는Memcached,Redis그리고데이터
베이스를별다른설정없이,동일한API로사용할수있습니다.
세션의설정은config/session.php파일에있습니다.이파일에는각각의옵션에대한정리된문서가포함되어있으므로잘확인
하시기바랍니다.기본으로는file세션드라이버가사용됩니다.
참고:세션을암호화하여저장하고자한다면encrypt설정옵션을true로지정하십시오.
예약어
XE는내부적으로flash라는세션키를사용하고있습니다.이이름으로세션을추가하지마시기바랍니다.
세션사용법
여러가지방법으로세션을엑세스할수있습니다.HTTP요청-request의session메소드를사용하는방법,Session파사드를
사용하는방법,그리고session헬퍼함수를사용할수있습니다.아무런전달인자없이session헬퍼함수를호출할때에는전체
세션객체가반환됩니다
아이템세션에저장하기
Session::put('key','value');
session(['key'=>'value']);
배열세션값으로저장하기
Session::push('user.teams','developers');
세션에서특정아이템가져오기
$value=Session::get('key');
$value=session('key');
특정아이템을찾거나기본값반환받기
$value=Session::get('key','default');
$value=Session::get('key',function(){return'default';});
아이템값가져온후삭제하기
$value=Session::pull('key','default');
세션(Session)
115
Page 116
세션의모든데이터가져오기
$data=Session::all();
세션에아이템이존재하는지확인하기
if(Session::has('users'))
{
//
}
세션에서특정아이템삭제하기
Session::forget('key');
세션의모든아이템삭제하기
Session::flush();
세션ID재생성하기
Session::regenerate();
임시데이터
때로는바로다음번의요청에서만사용하기위해서세션에아이템을저장할수있습니다.바로Session::flash메소드를사용하는
것입니다:
Session::flash('key','value');
현재의임시데이터를다른요청에서사용하기위해서다시임시저장하기
Session::reflash();
임시데이터의일부값만다시임시저장하기
Session::keep(['username','email']);
데이터베이스세션
database세션드라이버를사용하는경우세션아이템들이저장될테이블을생성해야합니다.다음의Schema예제를통해서테이블을생성할수있습니다.
Schema::create('sessions',function($table)
{
$table->string('id')->unique();
$table->text('payload');
$table->integer('last_activity');
});
세션(Session)
116
Page 117
session:table아티즌명령어를사용하면이마이그레이션을생성할수있습니다!
phpartisansession:table
composerdump-autoload
phpartisanmigrate
세션드라이버
세션“드라이버”는각각의요청에따라세션이어디에저장될지를결정합니다.XE는별다른설정없이다양한드라이버를사용할
수있습니다:
file-세션이storage/framework/sessions폴더에저장됩니다.cookie-암호화된쿠키를사용하여안전하게세션을저장할것입니다.database-세션은어플리케이션에의해서데이터베이스에저장됩니다.memcached/redis-세션은캐시기반의드라이버들에의해빠르게저장됩니다.array-세션은간단한PHP배열에저장되지만현재요청에한해일시적으로저장됩니다.
Redis세션을사용하기위해서는컴포저를통해서predis/predis(~1.0)패키지를설치해야만합니다.
주의:배열드라이버의경우에는실제로세션은유지되지않기때문에unittests를수행하는데에만사용하시기바랍니다.
세션(Session)
117
Page 118
헬퍼(helpers)
헬퍼(helpers)
118
Page 119
쿠키(cookie)XE에서모든쿠키는서버에서미들웨어를거치면서인증코드와함께암호화됩니다.이말은클라이언트가변경되었을때쿠키가
유효하지않다는것을의미합니다.쿠키값은Request인스턴스와Response인스턴스를사용하여조회하거나저장할수있습니
다.
쿠키값가져오기
$value=$request->cookie('name');
Response에새로운쿠키추가하기
cookie헬퍼함수는새로운Symfony\Component\HttpFoundation\Cookie인스턴스를생성하기위한간단한팩토리로작동합니다.쿠키를Response인스턴스에추가하려면withCookie메소드를사용하면됩니다:
$response=newIlluminate\Http\Response('HelloWorld');
$response->withCookie(cookie('name','value',$minutes));
영원히남게되는쿠키생성하기
영원히는실제로는5년을의미합니다.
$response->withCookie(cookie()->forever('name','value'));
쿠키큐처리하기
플러그인클래스의boot메소드는XE의라이프사이클에서매우이른시점에실행됩니다.이렇게Response인스턴스가작성되
기전에실행되는코드에서는반환되는Response에추가할쿠키를“큐처리”할수있습니다:
<?phpnamespaceApp\Http\Controllers;
useCookie;
useIlluminate\Routing\Controller;
classUserControllerextendsController
{
/**
*Updatearesource
*
*@returnResponse
*/
publicfunctionupdate()
{
Cookie::queue('name','value');
returnresponse('HelloWorld');
}
}
쿠키(cookie)
119
Page 120
UI오브젝트및폼빌더
XE에서는화면에자주출력되는UI요소들이있습니다.텍스트입력필드나셀렉트박스(select)와같은기본적인폼요소들이있고,테마선택기나메뉴선택기,또는권한설정UI와같이XE에서만사용되는특별한요소들도있습니다.UI오브젝트는이렇게자주사용되는UI요소를개발자들이쉽게출력할수있는방법을제공합니다.
기본사용법
uio()함수를사용하여UI오브젝트를출력할수있습니다.
만약한줄텍스트폼필드를출력하려면아래와같이작성할수있습니다.
//inbladetemplatefile
{{uio('formText',
['label'=>'전화번호',
'name'=>'phone',
'id'=>'inputPhone',
'description'=>'휴대폰사용하여입력하세요',
'value'=>'010-000-0000'
])}}
위코드는실제로아래와같이변환되어출력됩니다.
<divclass="form-group">
<labelfor="inputPhone">전화번호</label>
<inputtype="text"class="form-control"
id="inputPhone"
name="phone"value="010-000-0000">
<pclass="help-block">휴대폰사용하여입력하세요</p>
</div>
uio함수
uio함수의원형입니다.
uio($id,$args=[],$callback=null)
첫번째파라미터$id는UI오브젝트의아이디(또는별칭)입니다.
두번째파라미터$args는UI오브젝트를출력하기위해전달할정보를담은배열입니다.각UI오브젝트마다전달하는정보의항목
은상이합니다.
세번째파라미터$callback를사용하면UI오브젝트가출력하는html을커스터마이징할수있습니다.
기본제공되는UI오브젝트
XE는아래목록과같이UI오브젝트를기본으로제공하고있습니다.
UI오브젝트/폼빌더
120
Page 121
별칭 설명
formText <inputtype="text">를출력
formPassword <inputtype="password">를출력
formTextarea <textarea>를출력
formSelect <select>를출력,사용자로부터단일선택을받을때사용하십시오.
formCheckbox <inputtype="checkbox">를출력사용자로부터다중선택을받을때사용하십시오.
formFile<inputtype="file">출력,파일을업로드받을때사용하십시오.이UI오브젝트는jQueryFileUpload를사용합니다.
formImageformFile과같이파일업로드를출력합니다.하지만이UI오브젝트를사용하면사용자가선택한이미지파일을미리보기할수있습니다.
formMenu XE에등록된메뉴목록을<select>형식으로출력합니다.
formLangText
(=langText)
formText와같이한줄텍스트박스를출력합니다.하지만이UI오브젝트는다국어를입력받을수있습니다.
formLangTextarea
(=langTextArea)여러줄의다국어를출력합니다.
permission 권한설정UI를출력합니다.
themeSelect 테마선택UI를출력합니다.
captcha 캡챠입력UI를출력합니다.
이UI오브젝트들이외에여러플러그인이등록한UI오브젝트를사용할수도있습니다.
UI오브젝트두번째파라미터사용예제
formText,formLangText
$args=[
'label'=>'전화번호',
'name'=>'phone',
'id'=>'inputPhone',
'description'=>'휴대폰사용하여입력하세요',
'value'=>'010-000-0000'
]
formPassword
$args=[
'label'=>'비밀번호',
'name'=>'password',
'id'=>'password',
'description'=>'영문+숫자조합8자리이상입력하세요.',
]
formTextarea,formLangTextarea
$args=[
'label'=>'Copyright',
'name'=>'copyright',
'id'=>'footer_copyright',
'description'=>'푸터하단문구를입력하세요.',
]
UI오브젝트/폼빌더
121
Page 122
formSelect
$args=[
'label'=>'Colorset',
'name'=>'layout_colorset',
'id'=>'layout_colorset',
'class'=>'layout_colorset',
'description'=>'색상을선택하세요.',
'options'=>[
'blue'=>'파랑색',
'red'=>'붉은색',
'green'=>'초록색'
]
]
options는3가지의형태로사용할수있습니다.
'options'=>[
'blue'=>'파랑색',
'red'=>'붉은색',
'green'=>'초록색'
]
//key의값이value의값과같음
'options'=>[
'blue',
'red',
'green'
]
//
'options'=>[
['text'=>'파랑색',value=>'blue',selected=>true],
['text'=>'붉은색',value=>'red'],
['text'=>'초록색',value=>'green']
]
formCheckbox
$args=[
'label'=>'취미',
'name'=>'hobby',
'id'=>'hobby',
'class'=>'hobby',
'description'=>'취미를선택하세요.',
'options'=>[
'basketball'=>'농구',
'baseball'=>'야구',
'football'=>'축구'
]
]
options는checkboxes라는프로퍼티명으로도사용가능하며formSelect와같이3가지의형태로사용할수있습니다.
'options'=>[
'basketball'=>'농구',
'baseball'=>'야구',
'football'=>'축구'
]
UI오브젝트/폼빌더
122
Page 123
'options'=>[
'basketball',
'baseball',
'football'
]
'options'=>[
['text'=>'농구',value=>'basketball',checked=>true],
['text'=>'야구',value=>'baseball'],
['text'=>'축구',value=>'football']
]
formMenu
$args=[
'label'=>'메인메뉴',
]
폼빌더(폼UI오브젝트)폼은웹페이지에서사용자에게입력을받기위해매우빈번히사용됩니다.특히테마나위젯,스킨과같은컴포넌트는사이트관리
자에게폼으로구성되어있는설정페이지를제공하므로개발자가설정페이지를작성해주어야합니다.이런개발자의수고를줄이
기위해UI오브젝트중에는폼작성을편하게할수있도록도와주는UI오브젝트들이있습니다.
폼필드UI오브젝트
폼은text,textarea,checkbox와같은다양한'폼필드'들로구성됩니다.UI오브젝트중별칭(alias)이form*형식인UI오브
젝트들이바로'폼필드UI오브젝트'입니다.폼필드UI오브젝트를사용하면손쉽게폼필드를출력할수있습니다.
폼UI오브젝트
또,UI오브젝트중에별칭(alias)이form인UI오브젝트가있는데,이UI오브젝트는조금특별합니다.이UI오브젝트는하나의완성
된폼을출력합니다.물론,폼에대한기본정보(action,method등)와폼을구성하는필드의목록은파라미터로입력받습니다.
폼UI오브젝트도다른UI오브젝트와같이uio함수로사용할수있습니다.
{{uio('form',$formData);}}
두번째파라미터인$formData는아래와같은형식을가집니다.
UI오브젝트/폼빌더
123
Page 124
$formData=[
'fields'=>[
'title'=>[
'_type'=>'text',
'_section'=>'섹션1',
'label'=>'제목',
'placeholder'=>'제목을입력하세요.',
],
'content'=>[
'_type'=>'textarea',
'_section'=>'섹션1',
'label'=>'내용',
'placeholder'=>'내용을입력하세요.',
],
],
'value'=>[
'title'=>'foo..',
'content'=>'bar..'
],
'action'=>'/',
'method'=>'POST',
];
위코드의출력은아래와같습니다.
<formmethod="POST"enctype="multipart/form-data"action="/">
<divclass="panelform-section-섹션1">
<divclass="panel-heading">
<divclass="pull-left"><h3>섹션1</h3></div>
</div>
<divclass="panel-body">
<divclass="row">
<divclass="col-md-6form-col-title">
<divclass="form-group">
<labelfor="">제목</label>
<inputtype="text"class="form-control"id=""placeholder="제목을입력하세요."name="title"value
="foo..">
<pclass="help-block"></p>
</div>
</div>
<divclass="col-md-6form-col-content">
<divclass="form-group">
<labelfor="">내용</label>
<textareaclass="form-control"rows="3"placeholder="내용을입력하세요."name="content">bar..</t
extarea>
</div>
</div>
</div>
</div>
</div>
</form>
두번째파라미터로전달한배열은다음항목을제공해야합니다.
fields
폼이포함하고있는필드들의목록입니다.이배열의아이템하나는폼필드하나의정보를나타냅니다.아이템의키는폼필드의
name애트리뷰트로사용됩니다.(위의예에서는title,content에해당)
또아이템하나의정보에서_type,_section은각각폼필드의타입과폼필드가출력될그룹(섹션)명을지정합니다.그이외의정보들은모두지정된폼필드를설정할때전달됩니다.
_type에지정되는값은폼필드UI오브젝트의별칭에서접미사인form을제외한나머지부분의별칭만사용됩니다.위의예제에
서첫번째필드인title의경우,타입(_type)이text로지정돼있습니다.text타입에해당하는폼필드UI오브젝트는
formTextUI오브젝트입니다.label및placeholder정보는formTextUI오브젝트에게그대로전달됩니다.
value
UI오브젝트/폼빌더
124
Page 125
폼이포함하고있는필드들이가지는값(value)의목록입니다
UI오브젝트/폼빌더
125
Page 126
카운터(counter)수를세는일은중요하지도않고복잡하지도않으며자주사용되는기능입니다.우리는글조회수를세거나투표,신고등여러가지
기능을구현할때로그를남기고그수를세려고합니다.
수를세는기능들은어떤대상을기준(이하targetId)으로인터넷주소(IP)나회원아이디로구분하여처리합니다.
카운터패키지는IP와회원아이디를기준으로하는두가지방법에대해서수를셀수있는기능을제공합니다.
카운터인스턴스
카운터를사용하기위해서는카운터인스턴스를획득해야합니다.수를셀때두가지방식을제공합니다.그첫번째는게시물조회
수를처리할때처럼targetId에대해서IP,회원아이디를기준으로처리하면되는방식입니다.이때조회수를처리하기위해카운
터인스턴스를획득하는코드는아래와같습니다.
//문서조회에사용할카운터반환
$readCounter=XeCounter::make($request,'read');
두번째방식은투표수를셀때사용하는방식입니다.투표는targetId에대해서몇개의옵션을설정하고각옵션별수를셉니다.그리고IP,회원아이디는targetId에한번만처리되어야합니다.이러한카운터의인스턴스를획득하는코드는아래와같습니다.
//투표(찬성,반대)에사용할카운터반환
$voteCounter=XeCounter::make($request,'vote',['assent',dissent']);
세번째인자['assent',dissent']를추가해서사용가능합니다.
XeCounter::make()로반환된카운터인스턴스는InterceptionProxy인스턴스로intercept할수있습니다.
조회수증가
$doc=Document::find('id');
$readCounter=XeCounter::make($request,'read');
//Counter는$user가Guest라면IP를기준으로수를셉니다.
$readCounter->setGuest();
$user=Auth::user();
if($readCounter->has($doc->id,$user)===true){
$readCounter->add($doc->id,$user);
}
$doc->readCount=$readCounter->getPoint($doc->id);
$doc->save();
조회수를적용하기위해서는이와같이조회수를카운터가전달한조회수를문서에직접입력해야합니다.
투표취소
카운터(counter)
126
Page 127
$doc=Document::find('id');
//투표는회원에대해서만동작합니다.
$voteCounter=XeCounter::make($request,'vote',['assent','dissent']);
//인스턴스에설정된두개의옵션중에서'assent'에투표
$option='assent';
$user=Auth::user();
//회원에대해서동작하기때문에로그인하지않은회원인경우GuestNotSupportException발생
try{
$voteCounter->remove($doc,$user,$option);
}catch(GuestNotSupportException$e){
thrownewAccessDeniedHttpException;
}
$doc->assentCount=$readCounter->getPoint($doc->id,'assent');
$doc->save();
투표로그
투표에참여한사용자로그를확인합니다.
$doc=Document::find('id');
//투표는회원에대해서만동작합니다.
$voteCounter=XeCounter::make($request,'vote',['assent','dissent']);
//찬선에투표한회원목록을가져옵니다.
$users=$voteCounter->getUsers($doc->id,'assent');
카운터(counter)
127
Page 128
휴지통(trash)Trash는서비스또는서드파티에서휴지통(bin)을등록받아처리합니다.BinInterface를따르는구현체를구현하고
TrashManager를통해등록하면휴지통비우는요청이있을때관련된휴지통을찾아처리합니다.
Trash서비스는휴지통을관리하며휴지통에포함된아이템을직접컨트롤하지않습니다.커맨드를통해사용가능한휴지통을확인
하고휴지통비우기기능을제공할뿐입니다.
기본사용법
XeTrash파사드로TrashManager를사용합니다.
휴지통의기능은휴지통에포함된요약정보와그것을비우는역할이므로코드보다는명령어기준으로설명합니다.아래나열된사용법은터미널에서입력하는명령어사용방법입니다.
휴지통등록
휴지통구현체를TrashManager를통해등록합니다.artisan명령어로휴지통을제어하기위해서휴지통을등록해야하합니다.
XeTrash::register(NAME_SPACE\CLASS_NAME::class);
요약정보확인
$bins=XeTrash::gets();
foreach($binsas$bin){
echo$bin::summary();
}
전체휴지통비우기
XeTrash::clean();
명령어사용
터미널에서아래와같이명령어를실행합니다.
전체휴지통요약정보
$phpartisantrash
게시판휴지통정보보기
$phpartisantrashboard
댓글,게시판의요약정보
한개이상의휴지통요약정보를확인하고자할경우에는콤마(,)로구분해서입력합니다.
휴지통(trash)
128
Page 129
$phpartisantrashboard,comment
전체휴지통비우기
$phpartisantrash:clean
게시판휴지통비우기
$phpartisantrash:cleanboard
댓글,게시판비우기
$phpartisantrash:cleanboard,comment
휴지통(trash)
129
Page 130
유효성검사(validation)
소개
XE는유입되는데이터의유효성을검사하기위한다양한방법을제공합니다.
유효성검사살펴보기
유효성검사기능에대해알아보기위해서,form을확인한뒤사용자에게에러메세지를보여주는예제를살펴보도록하겠습니다.
라우트정의하기
우선다음의라우트들이정의되어있다고가정해보겠습니다:
//Displayaformtocreateablogpost...
Route::get('post/create','PostController@create');
//Storeanewblogpost...
Route::post('post','PostController@store');
GET라우트는사용자가새로운블로그포스트를생성하기위한form을나타낼것이고,POST라우트는데이터베이스에새로운블로그포스트를저장할것입니다.
컨트롤러생성하기
다음으로,이라우트들을다루는간단한컨트롤러를살펴보겠습니다.지금은store메소드를비어있는채로둘것입니다:
<?php
namespaceApp\Http\Controllers;
useIlluminate\Http\Request;
useApp\Http\Controllers\Controller;
classPostControllerextendsController
{
/**
*Showtheformthecreateanewblogpost.
*
*@returnResponse
*/
publicfunctioncreate()
{
returnview('post.create');
}
/**
*Storeanewblogpost.
*
*@paramRequest$request
*@returnResponse
*/
publicfunctionstore(Request$request)
{
//Validateandstoretheblogpost...
}
}
유효성검사(validation)
130
Page 131
유효성검사로직작성하기
이제새로운블로그포스트에대해유효성을검사하는로직을store메소드에채워넣을준비가되었습니다.베이스컨트롤러
(App\Http\Controllers\Controller)클래스를살펴보면클래스가ValidatesRequests트레이트-trait을사용한다는것을알수있습니다.이트레이트(trait)는모든컨트롤러에서편리하게사용할수있는validate메소드를제공합니다.
validate메소드는HTTP요청의유입과유효성검사룰의집합을전달받습니다.유효성검사룰들을통과하게되면코드는계속
해서정상적으로실행될것입니다.하지만유효성검사를통과하지못할경우,예외(exception)가던져지고적절한오류응답이사용자에게자동으로보내질것입니다.전통적인HTTP요청의경우,리다이렉트응답이생성될것이며AJAX요청에는JSON응답
이보내질것입니다.
validate메소드에대해더잘이해하기위해,다시store메소드로돌아가보겠습니다:
/**
*Storeanewblogpost.
*
*@paramRequest$request
*@returnResponse
*/
publicfunctionstore(Request$request)
{
$this->validate($request,[
'title'=>'required|unique:posts|max:255',
'body'=>'required',
]);
//Theblogpostisvalid,storeindatabase...
}
위에서볼수있듯이,간단하게유입되는HTTP요청과유효성검사룰들을validate메소드로전달하면됩니다.이때에도유효
확인이실패하면적절한응답이생성될것입니다.유효성검사를통과하면컨트롤러는계속해서정상적으로수행합니다.
중첩된속성에대한유의사항
HTTP요청이"중첩된"파라미터를가지고있다면".(점)"문법을사용하여유효성검사규칙을지정할수있습니다:
$this->validate($request,[
'title'=>'required|unique:posts|max:255',
'author.name'=>'required',
'author.description'=>'required',
]);
유효성검사에러표시하기
그럼입력되는요청(request)의파라미터들이유효성검사를통과하지못하는경우에는어떻게될까요?이경우앞서언급한대로,자동으로사용자를이전의위치로리다이렉트합니다.또한모든유효성확인에러는자동으로세션에임시저장될것입니다.
GET라우트에서도에러메세지를뷰와명시적으로출력하지않아도됩니다.왜냐하면,기본적으로세션에저장된유효성확인에러를출력합니다.
이예제에서,유효성검사를통과하지못할경우사용자는컨트롤러의create메소드로리다이렉트될것이고,XE는세션에저장
된유효성확인에러를확인한후,토스트팝업(팝업레이어형식)으로오류메시지를출력합니다.
AJAX요청과유효성검사
이예제에서는,어플리케이션에전통적인form을이용하여데이터를보냈습니다.하지만많은어플리케이션이AJAX요청을사용
합니다.AJAX요청중에서validate메소드를사용한다면라라벨은리다이렉트응답을생성하지않습니다.대신라라벨은유효
성검사의모든실패에러들을포함하는JSON응답을생성할것입니다.이JSON응답은422HTTP상태코드와함께보내질것입
니다.
유효성검사(validation)
131
Page 132
다른유효성검사방법
수동으로Validator생성하기
컨트롤러에서제공하는validate메소드를사용하고싶지않다면Validator파사드를사용하여validator인스턴스를수동으로
생성할수있습니다.파사드에make메소드를사용하면새로운validator인스턴스가생성됩니다:
<?php
namespaceApp\Http\Controllers;
useValidator;
useIlluminate\Http\Request;
useApp\Http\Controllers\Controller;
classPostControllerextendsController
{
/**
*Storeanewblogpost.
*
*@paramRequest$request
*@returnResponse
*/
publicfunctionstore(Request$request)
{
$validator=Validator::make($request->all(),[
'title'=>'required|unique:posts|max:255',
'body'=>'required',
]);
if($validator->fails()){
returnredirect('post/create')
->withErrors($validator)
->withInput();
}
//Storetheblogpost...
}
}
make메소드로전달되는첫번째인자는유효성검사를받을데이터입니다.두번째인자는데이터에적용되어야하는유효성검사
규칙들입니다.
요청(request)정보가유효성검사를통과하지못한것을확인였다면withErrors메소드로세션에에러메세지를임시저장(flash)할수있습니다.이메소드를사용하면리다이렉트후에$errors변수가자동으로뷰에서공유되어손쉽게사용자에게보여질수있습니다.withErrors메소드는validator,MessageBag,혹은PHParray를전달받습니다.
이름이지정된ErrorBags
한페이지안에서여러개의form을가지고있다면에러들의MessageBag에이름을붙여지정한form에맞는에러메세지를조회할
수있도록할수있습니다.단순히withErrors에이름을두번째인자로전달하면됩니다:
returnredirect('register')
->withErrors($validator,'login');
그러면$errors변수에서지정된MessageBag인스턴스에접근할수있습니다.
{{$errors->login->first('email')}}
에러메시지사용하기
유효성검사(validation)
132
Page 133
Validator인스턴스의messages메소드를호출하면,에러메시지를편하게사용할수있는다양한메소드를가진MessageBag인스턴스를받을수있습니다.
하나의필드에대한첫번째에러메시지조회하기
특정필드에대한첫번째에러메세지를조회하려면first메소드를사용하면됩니다:
$messages=$validator->errors();
echo$messages->first('email');
하나의필드에대한모든에러메세지조회하기
간단하게하나의필드에대한모든에러메세지를조회하고싶다면get메소드를사용하면됩니다:
foreach($messages->get('email')as$message){
//
}
모든필드에대한모든에러메세지조회하기
모든필드에대한모든에러메세지를조회하기위해서는all메소들사용하면됩니다:
foreach($messages->all()as$message){
//
}
하나의필드에대하여에러메시지가존재하는지검사하기
if($messages->has('email')){
//
}
특정형식으로에러메시지획득하기
echo$messages->first('email','<p>:message</p>');
특정형식으로모든에러메시지획득하기
foreach($messages->all('<li>:message</li>')as$message){
//
}
사용자지정(커스텀)에러메세지
필요하다면기본적인에러메세지대신에커스텀에러메세지를유효성검사에사용할수있습니다.커스텀메세지를지정하는데에
는여러가지방법이있습니다.먼저Validator::make메소드에커스텀메세지를세번째인자로전달할수있습니다:
$messages=[
'required'=>'The:attributefieldisrequired.',
];
$validator=Validator::make($input,$rules,$messages);
유효성검사(validation)
133
Page 134
다음의예에서:attribute플레이스홀더는유효성검사를받는필드의실제이름으로대체됩니다.유효성검사메세지에서다른
플레이스홀더들또한활용할수있습니다.예를들어:
$messages=[
'same'=>'The:attributeand:othermustmatch.',
'size'=>'The:attributemustbeexactly:size.',
'between'=>'The:attributemustbebetween:min-:max.',
'in'=>'The:attributemustbeoneofthefollowingtypes::values',
];
주어진속성에대해커스텀메세지지정하기
종종하나의특정필드에대해서만커스텀오류메세지를지정해야하는경우가있습니다.이것은".(점)"표기법을통해서할수있습니다.속성의이름을먼저지정하고,규칙을명시하면됩니다:
$messages=[
'email.required'=>'Weneedtoknowyoure-mailaddress!',
];
언어파일에커스텀메세지지정하기
많은경우에서,Validator에직접메세지를전달하는대신,언어파일에속성에따른커스텀메세지를지정하기원할수있습니다.이렇게하기위해서는resources/lang/xx/validation.php언어파일의custom배열에메제지를추가하면됩니다.
'custom'=>[
'email'=>[
'required'=>'Weneedtoknowyoure-mailaddress!',
],
],
사용가능한유효성검사규칙들
다음은모든유효성검사규칙과그들의함수목록입니다.
AcceptedActiveURLAfter(Date)AlphaAlphaDashAlphaNumericArrayBefore(Date)BetweenBooleanConfirmedDateDateFormatDifferentDigitsDigitsBetweenE-MailExists(Database)Image(File)InInteger
유효성검사(validation)
134
Page 135
IPAddressJSONMaxMIMETypes(File)MinNotInNumericRegularExpressionRequiredRequiredIfRequiredUnlessRequiredWithRequiredWithAllRequiredWithoutRequiredWithoutAllSameSizeStringTimezoneUnique(Database)URL
accepted
필드의값이yes,on,1,또는true이어야합니다.이것은"이용약관"동의와같은필드의검사에유용합니다.
active_url
필드의값이PHP함수checkdnsrr에기반하여올바른URL이어야합니다.
after:date
필드의값이주어진날짜이후여야합니다.이때날짜는strtotimePHP함수를통해생성된값입니다.
'start_date'=>'required|date|after:tomorrow'
strtotime에의해계산될날짜문자열을전달하는대신날짜와비교할다른필드를명시할수있습니다:
'finish_date'=>'required|date|after:start_date'
alpha필드의값이완벽하게(숫자나기호가아닌)알파벳[자음과모음]문자로이루어져야합니다.
(역자주:영문알파벳만을의미하지않고,숫자나기호가아닌경우에해당하여,한글도허용합니다.)
alpha_dash
필드의값이(숫자나기호가아닌)알파벳[자음과모음]문자및숫자와dash(-),underscore(_)로이루어져야합니다.
alpha_num
필드의값이완벽하게(숫자나기호가아닌)알파벳[자음과모음]문자및숫자로이루어져야합니다.
유효성검사(validation)
135
Page 136
array
필드의값이반드시PHP배열형태이어야합니다.
before:date
필드의값이반드시주어진날짜보다앞서야합니다.날짜는strtotimePHP함수를통해비교됩니다.
between:min,max
필드의값이,주어진min과max의사이의값이어야합니다.문자열,숫자,그리고파일이size룰에의해같은방식으로평가될
수있습니다.
boolean
필드의값이반드시boolean으로캐스팅될수있어야합니다.허용되는값은true,false,1,0,"1","0"입니다.
confirmed
필드의값이foo_confirmation의매칭되는필드를가져야합니다.예를들어만약필드가password라면,password_confirmation라는필드가입력값중에있어야합니다.
date
필드의값이strtotimePHP함수에서인식할수있는올바른날짜여야합니다.
dateformat:_format
필드의값이반드시주어진format과일지해야합니다.주어진포맷은date_parse_from_formatPHP함수에의해서연산될것입니
다.필드의유효성을검사할때에는date와date_format중하나만사용해야합니다.
different:field필드의값이주어진field의값과달라야합니다.
digits:value필드의값이반드시숫자여야하고,길이가value이어야합니다.
digitsbetween:_min,max
필드의값이주어진min과max사이의길이를가져야합니다.
email필드의값이이메일주소형식이어야합니다.
exists:table,column
필드의값이주어진데이터베이스테이블에존재하는값이어야합니다.
exists룰의기본사용법
'state'=>'exists:states'
유효성검사(validation)
136
Page 137
특정컬럼명지정하기
'state'=>'exists:states,abbreviation'
쿼리문의"where"구문에추가될더많은조건을지정할수도있습니다:
'email'=>'exists:staff,email,account_id,1'
"where"구분에NULL혹은NOT_NULL을전달할수도있습니다:
'email'=>'exists:staff,email,deleted_at,NULL'
'email'=>'exists:staff,email,deleted_at,NOT_NULL'
image
이미지파일(jpeg,png,bmp,gif,svg)이어야합니다.
in:foo,bar,...
필드의값이주어진목록에포함돼있어야합니다.
integer
필드의값이정수여야합니다.
ip
필드의값이IP주소여야합니다.
json
필드의값이유효한JSON문자열이어야합니다.
max:value
필드의값이반드시value보다작거나같아야합니다.문자열,숫자,그리고파일이size룰에의해같은방식으로평가될수있습
니다.
mimes:foo,bar,...
파일의MIME타입이주어진확장자리스트중에하나와일치해야합니다.
MIME룰의기본사용법
'photo'=>'mimes:jpeg,bmp,png'
여러분은확장자를지정하기만하면되지만,이경우파일의컨텐트를읽고MIME타입을추정함으로써이파일의MIME의유효성
을검사합니다.
MIME타입과그에상응하는확장의전체목록은다음의위치에서확인하실수있습니다:http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
min:value
유효성검사(validation)
137
Page 138
필드의값이반드시value보다크거나같아야합니다.문자열,숫자,그리고파일이size룰에의해같은방식으로평가될수있습
니다.
notin:_foo,bar,...
필드의값이주어진목록에존재하지않아야합니다.
numeric
필드의값이숫자여야합니다.
regex:pattern
필드의값이주어진정규식표현과일치해야합니다.
참고:regex패턴을사용할때,특히정규표현식에파이프문자열이있다면,파이프구분자를사용하는대신배열형식을사용하
여룰을지정할필요가있습니다.
required
입력값중에해당필드가존재해야하며비어있어서는안됩니다.필드는다음의조건중하나를충족하면"빈(empty)"것으로간주
됩니다:
값이null인경우.값이비어있는문자열인경우.값이비어있는배열이거나,비어있는Countable객체인경우
값이경로없이업로드된파일인경우
requiredif:_anotherfield,value,...만약filed의값이value중의하나와일치한다면,해당필드가반드시존재해야합니다.
requiredunless:_anotherfield,value,...anotherfield가어떤value와동일하지않은이상필드가존재해야합니다.
requiredwith:_foo,bar,...
다른지정된필드중하나라도존재한다면,해당필드가반드시존재해야합니다.
requiredwith_all:_foo,bar,...
다른지정된필드가모두존재한다면,해당필드가반드시존재해야합니다.
requiredwithout:_foo,bar,...
다른지정된필드중하나라도존재하지않으면,해당필드가반드시존재해야합니다.
requiredwithout_all:_foo,bar,...
다른지정된필드가모두존재하지않으면,해당필드가존재해야합니다.
same:field
필드의값이주어진field의값과일치해야합니다.
유효성검사(validation)
138
Page 139
size:value
필드의값이주어진value와일치하는크기를가져야합니다.문자열데이터에서는문자의개수가value와일치해야합니다.숫자형
식의데이터에서는주어진정수값이value와일치해야합니다.파일에서는킬로바이트형식의파일사이즈가size와일치해야합니
다.
string
필드의값이반드시문자열이어야합니다.
timezone
필드의값이timezone_identifiers_listPHP함수에서인식가능한유효한timezone식별자여야합니다.
unique:table,column,except,idColumn
필드의값이주어진데이터베이스테이블에서고유한값이어야합니다.만약column이지정돼있지않다면필드의이름이사용됩
니다.
특정컬럼명지정하기:
'email'=>'unique:users,email_address'
특정데이터베이스커넥션
때때로,여러분은Validator에의해서생성되는데이터베이스쿼리에사용자가지정한커넥션을필요로할지도모릅니다.위에서의
검증규칙unique:users에서는데이터베이스를쿼리하기위해기본데이터베이스커넥션이사용됩니다.이를재지정하려면테이
블이름후에"."표기법으로커넥션을지정하십시오:
'email'=>'unique:connection.users,email_address'
주어진ID에대해서유니크규칙을무시하도록강제하기:
때때로유니크검사를할때특정ID를무시하고자할수있습니다.예를들어사용자이름,이메일주소그리고위치를포함하는"프로필업데이트"화면이있습니다.물론이메일주소가고유하다는것을확인하고싶을것입니다.하지만사용자가이름필드만바꾸
고이베일필드를바꾸지않는다면사용자가이미이메일주소의주인이기때문에유효검사오류가던져지지않아야합니다.유효
검사오류는사용자가다른사용자가이미사용하고있는이메일주소를제공할때에만던져져야합니다.유니크규칙에사용자ID를무시하라고강제하기위해서는세번째파라미터로ID를전달해야합니다:
'email'=>'unique:users,email_address,'.$user->id
테이블이id가아닌primary키컬럼이름을사용한다면그이름을네번째파라미터로지정하면됩니다:
'email'=>'unique:users,email_address,'.$user->id.',user_id'
추가적인Where구문추가하기:
더추가되는조건은쿼리에추가될"where"구문에대해서지정될것입니다.
'email'=>'unique:users,email_address,NULL,id,account_id,1'
위의룰에서는,account_id가1인데이터만이유니크검사에포함됩니다.
url
유효성검사(validation)
139
Page 140
필드는반드시PHPfilter_var함수에따라유효한URL이어야합니다.
유효성검사(validation)
140
Page 143
위젯박스
위젯박스는말그대로위젯을담는박스라고생각할수있습니다.위젯박스는사이트의화면에출력됩니다.사이트관리자는위젯박
스의편집페이지에서다수의위젯을생성한다음,생성된위젯들을위젯박스에배치할수있습니다.사이트관리자나개발자는위젯박스삽입코드를사용하여화면어디에나위젯박스를출력할수있습니다.
예를들어,위젯박스는사이트의사이드바영역에사용될수있습니다.일반적인테마의레이아웃을고려해보면,헤더와푸터,그리
고메인컨텐츠영역으로구성되며,사이드바가추가되기도합니다.사이드바영역은보통사이트관리자가원하는위젯들을자유롭
게배치할수있기를원합니다.테마개발자가사이드바영역에위젯박스의삽입코드를추가해놓으면,사이트관리자는사이드바
영역을자신이원하는대로구성할수있습니다.비단사이드바영역이아니더라도개발자가원하는어느영역이든위젯박스를출력
할수있습니다.
위젯박스출력하기
위젯박스는아래와같은코드로간단하게출력할수있습니다.
{{uio('widgetbox',['id'=>'sidebar'])}}
위젯박스는고유의아이디를가지며,위코드는아이디가sidebar인위젯박스를화면에출력합니다.만약아이디가sidebar인위젯박스가아직존재하지않는다면,사이트관리자가클릭하면즉시위젯박스를생성할수있는링크를아래와같이출력합니다.
위젯박스[sidebar]를찾을수없습니다.[바로생성하기]
이미sidebar위젯박스가존재한다면,바로위젯박스가출력되며,위젯박스하단에위젯박스의편집페이지링크가출력됩니다.
위젯박스생성하기
위젯박스를출력하기전에미리위젯박스를생성할수있습니다.XeWidgetBox파사드를사용하십시오.
XeWidgetbox::create(['id'=>'sidebar','title'=>'사이드바','content'=>'');
위젯박스삭제하기
생성된위젯박스는XeWidgetBox파사드의delete메소드를사용하여삭제할수있습니다.
XeWidgetbox::delete('sidebar');
위젯박스(widgetbox)
143
Page 145
테마
테마컴포넌트는테마생성커맨드를제공합니다.테마생성커맨드를사용하면,전문적인PHP지식이없어도테마를제작할수있도록테마를구성하는여러가지파일을자동으로생성해주고,테마를XE에등록해해줍니다.
테마생성커맨드를사용하지않더라도테마를제작할수있으며,좀더자유롭운형식으로테마를제작할수있습니다.하지만이문서는테마생성커맨드를사용한보편적인방법의테마제작방법을설명합니다.
빈테마생성
테마생성커맨드를사용하려면우선테마가소속될플러그인이마련되어있어야합니다.아직플러그인이준비되지않았다면,플러
그인생성하기문서를참고하여플러그인을생성하길바랍니다.
플러그인이준비되었다면아래커맨드를사용하여빈테마를생성합니다.
$phpartisanmake:theme<path><title>
path는테마가위치할디렉토리의경로입니다.플러그인의디렉토리이름을포함한경로를입력해줍니다.
title에는테마의제목을지정해주십시오.지정한테마제목은사이트관리자에서테마의이름으로표시됩니다.
만약my_plugin플러그인에테마를넣고,테마이름을FirstTheme로지정하고싶다면,아래와같이커맨드를실행하시면됩니
다.커맨드를실행하면생성되는테마의개략적인정보를터미널에서볼수있습니다.
$phpartisanmake:thememy_plugin/theme'FirstTheme'
[Newthemeinfo]
plugin:my_plugin
path:my_plugin/theme
classfile:my_plugin/theme/Theme.php
classname:SungbumHong\MyPlugin\Theme
id:theme/my_plugin@theme
title:FirstTheme
description:TheThemesupportedbyMy_pluginplugin.
Doyouwanttoaddtheme?[yes|no]:
>yes
Generatingautoloadfiles
Themeiscreatedsuccessfully.
테마등록
테마생성커맨드를사용할경우,자동으로테마를등록해줍니다.플러그인의composer.json파일에아래와같이컴포넌트정보
가등록되어있습니다.
테마
145
Page 146
//plugins/my_plugin/composer.json
...
"extra":{
"xpressengine":{
"title":"myplugin",
"component":{
"theme/my_plugin@theme":{
"class":"SungbumHong\\MyPlugin\\Theme\\Theme",
"name":"FirstTheme",
"description":"TheThemesupportedbyMy_pluginplugin."
}
}
}
},
...
테마아이디도자동으로생성됩니다.위의예시에서는테마아이디가theme/my_plugin@theme로생성되었습니다.
테마디렉토리구조
생성된테마는아래의디렉토리구조를가집니다.plugins/my_plugin/theme/디렉토리는테마의모든파일이담겨있는'테마디렉
토리'입니다.
plugins/my_plugin/theme/
├──Theme.php
├──assets/
│└──css/
│└──theme.css
├──info.php
└──views/
├──gnb.blade.php
└──theme.blade.php
assets디렉토리
테마에필요한.js나.css파일그리고이미지파일과같은asset파일을담는디렉토리입니다.
views디렉토리
테마를출력할때사용하는템플릿파일을저장하는디렉토리입니다.템플릿파일은blade템플릿언어로작성되어야합니다.blade템플릿언어의사용법은템플릿문서에자세히기술되어있습니다.
Theme.php파일
Theme.php는테마클래스파일입니다.테마생성커맨드로생성된테마의클래스는\Xpressengine\Theme\GenericTheme클래스를
상속받고있습니다.또,GenericTheme클래스는테마컴포넌트의추상클래스인\Xpressengine\Theme\AbstractTheme를상속받고
있습니다.
\Xpressengine\Theme\AbstractTheme
└──\Xpressengine\Theme\GenericTheme
└──Theme(Theme.php)
PHP개발자가아닌테마제작자가테마클래스를직접작성하는것은쉬운일이아닙니다.GenericTheme클래스는테마제작자들
에게규격화된테마구조를제공함으로써테마제작을손쉽게할수있도록도와줍니다.Theme.php파일을처음생성한다음부터
는거의직접수정할필요가없습니다.Theme.php를직접수정하는대신,info.php를수정하십시오.
일반적으로Theme.php파일을직접수정하지않아도원하는테마를제작하는데에는문제가없습니다.다만,좀더고수준의테마
를제작하고싶거나,특별한기능을테마에추가하고싶다면Theme.php파일을직접수정하셔도됩니다.Theme.php파일을수정
하면훨씬자유롭고편하게테마를제작할수있습니다.물론그전에테마클래스의각메소드가갖는역할과원리를잘이해하고
테마
146
Page 147
있어야합니다.
info.php파일
info.php파일은테마의구동에필요한여러가지정보를입력하는파일입니다.간단한php문법으로쉽게작성할수있습니다.
<?php
return[
'view'=>'theme',
'setting'=>[
...
],
'setting.assets'=>[
'js'=>['js/theme.js'],
'css'=>['css/theme.css']
],
'support'=>[
'mobile'=>true,
'desktop'=>true
],
'editable'=>[
'theme.blade.php',
'gnb.blade.php',
]
];
view필드는테마를출력할때사용할템플릿파일을지정하는필드입니다.위설정의경우theme로지정되어있습니다.이는
views디렉토리에들어있는theme.blade.php을지정한것입니다.
setting필드에는테마설정페이지에서사용할설정항목에대한정보를지정합니다.
setting.assets필드에는테마설정페이지에서로드할javascript,css파일을지정합니다.경로는assets하위디렉토리부터지정합니다.
support필드에는이테마가데스크탑버전과모바일버전을지원하는지를지정합니다.
editable필드에는테마편집페이지에서편집할수있는파일의목록을지정합니다.
테마출력
본격적으로테마를제작해보겠습니다.
테마가웹페이지에출력될때사용될템플릿은info.php파일의view필드에지정되어있습니다.위설정의경우실제로는
views/theme.blade.php파일에해당됩니다.
메인컨텐츠출력하기
웹페이지가출력될때,테마는그웹페이지의메인컨텐츠를반드시출력해줘야합니다.XE는웹페이지를출력할때,메인컨텐츠의
내용을$content변수에담아테마에전달합니다.
메인컨텐츠가출력될영역에아래와같이변수를출력하십시오.
<divclass="content"id="content">
{!!$content!!}
</div>
레이아웃구성하기
테마에서출력할내용이많아지면theme.blade.php이너무복잡해집니다.이럴때블레이드문법의@extends,@section,@yield@include키워드를사용하면템플릿파일을분리할수있습니다.레이아웃을구성하는블레이드문법의사용법은템플릿
문서에자세히설명되어있습니다.
테마
147
Page 148
theme.blade.php에서@include를사용하여header와footer영역을분리해보았습니다.
<!--theme.blade.php-->
@include($theme::view('header'))
<divclass="content"id="content">
{!!$content!!}
</div>
@include($theme::view('footer'))
header와footer에해당하는파일은views디렉토리에미리추가되어있어야합니다.
..
└──views/
├──header.blade.php
├──footer.blade.php
└──theme.blade.php
테마의템플릿파일에서뷰이름을지정할때에는$theme::view()메소드를사용하십시오.이메소드는views디렉토리를기준으
로하는상대경로로템플릿파일의경로를지정할수있도록도와줍니다.
asset파일로드하기
이미지파일로드하기
이미지파일을로드하려면$theme::asset()메소드를사용하십시오.assets디렉토리를기준으로파일의상대경로를입력해주
시면됩니다.
<!--theme.blade.php-->
<imgsrc="{{$theme::asset('img/logo.png')}}"alt="로고이미지">
css,js파일로드하기
css,js파일은XeFrontend파사드(Frontend서비스)를사용하여로드하십시오.
<!--theme.blade.php-->
{{XeFrontend::css(
$theme::asset('css/theme.css')
)->load()}}
역시$theme::asset()메소드를사용하면상대경로로파일경로를입력할수있습니다.
XeFrontend파사드의자세한사용법은Frontend서비스문서를참고하십시오.
테마설정변수사용하기
사이트관리자가테마설정페이지를통해지정한설정들의값은테마를출력할때$config변수를사용하여참조할수있습니다.
<!--theme.blade.php-->
@if($config->get('use_sidebar',false))
<divclass="sidebar">
...
</div>
@endif
테마
148
Page 149
$config변수는ConfigEntity의인스턴스입니다.get메소드를사용하면손쉽게설정값을가져올수있습니다.get메소드
의두번째파라메터는기본값(defaultvalue)입니다.
//use_sidebar설정조회,use_sidebar설정이존재하지않을경우기본값으로false를반환
$config->get('use_sidebar',false);
메뉴출력하기
XE에등록된메뉴도테마설정변수로저장할수있습니다.
만약설정변수에'mainMenu'라는이름으로메뉴가저장되어있다면,menu_list함수를사용하여메뉴정보를가져올수있습니
다.
@foreach(menu_list($config->get('mainMenu'))as$menu)
...
@endforeach
menu_list는지정된메뉴에속해있는메뉴아이템의목록을배열로반환합니다.위의예제에서는$menu변수가하나의메뉴아이템입니다.
메뉴아이템은아래와같은프로퍼티를가지고있습니다.이프로퍼티를사용하여메뉴를원하는대로출력하십시오.
$menu['url']-메뉴의링크주소
$menu['link']-메뉴의링크텍스트,만약메뉴이미지가사용됐을경우이미지를출력하는태그를출력합니다.$menu['selected']-현재페이지에해당하는메뉴일경우true를가집니다.$menu['children']-자식메뉴아이템리스트
업로드한이미지출력하기
테마설정페이지에서사이트관리자는이미지를업로드할수있습니다.
사이트관리자가업로드한이미지를출력할때에는아래와같이코드를작성하십시오.
<imgsrc="{{$config->get('logoImage.path')}}">
이미지를저장한변수명(logoImage)에.path를붙여서출력하면됩니다.
테마설정
테마는사이트관리자는테마를커스터마이징할수있도록테마설정페이지를제공할수있습니다.테마설정페이지를작성하는
방법은크게두가지가있습니다.
폼빌더로작성하기
폼빌더로작성하는방법을사용하면손쉽게테마설정페이지를작성할수있습니다.
info.php파일의setting항목에폼에서사용할필드의정보를담은배열을추가합니다.setting항목을작성하는규칙은폼빌더문서의fields항목을참고하시기바랍니다.
템플릿파일로작성하기
폼빌더를사용할경우,간편하게테마설정페이지를작성할수는있지만,자유롭지못한단점이있습니다.좀더자유롭게설정페이지를작성하고싶다면설정페이지를출력하는블레이드템플릿파일을작성하십시오.그리고info.php파일의setting항목
에블레이드템플릿파일의경로를지정하면됩니다.
테마
149
Page 150
이때템플릿파일은반드시views디렉토리에존재해야합니다.만약views/config.blade.php를설정페이지의템플릿으
로사용하고싶다면,아래와같이setting항목을지정합니다.
<?php
return[
...
'setting'=>'config',
...
];
테마편집
사이트관리자는테마의템플릿을직접편집하여테마를커스터마이징할수있습니다.테마제작자는사이트관리자가편집할수있는파일목록을지정해주어야합니다.
info.php파일의editable필드에편집할수있는템플릿파일목록을지정해주십시오.
테마
150
Page 151
스킨
스킨컴포넌트는스킨생성커맨드를제공합니다.스킨생성커맨드를사용하면,전문적인PHP지식이없어도스킨을제작할수있도록스킨을구성하는여러가지파일을자동으로생성해주고,스킨을XE에등록해해줍니다.
스킨생성커맨드를사용하지않더라도스킨을제작할수있으며,좀더자유롭운형식으로스킨을제작할수있습니다.하지만이문서는스킨생성커맨드를사용한보편적인방법의스킨제작방법을설명합니다.
스킨타겟
스킨은항상그스킨이사용되는대상(타겟)이있습니다.예를들어게시판스킨은게시판이대상이되며,회원프로필스킨은회원
프로필컨트롤러가대상이됩니다.또특정위젯이대상이될수도있습니다.게시판,회원프로필컨트롤러,위젯과같이스킨이적용되는대상을'스킨타겟'이라고합니다.모든스킨은스킨타겟을반드시지정해야합니다.
XE의구성요소중HTML을출력하는모든구성요소는스킨타겟이될수있습니다.스킨타겟은HTML을출력할때,스킨시스템이
적용될수있도록구현되어야하며,스킨타겟아이디를지정해주어야합니다.
빈스킨생성
스킨생성커맨드를사용하려면우선스킨이소속될플러그인이마련되어있어야합니다.아직플러그인이준비되지않았다면,플러
그인생성하기문서를참고하여플러그인을생성하길바랍니다.또,스킨타겟의아이디를미리알고있어야합니다.
소속될플러그인과스킨타겟아이디가준비되었다면아래커맨드를사용하여빈스킨을생성합니다.
$phpartisanmake:skin<path><skin_target_id><title>
path는스킨이위치할디렉토리의경로입니다.플러그인의디렉토리이름을포함한경로를입력해줍니다.
skin_target_id는스킨타겟아이디를지정해주십시오.
title에는스킨의제목을지정해주십시오.지정한스킨제목은스킨선택시스킨의이름으로표시됩니다.
만약my_plugin플러그인에스킨을넣고,스킨이름을나만의프로필스킨으로지정하고싶다면,아래와같이커맨드를실행하시면
됩니다.커맨드를실행하면생성되는스킨의개략적인정보를터미널에서볼수있습니다.회원프로필컨트롤러의스킨타겟아이
디는member/profile입니다.
$phpartisanmake:skinmy_plugin/skins/profilemember/profile"나만의프로필스킨"
[Newskininfo]
plugin:my_plugin
path:my_plugin/skins/profile
classfile:my_plugin/skins/profile/ProfileSkin.php
classname:SungbumHong\MyPlugin\ProfileSkin
id:member/profile/skin/my_plugin@profileskin
title:나만의프로필스킨
description:TheSkinsupportedbyMy_pluginplugin.
Doyouwanttoaddskin?[yes|no]:
>yes
Generatingautoloadfiles
Skiniscreatedsuccessfully.
스킨컴포넌트아이디
스킨
151
Page 152
스킨도컴포넌트이므로컴포넌트아이디를가지고있어야합니다.스킨의컴포넌트아이디는아래규칙을따라야합니다.
<skin_target_id>/skin/<plugin_id>@<pure_id>
위의예시에서생성한스킨의아이디는member/profile/skin/my_plugin@profileskin입니다.
스킨등록
스킨생성커맨드를사용할경우,자동으로스킨을등록해줍니다.플러그인의composer.json파일에아래와같이컴포넌트정보
가등록되어있습니다.
//plugins/my_plugin/composer.json
...
"extra":{
"xpressengine":{
"title":"myplugin",
"component":{
"member/profile/skin/my_plugin@profileskin":{
"class":"SungbumHong\\MyPlugin\\Skin\\ProfileSkin",
"name":"나만의프로필스킨",
"description":"TheSkinsupportedbyMy_pluginplugin."
}
}
}
},
...
"autoload":{
"classmap":[
"skins/profile/ProfileSkin.php"
]
}
autoload항목에스킨클래스가등록돼있는것도볼수있습니다.
스킨디렉토리구조
생성된스킨은아래의디렉토리구조를가집니다.plugins/my_plugin/skins/profile디렉토리는스킨의모든파일이담겨있는'스킨디렉토리'입니다.
plugins/my_plugin/skins/
└──profile/
├──assets/
│└──css/
│└──skin.css
├──views/
│└──index.blade.php
├──ProfileSkin.php
└──info.php
assets파일
스킨에필요한.js나.css파일그리고이미지파일과같은asset파일을담는디렉토리입니다.
views디렉토리
스킨을출력할때사용하는템플릿파일들을저장하는디렉토리입니다.템플릿파일은blade템플릿언어로작성되어야합니다.blade템플릿언어의사용법은템플릿문서에자세히기술되어있습니다.
스킨
152
Page 153
Skin.php파일
Skin.php파일(위의예에서는ProfileSkin.php파일)은스킨클래스파일입니다.스킨생성커맨드로생성된스킨의클래스는
\Xpressengine\Skin\GenericSkin클래스를상속받고있습니다.또,GenericSkin클래스는스킨컴포넌트의추상클래스인
\Xpressengine\Skin\AbstractSkin를상속받고있습니다.
\Xpressengine\Skin\AbstractSkin
└──\Xpressengine\Skin\GenericSkin
└──Skin(Skin.php)
PHP개발자가아닌스킨제작자가스킨클래스를직접작성하는것은쉬운일이아닙니다.GenericSkin클래스는스킨제작자들
에게규격화된스킨구조를제공함으로써스킨제작을손쉽게할수있도록도와줍니다.Skin.php파일은처음생성한다음부터는
거의직접수정할필요가없습니다.Skin.php를직접수정하는대신,info.php를수정하십시오.
일반적으로Skin.php파일을직접수정하지않아도원하는스킨을제작하는데에는문제가없습니다.다만,좀더고수준의스킨을
제작하고싶거나,특별한기능을스킨에추가하고싶다면Skin.php파일을직접수정하셔도됩니다.Skin.php파일을수정하면
훨씬자유롭고편하게스킨을제작할수있습니다.물론그전에스킨클래스의각메소드가갖는역할과원리를잘이해하고있어야
합니다.
info.php파일
info.php파일은스킨의구동에필요한여러가지정보를입력하는파일입니다.간단한php문법으로쉽게작성할수있습니다.
<?php
return[
'setting'=>[
'sample_text'=>[
'_type'=>'text',
'_section'=>'기본설정',
'label'=>'샘플문구',
'placeholder'=>'샘플용설정필드입니다.',
'description'=>'샘플용설정필드입니다.',
],
],
'support'=>[
'mobile'=>true,
'desktop'=>true
]
];
setting필드에는스킨설정페이지에서사용할설정항목에대한정보를지정합니다.
support필드에는이스킨이데스크탑버전과모바일버전을지원하는지를지정합니다.
스킨의출력
하나의스킨은하나이상의페이지의출력을담당할수있습니다.출력하는페이지의갯수는스킨타겟에따라다릅니다.게시판의
경우,글보기,글쓰기,글목록등의페이지를가지고있는데,스킨은게시판이필요로하는모든페이지의출력을담당합니다.
스킨설정
스킨
153
Page 154
위젯
가장먼저해야할작업은다른컴포넌트와마찬가지로추상클래스인\Xpressengine\Widget\AbstractWidget을상속받는클래스를
구현해야합니다.그다음구현한클래스를XE에등록해주면됩니다.
클래스생성하기
이문서에서는위젯작성법을쉽게이해할수있도록'로그인정보위젯'을예로들어설명하겠습니다.로그인정보위젯은현재로그
인되어있는회원의정보를출력하는간단한위젯입니다.
<?php
//plugins/myplugin/src/Widgets/UserInfoWidget.php
namespaceMyPlugin\Widgets;
classUserInfoWidgetextends\Xpressengine\Widget\AbstractWidget
{
publicfunctionrender()
{
}
publicfunctionrenderSetting(array$args=[])
{
}
publicfunctionresolveSetting(array$inputs=[])
{
}
}
위와같이작성한클래스파일을컴포넌트를담을플러그인에생성합니다.파일의위치는플러그인디렉토리내의어느곳이든상관
없습니다.다만플러그인디렉토리의src/Widgets/UserInfoWidget.php에생성하는것을권장합니다.
위젯클래스는기본적으로작성해야할메소드가있습니다.
render-위젯을출력할때호출되는메소드입니다.
renderSetting-위젯변수를입력받기위한폼을출력합니다.위젯변수는위젯을출력할때필요한컨텐츠에대하여사이트관리
자로부터입력받는값을말합니다.사이트관리자가위젯생성기를통해위젯(위젯코드)을생성할때,위젯생성기는이메소드를실행하여위젯변수입력폼을출력합니다.
resolveSetting-사이트관리자가위젯생성기에서입력한위젯변수를조합하여위젯코드를생성합니다.이메소드는입력받은
위젯변수를파라미터로전달받은다음,위젯변수를한번재가공하여반환합니다.
위젯등록하기
작성한위젯클래스는다른컴포넌트와마찬가지로XE에등록해야합니다.위젯은widget/<plugin_name>@<pure_id>형식의컴포
넌트아이디를지정해야합니다.여기에서는widget/myplugin@userinfo를아이디로사용하겠습니다.
등록방법은컴포넌트등록문서를참고하시기바랍니다.
성공적으로등록되었는지아래코드로테스트해볼수있습니다.
<xewidgetid="widget/myplugin@userinfo"></xewidget>
위젯출력하기
위젯
154
Page 155
기본적인방법으로출력하기
앞서말한바와같이,위젯이출력될때에는render메소드가사용됩니다.
<?php
//plugins/myplugin/src/Widgets/UserInfoWidget.php
namespaceMyPlugin\Widgets;
classLoginInfoWidgetextends\Xpressengine\Widget\AbstractWidget
{
publicfunctionrender()
{
//로그인상태일경우,로그인회원의이름이출력
if(auth()->check()){
returnauth()->user()->getDisplayName();
}else{
return'로그인상태가아닙니다'
}
}
}
로그인회원의이름을출력하거나'로그인상태가아닙니다'메시지를출력하도록작성된코드입니다.이예제에서는간단히문자열
을반환했습니다.좀더복잡한위젯일경우블레이드템플릿을사용할수도있습니다.
위젯스킨을사용하여출력하기
위젯은스킨시스템을기본적으로지원합니다.동일한컨텐츠를출력하는위젯이라고해도,사이트의디자인이나테마에따라다른
스킨을선택하여출력할수있습니다.
<?php
//plugins/myplugin/src/Widgets/UserInfoWidget.php
...
publicfunctionrender()
{
$data=[
'isLogged'=>auth()->check(),
'user'=>auth()->user()
];
return$this->renderSkin($data);
}
위코드에서와같이renderSkin을사용하면됩니다.renderSkin메소드는위젯코드에지정된스킨정보를자동으로가져온다음,스킨을적용하여출력합니다.
위젯변수사용하기
사이트관리자로부터위젯변수를입력받기위해위젯생성기는위젯변수입력폼을출력합니다.위젯변수를사용하려면위젯변수
입력폼을먼저작성해야합니다.
위젯설정폼구현
위젯클래스의renderSetting메소드가위젯변수입력폼을출력하도록하십시오.
위젯
155
Page 156
<?php
//plugins/myplugin/src/Widgets/UserInfoWidget.php
...
publicfunctionrenderSetting(array$args=[])
{
//회원이름뒤에나오는호칭(예:xxx님)
returnuio('formText',['name'=>'postfix','value'=>array_get($args,'postfix'),'label'=>'호칭','description'
=>'회원이름뒤에출력될호칭을적어주세요']);
}
위코드의경우,위젯생성기에하나의텍스트박스가출력됩니다.텍스트박스를출력하기위해UI오브젝트를사용하고있습니다.
입력된설정정보처리
사이트관리자가위젯변수입력폼에입력한내용을위젯코드로변환하기전에한번더재가공할수있습니다.resolveSetting메소드를사용하십시오.위젯시스템은위젯코드를생성하기전에이메소드를실행합니다.만약사용자가입력한위젯변수의유효성
검사나재처리가필요하다면이메소드에구현하면됩니다.
<?php
//plugins/myplugin/src/Widgets/UserInfoWidget.php
...
publicfunctionresolveSetting(array$inputs=[])
{
if(!array_get($inputs,'postfix'))
{
thrownewValidationException();
}
return$input;
}
위젯을출력할때위젯변수사용하기
사이트관리자가입력한위젯변수는render메소드에서바로사용할수있습니다.위젯변수는$this->config배열에저장되어
있습니다.
<?php
//plugins/myplugin/src/Widgets/UserInfoWidget.php
...
publicfunctionrender()
{
//로그인상태일경우,로그인회원의이름이출력
if(auth()->check()){
returnauth()->user()->getDisplayName().array_get($this->config,'postfix');
}else{
return'로그인상태가아닙니다'
}
}
}
위젯
156
Page 157
모듈
XE는사이트관리페이지를통해각각의메뉴를정의할수있는기능을제공합니다.메뉴의아이템으로등록되는컴포넌트는모듈
로생성하여등록할수있습니다.
모듈생성
새로운모듈을생성하기위해선Xpressengine\Menu\AbstractModule을상속받아클래스를구현해야합니다.이클래스는XE에서
사용되어지기위해다음과같이모듈을지칭하는아이디를가져야합니다.
module/<plugin_id>@<module_id>
모듈구조
모듈은메뉴에등록,수정또는삭제처리를위한메서드를구현해야합니다.
useXpressengine\Menu\AbstractModule;
classMyModuleextendsAbstractModule
{
publicfunctioncreateMenuForm()
{
...
}
publicfunctionstoreMenu($instanceId,$menuTypeParams,$itemParams)
{
...
}
publicfunctioneditMenuForm($instanceId)
{
...
}
publicfunctionupdateMenu($instanceId,$menuTypeParams,$itemParams)
{
...
}
publicfunctionsummary($instanceId)
{
...
}
publicfunctiondeleteMenu($instanceId)
{
...
}
publicfunctiongetTypeItem($id)
{
...
}
}
createMenuForm와editMenuForm메서드는메뉴를생성또는수정할때,모듈이필요로하는값을사이트관리자로부터입력받기
위한폼을반환하는메서드입니다.이폼에의해입력된값은메뉴를등록하거나수정할때호출되는storeMenu,updateMenu메서드의$menuTypeParams파라미터로전달됩니다.
모듈
157
Page 158
메뉴가삭제될때에는deleteMenu메서드를호출하게되는데,만약삭제를처리하기전사용자에게알려야할메시지가있다면
summary메서드에작성하십시오.해당메시지가사용자에게노출됩니다.
때때로XE의기능요소들이메뉴를통해특정모듈에속한객체아이템에접근하고자하는경우가있습니다.getTypeItem는이럴
때모듈의객체아이템을반환하기위해정의된메서드입니다.
라우트
메뉴는대부분각각의메뉴아이템에연결된주소로의이동을위해라우트를가집니다.그러나외부링크와같은특별한경우에는라우트를가지지않기도합니다.이럴때는isRouteAble메서드를통해false값을반환해야합니다.
classMyModuleextendsAbstractModule
{
...
publicstaticfunctionisRouteAble()
{
returnfalse;
}
...
}
설정페이지
모듈은다른컴포넌트와마찬가지로getSettingsURI메서드를통해설정페이지주소를반환합니다.하지만모듈은각각의메뉴의
아이템으로서생성되어지면서인스턴스를가지게됩니다.이때각인스턴스별로설정페이지가존재할수있습니다.이럴땐
getInstanceSettingURI메서드를통해각인스턴스별설정페이지주소를반환해야합니다.
classMyModuleextendsAbstractModule
{
...
publicstaticfunctiongetInstanceSettingURI($instanceId)
{
returnroute('mypluing.mymodule.setting',$instanceId);
}
...
}
모듈
158
Page 159
UI오브젝트
UI오브젝트는아래와같이매우간단한절차로작동합니다.
1. PHP코드나블레이드파일에서uio($id,$arguments,$callback)함수호출
2. uio함수는$id에해당하는UI오브젝트의인스턴스를생성(생성시$arguments와$callback을파라미터로전달)3.생성된인스턴스의render메소드를호출
4. render메소드가반환하는결과값을출력
가장먼저해야할작업은다른컴포넌트와마찬가지로추상클래스인\Xpressengine\UIObject\AbstractUIObject를상속받는클래
스를구현해야합니다.그다음구현한클래스를XE에등록해주면됩니다.
클래스생성하기
이문서에서는UI오브젝트의작성법을쉽게이해할수있도록'이미지UI오브젝트'를예로들어설명하겠습니다.이미지UI오브젝트
는이미지파일주소를입력받아<imgsrc="이미지파일주소">형태로출력하는간단한역할을합니다.
<?php
//plugins/myplugin/src/UIObjects/ImageUIObject.php
namespaceMyPlugin\UIObjects;
classImageUIObjectextendsAbstractUIObject
{
publicfunctionrender()
{
//implementcode
returnparent::render();
}
}
위와같이작성한클래스파일을컴포넌트를담을플러그인에생성합니다.파일의위치는플러그인디렉토리내의어느곳이든상관
없습니다.다만플러그인디렉토리의src/UIObjects/ImageUIObject.php에생성하는것을권장합니다.
출력코드작성하기
클래스를생성하였다면,render메소드를구현합니다.
render메소드에서는입력된파라미터를사용하여출력할html스트링을생성한다음,$this->template변수에저장합니다.
publicfunctionrender()
{
//입력값가져오기
$src=$this->arguments['src'];
$alt=$this->arguments['alt'];
//이미지태그생성
$this->template='<imgsrc="'.$src.'"alt="'.$alt.'">';
//출력
returnparent::render();
}
마지막으로는반드시returnparent::render()를해주어야합니다.parent::render()메소드는$callback입력이있는지검사
하고,$callback를자동으로실행해주는역할을합니다.
UI오브젝트
159
Page 160
UI오브젝트등록하기
작성한UI오브젝트클래스는다른컴포넌트와마찬가지로XE에등록해야합니다.UI오브젝트는
uiobject/<plugin_name>@<pure_id>형식의컴포넌트아이디를지정해야합니다.여기에서는uiobject/myplugin@image를아이디
로사용하겠습니다.
등록방법은컴포넌트등록문서를참고하시기바랍니다.
성공적으로등록되었는지아래코드로테스트해볼수있습니다.
uio('uiobject/myplugin@image',['src'=>'path/to/image.jpg','alt'=>'testimage']);
alias등록하기
위예제에서생성한UI오브젝트의아이디는uiobject/myplugin@image로꽤복잡합니다.XE에서는UI오브젝트에별칭(alias)를지정할수있습니다.별칭이지정된UI오브젝트는실제아이디대신별칭을사용할수있습니다.UI오브젝트의별칭은사이트관리자
가config/xe.php의uiobject>aliases항목에지정할수있으며,코드상에서는아래의방법으로지정할수있습니다.
XeUIObject::setAlias($alias,$id);
위예제에서제작한UI오브젝트에image라는별칭을아래와같이지정할수있습니다.
XeUIObject::setAlias('image','uiobject/myplugin@image');
위와같이지정한이후부터는별칭을사용할수있습니다.
uio('image',['src'=>'path/to/image.jpg','alt'=>'testimage']);
폼관련UI오브젝트
config/xe.php에지정된UI오브젝트의별칭목록입니다.
'uiobject'=>[
'aliases'=>[
'form'=>'uiobject/xpressengine@form',
'formText'=>'uiobject/xpressengine@formText',
'formPassword'=>'uiobject/xpressengine@formPassword',
'formTextarea'=>'uiobject/xpressengine@formTextArea',
'formSelect'=>'uiobject/xpressengine@formSelect',
'formCheckbox'=>'uiobject/xpressengine@formCheckbox',
'formFile'=>'uiobject/xpressengine@formFile',
'formImage'=>'uiobject/xpressengine@formImage',
'formMenu'=>'uiobject/xpressengine@menuSelect',
'formLangText'=>'uiobject/xpressengine@formLangText',
'formLangTextarea'=>'uiobject/xpressengine@formLangTextArea',
'langText'=>'uiobject/xpressengine@langText',
'langTextArea'=>'uiobject/xpressengine@langTextArea',
'menuType'=>'uiobject/xpressengine@menuType',
'permission'=>'uiobject/xpressengine@permission',
'themeSelect'=>'uiobject/xpressengine@themeSelect',
'captcha'=>'uiobject/xpressengine@captcha',
]
],
위별칭목록중에form*의형식으로등록된UI오브젝트가있습니다.이UI오브젝트들은의미그대로폼을구성할때사용할수있는UI오브젝트들입니다.이UI오브젝트들은테마나스킨,위젯와같은컴포넌트가사이트관리자에게설정폼을출력할때사용됩
니다.폼관련UI오브젝트의사용법은폼출력문서에서자세히설명합니다.
UI오브젝트
160
Page 161
폼관련UI오브젝트로등록되는UI오브젝트는몇가지규칙이준수해야합니다.
아래의마크업형식으로출력해야합니다.(이마크업형식은bootstrap형식을따르고있습니다.)
<divclass="form-group">
<labelfor=""></label>
<!--실제폼요소를여기에작성-->
<pclass="help-block"></p>
</div>
label,description파라미터를받아서처리할수있어야합니다.label은폼요소의라벨문자열입니다.<labelfor=""></label>에출력되어야합니다.description은<pclass="help-block"></p>에출력될폼요소에대한설명입니다.
id,name필드를파라미터로받아서처리할수있어야합니다.이두필드는실제폼요소의애트리뷰트로지정됩니다.id필드는label의for애트리뷰트의값으로도지정해주어야합니다.
values파라미터를받아서처리할수있어야합니다.values는폼요소에지정할값(value)입니다.values의형식은각폼요소마다다릅니다.
UI오브젝트
161
Page 162
토글메뉴
토글메뉴는형태에따라3가지타입을지원합니다.
MENUTYPE_EXECMENUTYPE_LINKMENUTYPE_RAW
토글메뉴생성
추가하고자하는새로운토글메뉴클래스에서AbstractToggleMenu를상속받습니다.그리고명시된추상메서드를구현합니다.
getText:메뉴가펼쳐졌을때보여지게될문자열입니다.getType:MENUTYPE_EXEC,MENUTYPE_LINK,MENUTYPE_RAW네가지중한가지를반환해야합니다.getAction:해당메뉴를클릭했을때실행되어질js문자열입니다.만약타입이raw인경우html이반환되어야합니다.getScript:메뉴의동작을위해특정js파일이필요한경우해당파일의경로를반환해줍니다.
exec
exec타입은getAction에의해반환된문자열이그자체로js로실행가능한형태를가집니다.특정함수를실행하는경우함수
에서필요로하는인자는해당토글메뉴내에서제공되어야합니다.
publicfunctiongetType()
{
returnstatic::MENUTYPE_EXEC;
}
publicfunctiongetAction()
{
return'alert("hello")';
}
link
link타입은클릭시다른페이지로이동하는메뉴입니다.
publicfunctiongetType()
{
returnstatic::MENUTYPE_LINK;
}
publicfunctiongetAction()
{
return'/';
}
raw
raw타입은메뉴에표현될형태부터실행될방식까지직접지정하여사용하는방식입니다.
publicfunctiongetType()
{
returnstatic::MENUTYPE_RAW;
}
publicfunctiongetAction()
{
return'<ahref="#"onclick="method('argument')">Raw메뉴</a>';
}
토글메뉴
162
Page 164
다이나믹필드(draft)사이트관리자가사용자입력필드를추가할수있는기능을제공합니다.회원,게시판관리페이지에서기능을제공하고있습니다.입력필드추가기능은다른서드파티플러그인에서도제공할수있습니다.
다이나믹필드는다양한형태를제공할수있도록설계되었습니다.AbstractType클래스로데이터를처리하기위한형태만제한하
고다이나믹필드제작자가유연하게구현할수있도록했습니다.다이나믹필드는데이터를처리하는필드타입과출력에필요한
처리를담당하는필드스킨으로구성됨니다.여기는필드타입에대한설명입니다.
AbstractType다이나믹필드를만들때사용되는추상클래스입니다.모든다이나믹필드는반드시이추상클래스를사용해합니다.이것은각구현
체가제공하기위한필요한데이터베이스테이블컬럼의정의와데이터를처리하는데집중할수있도록해줍니다.
classAddressextendsAbstractType{}
classCategoryextendsAbstractType{}
데이터베이스테이블생성,삭제그리고데이터등록,수정,삭제및검색에필요한요소를구현했습니다.제작자는다이나믹필드의
이름,설명,데이터베이스테이블컬럼구성등의정보만처리하여새로운필드를만들수있습니다.
빈다이나믹필드생성(draft)다이나믹필드생성커맨드를사용하려면우선플러그인이마련되어있어야합니다.플러그인생성은플러그인개발시작하기를참고바랍니다.
아래커맨드로빈다이나믹필드를만들수있습니다.
$phpartisanmake:dyanmicField<path><component_id><title>
path는다이나믹필드가위치할디렉토리경로입니다.플러그인디렉토리이름을포함한경로를입력합니다.component_id에는
다이나믹필드의아이디를입력합니다.title에는관리자사이트에서표시할이름입니다.
컴포넌트아이디
컴포넌트아이디는아래와같은규칙으로작성합니다.
fieldType/<plugin_id>@<pure_id>
plugin_id는플러그인디렉토리이름이고pure_id는다이나믹필드의아이디입니다.
다이나믹필드등록
커맨드를사용할경우,자동으로등록됩니다.플러그인의composer.json파일에아래와같이컴포넌트정보가등록되어있습니다.
다이나믹필드
164
Page 165
//plugins/my_plugin/composer.json
...
"extra":{
"xpressengine":{
"title":"myplugin",
"component":{
"fieldType/my_plugin@my_field":{
"class":"Sample\\MyPlugin\\MyField",
"name":"나의다이나믹필드",
"description":"나의다이나믹필드"
}
}
}
},
...
관리자설정페이지
다이나믹필드를생성할때사용자로부터입력값이필요하다면getSettingsView()메소드를구현합니다.제작자는$config를이용해설정입력폼을추가할수있습니다.카테고리다이나믹필드(/app/FieldTypes/Category.php)클래스를참고하세요.
다이나믹필드
165
Page 166
다이나믹필드스킨(draft)사이트관리자는다이나믹필드를이용해서사용자입력필드를추가할수있습니다.추가된다이나믹필드는데이터베이스테이블
구조및데이터입출력을제어하는필드타입과디자인요소를처리하는필드스킨으로구분됩니다.
필드스킨은등록,수정,검색폼,상세보기등의디자인을구성할수있도록구조를제공합니다.
AbstractSkin스킨을만들때사용하는추상클래스입니다.
classDefaultSkinextendsAbstractSkin{}
빈스킨생성(draft)스킨생성커맨드를사용하려면플러그인과대상다이나믹필드가마련되어있어야합니다.플러그인생성과다이나믹필드생성은
다이나믹필드매뉴얼을참고바랍니다.
아래커맨드로스킨을만들수있습니다.
$phpartisanmake:dyanmicFieldSkin<path><component_id><title>
path는스킨이위치할디렉토리경로입니다.플러그인디렉토리이름을포함한경로를입력합니다.
component_id에는스킨의아이디를입력합니다.
title에는관리자사이트에서표시할이름입니다.
컴포넌트아이디
컴포넌트아이디는아래와같은규칙으로작성합니다.
<field_type>/fieldSkin/<plugin_id>@<pure_id>
field_type스킨이사용될대상다이나믹필드아이디입니다.아이디는대상클래스에dd(SampleType:getId())로확인할수있습니다.
plugin_id는플러그인디렉토리이름입니다.
pure_id는스킨의아이디입니다.
스킨등록
커맨드를사용할경우,자동으로등록됩니다.플러그인의composer.json파일에아래와같이컴포넌트정보가등록되어있습니다.
다이나믹필드스킨
166
Page 167
//plugins/my_plugin/composer.json
...
"extra":{
"xpressengine":{
"title":"myplugin",
"component":{
"fieldType/my_plugin@my_field/fieldSkin/my_plugin@my_skin":{
"class":"Sample\\MyPlugin\\MySkin",
"name":"나의다이나믹필드스킨",
"description":"나의다이나믹필드스킨"
}
}
}
},
...
디자인파일처리
AbstractSkin추상클래스는제작자가스킨구현에집중할수있도록blade템플릿파일에서사용할데이터를처리하여제공합니다.제작자가구현체클래스에getPath()를구현하여블레이드템플릿파일이있는디렉토리경로를설정하면이추상클래스는스킨
에서사용해야할설정과데이터베이스데이터를전달합니다.
전달데이터
AbstractSkin이View로전달하는데이터는아래와같습니다.
$config다이나믹필드를만들때관리자에서입력한설정값입니다.$data데이터베이스테이블에등록된정보입니다.$key다이나믹필드에서정의한데이터베이스컬럼이름과실제사용하는입력필드이름의정보입니다.
스킨에서만들어야하는템플릿파일은아래와같습니다.
create.blade.php회원등록,새글작성에사용합니다.edit.blade.php회원정보수정,글수정에사용합니다.show.blade.php회원정보보기,작성된글보기페이지에사용합니다.search.blade.php게시판리스트의상세검색폼에사용합니다.settings.blade.php관리자에서다이나믹필드를생성할때설정값을입력받기위해사용합니다.
다이나믹필드스킨
167
Page 168
에디터
XE에서는에디터를추가하여사용할수있고,또한에디터에서사용할도구를추가할수있습니다.
에디터생성
컴포넌트추가
에디터가사용되기위해선AbstractEditor를상속받은컴포넌트가생성되어야합니다.그리고정의된추상메서드를구현하세요.
getName:js스크립트에서정의된에디터의이름입니다.htmlable:해당에디터가wisywig에디터인지를반환합니다.compileBody:작성된내용이에디터를통해출력되기전출력을위한처리동작을수행합니다.
스크립트load
에디터는frontend에서동작하므로js파일을필요로합니다.이때해당파일을불러오기위한방법으로다음과같은방식을권장합
니다.
이벤트
에디터는표현되기전에처리하기위한이벤트시점이등록되어있습니다.이이벤트시점에에디터에서사용하고자하는스크립트
파일을불러올수있습니다.
app('event')->listen('xe.editor.render',function($editor){
app('xe.frontend')->js('path/to/editor.js')->load();
app('xe.frontend')->js('path/to/editor.define.js')->before(['path/to/editor.js','assets/core/common/js/xe.editor.c
ore.js'])->load();
});
override
에디터는AbstractEditor의render메서드를통해표현됩니다.에디터는이메서드를구현하여원하는동작을추가할수있습
니다.
publicfunctionrender()
{
app('xe.frontend')->js('path/to/editor.js')->load();
app('xe.frontend')->js('path/to/editor.define.js')->before(['path/to/editor.js','assets/core/common/js/xe.editor.c
ore.js'])->load();
returnparent::render();
}
에디터를정의한스크립트파일은반드시XE의xe.editor.core.js를필요로합니다.그러므로XEcore의스크립트가해당에디터스크립트보다먼저불러질수있도록하여야합니다.
Frontend
XEeditor
프론트엔드에서는기본적으로에디터Core가있고인터페이스구현을통해에디터기능을정의할수있습니다.XEeditor에서는구현되는에디터들의인터페이스를정의하고생성된에디터인스턴스들을관리합니다.에디터Core의코드는
xpressengine/assets/core/common/js/xe.editor.core.js에서확인할수있습니다.
XEeditor.define(object)
에디터
168
Page 169
xe.editor.core.js에서는에디터의인터페이스가존재하며에디터를정의하기위해아래의형식과같이XEeditor.define을통해아래의형식들을구현하여에디터를정의합니다.
XEeditor.define({
editorSettings:{
name:"",
configs:{},
},
interfaces:{
initialize:function(selector,options){},
getContents:function(){},
setContents:function(text){},
addContents:function(text){},
on:function(eventName,callback){},
reset:function(){},
},
});
editorSettings
name(string)필수
에디터의명칭을정의합니다.추후에디터사용시XEeditor.getEditor('에디터명')으로정의된에디터를가져올때사용됩니다.
configs(object)
에디터생성시사용되는공통config들을정의합니다.
interfaces
에디터Core에서정의된인터페이스들로일부인터페이스들은필수로구현되어야합니다.
addProps(object)
각구현되는인터페이스들의함수내부에서사용가능하며object로데이터를저장할수있습니다.추가된데이터는this.props['프로
퍼티명']으로사용가능합니다.
initialize(selector,options)필수구현
selector(string)options(object)
에디터생성시호출되며selector와configs에서정의된옵션을인자로받을수있습니다.
//예시
initialize:function(selector,options){
vareditor=CKEDITOR.replace(selector,options);
//selector를저장
this.addProps({
selector:selector
});
this.on("instanceReady",...
}
getContents필수구현
에디터의컨텐츠를리턴해주는인터페이스로필수구현되어야합니다.
//예시
getContents:function(){
returnCKEDITOR.instances[this.props.selector].getData();
}
에디터
169
Page 170
setContents(content)필수구현
content(string)
에디터의컨텐츠를입력하는인터페이스로필수구현되어야합니다.
//예시
setContents:function(content){
CKEDITOR.instances[this.props.selector].setData(content);
}
addContents(content)필수구현
content(string)
에디터의컨텐츠를추가하는인터페이스로필수구현되어야합니다.
//예시
addContents:function(content){
CKEDITOR.instances[this.props.selector].insertHtml(content);
}
on(eventName,callback)
eventName(string)callback(function)
에디터의이벤트를정의하는인터페이스입니다.
//예시
on:function(eventName,callback){
CKEDITOR.instances[this.props.selector].on(eventName,callback);
}
reset
에디터를초기화하는인터페이스입니다.
//예시
reset:function(){
this.setContents("");
}
addTools(toolsMap,toolInfoList)
toolsMap(object)정의된툴의정보
toolInfoList(array)에디터가create될때4번째인자로받은값(id,icon정보등)
툴정보가있을경우에디터Core에서등록된툴정보들을addTools인터페이스를통해전달합니다.정의된에디터에서는addTools인터페이스를통해넘겨받은tool정보로툴들을등록하는코드를구현해야합니다.
에디터
170
Page 171
//예시
addTools:function(toolsMap,toolInfoList){
vareditor=this.props.editor;
varcomponent=toolsMap[toolInfoList[0].id];
vartoolOption=component.props;
toolOption.options.icon=toolInfoList[0].icon;
editor.ui.add(editorOption.name,CKEDITOR.UI_BUTTON,toolOption);
editor.addCommand(editorOption.options.command,function(){
return{
exec:function(editor){
component.events.iconClick(editor);
}
}
})
}
에디터생성
XEeditor.getEditor('에디터명').create(selector,options,editorOptions,toolInfoList)
selector(string)-생성되는에디터의selectoroptions(object)-에디터생성시필요한옵션
editorOptions(object)-에디터옵션
toolInfoList(array)-등록된tool정보
정의된에디터를생성하기위해서는XEeditor.getEditor('에디터명').create를통해생성할수있습니다.
에디터
171
Page 172
에디터툴
에디터툴은에디터상에서제공되는기능도구들을말합니다.기본적으로제공되는도구이외에사용자가에디터에서사용가능한도구들을추가할수있습니다.
에디터도구생성
컴포넌트추가
에디터도구는AbstractTool을상속받아구현되어야합니다.그리고다음추상메서드를구현해야합니다.
initAssets:도구가동작하는데필요로하는파일들을불러옵니다.(js,css)getIcon:에디터도구영역에추가되기위해필요한아이콘파일입니다.해상도에따라다른게사용되기위해배열로
normal,large두개의파일경로가제공되어야합니다.compile:등록된내용중해당도구로작성된영역을표현하기위한동작을수행합니다.
classSomeToolextendsAbstractTool
{
publicfunctioninitAssets()
{
XeFrontend::js(asset('path/to/sometool.js'))->load();
}
publicfunctiongetIcon()
{
returnasset('path/to/icon.gif');
}
publicfunctioncompile($content)
{
$content=/*내용변환코드*/;
return$content;
}
}
사용제한
때로는특정사용자만추가된에디터도구를사용하기를원할수있습니다.이럴땐enable메서드를작성하여현재사용자가해당
도구를사용할수있는지에대한권한검사를수행해주어야합니다.
publicfunctionenable()
{
return/*trueorfalse*/;
}
XE의기능을이용하여검사를하고자한다면권한페이지를확인해주십시오.
Frontend
XEeditor.tools.define
에디터의툴스크립트를정의하기위해서는XEeditor.tools.define을통해아래의형식과같이정의되어야합니다.XEeditor객체
에서는Tool의인터페이스를제공하며인스턴스를관리합니다.
에디터툴
172
Page 173
XEeditor.tools.define({
id:'',
events:{
iconClick:function(){},
elementDoubleClick:function(){},
}
});
id(string)
tool의유니크값을사용합니다.다른tool들과구분을위해사용됩니다.
//예시
id:'editortool/navermap@navermap'
events
iconClick(function)
에디터툴이클릭됐을경우호출되는이벤트입니다.
//예시
iconClick:function(){
window.open('/tool');
}
elementDoubleClick(function)
에디터내의생성된요소가더블클릭될경우호출되는이벤트입니다.
//예시
elementDoubleClick:function(){
window.open('/tool_edit');
}
Tool사용
XEeditor.getEditor('에디터명').create시4번째인자로툴정보를넣어주게됩니다.해당툴정보들은정의된에디터의addTools인터페이스를통해등록됩니다.
//예시
XEeditor.getEditor('에디터명').create('content',{},{},[{"id":"editortool\/navermap@navermap","icon":"http:\/\/domai
n\/plugins\/template_tool\/assets\/icon.gif","options":[],"enable":true}]
)
에디터툴
173