nazolabo

フリーランスのWebエンジニアが近況や思ったことを発信しています。

Yiiを使う

なにそれ

Yii Framework(イーと読むらしい)
なんかPRADOの人あたりが作ってるsymfonyをインスパイアしたPHPフレームワーク
12/3に1.0がリリースされた

インストール

どっかに置く。
誰かPEAR Channel作って!openpearに勝手に入れちゃう?それライセンス的にいいの?

プロジェクト作成

/path/to/yii/framework/yiic webapp [アプリケーション名]
アプリケーション名のフォルダが作られ、その下に各ファイルが展開される。

設定

基本的にprotected以下のファイルを編集する。以降特に注意書きがなければprotected以下のファイルである。

  • nameにアプリケーション名を書く
  • dbのconnectionStringにDSNを書く。DSNの書式はPDO準拠
    • ユーザ名、パスワードが必要ならdbにusername、passwordを作る
<?php
...
return array(
	'basePath'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..',
	'name'=>'アプリケーション名',

	// autoloading model and component classes
	'import'=>array(
		'application.models.*',
		'application.components.*',
	),

	// application components
	'components'=>array(
		'user'=>array(
			// enable cookie-based authentication
			'allowAutoLogin'=>true,
		),
		// uncomment the following to set up database

		'db'=>array(
			'connectionString'=>'mysql:dbname=testapp;hostname=localhost;',
			'username'=>'testuser',
			'password'=>'testpass',
		),

	),
);

Modelを書く

ActiveRecordパターンで書ける。

create table Post (
    id integer not null auto_increment,
    title varchar(128) not null,
    content text not null,
    create_time datetime not null,
    primary key(id)
);

というテーブルを作る。
アプリケーション直下(protectedの上)で、
$ protected/yiic shell
を叩くとインタラクティブシェルが起動する。

Yii Interactive Tool v1.0
Please type 'help' for help. Type 'exit' to quit.
>>model Post
   generate Post.php

The 'Post' class has been successfully created in the following file:
    /path/to/app/protected/models/Post.php

If you have a 'db' database connection, you can test it now with:
    $model=Post::model()->find();
    print_r($model);

これで雛形が作成される。
ちなみにテーブル名に無いmodelを作ろうとすると警告が出るがそのまま作ることはできる。テーブル名とクラス名の対応を変えたい場合は、先に生成してから、生成されたファイルのtableNameが返す値を変えればいい。

とりあえずScaffold

Yii Interactive Tool v1.0
Please type 'help' for help. Type 'exit' to quit.
>> crud Post
   generate PostController.php
   generate create.php
      mkdir /path/to/app/protected/views/post
   generate update.php
   generate list.php
   generate show.php
   generate admin.php

Crud 'post' has been successfully created. You may access it via:
http://hostname/path/to/index.php?r=post

デフォルトだとユーザ認証が有効になるので、controllers/PostController.phpのfiltersからaccessControlを外すなりaccessRulesを変えるなりするといいかもしれない。
↑に書いてあるURLを参考にしてアクセスする

create_timeを入れないとエラーなんだけど何なの?死ぬの?

テーブル定義側でやれよ
beforeSave()で入れればいいんじゃね?

<?php
...
	public function beforeSave()
	{
		if ($this->isNewRecord) {
			if (empty($this->create_time)) {
				$this->create_time = date('Y-m-d H:i:s', YII_BEGIN_TIME);
			}
		}

		return true;
	}

titleとかcontentとかNOT NULLのはずなのに空欄でも入るんだけど馬鹿なの?死ぬの?

空欄って普通''の扱いだから正しくね?
validation書くといいよ

<?php
...
	public function rules()
	{
		return array(
			array('title', 'length', 'max'=>128),
			array('title', 'required'),
			array('content', 'required'),
		);
	}

てかもうcreate_timeとか表示されてる意味なくない?入力欄消したいんだけど

views/post/のcreate.phpとupdate.phpからフォーム消せ

scaffoldそろそろ飽きたんだけど実際の画面はどうやって組むの

なぜか途中で口調が悪くなってるけど気にしない

controllerを生成する。

>> controller blog
   generate BlogController.php
   generate index.php
      mkdir /path/to/app/protected/views/blog

Controller 'blog' has been created in the following file:
    /path/to/app/protected/controllers/BlogController.php

You may access it in the browser using the following URL:
    http://hostname/path/to/index.php?r=blog

このへんっていちいちshellに入らないといけないのかな…

ところでURLださいんだけど2008年のフレームワークとしてこれは(略

ちゃんと変えれるよ
config/main.phpを書き足す

<?php
...
	'components'=>array(
		'urlManager'=>array(
			'urlFormat'=>'path',
		),
...

これでhttp://.../index.php/controller/actionみたいなのでアクセスできる

実装

controllers/BlogController.php

<?php
...
	public function actionIndex()
	{
		$posts = Post::model()->findAll();

		$this->render('index', array('posts'=>$posts));
	}

views/blog/index.php

<?php foreach($posts as $post): ?>
<div>
<h1><?php echo $post->title ?></h1>
<div><?php echo nl2br($post->content) ?></div>
</div>
<?php endforeach ?>

おいこれXSSし放題だぞ!まさかhtmlspecialcharsとかめんどくさくて書いてられねー

CHtml::encodeってものがあるよ!

<?php foreach($posts as $post): ?>
<div>
<h1><?php echo CHtml::encode($post->title) ?></h1>
<div><?php echo nl2br(CHtml::encode($post->content)) ?></div>
</div>
<?php endforeach ?>

なんか余計面倒な気が…

ってかもう<?php echoとか長くてだるいんだけど

CPradoViewRendererとかあるよ!
config/main.phpを書き足す

<?php
...
	'components'=>array(
		'viewRenderer'=>array(
			'class'=>'CPradoViewRenderer',
		),
...

テンプレートを書き換える

<?php foreach($posts as $post): ?>
<div>
<h1><?= CHtml::encode($post->title) ?></h1>
<div><?= nl2br(CHtml::encode($post->content)) ?></div>
</div>
<?php endforeach ?>

short_open_tag=Offでもテンプレートだけshort_open_tagが使えるようになるよ!
ついでに自動エスケープもしてくれないのかなぁ

ところでこのままだとレコードが100件とかになったらやばくね?

findAllでlimit指定すればいいんじゃね?

<?php
...
	public function actionIndex()
	{
		$posts = Post::model()->findAll(array('limit'=>10));

		$this->render('index', array('posts'=>$posts));
	}

おいそれだと過去のデータ見れないだろ!paginationとかないの!

ちゃんとあるから落ち着け

<?php
...
	public function actionIndex()
	{
		$pages = $this->paginate(Post::model()->count(), 2);
		$conditions = array(
			'limit'=>$pages->pageSize,
			'offset'=>$pages->currentPage*$pages->pageSize,
		);
		$posts = Post::model()->findAll($conditions);

		$this->render('index', array('posts'=>$posts, 'pages'=>$pages));
	}
<?php $this->widget('CLinkPager',array('pages'=>$pages)); ?>

<?php foreach($posts as $post): ?>
<div>
<h1><?= CHtml::encode($post->title) ?></h1>
<div><?= CHtml::encode($post->content) ?></div>
</div>
<?php endforeach ?>

<?php $this->widget('CLinkPager',array('pages'=>$pages)); ?>

とりあえずここまで

詳しくは公式のドキュメントを見てね!
若干怪しい部分があるけど、使いやすそうなので期待