考える×つくる×動かす

主に技術系のことを書いていきます。

CakePHPでBlogを作る

某社の課題でBlogを作る。

基本設計編

まず基本となる言語環境選択

PHP(一番得意)
Perl(はてななら)
RubyRails使うなら)

今回は単純に一番得意なPHPにて開発することにした。

次にデータベースの選択

Mysql(ローカルにインストール済)
PostgreSQL(ローカルに未インストール)
Sqlite(環境が変わってもファイルなんで手軽)

ローカル環境なので何も考えずにMysqlで作ることにした。

次にフレームワークの選択

・ZendFramework(ちょっと規模が小さい)
CakePHP(お手軽)
Ethna(昔使ってた)

ZendFrameworkだと1時間かからずに作成できるけど、
自分フレームワークも含んでいるためかなり規模が大きくなって、
Zend内部と自分フレームワークと2重の説明をしないといけなくて煩雑になりそう。
お気軽っていう意味でCakePHPを選択した。
ただCakePHPは1.3系までしか使ったことがないのと、
最後に使ったのが数年前なのでちょっとブランクあり。


ローカル環境編

ホストの設定

hostsファイルにローカルアクセスできるように以下を記述する。

sudo vi /etc/hosts 
127.0.0.1       blog.localhost
Apacheの設定

バーチャルホストの設定する。

sudo vi /opt/local/apache2/conf/extra/httpd-vhosts.conf
<VirtualHost *:80>
    DocumentRoot "/Users/kazuya/home/blog/app/webroot/"
    ServerName blog.localhost
</VirtualHost>
サーバー再起動
sudo apachectl restart
データベース作成

本当ならユーザーも作成すべきだけどローカル環境なのでrootで全てやっちゃう。

mysql -u root -p   
# (パスワード入力)
mysql > CREATE DATABASE  `blog` DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
CakePHPのインストール

http://cakephp.jp/ より最新版をゲットして/Users/kazuya/home/blogに設置する。

CakePHPの設定

セキュリティーの値を変更

app/Config/core.php以下の値を変更する。

'Security.cipherSeed'
'Security.salt'
データベースアクセスの設定

app/Config/databese.phpを作成する。
ファイル内容はこんな感じ。(パスワードとユーザー名は適宜変えてね)
またMacportsでインストールしたMysqlだとソケットを指定しないと動かないので注意する。

	public $default = array(
		'datasource' => 'Database/Mysql',
		'persistent' => false,
		'host' => 'localhost',
		'login' => 'root',
		'password' => 'XXXXXXXXX',
		'database' => 'blog',
		'prefix' => '',
		'encoding' => 'utf8',
	    'unix_socket' => '/opt/local/var/run/mysql5/mysqld.sock'
	);
書き込みの権限を付与

キャッシュとかセッションを保存する一時フォルダーに書き込み権限をつける。

chmod -Rf 0777 app/tmp


http://blog.localhost/ にアクセスして問題なく動作していることを確認。
f:id:kazupyong:20121104231442j:plain


アプリケーション設計

データベース設計

データベースはERMasterで作った。
Blogで最低限記事とタグ付けが出来ればいいとの課題なので、
記事とタグと関連付けテーブル。あと認証を使いたいのでユーザーテーブルを作成する。

ER図はこんな感じ。
f:id:kazupyong:20121104232233p:plain

ERMasterでSQLをエクスポートしてコマンドラインからテーブルを作成する。

> mysql -u root -p blog < blog.sql 
Bakeでモデル、コントローラー、ビューを作成する

CakePHPにはBakeっていうインタラクティブなコマンドラインツールがあるんだけど、
2.2で久しぶりに使ってみたらかなり使えるツールになってた。

php -f ./app/Console/cake.php bake
---------------------------------------------------------------
Interactive Bake Shell
---------------------------------------------------------------
[D]atabase Configuration
[M]odel
[V]iew
[C]ontroller
[P]roject
[F]ixture
[T]est case
[Q]uit
What would you like to Bake? (D/M/V/C/P/F/T/Q) 

と表示されるのでMを選択してModelを作成する。

Use Database Config: (default/test) 
[default] > 
Possible Models based on your current database:
1. ArticleTag
2. Article
3. Tag
4. User

と4テーブル分のモデルをチュートリアルに従って作成する。
バリデーターとかも事細かにそれぞれのフィールドごとに指定できる。

ここで大切なのは
ArticleとTagはArticleTagにhasAndBelongsToManyであり、
ArticleTagはArticleとTagにbelongsToしていると関連付けしておく。

ControllerとViewも同様にして作成する。


アプリケーション実装

ルーティング処理

articlesのindexがblog.localhostにアクセスした際にデフォルトの表示になるように
app/Config/routes.phpに以下を記述する。

	Router::connect('/', array('controller' => 'articles', 'action' => 'index'));

またコントローラーのadmin_の関数を有効化する為に
app/Config/core.phpに以下を記述

	Configure::write('Routing.prefixes', array('admin'));
ページング処理

ArticlesControllerにページング処理の設定。
記事の公開日時順に並び替えたいのとデフォルトで5件表示に変更。

クラス変数に以下を追記

public $paginate = array(
        'Article' => array(
        'maxLimit' => 5,
        'order' => array('published' => 'desc'),
));

あとはタグIDが指定されている場合にそのタグの記事だけが表示されるように、
ArticlesControllerのページング処理に絞込みの条件を指定。

function index ()内に以下を追記した。

	if(isset($this->params['named']['tag'])){
	    $cond = "ArticleTag.tag_id = {$this->params['named']['tag']} and ";
	    $this->paginate['Article']['joins'] = array(array('type' => 'LEFT', 'alias' => 'ArticleTag', 'table' => 'article_tags','conditions' => 'Article.id = ArticleTag.article_id'));
	}
ログイン処理

LoginControllerとAppControllerにadminのログイン処理を記述。
今回はAuthコンポーネントを使って処理する。

AppControllerにコンポーネントの有効化を記述することにより、
基本すべてのコントローラーがAppControllerを継承しているので全てに反映される。

クラス変数に以下を追記

    public $components = array(
            'Session',
            'Auth' => array(
                    'loginRedirect' => Array('controller'  => 'admin', 'action' => 'articles'),
                    'logoutRedirect' => Array('controller' => 'login', 'action' => 'index'),
                    'loginAction' => Array('controller' => 'login', 'action' => 'index')),
            );

ただ、Blogのトップと記事詳細はログインしてなくてもアクセスできるように、
AppControllerのbeforeFilterに以下を記述。

    function beforeFilter() {
        $this->Auth->authError = __('Login Error');
        $this->Auth->allow('article','index');
        $this->Auth->allow('article','view');
    }
日本語化対応

デフォルトでエラーメッセージやデータベースのカラムとかは全て英語表示。
一応日本語化しておく。
コマンドで全ファイル内の文字出力関数__()内の翻訳ファイルを作る。

php -f ./app/Console/cake.php i18n 

これでapp/Locale/以下にdefault.potファイルが出来る。

mkdir -R app/Locale/jpn/LC_MESSAGES/
cp app/Locale/default.pot app/Locale/jpn/LC_MESSAGES/default.po

日本語の翻訳メッセージを入力しておく。
デフォルトで英語環境で使っているので日本語のFirefoxをインストールして確認した。



こんな感じで最低限ひと通りの機能は完成。


ソースコードは以下で。
https://github.com/kazupyong/Blog