2012年2月25日

CakePHPブログチュートリアル

今までどこにもなかった『CakePHPブログチュートリアル』の説明の決定版!!

-------------------------------------------------------------------------------------------------------------
ちなみにこの記事は
http://book.cakephp.org/ja/view/876/%E3%83%9E%E3%83%8B%E3%83%A5%E3%82%A2%E3%83%AB
上記サイト 11 開発例 11.1 CakePHPブログチュートリアル に沿って、CakePHPの導入例を説明します。
-------------------------------------------------------------------------------------------------------------


<どこよりもやさしいCakePHPチュートリアルの操作手順書>

1) CakePHPをダウンロード
CakePHPをダウンロードして、サーバーにアップロードします。

・ CakePHPのダウンロードをしたサイト → http://cakephp.jp/

・ 今回はCakePHPの "ver.1.3.14安定版" を使用します。

・ ダウンロードしたフォルダ名を"cakephp"に変更します。(←本当はフォルダ名は何でもいいです。)

・ "cakephp"フォルダをサーバーのドキュメントルート内にFTPソフト等を使用してアップロードします。

・ アップロードすると以下のようなディレクトリ構成になります。
/cakephp
/app
/cake
/plugins
/vendors
.gitignore
.htaccess
index.php
README



2) ブログデータベースの作成
MySQL(データベース)にテーブルを作成します。

・ MySQLがインストールしてあって、さらにデータベースが存在するものとして話を進めます。

・ 『phpMyAdmin』にログインして、データベース内で以下のSQL文を実行することで"posts"テーブルの作成をします。
CREATE TABLE posts (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(50),    body TEXT,
    created DATETIME DEFAULT NULL,
    modified DATETIME DEFAULT NULL
);

・ 『phpMyAdmin』にログインして、先ほどと同じデータベース内にて
以下のSQL文を実行することで"posts"テーブル内にサンプルとなるレコードを3行追加します。
INSERT INTO posts (title,body,created)
    VALUES ('タイトル', 'これは、記事の本文です。', NOW());
INSERT INTO posts (title,body,created)
    VALUES ('またタイトル', 'そこに本文が続きます。', NOW());
INSERT INTO posts (title,body,created)
    VALUES ('タイトルの逆襲', 'こりゃ本当に面白そう!うそ。', NOW());



3) Cakeのデータベース設定
CakePHP側にMySQL(データベース)への接続情報の設定をします。

・ /app/config/database.php.default のファイルをコピーし、コピーしたファイル名を"database.php"にします。

・ コピーした"database.php"は同一ディレクトリに配置します。

・ "database.php"のファイルを開きます。

・ 76~85行目に記述されている$default配列にCakePHPがデータベースに接続する設定が記述されてます。
host, login, password, databaseにMySQLに接続する設定を記述しす。
//'endoding' => 'utf8', となってるのを"//"を削除し 'encoding' => 'utf8', にします。

・ http://www/.ドメイン名/cakephp/ を開いてみて、下のようなイメージが表示されたら"DB接続"が成功です。
※上記の一連操作はサーバー側で操作するのではなく、ローカル側で操作したものをアップロードしても大丈夫です。





4) 追加の設定
セキュリティハッシュ用のランダムな文字列を、初期値から任意の文字列に変更をします。

・ /app/config/core.php のファイルを開きます。

・ 204行目の Configure::write('Security.salt', '任意の文字列'); を変更します。

・ 209行目の Configure::write('Security.cipherSeed', '任意の文字列'); を変更します。

・ /app/tmp のディレクトリのパーミッションを書き込みできるように変更します。
※上記の一連操作はサーバー側で操作するのではなく、ローカル側で操作したものをアップロードしても大丈夫です。




5) mod_rewriteについて
画面表示がおかしい場合の対処方法です。

・ 問題なく表示できたので割愛します。

・ もし問題が起こったら他の人に助けを求めてください。




6) Postモデルの作成
サンプル(投稿記事)の作成とアップロードをします。

・ 下記のソースを記述した"post.php"というファイルを作成します。
<?php
class Post extends AppModel{
 var $name = 'Post';
}
?>
作成した"post.php"ファイルを/app/modelsディレクトリ内にアップロードします。




7) Postsコントローラの作成
コントローラ側のロジックの作成とアップロードをします。

・ 下記のソースを記述した"posts_controller.php"というファイルを作成します。
<?php
class PostsController extends AppController {
    var $name = 'Posts';
  function index() {
        $this->set('posts', $this->Post->find('all'));
   }
}
?>

・ 作成した"posts_controller.php"ファイルを/app/controllersディレクトリ内にアップロードします。




8) Postビューの作成
ビューファイルの作成とアップロードと記事の表示をします。

・ 下記のソースを記述した"index.ctp"というファイルを作成します。
<h1>ブログの投稿</h1>
<table>
    <tr>
        <th>Id</th>
        <th>タイトル</th>
        <th>作成日</th>
    </tr>
    <!-- ここから、$posts配列をループして、投稿記事の情報を表示 -->
    <?php foreach ($posts as $post): ?>
    <tr>
        <td><?php echo $post['Post']['id']; ?></td>
        <td>
            <?php echo $html->link($post['Post']['title'], "/posts/view/".$post['Post']['id']); ?>
        </td>
        <td><?php echo $post['Post']['created']; ?></td>
    </tr>
    <?php endforeach; ?>
</table>

・ /app/viewsディレクトリ内に新たに/postsディレクトリを作成します。
※ディレクトリ構成は多分こんな感じになります。
/app
/views
      /element
      /errors
      /heopers
      /layouts
      /pages
      /posts ←コレを作成
      /scaffolds

・ 作成した"index.ctp"ファイルを/app/views/postsディレクトリ内にアップロードします。

・ http://ドメイン名/cakephp/posts/index を開くと下記のようなイメージが表示されます。
※ただし、タイトル部分のリンクをクリックすると、エラーが表示されます。




・ /app/controllersディレクトリ内にある"posts_controller.php"ファイルを下記のソースに変更(追記)します。
<?php
class PostsController extends AppController {
    var $name = 'Posts';
    function index() {
        $this->set('posts', $this->Post->find('all'));
        }
    function view($id = null) {
        $this->Post->id = $id;
        $this->set('post', $this->Post->read());
    }
}
?>

・ 下記のソースを記述した"view.ctp"というファイルを作成します。
<h1><?php echo $post['Post']['title']?></h1>
<p><small>Created: <?php echo $post['Post']['created']?></small></p>
<p><?php echo $post['Post']['body']?></p>

・ 作成した"view.ctp"ファイルを/app/views/postsディレクトリ内にアップロードします。

・ http://ドメイン名/cakephp/posts/index 再度を開き、タイトル部分のリンクをクリックします。

・ 今度はエラーではなく、下のようなイメージが表示されるようになります。





9) 記事の追加
記事の投稿をできるようにします。

・ /app/controllersディレクトリ内にある"posts_controller.php"ファイルを下記のソースに変更(追記)します。
<?php
class PostsController extends AppController {
    var $name = 'Posts';
    function index() {
        $this->set('posts', $this->Post->find('all'));
        }
    function view($id = null) {
        $this->Post->id = $id;
        $this->set('post', $this->Post->read());
    }
    function add() {
        if (!empty($this->data)) {
            if ($this->Post->save($this->data)) {
            $this->flash('記事は保存されました。','/posts');
            }
        }
    }
}
?>



10) データのバリデーション
記事の投稿をできるようにします。

・ 下記のソースを記述した"add.ctp"というファイルを作成します。
<h1>記事の追加</h1>
<?php
echo $form->create('Post');
echo $form->input('タイトル');
echo $form->input('本文', array('rows' => '3'));
echo $form->end('記事の保存');
?>

・ 作成した"add.ctp"ファイルを/app/views/postsディレクトリ内にアップロードします。

・ 以下の操作は11:投稿記事の削除の作業後に行うと確認できます。

・ http://ドメイン名/cakephp/posts/index 再度を開きます。

・ 画面左上に新たに表示された"記事の追加"というリンクをクリックします。

・ http://www/.ドメイン名/cakephp/posts/add を開きます。

・ /app/modelsディレクトリ内の"post.php"ファイルを下記のソースに変更(追記)します。
<?php
class Post extends AppModel {
    var $name = 'Post';
    var $validate = array(
        'titile' => array(
            'rule' => array('minLength', 1)
        ),
        'body' => array(
            'rule' => array('minLength', 1)
        )
    );
}
?>

11) 投稿記事の削除
記事の削除をできるようにします。
・ /app/controllersディレクトリ内にある"posts_controller.php"ファイルを下記のソースに変更(追記)します。
<?php
class PostsController extends AppController {
 var $name = 'Posts';
    function index() {
     $this->set('posts', $this->Post->find('all'));
        }
    function view($id = null) {
       $this->Post->id = $id;
        $this->set('post', $this->Post->read());
    }
    function add() {
        if (!empty($this->data)) {
            if ($this->Post->save($this->data)) {
         $this->flash('記事は保存されました。','/posts');
            }
        }
    }
    function delete($id) {
        $this->Post->del($id);
        $this->flash('id:'.$id.'の記事は削除されました。', '/posts');
    }
}
?>
・ /app/views/postsディレクトリ内の"index.ctp"ファイルを下記のソースに変更(追記)します。
<h1>ブログの投稿</h1>
<p><?php echo $html->link('記事の追加', '/posts/add'); ?></p>
<table>
    <tr>
        <th>Id</th>
        <th>タイトル</th>
        <th>操作</th>
        <th>作成日</th>
    </tr>
    <!-- ここから、$posts配列をループして、投稿記事の情報を表示 -->
    <?php foreach ($posts as $post): ?>
    <tr>
        <td><?php echo $post['Post']['id']; ?></td>
        <td>
            <?php echo $html->link($post['Post']['title'], "/posts/view/".$post['Post']['id']); ?>
        </td>
        <td>
        <?php echo $html->link('削除', "/posts/delete/{$post['Post']['id']}", null, '削除しますか?'); ?>
        </td>
        <td><?php echo $post['Post']['created']; ?></td>
    </tr>
    <?php endforeach; ?>
</table>

・ http://ドメイン名/cakephp/posts/index 再度を開きます。

・ 操作の削除のリンクをクリックします。




12) 投稿記事の編集
記事の編集をできるようにします。

・ /app/controllersディレクトリ内にある"posts_controller.php"ファイルを下記のソースを追記します。
※追記する位置はコレまでのposts_controller.phpへの追記手順を参照してください。
※頭を使わないと覚えないから、作業の意味がない。
function edit($id = null) {
    $this->Post->id = $id;
    if (empty($this->data)) {
        $this->data = $this->Post->read();
    } else {
        if ($this->Post->save($this->data['Post'])){
        $this->flash('その記事は既に投稿されています。','/posts');
        }
    }
}

・ 下記のソースを記述した"edit.ctp"というファイルを作成します。
<h1>記事の編集</h1>
<?php
echo $form->create('Post', array('action' => 'edit'));
echo $form->input('タイトル');
echo $form->input('本文', array('rows' => '3'));
echo $form->input('id', array('type'=>'hidden'));
echo $form->end('記事の保存');
?>

・ 作成した"edit.ctp"ファイルを/app/views/postsディレクトリ内にアップロードします。

・ /app/views/postsディレクトリ内の"index.ctp"ファイルを下記のソースに変更(追記)します。
<h1>ブログの投稿</h1>
<p><?php echo $html->link('記事の追加', '/posts/add'); ?></p>
<table>
    <tr>
        <th>Id</th>
        <th>タイトル</th>
        <th>操作</th>
        <th>作成日</th>
    </tr>
    <!-- ここから、$posts配列をループして、投稿記事の情報を表示 -->
    <?php foreach ($posts as $post): ?>
    <tr>
        <td><?php echo $post['Post']['id']; ?></td>
        <td><?php echo $html->link($post['Post']['title'], "/posts/view/".$post['Post']['id']); ?>
        </td>
        <td><?php echo $html->link('削除', "/posts/delete/{$post['Post']['id']}", null, '削除しますか?'); ?>
            <?php echo $html->link('編集', "/posts/edit/".$post['Post']['id']); ?>
        </td>
        <td><?php echo $post['Post']['created']; ?></td>
    </tr>
    <?php endforeach; ?>
</table>

・ http://ドメイン名/cakephp/posts/index 再度を開きます。

・ 操作項目の"編集"のリンクをクリックすると下のようなイメージ画面に遷移します。




13) ルーティング(Routes)
ルートの設定をデフォルトからユーザー設定に変更します。

・ 飛ばします。

・ ルートの設定が必要だと思ったら、勉強します。




14) まとめ
やったことのおさらいをします。

・ CakePHPをダウンロードしてアップロード

・ MySQLにテーブルの作成

・ database.phpにデータベース接続情報の設定

・ core.phpのセキュリティ部分の記述のデフォルト値の変更

・ /app/tmpディレクトリのパーミッションの変更

・ 新規、変更、削除、表示といった基本機能の割り振りを記述したposts_controller.phpの作成

・ トップページとなるindex.ctpの作成

・ 操作で遷移する画面 view.ctp add.ctp edit.ctp の作成

・ モデル post.php の作成



以上です。

プログラムをはじめて45日目の初心者が作成したマニュアルですが
初心者は初心者の気持ちがわかる!!
きっとかゆいところに手が届いたはずです。

2012年2月19日

CSVファイルに上書きすると文字化け (´・ω・`)ショボーン

既存のCSVファイルにデータを上書きする場合。

HTML側から文字コードをEUC-JPで設定していても、
書き込む側のCSVファイルの文字コードがSJISだったりすると、
どちらの文字コードが優先されるか。

当然、CSVファイル側の文字コード。

HTML側からはEUC-JPの情報で渡すが
CSVファイル側ではそれをSJISで出力するため
文字化けが起こる。

この場合はHTML側から出力する文字コードをCSVファイルと合わせるか
CSVファイルの文字コードをHTML側から出力する文字コードに合わせる。

mb_internal_encoding()関数

PHP内部の文字コードの設定をする。
mb_language("ja");
mb_internal_encoding("eucJP-win");

mb_language()はメールの時に必要っぽいので、
メール送信などの機能がないのでれば不要かも。
でも念のため。

magic_quotes_gpcの設定がON

get_magic_quotes_gpc()関数を使って
magic_quotes_gpcの設定がONの場合、
入力フォームの情報(配列)に対して以下のようなエスケープ処理が施される。
「" → ¥"」「' → \'」「¥ → ¥¥」

stripslashes()関数でバックスラッシュを取り除く。

さらにhtmlspecialchars()関数によってHTML特殊文字は以下のように処理される。
「& → &amp;」「" → &quot;」「' → &#039;」「< → &lt;」「> → &gt;」
このことを加味して
入力フォームの文字には危険が潜んでいるので
エスケープ処理を施す。

以下、POST型とGET型の記述。

if ($_SERVER["REQUEST_METHOD"] == "POST") {
  foreach($_POST as $key => $value) {
    if (get_magic_quotes_gpc()) {
      $value = stripslashes($value);
    }
  $value = htmlspecialchars($value);
  $_POST[$key] = $value;
  }
}
if ($_SERVER["QUERY_STRING"] != "") {
  foreach($_GET as $key => $value) {
    if (get_magic_quotes_gpc()) {
      $value = stripslashes($value);
    }
  $value = htmlspecialchars($value);
  $_GET[$key] = $value;
  }
}

※magic_quotes_gpcはPHP 5.3.0で非推奨になったので設定はOFFが推奨。
※htmlspecialchars()によって逆に文字列が崩れてしまい、ログインフォームやDB接続などがうまくいかなくなることもあるので、状況に応じてカットする。

accept-charsetは未完全

HTMLの入力フォームの内容をSubmitボタンで送信する時、
フォームを設置しているページの文字コードと
情報を送信する先の文字コードが違う場合
form内に
accept-charset=""
を追記する。
・・・ただし、
致命的なことにIEが認識しないなどの欠陥があるので、
根本的な解決策としては使用できない。



ふざけるなっ!



ごもっと。



そこで複数のサイトでは
Javascriptのonclickを用いてなんとか解決している。

入力フォームの設置ページと
入力情報の送信先の文字コードが違う場合

HTMLの記述
<form method="POST" action="<?php echo $_SERVER["PHP_SELF"] ?>" accept-charset="utf-8">
<input type="hidden" name="dummy" value="&#65533;">
<!-- 上記 value="&#65533;" はUTF-8 の場合 -->
<!-- EUC-JPの場合はvalue="&#xFDFE;" にする -->
<!-- もしくはサブミット部に下記のようなjavascriptを施して回避する方法もあり -->
<input type="submit" onClick="buff=document.charset;
document.charset='UTF-8';document.form[0].submit();
document.charset=buff;">

mb_detect_order で文字コードの検出順を指定!!

文字コードの検出順を指定する。
デフォルトの"auto"での検出順は
ASCII→ JIS→ UTF-8→ EUC-JP→ SJIS
になるが、最初のASCIIが曲者、というか邪魔者。

とりあえず、
必ず文字コードの検出する順番はこちらで設定する。
以下の検出順が割とオススメ。

mb_detect_order ("eucJP-win, UTF-8, SJIS-win, jis");

PDO::MYSQL_ATTR_INIT_COMMAND

PDOを使ったDB接続の場合
$conn = new PDO("$dbtype:dbname=$dbname;host=$sv", $user, $pass,
  array(PDO::MYSQL_ATTR_INIT_COMMAND=>"SET CHARACTER SET `utf8`")
);

mbstring.internal_encoding

php.iniの設定とPHPのスクリプトファイルの文字コードを一致させる。

php.ini内のmbstringの項目内にある
「mbstring.internal_encoding」の文字コードの設定と
PHPのスクリプトファイルの文字コードを比較。

差異があった場合は、文字コードを統一する。

skip-character-set-client-handshake

クライアントの文字コード設定を強制的にサーバー側と統一する。
my.ini中の[mysqld]セクションに記述
default-character-set = utf8
skip-character-set-client-handshake

※mysql4.1.15 or 5.0.13以降
※SQLインジェクションへの脆弱性から使用しない方がいい

2012年2月18日

SET NAMES での文字化け対策

データ接続の直後に記述をする。
クライアント側から送信される文字コードの指定(設定)。



mysql_query("SET NAMES eucJP-win", $conn);

※PEAR::DBの場合
$conn->query('SET NAMES eucJP-win;');

※PDOの場合
$stmt = $conn->query("SET NAMES eucJP-win;");



ただし
このSET NAMES
SQLインジェクションへの脆弱性から使用しない方がいいとの
もっぱらの噂。

mysql_set_charset()関数

MySQLのクライアント側の文字コードまで設定します。
UTF8部分にはMySQL側の文字コードを指定する。


mysql_set_charset ($conn, "UTF8");

これは便利。
というか、これでほぼ解決!!

ちなみに"UTF-8"はハイフンを省略して"UTF8"で記述しないと認識しない。
※mysql5.0.7以降、PHP5.2.3以降
※PDOで使えない

HTML メタ情報に文字コード

メタ情報に文字コードの指定をします。
具体的にはhttp-equiv属性をhead要素の中に記述します。



出力したい文字コードがecu-JP-winだとすると
HTMLのヘッダー部の記述としてのサンプルは以下の通りです。
<meta http-equiv="content-type" content="text/html; charset=eucJP-win">


ただしレンタルサーバーなど、
HTTPのヘッダーが固定されてる場合はPHPから設定します。
<?php
header("Content-type: text/html; charset=eucJP-win");

mb_convert_encoding()関数で解決!!

文字コードの変換を行います。

下記サンプルはeucJP-winからUTF-8に変換。

$string = mb_convert_encoding($string, "UTF8", "eucJP-win");

PHP 文字化けの原因

PHPとMySQLを扱うに当たって、文字化けという不幸、不運に出くわすことがあると思います。
単純でありながら奥が深い文字化け。
海外ではmojibakeというらしいですが
とにかくマルチバイトの文字コードを扱う日本人にとって
文字化けは切っても切り離せないものです。

しかし、今の日本は
文字化けごときで立ち止まっている暇はないのです。
他のアジア圏の国々との競争に勝ち残っていかないといけません。

つまり、日本語のマルチバイトによる
文字化けでつまづいて
ITサービスの品質低下を招いている場合ではないのです。

さー、力を出し合って
日本の底力、発揮していきましょう!!



とゆーわけで本題。
最近、・・・というか2012年から始めたプログラマーという職業。
孤独だけど高いハードルが待っていそうです。
プログラムの『プ』の字もしらない
ペーペーの自分がやっていけるか不安ですが
とりあえず、勉強していくうえで
文字化けは避けて通れそうにないので
まとめてみました。

間違いや不備があったら申し訳ありません。
完全に勘違いや知識不足によるもでです。
先に、お詫び申し上げます。




【PHPの文字化けの原因】
1.サーバー(MySQL、PHPソースコードなど)、クライアント(WEBブラウザなど)、
   外部ファイル(CSVなど)の文字コードの不一致
2.文字コードの変換に失敗



PHPの文字化けなんて簡単!
すぐ解決しちゃう!

そんな心強い存在になりたいです。