アナログCPU:5108843109

ゲームと音楽とプログラミング(酒と女とロックンロールのノリで)

('ω') < イザユケエンジニャー

CodeIgniter入門 #7:データベースの操作<クエリビルダ編・参照系の巻>

CodeIgniter入門シリーズ カテゴリーの記事一覧 - アナログCPU:5108843109


正直PHPよりSQLの方が得意なくらいでクエリビルダは大嫌いなんですが、
仕事で使うことは多い(のにいまいちわかっていない)ので
ひたすら公式マニュアル見て書いて動かしてみました。
クエリビルダクラス — CodeIgniter 3.2.0-dev ドキュメント
いろいろ動かしてみたのを全部書くと上記の公式マニュアルと大体同じになっちゃうので、軽くまとめ直してみました。
それでも長くなったので今回はSELECT編。

今回やること

  • クエリビルダを使ってSELECTしてみる
  • WHERE, LIMIT, ORDER, JOIN, 集計系などある程度カバーする

先に書いておくと

今回と次回でクエリビルダの書き方をいろいろ試していますが、
仕事ではともかく、趣味の開発ではSQL直書きでいくことにします。

上記公式マニュアルには、

クエリを自分で書きたい場合は、データベース設定ファイルでこの クラスを使用できないようにすることもできます。そうすることで、コアの データベースライブラリとアダプタに、少ないリソースを有効活用させることができます。

とありますので、クエリビルダを使わない場合は config/database.php

// クエリビルダを使わないのでFALSEにする
// $query_builder = TRUE;
$query_builder = FALSE;

こうしてやればよさそうです。

とりあえず

まあそういうわけで「SQL直書きで良くねえ?」という感想です。

例えば、「SELECT * FROM `user`」なら

// SQL直書き
$result1 = $this->db->query("SELECT * FROM `user`")->result();
// クエリビルダ
$result2 = $this->db->get('user')->result();

となり、まあ短いしテーブル名だけ書けばいいだけだし…というわけですが…

…いや、実務でそんなクエリほとんど書かないじゃん?

ややこしいクエリになればなるほど、クエリビルダではわけがわからない見た目になる印象でした。
でもやってみる。

現実的(実務で使う程度)なレベルの簡単なSELECT

とりあえずSELECTしてみる

実務で使う程度に簡単なクエリといえばこれくらいですかね。
退会していないユーザーをID降順に50件取得、という感じです。
ページャ想定しての50件として、全部で何件あるかも知れればいいですね。

SELECT `id`, `name`
FROM `user`
WHERE `delete_flag` <> 1
ORDER BY `id` DESC
LIMIT 50, 0

これをクエリビルダで書いてみるとこうなりました。

$this->db->select("id, name");
$this->db->from("user");
$this->db->where("delete_flag <>", 1); // 比較演算子はイコールなら省略できますが、それ以外ならこんな感じになります
$this->db->order_by("id", "DESC"); // ※第二引数は省略するとASCになります
$this->db->limit(50);
$this->db->offset(0);
$result = $this->db->get()->result(); // $result に戻り値が入る

うーん…?
既に微妙な感じが…。
もう少し省略することも可能なのですが(from, limit, offsetは省略してgetの引数に入れることもできる)
省略すればするほど、省略できない書き方になったときに色んな書き方が混在しやすくなります。

全部で何件あるかを調べるには

クエリビルダではできなさそうでした(さっそくつまづく)
やり方あるのかな…ご存じの方は教えてください。
ということでただのMySQLの話になってしまうのですが、以下のように書き換えればOKです。

$this->db->select("SQL_CALC_FOUND_ROWS `id`, `name`", FALSE); // ここを書き換え
$this->db->from("user");
$this->db->where("delete_flag <>", 1);
$this->db->order_by("id", "DESC");
$this->db->limit(50);
$this->db->offset(0);
$result = $this->db->get()->result();
$count_all = $this->db->query('SELECT FOUND_ROWS() AS cnt')->row()->cnt; // $count_all に全件数

selectの第二引数のFALSEは、簡単に言うと第一引数のカラム名をバッククォートでくくるかどうかです。
これを省略(TRUE)していると、内部では「SELECT `SQL_CALC_FOUND_ROWS` `id`, `name` ... 」となってしまいます。
なのでここではFALSEを指定の上、必要なところは自分でバッククォート入れています。

参考: [CodeIgniter] SQL_CALC_FOUND_ROWSを使って一覧と件数を一発で取得する │ M0DE

SELECTで取得してくるカラムやFROMで指定するテーブルに別名を付ける

select内やfrom内にベタ書きでOKでした。

// SELECT `A`.`id` AS `A_id`
// FROM `user` AS `A`
$this->db->select("A.id AS A_id");
$this->db->from("user AS A");
複数指定したいときは…

例えば、WHERE句で複数条件をANDでつないで指定したいとき。
ORDER句やGROUP句の中身を複数並べたいとき。
SELECTの中身が長くなるのでバラして書きたいとき。
こんな感じで並べるだけでOKでした。

// SELECT `id`, `name`
// FROM `user`
// WHERE `delete_flag` <> 1
//   AND `address` = 'tokyo'
// ORDER BY `id` DESC
//         ,`age` ASC
// LIMIT 0, 50
$this->db->select("id");
$this->db->select("name");
$this->db->from("user");
$this->db->where("delete_flag <>", 1);
$this->db->where("address", "tokyo");
$this->db->order_by("id", "DESC");
$this->db->order_by("age", "ASC");
$this->db->limit(50);
$this->db->offset(0);
複数指定したいときは… その2

配列を渡せるケースもあります。

// WHEREだけ抜粋:WHERE `age` < 20 AND `address` = 'tokyo')
$this->db->where(array("age <" => 20, "address" => "tokyo"));
SQL直書きできるケースも多い
$this->db->select("id, name");
$this->db->from("user");
$this->db->where("delete_flag <> 1 AND address = 'tokyo'");
$this->db->order_by("id DESC, age ASC");
$this->db->limit(50);
$this->db->offset(0);

個人的には「は???」って印象でしかないのですが
このような感じで引数にSQL文をそのまま渡すこともできることが多いようです。

WHERE句のバリエーションがすごい

id = 1
$this->db->where("id", 1);
id > 1
$this->db->where("id >", 1);
id > 1 AND age < 20
$this->db->where("id >", 1);
$this->db->where("age <", 20);
id > 1 OR age < 20
$this->db->where("id >", 1);
$this->db->or_where("age <", 20);

えっこれ気持ち悪くない?自分だけ?

id IN (1, 2, 3)
$this->db->where_in("id", array(1, 2, 3));
id IN (1, 2, 3) OR address IN ('tokyo', 'osaka')
$this->db->where_in("id", array(1, 2, 3));
$this->db->or_where_in("id", array("tokyo", "osaka"));
id NOT IN (1, 2, 3) OR address NOT IN ('tokyo', 'osaka')
$this->db->where_not_in("id", array(1, 2, 3));
$this->db->or_where_not_in("id", array("tokyo", "osaka"));

or_where_not_inて。

部分一致検索
// 名前に「ほげ」を含み、住所が「東京」で始まり、メールアドレスが「@gmail.com」で終わる
// (name LIKE '%ほげ%' AND address LIKE '東京%' AND mail LIKE '%@gmail.com')
$this->db->like('name', 'ほげ', 'both');
$this->db->like('address', '東京', 'after');
$this->db->like('mail', '@gmail.com', 'before');

第三引数は、bothの場合は省略できる模様。
もう書かないけど、whereと同じようなノリで or_like, not_like, or_not_like もあります。

集計やグルーピング

GROUP BY
// SELECT group FROM user GROUP BY group
$this->db->select("group")
$this->db->from("user")
$this->db->group_by("group");
DISTINCT
// SELECT DISTINCT group FROM user
$this->db->select("group");
$this->db->from("user");
$this->db->distinct();
集計関数を使う

MIN, MAX, AVG, SUMといった集計関数もちゃんと用意されてました。

// SELECT MAX(age) AS max, MIN(age) AS min, ...
// FROM user
// GROUP BY `group`
$this->db->select("group"); 
$this->db->select_max("age", "max"); // 第二引数は別名(MAX(age) AS max)
$this->db->select_min("age", "min");
$this->db->select_avg("age", "avg");
$this->db->select_sum("age", "sum");
$this->db->from("user");
$this->db->group_by("group");

…COUNT関数は…?(調べてみても見当たらなかった)
仕方ないのでベタ書き戦法です。かゆいところに手が届かないなあ。

$this->db->select("COUNT(`id`) AS `count`", FALSE);
$this->db->from("user");
$this->db->group_by("group");
HAVINGする

HAVINGはサブクエリ使って書くより遅くなりがちなんですけど
HAVING句の使い方と速度検証 - アナログCPU:5108843109
クエリビルダだと複雑なクエリが余計に読みにくくなるし書くのも嫌になってくるので、もうHAVINGでいいかって気になってくる。
いいのかそれで。

// グループごとの合計人数が5人より多いレコードだけSELECT
$this->db->select("COUNT(`id`) AS `count`", FALSE);
$this->db->from("user");
$this->db->group_by("group");
$this->db->having("count >", 5);

WHEREと同じく、比較演算子がイコールなら省略してOKです。

そしてやっぱりor_havingも存在する。

JOINする

// SELECT user.id AS user_id, log.id AS log_id
// FROM user
// LEFT JOIN log ON user.id = log.user_id
$this->db->select("user.id AS user_id, log.id AS log_id");
$this->db->from("user");
$this->db->join("log", "user._id = log.user_id", "left");

join句の第三引数で「left」を指定していますが、もちろん必要に応じてinnerも指定できます。
rightとかもあるけど使わんかろ。

複数JOINする場合は例によって並べるだけでした。

// SELECT user.id AS user_id, log.id AS log_id, log_category.id AS log_category_id
// FROM user
// LEFT JOIN log ON user.id = log.user_id
// LEFT JOIN log_category ON log.category = log_category.id
$this->db->select("user.id AS user_id, log.id AS log_id");
$this->db->from("user");
$this->db->join("log", "user._id = log.user_id", "left");
$this->db->join("log_category", "log.category = log_category.id", "inner");

また、ON句の条件を複数にしたい場合は…クエリビルダっぽい書き方は見つけられませんでした。
ベタ書きで動きます…。

$this->db->join("log", "user._id = log.user_id AND delete_flag <> 1", "inner");

とても複雑なクエリを書く

めんどくさいので公式マニュアルからの引用です。

クエリのグルーピングでは、 WHERE 句を括弧で囲むことでグループを作ることができます。 これにより複雑な WHERE 句のクエリを作ることが可能です。
例:

$this->db->select('*')->from('my_table')
        ->group_start()
                ->where('a', 'a')
                ->or_group_start()
                        ->where('b', 'b')
                        ->where('c', 'c')
                ->group_end()
        ->group_end()
        ->where('d', 'd')
->get();

// 次のようになります:
// SELECT * FROM (`my_table`) WHERE ( `a` = 'a' OR ( `b` = 'b' AND `c` = 'c' ) ) AND `d` = 'd'

お、おう…。
引数にクエリをベタ書きするのを避けるならこうなるんですかね。
これくらいのネストができるクエリって結構あると思うんですけど、慣れてないと読みにくいっすね…。

クエリビルダが嫌いな理由

突然の愚痴

データベースのエラーとかスローログとかを元に調査するとき、検索性が悪くて困る…。
自分が書いたコードなら大体の場所は見当つくかもしれないけど、そうでないと結構むずい。
これがSQL直接書いてるだけなら特徴的な部分を抜き出して一発grepかけるだけなんだけど。
何かいい方法あるのかなこれ。

あと、クエリビルダ自体が悪いわけじゃないんだけど、
こっちのほうが書きやすい・見やすいって言う人は「そもそもSQLやデータベースそのものがよく分かっていない」っていうケースが多い。
SQLを理解せずに見よう見まねのクエリビルダで書かれるとヤバいコードが生産されがちで困る。
(ありがちなのが、JOINの概念が分からなかったのか適当にSELECTしてきたデータをアプリ側でつなぎ合わせてるとか、集計関数を知らないのか同じくアプリ側で集計しなおしてるとか)
データベースの知見がある上でクエリビルダを使いこなしている方も当然いるんだろうけど、いくつかの現場を見た限り出会うことはなかったのでたぶん少数派。


うーん、使うメリットがわからん…。今は嫌いだから使いたくないけどメリットがあるのならもっと考えたいから誰か教えてくれ。
データベースの種類ごとまるっと乗り換える(MySQLからPostgreSQLにするとか)場合は修正箇所が少なくて済む、とかもあるんだろうけど、日頃の使い勝手悪い方が困るわ。そんな機会そうそうないでしょ。
(と言いつつ今やってる案件ではあるんですが(乗り換えではないけど、ほとんど同じシステムのDB違い版がある)結局レアケースだろうし…)
MySQLだろうがOracleだろうが書き方が変わらないので覚えることが少なくて済む…かもしれないけど、クエリレベルの話なんだからせいぜい方言差だし、どっちかというとフレームワークごとのクエリビルダの違いの方が大きいんじゃないのか??
SQL知らなくても使えるっていうのは先述のとおりメリットよりデメリットの方が大きそうだし。

複数のファイルをフォルダごとコピーするスクリプト

今の現場、開発サーバーにソースコード上げる作業がかなりめんどくさいので、ちょっとだけスクリプト化しました。

gitにコミットしても自動で反映されるわけじゃない。
しかもFTPとかじゃなくてリモート接続。

なので、

  • サーバー側の変更対象ファイルをバックアップしておく
  • バックアップしたファイルとローカル側のファイルを差分チェックする
  • ローカル側のファイルをサーバーに設置する
  • 動作確認とかする
  • 場合によっては元に戻したりする

とかいう手順を踏むわけです。

このうちサーバー側の必要ファイルバックアップと、ローカルからサーバーに持っていくファイルを抽出する作業が地味にめんどくさいのですが
この二つの作業が大体同じなので、その部分だけスクリプト化してみました。
ローカルもサーバーもWindowsなのでまあVBSで。

やっていることは、「テキストファイルに一覧化したファイルを全部コピーして、フォルダ構造も再現しつつ指定したフォルダにペースト」です。
例えば、

C:\hoge\fuga\a.txt
C:\hoge\piyo\b.txt

というリストを作り、「C:\backup\」にコピーするようにしてスクリプトを実行すると、

C:\backup\hoge\fuga\a.txt
C:\backup\hoge\piyo\b.txt

にコピーされてくる、という感じです。
フォルダ階層が深いときはコピー先も無駄に深くなるけど…。

これさえあれば、このスクリプトでバックアップ対象と更新対象を抽出して、そのフォルダごとWinmergeで確認して、更新対象フォルダをサーバー側に放り込むだけで済みます。
もちろん元に戻すときはバックアップしておいたフォルダを放り込むだけ。
(とはいえフォルダを放り込むだけなので、更新にしろ元に戻すときにしろ、削除が発生する部分は手作業ですが…)

使い方

  • テキストファイルに、コピーしたいファイルをフルパスの改行区切りで並べておく
  • そのテキストファイルのパスと、コピー先のディレクトリパスを下記スクリプト内で設定する
  • スクリプト実行

プログラム

続きを読む

CodeIgniter入門 #6:データベースの操作

CodeIgniter入門シリーズ カテゴリーの記事一覧 - アナログCPU:5108843109


まあ公式マニュアルにまとまってるんですけどね。
データベースへの接続 — CodeIgniter 3.2.0-dev ドキュメント
クエリ — CodeIgniter 3.2.0-dev ドキュメント

今回やること

  • コントローラからデータベースの操作(SELECT、INSERT、UPDATE、DELETE)を行う
  • プレースホルダを使う
  • エスケープする

データベース

データベースはこんな感じ。
ユーザーマスタ的なものを想定。
とはいえお試しなので超シンプルに、users テーブルに、連番の id と文字列を入れる name で。

CREATE TABLE `user` ( `id` INT NOT NULL AUTO_INCREMENT , `name` VARCHAR(16) NOT NULL , PRIMARY KEY (`id`)) ENGINE = InnoDB;

データベースへの接続

以前書いたことと少しかぶるので簡単に。
まず、設定値は config の database.php へ。
そこだけ設定しておけば、コントローラ内に

$this->load->database('default');

と書くだけで接続できます。

ほとんどのコントローラで接続を行う場合など、これをいちいち書きたくない場合は config の autoload.php のライブラリに「database」を追加しておけばOKです。

$autoload['libraries'] = array(
	'database',
	// 他にもライブラリ系を毎回ロードしておきたい場合はここに追加するものと思われる
);

INSERTする

テストデータを手動で入れるのすら面倒なので、INSERTから作ります。

今回はUserコントローラを用意し、その中に insert 関数を設置しました。
/user/insert/ でアクセスできます。

一番シンプルなのはこれだけ。

public function insert()
{
    $sql  = "INSERT INTO `user` (`name`) ";
    $sql .= "VALUES('なまえ') ";
    $this->db->query($sql);
}

SELECT

次はINSERTされているかどうか確認するためにもSELECT作ります。

とりあえず全行SELECTしてくるやつ。

public function select()
{
    $sql  = "SELECT `id`, `name` ";
    $sql .= "FROM `user` ";
    var_dump($this->db->query($sql)->result());
}

UPDATE

無事にINSERTしたやつがSELECTできることを確認できたので、次はUPDATEしてみます。
変わり映えしないですが…。

public function update()
{
    $sql  = "UPDATE `user` ";
    $sql .= "SET `name` = '変更後だよ!!!!!' ";
    $sql .= "WHERE `id` = 3 ";
    $this->db->query($sql);
}

SELECT動かして変更されることを確認。

DELETE

追加・変更・取得ときたので最後は削除。

public function delete()
{
    $sql  = "DELETE FROM `user` ";
    $sql .= "WHERE `id` = 3 ";
    $this->db->query($sql);
}

これで一通り動かしてみることができました。

プレースホルダを使う

値を動的に組み込みたい場合はプレースホルダを使います。
もちろんエスケープ等はCodeIgniterさんがやってくれるのでこれだけで安全になります。
INSERTのコードの、VALUES句の中身を動的にしてみます。

public function insert()
{
    $sql  = "INSERT INTO `user` (`name`) ";
    $sql .= "VALUES(?) ";
    $this->db->query($sql, array("ほげ"));
}

query の第二引数に渡した値が「?」に入ってくれます。

複数にも当然対応しています。
例えばSELECTで検索条件やページャ用の取得件数制御を入れてみます。

public function select()
{
    $sql  = "SELECT `id`, `name` ";
    $sql .= "FROM `users` ";
    $sql .= "WHERE `id` > ? ";
    $sql .= "LIMIT ?, ? ";
    $arg_list = array(
        1,
        0,
        5,
    );
    var_dump($this->db->query($sql, $arg_list)->result());
}

この場合はクエスチョンの順番通りに当てはめられていきますので、「WHERE `id` > 1 LIMIT 0, 5」となります。

明示的にエスケープする

escape メソッドを使えばクエリの文字列にエスケープした文字列を結合することもできます。
動的な値を入れるときはプレースホルダが使えればOKなんですけど、
テーブル名やカラム名を動的にするときなど場合によっては必要になるかも…?

public function insert()
{
    $sql  = "INSERT INTO `user` (`name`) ";
    $sql .= "VALUES(" . $this->db->escape("'',''ふが~~") . ") "; // なんか危なそうな文字列をエスケープして結合
    $this->db->query($sql);
}

CodeIgniter入門 #5:ログが記録されるようにする

CodeIgniter入門シリーズ カテゴリーの記事一覧 - アナログCPU:5108843109

今回やること

  • エラーページを設置する
  • エラーログが記録されるようにする

謎エラーが出た

なんかね、ちょっとコントローラがつがつ書いてたら、なんかミスってたらしくエラーが出たんですよ。
エラー内容読む前に「あそこ間違えてるわ」と気付いて直してみたものの、エラーが消えない。

Warning: include( ... \application\errors\html\error_php.php): failed to open stream: No such file or directory in  ... \system\core\Exceptions.php on line 268

… application/errors/html/error_php.php が存在しない?
ええ、ないですけど。
最初からそんなのないんですけど。

自前でとりあえず適当に設置してみたらそれが呼び出されました。
どういうこと???勝手に呼び出しておいて自分で設置する前提???
エラー処理 — CodeIgniter 3.2.0-dev ドキュメント

コードを漁ってみると、error_phpの他に、error_general, error_db, error_404, error_exception が呼び出される可能性があるっぽい?
あと、htmlディレクトリの他にcliディレクトリも。
うーん。また必要になったら考えることにする。同じように設置すればいいはず。

エラーログが更新されてなかった

で、自分で設置したファイルはほぼ空なので、結局本来のエラー内容が不明。
logsディレクトリを見てみても特に何も入っていない。

これは config/config.php 内に設定があったので以下のように調整。

$config['log_threshold'] = 1;

これでlogsディレクトリ以下にエラーログが記録されるようになりました。

CodeIgniter入門 #4:自作コアクラスを挟んでみる

CodeIgniter入門シリーズ カテゴリーの記事一覧 - アナログCPU:5108843109


CodeIgniterにデフォルトで入っているWelcomeコントローラは以下のように始まっています。

class Welcome extends CI_Controller { ...

これはsystem(コアシステム)内のクラスを継承しているのですが、
いろんなところで共通の処理を独自に入れたい、というときにsystem内をいじるのはあまりよろしくありません。
CI_Controllerを継承した独自のクラスをさらに継承して使いたいと思います。

今回やること

  • コアクラスとコントローラの間に自作のクラスを挟む

まずはドキュメントを見る

コアシステムクラスの作成 — CodeIgniter 3.2.0-dev ドキュメント

要約すると…

  • application/core の下に自作クラスを設置する
  • クラス名は「MY_」で始めること
    • application/config/config.php 内で変更は可能、ただし「CI_」は不可
  • コントローラの継承元を自作クラスにする

…という感じですね。

やってみる

application/core/TestClass.php

<?php
class MY_Controller extends CI_Controller {
}

application/controller/Welcome.php

<?php
class Welcome extends MY_Controller {
	public function index()
	{
		echo "test";
	}
}

まずはシンプルに用意してみたのですが…

…動かない…。

Fatal error: Class 'MY_Controller' not found in ... Welcome.php on line 3

と言われてしまいます。

作ったクラスが読み込まれていないっぽい?

エラーメッセージでググってみる。

CodeIgniter Coreファイルが見つからない | ZEKIOM.NET
php - codeigniter MY_Controller not found - Stack Overflow
php - CodeIgniter 3.1.7 - Class 'MY_Controller' not found error occurred only on my server - Stack Overflow

解決していたりしていなかったりですが、このへんを参考にしつつ、
試しにWelcome.phpの先頭に

include_once(APPPATH.'core/TestClass.php');

を入れてみたら動きました。
どうやら読み込まれていなかった様子。デフォルトでは読まれないのか?
じゃあどこで読み込むべきなんだろうかとソースコードgrepしていると、
system/core/CodeIgniter.php 内にこんなものを見つけました。

if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))
{
	require_once APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
}

これはまさかとクラスのファイル名をMY_Controller.phpにしてみたら、動いた。

えええ…。
でも公式ドキュメントの例だと some_class.php とかいう適当な名前なんですけど…。
coreディレクトリ以下をまるっと読んだりしてくれないの?

複数ファイルを使い分けたりできないのかと考えるとしっくりこないのでもう少し探してみたのですが、
system/core/Common.php 内の load_class とかいう関数が怪しいかなーと思いつつ、これもまたしっくりこないので諦めた。
とりあえずMY_Controller.php で乗り切ることにします。

MY_Controller.php をなんとか使いまわす方法

この方法が正しいのかどうかはわかりませんが…
Step 9 - No more MY_Controller monopoly. How you can create more than one base controller - Avenir
を参考に。

複数のクラスを使いたくなったら、こんな感じで乗り切るのがお手軽かも。
ロードする順番に気を使わなくて済むし。

<?php
class MY_Controller extends CI_Controller
{
    // ...
}
 
class A_Controller extends MY_Controller
{
    // ...
}
 
class B_Controller extends MY_Controller
{
    // ...
}

Mercuryを使ってみる

XAMPPにくっついてくるMercury、今まで使ったことがなかったのですがついに仕事で使う機会があったのでメモ。

ローカル環境でメール送受信ができます。

Mercuryがローカルでメールサーバーとして動く状態にする

  • XAMPP内のmercury.exeを管理者権限で起動
    • もしくは管理者権限で起動したXAMPPのMercuryのAdminをクリック
  • Contigration > Protocol modules にて以下にチェックを入れる
    • MercuryS SMTP server
    • MercuryP POP3 server
    • MercuryE SMTP end-to-end delivery server
  • Contigration > MercuryS SMTP Server にてIPアドレスの設定を行う
    • Connection control タブで「Add restriction」をクリック
    • IP Address range のfromとtoを両方「127.0.0.1」に設定する
  • Contigration > MercuryP POP Server にてIPアドレスの設定を行う
    • Connection control タブで「Add restriction」をクリック
    • IP Address range のfromとtoを両方「127.0.0.1」に設定する

メールアドレスを追加する

  • XAMPP内のmercury.exeを管理者権限で起動
    • もしくは管理者権限で起動したXAMPPのMercuryのAdminをクリック
  • Contigration > Manage local users... をクリック
    • 「Add」をクリックして出てきたウインドウで以下を設定
      • Username:メールアドレスの@以前の部分
      • Personal name:Usernameと同じでよい
      • Mail password:パスワード
      • APOP secret:Mail passwordと同じでよい

ドメインを設定する

  • XAMPPのsendmail/sendmail.ini にて以下のように書き換える
    • smtp_server=hogehoge.foo
      • hogehoge.fooの部分は別のものでもよいが、このように存在しないドメインを使うなどしておくと安全
  • XAMPPのMercuryMail/MERCURY.INI にて以下のように書き換える
[Domains]
localhost: localhost
localhost: localhost.net
localhost: localhost.org
localhost: localhost.com
localhost: hogehoge.foo ; # この行を追加

動作確認

メールソフト等で送受信ができるかどうか確認して終了

CodeIgniter入門 #3:複数サイトの設置に対応してみる

CodeIgniter入門シリーズ カテゴリーの記事一覧 - アナログCPU:5108843109

入門どころかいろいろすっ飛ばして応用してる感がありますが

なんかうまいこと複数サイトを運用する方法あるかな、と思って考えてみました。
レンタルサーバーなどドキュメントルートが一つなので共有するしかない、みたいな前提)

といってもかなり直球な方法なので大仰に書くほどでもないのですが…

今回やること

  • 複数サイトを想定してディレクトリ構造を変更し、動かしてみる

ファイル設置してみる

こんな感じで。

  • codeigniter
    • application
    • application_a
    • system
  • www(←ドキュメントルート)

前回の構造は基本的にそのままで、「applicationディレクトリを複製」「index.php.htaccessのセットを複製したやつを別ディレクトリ切って設置」しています。
systemは共有して良さそうに見えるのでそのままにしていますが、問題があればapplicationと同じ対応でよいはずです。
逆にapplicationも共有する場合はそもそも複製しなくてOK…なのでしょうけど、そんな運用あるかな…

ドキュメントルート下にcssやjsなどのディレクトリを切る場合もまあ好きなところでよいでしょう。

っていうかcodeigniterディレクトリっていうのがだいぶダサくなってきたな…。

アクセスできるようにしてみる

複製しただけなので、まずwww直下のindex.phpは何も変わりません。「/」でアクセスできます。
で、複製したところは「/a/」でアクセスできるようにしたいと思います。

といっても、www/a/index.php を実際のパスに合わせて以下のように調整するだけです。

$system_path = '../../codeigniter/system';
$application_folder = '../../codeigniter/application_a';

/a/ に無事アクセスできればOK。あとはそれぞれのapplicationディレクトリ以下で開発するだけ。

URLがかっこ悪いっていうのは…独自ドメインとかあればサブドメインに置き換えるとか上手いことできるもんなのかな?(よく知らない)

想定する用途

単に丸々別物のサイトの運用もできるでしょうけども、例えば
「/」は一般公開用ページとして、
「/user/」はログイン中専用ページ、「/admin/」は管理ページ、「/api/」はwebAPI用…みたいな感じでもよさそう。
adminとかapiとかってコントローラ作るのも何だし、だからって.htaccessなどでいじり回すのもアレだし。
そういう分け方しておくと、いざ複数サーバーに分散設置したいってときもちょっと楽そうだし。

CodeIgniter入門 #2:軽く改造してみる

CodeIgniter入門シリーズ カテゴリーの記事一覧 - アナログCPU:5108843109

今回やること

  • デフォルトのディレクトリ構造を変えてみる
  • データベースに接続する

ドキュメントルートのダイエット

やっぱドキュメントルート以下に丸々入ってるのが気持ち悪いので外に出しておくことにしました。

ドキュメントルート用のディレクトリを用意して、そこにindex.php.htaccessを移動させます。
名前は何でも良いのですが、個人的に使っているさくらサーバーがwwwなのでそうしておきます。
で、wwwと同じ階層にcodeigniterディレクトリを作り、そっちにapplicationとsystemを移動させました。

こういう感じです。構造も好みでOK。

こうしておけば外部からはwww以下しかアクセスされないので、
applicationやsystemディレクトリ以下のアクセス対策用index.htmlは消しても差支えないと思います。

で、移動したindex.phpの中にapplicationディレクトリやsystemディレクトリへのパスが書かれているので、そこを直します。
今回の例だと以下のような感じ。

$system_path = '../codeigniter/system';
$application_folder = '../codeigniter/application';

また、XAMPPのhttpd.confなども必要に応じて書き換えておきます。

アクセスを試してみて動けばOK。

いらないディレクトリとファイルを消す

codeigniter(=application, system), www以外のディレクトリやファイルは全て削除しても大丈夫…かもしれません。
とりあえず動きました。
不安なら残しておくなり別のところに退避させておくなり。最悪またダウンロードしてこればOKだと思います。

前項で書いた通り、ドキュメントルート外のindex.htmlもごっそり消して問題なさそうです。
わざわざ消すほどでもないかもしれませんが、なかなか鬱陶しいし…。

あとはapplication以下に空のディレクトリが多いけど何なんだろう。消してもいいのかな。
気になるけどとりあえず残しておきます。

データベースの接続設定をする

データベースとユーザーを用意します。(省略)
今回はMySQLlocalhost内に「sandbox」データベースと「user」ユーザー、「pass」というパスワード…ということにします。

codeigniter/application/config/database.php 内の設定を変更します。

$db['default'] = array(
	'dsn'	=> '',
	'hostname' => 'localhost',
	'username' => '',
	'password' => '',
	'database' => '',
	'dbdriver' => 'mysqli',
	'dbprefix' => '',
	'pconnect' => FALSE,
	'db_debug' => (ENVIRONMENT !== 'production'),
	'cache_on' => FALSE,
	'cachedir' => '',
	'char_set' => 'utf8',
	'dbcollat' => 'utf8_general_ci',
	'swap_pre' => '',
	'encrypt' => FALSE,
	'compress' => FALSE,
	'stricton' => FALSE,
	'failover' => array(),
	'save_queries' => TRUE
);

変更点を抜粋するとこんな感じです。

	'username' => 'user',
	'password' => 'pass',
	'database' => 'sandbox',
	'char_set' => 'utf8mb4',
	'dbcollat' => 'utf8mb4_general_ci',
);

接続できたかどうか確認するには、コントローラ内の適当なメソッド内(もちろんview呼び出しより前)に以下を追記してアクセスしてみます。

$this->load->database('default');

「追記前と何も変わらなければ」とりあえずOKです。
試しにユーザー名やデータベース名などを存在しないものに変えてリロードしてみてエラーになれば尚良しです。

参考資料
データベースへの接続 — CodeIgniter 3.2.0-dev ドキュメント

CodeIgniter入門 #1:Hello CodeIgniter World!!!

※「CodeIgniter入門する人向け記事」じゃなくて「自分がCodeIgniter入門してみた記事」です

CodeIgniter入門シリーズ カテゴリーの記事一覧 - アナログCPU:5108843109

仕事でCodeIgniter触ることになり自分でなんか作った方が理解早そうだなと思って、なんかやってみる。
あとPhalconほどじゃないけど名前がかっこいいし、高速で軽量で自由度が高いということで。
とりあえずHelloWorldまでやってみます。

CodeIgniter へようこそ — CodeIgniter 3.2.0-dev ドキュメント


開発環境はWindows、サーバーはLinuxの想定です。
まあXAMPPで動かすだけなんですけど。

今回やること

  • CodeIgniterのダウンロード
  • HelloWorld(サンプルページの改造から新規ページ追加まで)

CodeIgniterのダウンロード

以下からzipをダウンロードして展開します。
www.codeigniter.com

ちなみに今回ダウンロードしたのは3.1.10でした。

中身を見てみる

こんな構成でした。

  • application
  • system
  • user_guide
  • index.php
  • …などなど


えっ…これさあ…
もしかしてここがドキュメントルートになるの??自由すぎない???

applicationとかsystemとかのディレクトリを覗いてみると、
以下全ディレクトリに「403だよ」って表示するだけのindex.htmlが設置されている…。
まじかよ…。
じゃあドキュメントルートの下に置かなきゃいいじゃん、と思いましたが、ひとまずこの形で進めてみます。
(ちなみに仕事で触るプロジェクトはapplicationとsystemを外に移してました。せやろな)

ローカルで動かしてみる

きっとサンプルページ的なものは最初から出るだろうと踏んで、ダウンロードしたファイルには一切手を加えず適当なところに設置。
英語アレルギーに耐えつつreadmeを見てみるとPHP5.6以上ということだったので、適切なXAMPPを選ぶ。
httpd.confとhostsを編集してXAMPPを起動してアクセス。

f:id:honey8823:20190618130921p:plain

えー…

えーと…

このページのviewは application/views/welcome_message.php で、
controllerは application/controllers/Welcome.php だよ、ってことですかね。

とりあえず動いたっぽいので良し。

HelloWorldしてみる

じゃあ、ここを差し替えてHelloWorldしてみます。

その1:viewを書き換えてみる

application/views/welcome_message.php の内容を適当にいじってからリロード。
無事反映されました。まあそりゃそうなんですけど、確かにここに紐づいてますよということで。

元に戻します。

その2:別のviewを呼んでみる

application/views/welcome_message.php を複製して、HelloWorld仕様に適当に書き換えます。
ここでは hello_world.php としました。
それから application/controllers/Welcome.php から複製したviewを呼ぶよう変更。

public function index()
{
	// $this->load->view('welcome_message'); // これをコメントアウトして
	$this->load->view('hello_world'); // こうじゃ
}

それからリロードすると無事反映されました。
Welcome.php も元に戻します。

その3:別のcontrollerを呼んでみる

次は application/controllers/Welcome.php も複製して、HelloWorld仕様に書き換えます。
ここでは HelloWorld.php としました。
(書き換え内容はその2と同じ)

じゃあこれをどこから呼ぶんだ、と探してみた結果、
application/config/routes.php でした。

// $route['default_controller'] = 'welcome'; // これをコメントアウトして
$route['default_controller'] = 'helloworld'; // こうじゃ

こうやってwelcomeを呼んでいるのをhelloworldに書き換えてリロード。
無事反映されました。

routes.php ももとに戻します。

その4:新しくページを作る

さっきからトップページを書き換えているだけなので、今度は新しくページを作ってみます。

長くなるので結論だけ書くと、以下2つの手順でいけました。

① config/config.php の調整

// $config['index_page'] = 'index.php'; // このindex.phpを
$config['index_page'] = ''; // 消す

②ルートディレクトリ(index.phpと同じ階層)に、下記内容の .htaccess を設置

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]

この調整をしてから /helloworld/ にアクセスすると無事に表示されました!

ちなみに、controllerに hoge メソッドを追加すると、/helloworld/hoge/ でアクセスできます。

おしまい

無事にHelloWorldできましたのでとりあえず今回はここまで!

蛇足:ルーティングについて

> 長くなるので結論だけ書くと
と書きましたが、その部分の補足を。

まあ新しいページ追加するつってもルーティングをなんとかすればいけるっしょ、とユーザーガイドを見てみました。
URI ルーティング — CodeIgniter 3.2.0-dev ドキュメント

URI のセグメントは通常、つぎのパターンに従います:

example.com/class/function/id/

お、もしかして設定いらない? と思って /hellowold/ にアクセスしてみるものの、404。

じゃあ一応設定してみる?と思って

$route['helloworld'] = 'helloworld';

としてみましたが、まだ404。

軽くぐぐってみると同じような?症状の人を発見。未解決だけど。
PHP - CodeigniterでURIルーティングがうまく動作しない。|teratail

(私のサーバーのドメイン)/index.php/users/list/でアクセスできた

なんじゃそりゃ、と思いつつ /index.php/helloworld/ にアクセスしてみると確かに正常に表示された。
なんじゃそりゃ。

もうちょっとユーザーガイドを見てみるとこんなページを発見。
CodeIgniter の URL — CodeIgniter 3.2.0-dev ドキュメント

デフォルトでは index.php ファイルは URL に含まれます

…?
???
え、なんで?

なんでかは分かりませんが、↓も参考にしつつ先に書いた方法で解決しました。
CodeIgniter 3でURLに付加されるindex.phpを外したい(取り除きたい) | onocom

別々の設定を持ったChromeを同時に起動する

今いる作業場所、何故かChromeのプライベートモードが使えないよう制限されていて、
いろんなアカウントを使い分けるようなテストでちょっと困るので
cookieなど無駄に汚したくないし後から全リセットするのもヤダヤダ)
調べてみたところ、普通のChromeを複数同時に起動する方法がありました。

手順は以下の通り。

  • 任意の場所に、Chromeのデータを保存するためのディレクトリを作っておく
  • Chromeのショートカットを好きなところに作る
    • もちろん、今使っているものとは別のショートカット
    • 名前は好きに変更してどうぞ
  • 作ったショートカットを右クリック>プロパティ で、「リンク先」のところに以下を追記する
    • --user-data-dir=さっき作ったディレクトリのフルパス
    • 「OK」で保存して完了

リンク先のところは要するにこんな感じになります。

"C:\ ... \chrome.exe" --user-data-dir=C:\chromedata_1

この方法で、好きなだけ別々のChromeを量産できます。
作ったショートカットから起動してみると、まっさらの状態のChromeが出てきます。
プライベートモードとは違って、こっちはこっちで「普通」に使えるのが利点になり得ます。
例えば
「仕事用と個人用」とか、
「同時に別々のアカウントでひとつのウェブサービスにログインしたい」とか。
リセットしたけりゃフォルダを空にすればいいし、
いらなくなったらショートカットとフォルダを消してスッキリ、という寸法です。