gitでpushしたりpullしたり
なんかおかしいと思いつつも、決定的な情報がなかったために間違ったやり方をしていたのですが、ひょんなことから正しい情報をゲットしたので、私はこうやって使っていますという話を。
(もしかしたらまだまだ非効率なやりかたをしていると思うので是非ご指摘ください)
バージョン管理システム自体を知らなかった自分にも理解できるよう、かみ砕いた説明をしたいと思います。
基本的にgitは、sshが使えれば、導入をためらうことはありません。
サーバー間のやりとりはsshを使って行えますので、gitを導入するにあたって余計なポートをあけたりする必要はないです。
gitでは、今いるレポジトリ(ソースを入れてるディレクトリ)を特に「ローカルレポジトリ」と呼び、やりとりする相手方を「リモートレポジトリ」と呼びます。
普段開発している社内のPCを開発サーバー(開発環境)と呼び、Webサイトを公開しているサーバーにはテスト環境と本番環境が入っているとします。
それぞれ○○環境ごとに、CakePHPのappディレクトリがあると考えると良いですね。
ここで、開発サーバーも公開サーバーもコマンドラインによる操作ができ、公開サーバーは外部からSSHが利用できるものとします。(開発サーバーは外からsshでつながらなくて良いです)
開発サーバーをlocalhostと呼び、公開サーバーをexample.comと呼びます。
www.example.comで本番環境のサイトが表示され、dev.example.comにはアクセス制限をかけてテスト環境を表示していることにします。
appファイルはそれぞれ、/var/appと、/var/www/および/var/devにあることとします。
gitを利用する場合は、このほかに、もうひとつ、bareレポジトリという、共用のレポジトリを用意するのがコツです。
このレポジトリはどこからでもアクセスでき、ローカルで行った変更を、手軽に反映させることができるのです。
svnを利用している人には、逆に少し分かりづらいかもしれません。
svnにおいては、この共用レポジトリこそが唯一のレポジトリということになります。その他はチェックアウトしているだけで、それ自体はレポジトリとは呼びません。
しかし分散型のバージョン管理システムであるgitは、レポジトリから「clone」したその場所もまた、レポジトリなのです。
このことは、ローカルレポジトリの中だけで、バージョン管理ができるということを意味します。
そして必要に応じてのみ、共用レポジトリにその更新を伝えれば足り、すべてのバージョンをその都度(毎回)共用レポジトリに伝える必要がないため、ローカルレポジトリで作業する間は、極端な話インターネットにアクセスできなくても開発は可能です。(もちろんバージョン管理を行いながらです)
具体的な手順はより詳しい方の説明の方が良いと思いますのでそちらを参照してもらうとして、ここではCakePHPで実際どのような流れで開発がされるのかを説明していきます。
まず、共用レポジトリと、www.example.comとdev.example.comのふたつのレポジトリを作成しましょう。
(アプリケーションをbsとします)
# 準備として共用レポジトリ用のディレクトリを作成します $ mkdir /repos # 共用レポジトリ用のディレクトリを作成(共用の場合は.gitをつけるのが慣例です) $ mkdir /repos/bs.git $ cd /repos/bs.git # gitレポジトリ(共用 --bare)を作成 $ git clone --bare git://github.com/monsat/cakephp-plain-app.git #でCakePHP用の初期ファイルのセットが手に入ります。 # git init --bare # 空で作成する場合はcloneの代わりにこれ # テスト・本番用ディレクトリを作成 $ cd /var # 分かりやすく短いパスで説明しています $ git clone /repos/bs.git dev $ git clone /repos/bs.git www
次に開発環境のPCに、開発用のレポジトリを作成しましょう。
# localhost $ cd /var $ git clone ssh://example.com/repos/bs.git app
これで準備は整いました。
コマンドについては詳しく説明しませんが、cloneはレポジトリを複製するコマンドです。
いちからレポジトリを作成する場合は、ディレクトリの中で git init とすると、そのなかに .git という隠しディレクトリが作成されます。
(いつでもそれを削除することで通常のディレクトリになります。逆に言えば、この特別なディレクトリを誤って削除しないようにしましょう。変更履歴をすべて失いかねません)
それではアプリを開発してもらいますが、CakePHP特有の設定を。
gitでは、バージョン管理したくないファイルを .gitignore というファイルに記述することで設定することができます。
CakePHPの場合はtmpディレクトリ以下がそれにあたり、その場合には
tmp/**/*
と記述すると、tmp以下すべてとなります。
tmp以下には、ファイルのパスの情報も入っていますので、これらのファイルがテスト環境や本番環境にコピーされてしまうと、CakePHPは正しく動作しません。場合によってはただ真っ白なページが表示されるということがあります。
それでは開発をすすめていったとします。
(本当はbranchという考え方を理解する必要がありますがそれは省略)
開発の小さな区切りごとに commit という作業を行います。
このcommitがいわばマイルストーンとなり、commitした時点のレポジトリ内の状態に、いつでも戻せます。ファイルが更新される前とか、ディレクトリを作成する前、といった状態にです。
これがバージョン管理システムの肝でして、元に戻す命令を出すと、レポジトリ(ディレクトリ)の中は、あら不思議、一変するのです。元に戻した後に、最新の状態に復帰するのもコマンド一発ですので、またさらに元に戻すことができます。
しかしそれはあくまでcommitを単位として記録されていきますので、「ひとつの機能ごとに」こまめにコミットしていきましょう。
svnと違ってgitの良いところは、このコミットをするのはあくまでローカルレポジトリに閉じた話だということです。svnの場合はコミットごとに中央の(唯一の)レポジトリにアクセスすることになるため、今回の例でいえば、都度SSHによるアクセスが入ります。(公開サーバーへのアクセスはSSH経由です)
gitの場合はコミットはローカルレポジトリで行い、テスト環境や本番環境を変更するときだけ、共用レポジトリを利用する(=共用レポジトリにコミットの履歴を伝える)ことも可能です。
前述したインターネットにつながってなくても開発ができるという状態のことです。
コミットは以下のように行いますが、詳しくは例によって省略。
$ cd /var/app $ git add file_name # 新規作成したファイルがあれば実行。複数あれば git add . ですべて $ git commit -a
コミットの都度shaによる40桁のIDが生成されます。このIDは、元に戻ったりするときに使用されますが、一意に特定できれば先頭の数文字の指定で間に合います。(当初は一桁でもいけるはずです)
そして、公開サーバーにある共用レポジトリへコミットの履歴を反映させる手順です。
# localhost $ cd /var/app $ git push
cloneしてきたリモートレポジトリに反映をさせるのは、git push と引数を省略することができるのでらくちんです。
もし複数人の開発等で共用レポジトリが更新されている「可能性がある」のであれば、push の前に git pull を行います。賢いgitが、あなたと他人の更新がごっちゃにならないように、うまく融合(merge)してくれるでしょう。
あとは、テスト環境本番環境とも、それぞれ git pull するだけで、開発した内容をそれぞれに反映させることができます。
テスト環境を反映させ、公開サーバー上でもアプリに不具合がないことを充分にテストしてから、本番環境を更新しましょう。
# example.com # dev $ cd /var/dev $ git pull # テスト実行。問題なければ本番環境へ # www $ cd /var/www $ git pull
駆け足でしたが、以上です。
CakePHPにおける開発環境について
前回のIRC集会で話題になった開発環境について、記録としてポストしておきたいと思います。
以前は「FTPもしくはSCPでファイルをアップし動作確認」という面倒なことをやっていました。
しかし、仮想化ソフトを利用してマシン内にLinuxを入れてしまえばわざわざFTPすること無いよねと、至極当たり前のことに気づいたのです。
(もちろん物理的に開発用のLinuxがあれば仮想化も必要ありませんが)
基本的にウィンドウズ(XAMPP)で開発環境を構築したくはないという好き嫌いの話があって、Linuxは前提で。
さて、仮想化ソフトは何にしようかといろいろ調べ、どうやらVMwarePlayerというのが、使っているPCのスペックでも何とかなりそうだという結論に。
しかし「OSを何にするか」ではたと悩むことに。
というのも最近はあまりOSのインストールをしていなかったのと、そもそもVineLinuxくらいしかまともに触ったことがなかったので、安定しているという噂のCentOSでいけそうなのかどうかを、これまた半日がかりで調べました。
で、どうやらいけそうだと。
利用者も多そうで(ここ大事)ネットの情報が溢れてる。
というわけで、各所を見ながら、エイヤとインストール。
(この辺も後々のためにまとめておかなきゃ。後日)
当初はどうやってファイルを共有しようかともやもやしてましたが、何のことはないsambaで共有すれば良いのでした。vmwareの共有機能を使おうとしたのは何だったのか。。。
で、めでたくファイルの共有ができたので、普段使いのEclipseで新しくプロジェクトを作成します。
あとは、CentOSにgitをインストールし、gitでサーバーにアップできるようにして完了。
とにかくこのgitが超絶便利。
gitを使いたいというモチベーションがなかったら、VMwareでLinuxという環境はできていなかったほど、今の私には重要なソフトです。
環境構築後にTortoiseGitのバージョンがあがり使い勝手が良くなっていますが、そのときは使いづらかったのです。
svnには挫折した私も、この便利さ・快適さには勝てません。
CakePHPと一緒で、慣れるまでは覚えることが多くて苦労をしても、使い始めるともはや手放すことができません。
CakePHPも1.3はgitによって管理されています。
thechawというgitレポジトリもでき、各種Plugin等が公開されています。
zipでダウンロードして解凍して、、、なんて手間はなくなるし、バージョンアップしたらコマンド一発です。
でも、もっともっと便利な環境が、世の中にはあるのだろうなあ。それは多分半年後の話。
counterCache (HABTMでも)
カウンターキャッシュとは、hasManyしている関連Modelがあるとき、そのModelの件数を元Modelに保持しておく機能です。
http://book.cakephp.org/ja/view/81/belongsTo
たとえばArticle hasMany Comment というコメントを複数ぶらさげている記事があったとき、コメントの件数をArticle Modelに持たせることができます。
これによりわざわざコメントModelを取得することなく、コメントの件数を(記事一覧ページに)表示させるといったことが可能です。
データベースの構造からすると冗長なデータの持ち方ともいえますが、フィールドをひとつ増やすことでDBへのアクセスを減らすことが見込めますので、ぜひ積極的に活用したい機能です。
使い方は簡単です。
HABTMのcounterCacheは後述します。
まず、上記の例でArticleにあたるModelにフィールドを追加します。
- model_name_count integer
上記の例では comment_count です。
つぎに、関連先のModelのアソシエーションを以下のように変更します。
# models/comment.php var $belongsTo = array( 'Article' => array( 'counterCache' => true, 'counterScope' => '', ), );
これだけです。
これだけで、コメントがadd , edit ,delete されるたびに、紐付いたコメントの件数がArticleに書き込まれます。
実際にデバッグモードで発行されるSQL文を確認すると、insert等のあとにselect count(*) して、Articleをupdateしているのがわかります。
HABTMでcounterCacheを使う
さて、非常に簡単なcounterCacheですが、残念ながらHABTMでは使えません。
Article hasAndBelongsToMany Tag ※となっているとき、ArticleにはTagの件数を、TagにはArticleの件数を持っておきたいというのが人情です。
タグ(tag)付けされた、複数の記事(article)がある状況
そこで登場するのが、bakeryに投稿されたこのビヘイビア(CounterCacheHabtmBehavior)です。
このビヘイビアのすばらしいところは、それぞれのModelに xxx_count というフィールドを用意し、$actsAsで指定すれば、それだけで良いということです。
# model
// 両方のモデルに記述します
// カウントはxxx_countというフィールドが存在しているときのみ実行されます
var $actsAs = array('CounterCacheHabtm');
なお、片方のModelのみに xxx_count を用意した場合も、何の設定も必要なく、動作いたします。
(xxx_countが存在しない場合はカウントされません)
CakePHP IRC集会 (2009年07月)
昨日(2009/07/01)は、第3回CakePHP IRC集会が開催された。
今回は「平日の昼間開催」というおもしろい試みでした。
参加人数は約40人。みんな仕事中なのに何やってんだか(笑)
いつもどおりのゆるめの雰囲気で終始し、途中わたしの空気を読めない質問でCakePHP界の大御所たちの貴重な時間を使いつつ、閉会。
閉会後も残ったメンバーの一部が盛り上がり、次回の開催やオフ会の話題で夜まで話は尽きませんでした。
※オフ会は8月上旬に東京で開催予定だそうです。
今回もMASA-Pさんより話がありましたが、返す返すも「CakePHP界隈の雰囲気は良い」のです。
これは貴重なことですね。
さて、最後になりますが、ちょうど開催がCakePHP1.2ガイドブックの発売直後だったということもあり、著者を前にして「買った」「買います」の嵐。
なかには「会社の上司を説得するための必殺トーク教えて」というオーダーも。(しかもそれを著者自ら答えるというサービスぶり)
実際CakePHPガイドブックがなければCakePHPはここまで盛り上がったとは思えません。それくらい1.1版の前著はよくできていました。
その前著を50ページも増ページした今回も間違いなく「買い」です。
CakePHPはRapid Developmentに最適なツールではありますが、慣れるまで(いや慣れてからもしばしば)開発中にはまってしまうことがあります。
生産性が非常に高まるはずのCakePHPも、使用初期はその思想等に慣れるまでどうしても遠回りしてしまうことがあるのです。
そんなとき、マニュアルだけでなく、このような良書を手元に置いておくということは開発初期の生産性を保つための最善の手段です。
「多大の時間の損失を防ぐことができるでしょう」
また、前著をすでに持っている人や、すでに中級者レベルに達している人も、この本を持っておいたほうが良いと思います。
というのは、この本は、CakePHP1.0という初期からCakePHPに関わっているこの世界の重鎮による著作だからです。
ここにはCakePHPを利用するうえで非常に有効な「優れたコード」が満載です。
自分のCakePHPの使い方に「もっと良い方法がないか」と上を目指すためのヒントが見つかることでしょう。
そう考えると、この3,360円という価格は投資額として安価です。
なぜなら必ずやその何倍ものベネフィットをもたらすわけですから。
それではIRC集会に参加したみなさん。また次回。
cakephperさん、どうもお疲れさまでした。
Tree Behavior & Tree Helper
CakePHPのCore BehaviorであるTree Behavior は、簡単にツリー構造のデータを扱うことができる便利なビヘイビアです。
http://book.cakephp.org/ja/complete/91/Tree
さらに、bakeryで公開されている準公式HelperであるTree Helperと併せて利用すると、その簡単さに誰もが驚くことでしょう。
得意なデータはたとえば次のようなものがあります。
- ディレクトリ
- カテゴリ(親子形式のあるもの)
- ユーザーグループ
- その他
基本的に何らかのデータをhasManyするModelが適していると思われます。
例として、複数のArticleを持つCategory Model のテーブルをみてみましょう。
- id integer
- name string
- article_count integer # counterCache用 ※任意
- parent_id integer # Tree Behavior用
- lft integer # Tree Behavior用
- rght integer # Tree Behavior用
- その他必要なフィールド
関連付けは以下の通りです。
# Category Model
var $hasMany = array('Article');
var $actsAs = array('Tree');
# Article Model
var $belongsTo = array('Category' => array(
'counterCache' => true, # counterCache用 ※任意
));
# Categories Controller
var $helpers = array('Tree');
これだけで準備はできました。
なお、Tree Helper は、Bakeryよりコピーしてきてください。
他のappと共用しているpluginsディレクトリがあれば、そこに入れておくとよいでしょう。
基本的な使い方
その1:ArticlesControllerで、addやeditをするときのセレクトボックス用のデータを作成する
# ArticlesController
$categories = $this->Article->Category->generatetreelist(null ,null ,null ,' ');
# views/articles/edit.ctp
echo $form->input('category_id' ,aa('label',"カテゴリ"));
参照:http://book.cakephp.org/ja/view/517/generatetreelist
その2:カテゴリの一覧を取得する
# ArticlesController
$conditions = array(
'Category.article_count >' => 0,
); // Articleが一件以上登録されているもののみ取得する場合
$this->data = $this->Category->find('all' ,compact('conditions') );
これは通常通りに取得可能です。
その3:カテゴリパス用のデータを取得する
# CategoriesController
$parents = $this->Category->getpath( $category_id );
$this->set('parents' ,$parents);
# view
foreach ($parents as $parent) {
$html->addCrumbs(
$parent['Category']['name'] ,
'/categories/index/' . $parent['Category']['id']
);
}
$html->getCrumbs('>' ,'TOP');
viewのコードはHelperにおいておき、layoutファイルから読みだすとスマートです。
参照:http://book.cakephp.org/ja/view/235/getpath
その4:カテゴリの順序を変える
カテゴリの並びを変更するにはふたつのやり方があります。
ひとつは、親カテゴリをつけかえることです。
たとえば以下のようなツリーを想定します。
-
CakePHP
-
View
- element
- ヘルパー
-
Controller
- コンポーネント
- Model
-
View
- レイアウト
- CSS
- HTML
「レイアウト」のparent_idに「View」のidを指定すれば、レイアウトがViewの子供(のカテゴリ)になるので、ヘルパーの次に表示されるようになるでしょう。
# controller
$this->Category->id = 8; // 「レイアウト」の ID
$newParentId = $this->Category->field('id', array('name' => 'View'));
$this->Category->save(array('parent_id' => $newParentId));
では、ヘルパーをelementより先に表示したい場合はどうするのかというのが二つ目の方法です。
「同じ階層の中で順序を変更するとき」はmoveUpおよびmoveDownメソッドを使用します。
マニュアルでは階層を上下させることができるような記述がありますが、あくまで同じ階層のなかで順位を変更するメソッドです。
上記の例でelementをmoveUpしてもすでに同階層の先頭に位置しているため、moveUpは失敗します。
# controller // ひとつ下に順位を下げる $this->Category->moveDown($this->Category->id, 1); # controller // ひとつ上に順位を上げる $this->Category->moveUp($this->Category->id, 1);
参照:http://book.cakephp.org/ja/view/229/Advanced-Usage
マニュアルに詳しい使用例が載っているので参考にしてください。
その5:初期データを設定する
Tree Behaviorを使うときに真っ先に苦労するのが、初期データの登録です。
もともと何らかのデータがある場合、それをインポートするために、単純にphpMyAdminなどでinsertしようとしても、lftやrghtの値を自分でつけるわけにはいかずに右往左往してしまいます。
で、そんなときは、lft・rhtをnullのままにしておき、id,name,parent_idのみを(何らかの方法で)インポートしてください。
あとは、以下のメソッドを実行するだけで、すべてのlft,rghtが割り振られます。
# controller $this->Category->reorder();
admin_reorderというactionにしておくとよいかもしれません。
また、recoverというメソッドもあり、何らかの不用意な操作でTree構造が壊れたときは、これで修復が試みられるそうです。
私は、まだ実際に使ったことはありません。
その6:データの削除
addやeditは通常通りsave()で行いますが、delete()してしまうと、Tree構造が壊れてしまいます。
delete()の代わりにremoveFromTree()が用意されていますので、そちらを利用しましょう。
Tips
Modelであらかじめ $order = array('lft ASC'); としておくと、常に順序よくデータを取得できます。