カテゴリー
cakephp2

Cakephp2.xでモデル内から別のモデルを利用する

Fat modelな書き方をしていると、モデル内から別のモデルを呼び出したい場面が良く出てくる。

モデル内で使いたい別のモデルをimportし、必要な場所でインスタンス化して呼び出せばOK。
下記例はhogeモデル内からfugaモデルを呼び出す準備。

//hoge.phpのクラスに追記
App::import('Model','fuga');
//インスタンス化
$fuga= new Fuga;

//モデルfugaのメソッドを使用できる
$fuga->find('all');

hogeモデル全体で使いたい場合などは先頭でインスタンス化しておくが、その際$this->fugaにモデルインスタンスを格納すれば、hogeモデル内のどのメソッドからも$this->fuga->find(‘all’);できる。
まぁあたりまえっちゃぁ、あたりまえか。

カテゴリー
Sublimetext2

Sublimetext2のショートカットまとめ

Sublimetext3のリリースが待ち遠しいのですが、sublimetext2でよく使うけど忘れがちなデフォルトのショートカットまとめ。
(Winユーザーなので、windowsでのショートカットです)
まぁ、preference見ろという話ではありますが…。

コンソールの表示

Ctrl+@

コマンドパレット

Ctrl+shift+p

シンボル一覧(クラスや関数定義の一覧)

Ctrl+r

検索ボックス

Ctrl+Shift+f
※ただのCtrl+fだと操作中のファイルのみを対象とした検索用オプションしか表示されません。

検索置換ボックス

Ctrl+h

ペイン分割

Alt+Shift+[1~4の画面を分割したい数字]

プロジェクトの切替

Ctrl+Alt+p

他に出てきたら、また追記します。

カテゴリー
未分類

TeraTermのログイン用マクロ

TeraTerm使ってますかー?

いっぱいログイン先のサーバがある場合とか、いちいちログインダイアログに入力するのめんどくさいですよねー。
という人(私)向けの、ログインマクロをご紹介。

まずはパスワードでログインする場合

connect '192.168.0.0:22 /ssh /auth=password /passwd=your_password /user=login_account /F=theme_file.ini'

次にキーファイルを使ってログインする場合

connect '192.168.0.0:22 /ssh /2 /auth=publickey /user=login_account /keyfile=public_key_file.pem /F=theme_file.ini'

どちらも最後のパラメータF=はカラーテーマのファイルがある場合は指定してください。
指定しなければデフォルトカラーでログインします。
上記いずれかの内容を、ログイン先やアカウント、パスワードなどを適宜変更の上、拡張子をttlにしてTeraTermのexeファイルがあるディレクトリに保存しましょう。

AmazonのEC2などを利用していて、大量にサーバを抱える場合は重宝しますよ!

カテゴリー
cakephp

Cakephp2のAuthコンポーネントで複数ログイン機能を制御

CakePHP2による開発で、何種類かのログイン機能を一つのウェブサービスに実装したい場合のお話し。

たとえば管理者用機能と一般ユーザーでのログイン状態の切り分けや、一つのサイト内で別のサービスを立ち上げて2種類以上のユーザーを抱えるような場合、aclによる制御も一つの方法ですが、そこまで厳密にユーザー権限を管理する必要は無いケースも多々あるはず。

単純にユーザーテーブルとloginメソッド実装したコントローラーを複数用意して、「ログイン出来たー!」と思ったらログイン情報が錯綜してしまい、あっちの機能でログインしたら、こっちの機能のログイン状態がおかしくなった...。
という場合の対処方法。

簡単な話で、各コントローラごとにログインを管理するグループに合わせてAuthComponentのセッションキーを変えればOK。

たとえば管理者機能(Adminグループ)のログインと、一般ユーザー(Userグループ)でのログイン機能の2種類がある場合、Authコンポーネントで用いるユーザー用テーブルを2種類用意する。

#管理者ユーザーテーブル
CREATE TABLE `admins` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL,
  `password` varchar(45) DEFAULT NULL,
  `created` datetime DEFAULT NULL,
  `modified` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

#一般ユーザーテーブル
CREATE TABLE `users` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL,
  `password` varchar(45) DEFAULT NULL,
  `created` datetime DEFAULT NULL,
  `modified` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

当然対応するAdminModelとUserModelは作成するとして、コントローラも2種類(管理者用機能と一般ユーザー用機能)を用意する必要がある。

各コントローラでAuthコンポーネントを設定する時に、

  • ログインに使用するモデルをグループに合わせて指定
  • beforeFilterで各グループごとに個別のsessionKeyを指定

これだけで、狙ったユーザーグループを用いたログイン機能が実現できる。

//AdminsController
public $components = array(
	'Auth' => array(
		'authenticate' => array(
			'Form' => array(
				'fields' => array(
					'username' => 'name',
					'password' => 'password',
				),
				'userModel' => 'Admin',
			),
		),
	),
);
public function beforeFilter() {
	parent::beforeFilter();
	AuthComponent::$sessionKey = 'Auth.admins';
}
//UsersController
public $components = array(
	'Auth' => array(
		'authenticate' => array(
			'Form' => array(
				'fields' => array(
					'username' => 'name',
					'password' => 'password',
				),
				'userModel' => 'User',
			),
		),
	),
);
public function beforeFilter() {
	parent::beforeFilter();
	AuthComponent::$sessionKey = 'Auth.users';
}

こんな感じで設定しておけば、あとは通常のCakePHPアプリのようにloginアクションやふつうのアクションを定義していけば、2種類のログイン情報が錯綜することなく処理されます。

コントローラを増やす場合は、各コントローラで上記の設定を行う必要がある点を忘れないように注意!

カテゴリー
PHP

PHPで形態素解析

サイト内検索を実装する際、サーバの都合でMecabをインストールできなかったりする場合もある。
そんな時に重宝するのがPHPでできた形態素解析器「Igo-php」です。

元々はJavaで作られていたMecab互換を目指した形態素解析器で、それがさらにPHPに移植されたというもの。

PHP版には辞書ファイルを作成する機能が無いようなので、Mecabの辞書をダウンロードしてJava版のIgoで辞書ファイルをコンパイルする必要があります。

参考にしたのはこちらのサイト

カテゴリー
CSS

サイドバーの下部がサイトコンテンツの下部とそろわない場合

いろいろ対処方法があるのは知ってるよ。
でもね、古い作りのサイトとか、ぶっちゃけ場当たり的な対処をやっつけでやんなくちゃいけない場合ってのもある。

一番手っ取り早いのは、やっぱり背景レイヤーとなっている要素のサイドバー部分(右端とか左端)にbackground-imageを割り当てて、repeat-yするのが簡単かつ確実だったりするんだ。
固定幅のサイドバーでfloatしてたり、position:absoluteなんかだったら特にね!

カテゴリー
pukiwiki

pukiwiki plus! のdumpプラグインでリストアが動作しない!

なぜか、pukiwiki plus! のdumpプラグインでリストアが機能しない現象にあたりました。
そもそもpukiwikiに詳しくないため、dumpプラグインのソースを解析することになったのでその顛末を書きとめておきます。

そもそもリストアのプラグインが呼ばれないじゃないか!

dumpプラグインではリストアするファイルをPOSTしたフォームにMenuというパラメータが無ければリストアのアクションが起動しないようでした。

// メニューを表示する必要があるか?
if (! isset($vars['menu'])) {
	// 入力フォームを表示
	$body = plugin_dump_disp_form();
	return array('msg' => $msg, 'body' => $body);
}

このように$vars[‘menu’]が要求され、なければ74行目のreturnでプラグインが終了してしまいます。
にもかかわらず、これをPOSTするフォームはというと…

  <input type="hidden" name="cmd"  value="dump" />
  <input type="hidden" name="page" value="$defaultpage" />
  <input type="hidden" name="act"  value="$act_up" />

となっており、menuの値など一切渡されません。

そこで、フォームを変更してmenuに’1’をセットして渡すようにしました。

  <input type="hidden" name="cmd"  value="dump" />
  <input type="hidden" name="page" value="$defaultpage" />
  <input type="hidden" name="act"  value="$act_up" />
  <input type="hidden" name="menu" value="1" />

これでひとまず、リストア処理が呼び出されるところまで行きましたが…
なぜか「解凍するファイルが無い!」というエラーが発生します。

よくよく調べていくと、アップロードするtar.gzからファイルを取り出す処理でなにやらフィルターが…

if ($name == TARLIB_DATA_LONGLINK) {
  // LongLink
  $buff     = fread($this->fp, $pdsz);
  $longname = substr($buff, 0, $size);
} else if (preg_match("/$pattern/", $name) ) {
  $buff = fread($this->fp, $pdsz);
  // 既に同じファイルがある場合は上書きされる
  $fpw = @fopen($name, 'wb');
  if ($fpw !== FALSE) {
    flock($fpw, LOCK_EX);
    fwrite($fpw, $buff, $size);
    @chmod($name, 0666);
    @touch($name, $mtime);
    flock($fpw, LOCK_UN);

この752行目のpreg_matchのパターンが怪しい…

どうやらこのパターン、復元先を保証するために設定されているようで、
ダンプファイルを取得したディレクトリ構造と同じ位置に復元する
という仕様のようでした。

私が今回試していたのは、実は別のWikiサービスからとってきたDumpを自分のサーバに上げようとしていたため、取得したダンプファイルのディレクトリ構造とリストア先のサーバのディレクトリ構造が異なっていたため、このフィルタに引っ掛かって上手くリストアされていなかったようです。

うーん、Dumpプラグインを改造しなくちゃです。

カテゴリー
SQL

ツリーの閉包モデル リーフの移動、追加で自分への補足 「SQLアンチパターン」より

1月にオライリー・ジャパンより「SQLアンチパターン」が発売されました。
この本で紹介されているツリー構造をRDBMSで扱う手法の一つ「閉包モデル」は初めて知ったのですが、最後の方に出てくるpath_lengthを追加した場合のSQLが掲載されていなかったので、自分のために補足として書き残しておきます。

以下は本に掲載されているリーフを追加する際のSQLですが…

INSERT INTO TreePaths (ancestor, descendant)
SELECT t.ancestor, 8
FROM TreePaths AS t
WHERE t.descendant = 5
UNION ALL
SELECT 8, 8;

このままではpath_lengthが追加されません。
そこで、以下のようにサブクエリ部分のフィールドで既存レコードのpath_lengthに+1して返すようにすれば、追加されるリーフのpath_lengthは自動で計算されることになります。

INSERT INTO TreePaths (ancestor, descendant, path_length)
SELECT t.ancestor, t.path_length + 1, 8
FROM TreePaths AS t
WHERE t.descendant = 5
UNION ALL
SELECT 8, 8;

また、リーフを移動する際のSQLは以下のように掲載されていますが…

INSERT INTO TreePaths (ancestor, descendant)
SELECT supertree.ancestor, subtree.descendant
FROM TreePaths AS supertree
CROSS JOIN TreePaths AS subtree
WHERE supertree.descendant = 3
AND subtree.ancestor = 6;

これを以下のようにサブクエリ部分のフィールドでsupertree.path_length + subtree.path_length + 1を返せば、自動的にpath_lengthが計算された状態でリーフが移動されます。

INSERT INTO TreePaths (ancestor, descendant,path_length)
SELECT supertree.ancestor, subtree.descendant, supertree.path_length + subtree.path_length + 1
FROM TreePaths AS supertree
CROSS JOIN TreePaths AS subtree
WHERE supertree.descendant = 3
AND subtree.ancestor = 6;

以上、試してないけど、たぶんこれでいけるはず。

カテゴリー
Linux

ログ解析用Linuxコマンド

Apacheのアクセスログを解析するにあたって、便利なコマンドとその例文。
使うときは急ぐ場合が多いのに、たまにしか使わないからすぐ忘れる…。

Windowsで使う場合はCygwin必須。

1.ログの“何か”を基準に並べ替えて出力する。

【コマンド例】

$ cat /opt/nginx/logs/access.log | awk '{print $1}' | sort | uniq -c | sort -nr

【出力結果】

1900401 10.21.11.21
64325 10.21.62.40
11434 101.155.17.53
518 101.155.17.78

【補足】
awk'{print $1}’の部分で、並べ替え対象のフィールドを指定している。
$1の数字はログに含まれる行の何番目のフィールド(半角スペース区切りで)かを指定している。
Apacheのログでよく対象にすると思われるフィールドは以下の通り。
・1 = アクセス元のIP
・4 = アクセス日時
・8 = アクセス対象
・12 = リファラ
ちなみに$0は行全体を指す。

2.Grepいろいろ

・「検索文字列」を含む行を抽出

grep -i "検索文字列" hogehoge.log | less

・「検索文字列」を含まない行を抽出

grep -v "検索文字列" hogehoge.log | less

・-c オプションで結果の件数を出力

grep -c "検索文字列" hogehoge.log

・パイプでつなげて複数条件を指定
(検索文字列を含んだ行のうち、hoge.htmlを含まない結果を出力)

grep -i "検索文字列" hogehoge.log | grep -v "hoge.html" | less

パイプでlessに渡すと結果を表示。
リダイレクタでファイルを指定すればファイルに書き出す。
なお、Cygwinなどでファイルパスを指定する場合、パスのセパレータはwindowsでも\ではなく/でOK。