2012年10月31日水曜日

[itunes]Bonjourが削除できていなくてitunesが再インストールできない

このページを見て解決しました。

bonjour.msiを削除してiTunesの再インストールに成功

2012年10月26日金曜日

[jQuery]かっこいいアラートやプロンプトを使う。

このページを見てできました。
なかなか素敵。

ダイアログボックスを美しくみせる「jQuery Alert Dialogs」を使ってみる


別のやつで下記もいいかなと思ったんだけど、
なんか表示されるフェードインが遅すぎるんだよな。

jQuery Impromptu | 設置サンプル

[JavaAcript]外部ファイル化する際はonloadする必要がある

下記のページにあるよう、JavaScriptを外部ファイル化する際は、
外部化するスクリプト全体をwindwo.onloadで指定しなければならない。
ただ、これをしてしまえば、htmlのほうにJavaScriptを書く必要はない。

JavaScript の基本

2012年10月25日木曜日

[CakePHP]外部化したcssやjsをコントローラで出力する。

なるほどね。
cssやjavascriptを外部ファイル化してhtmlと切り離すのは、
SEO対策や読み込み速度といった面でメリットがあるのだけど、
ファイルパス等、開発途中で変更したくなる値を動的に変更できなく
なってしまうので、いちいち全部修正しなくてはならない、というデメリット
がある。

そこで、controllerで出力させてしまえば、自動的に値を変動させる
ことができる。

以下が参考。

[CakePHP] スタイルシートをコントローラーで作ってみる

2012年10月16日火曜日

[jQuery]bindについてわかったこと

・イベントによって生成された要素にbind()するには。 


生のHTMLに書かれた要素に対してイベント登録するには
単純にbind()すればよい。
だが、イベント発生後にJavaScriptで生成された要素に対してイベント登録するには
要素の作成時にbindしなくてはならない。

$("#submit-1528237246").bind("click", function (event) {
                $("#tags").append("<p><span class=\"tag\">"+$("#tag").val()+"</span> <span class=\"rm\">×</span></p>");
                rmTag();
});

function rmTag(){
    $(".rm").unbind();
    $(".rm").bind({
        click:function(event){
            $(this).parent().remove();
        },
        mouseenter:function(){
            $(this).css("cursor","pointer");
        },
        mouseleave:function(){
            $(this).css("cursor","default");
        }
    });
}
2行目によってクリック時に要素を追加する設定をしている。
ここで追加された要素に対してイベント登録するには、この作成時の時点で
イベント登録を行わなければならない。
なので3行目でイベント登録する関数を呼び出している。

・bindによるイベント登録は上書きではなくappend


ちなみに、7行目でunbindしているのは下記のサイトで説明されているよう
bindは上書きではなくappendしていくためだ。

jQueryのイベント時の関数を上書きする

上記のソースのようにイベント発生時にrmクラス全てにイベント登録して
いくと、最初の方にbindされた要素は同じイベント登録が複数回appendされて
しまうため、毎回全登録をunbindしている。

・一つの要素に複数のイベント登録をするには


また、一つの要素に複数のイベント登録をするには8~18行目のようにする。

・"hover"はbind()で使用できない。


 下記のようにhover()という関数を使ってイベント登録をできるが、
これをbind()で実現するには上記のソースのように、mouseenterとmouseleaveに
分けて記述する必要がある。

            $(function(){
                $(".rm").hover(
                    function(){
                        $(this).css("cursor","pointer"); 
                    },
                    function(){
                        $(this).css("cursor","default"); 
                    }
                );
                $(".rm").click(
                    function(){
                        $(this).parent().remove();
                    }
                );
            });


[eclipse][SVN]subversiveで削除コミットしてしまったファイルを戻す

eclipse×subversiveでSVN上から削除コミットしてしまったファイルを
以前のリビジョンから戻す手順を示す。


1.SVNリポジトリエクスプローラにて下記のようにヒストリを表示する。






2.削除コミットをする直近で対象のファイルに変更をしたリビジョン番号をクリックする。
3.下の画面の一覧に削除したファイルが表示されるので右クリックし、「改定リンクの追加」
  をクリックする。



4.すると下記のように改定リンクが表示されるので、リファクタリング→コピーをクリック。


5.戻す先のディレクトリを選択し、次へを押す。


6.コミットする。




7.PHP(Java)エクスプローラーに戻って、「チーム」→「更新」をクリックすると、対象ファイルが
 表示される。

8.リポジトリエクスプローラーに戻り、改定リンクを削除する。




2012年10月15日月曜日

[Error記録]"Sorry, that page does not exist","code":34

Twitter OAuth認証時に新たなErrorが出た。

ブラウザ上ではこのようなError。
5~11行目を見るとわかるように、requestTokenとaccessTokenの取得には成功しているのに、
なぜか、11行目でTwitter側から情報が取得できず、 "Sorry, that page does not exist","code":34
 というメッセージが返ってきている。
ただ、この状態で、別画面に遷移してみると、認証されてユーザ情報が表示された。
ログイン時、ユーザ情報をTwitter側から更新する仕組みになっているが、
更新は行われず、取得したaccessTokenでログインのみした状態となったと思われる。


Notice (8): Undefined index: id_str [APP\controllers\users_controller.php, line 66]Code | Context            )));
        }
        $output = call_user_func_array(array(&$controller, $params['action']), $params['pass']);$consumer    =    OAuth_Consumer

$requestToken    =    OAuthToken
OAuthToken::$key = "XXXXXXXXXXXXXXXXXXXXX"
OAuthToken::$secret = "XXXXXXXXXXXXXXXXXXXXX"
$accessToken    =    OAuthToken
OAuthToken::$key = "XXXXXXXXXXXXXXXXXXXXX"
OAuthToken::$secret = "XXXXXXXXXXXXXXXXXXXXX"
$json    =    "{"errors":[{"message":"Sorry, that page does not exist","code":34}]}"
$twitterData    =    array(
    "errors" => array(
    array()
)
)UsersController::twtrCallback() - APP\controllers\users_controller.php, line 66
Dispatcher::_invoke() - CORE\cake\dispatcher.php, line 204
Dispatcher::dispatch() - CORE\cake\dispatcher.php, line 171
[main] - APP\webroot\index.php, line 86
Notice (8): Undefined index: screen_name [APP\controllers\users_controller.php, line 67]Code | Context            )));
        }
        $output = call_user_func_array(array(&$controller, $params['action']), $params['pass']);$consumer    =    OAuth_Consumer

$requestToken    =    OAuthToken
OAuthToken::$key = "XXXXXXXXXXXXXXXXXXXXX"
OAuthToken::$secret = "XXXXXXXXXXXXXXXXXXXXX"
$accessToken    =    OAuthToken
OAuthToken::$key = "XXXXXXXXXXXXXXXXXXXXX"
OAuthToken::$secret = "XXXXXXXXXXXXXXXXXXXXX"
$json    =    "{"errors":[{"message":"Sorry, that page does not exist","code":34}]}"
$twitterData    =    array(
    "errors" => array(
    array()
)
)UsersController::twtrCallback() - APP\controllers\users_controller.php, line 67
Dispatcher::_invoke() - CORE\cake\dispatcher.php, line 204
Dispatcher::dispatch() - CORE\cake\dispatcher.php, line 171
[main] - APP\webroot\index.php, line 86
Notice (8): Undefined index: profile_image_url [APP\controllers\users_controller.php, line 70]Code | Context            )));
        }
        $output = call_user_func_array(array(&$controller, $params['action']), $params['pass']);$consumer    =    OAuth_Consumer

$requestToken    =    OAuthToken
OAuthToken::$key = "XXXXXXXXXXXXXXXXXXXXX"
OAuthToken::$secret = "XXXXXXXXXXXXXXXXXXXXX"
$accessToken    =    OAuthToken
OAuthToken::$key = "XXXXXXXXXXXXXXXXXXXXX"
OAuthToken::$secret = "XXXXXXXXXXXXXXXXXXXXX"
$json    =    "{"errors":[{"message":"Sorry, that page does not exist","code":34}]}"
$twitterData    =    array(
    "errors" => array(
    array()
)
)UsersController::twtrCallback() - APP\controllers\users_controller.php, line 70
Dispatcher::_invoke() - CORE\cake\dispatcher.php, line 204
Dispatcher::dispatch() - CORE\cake\dispatcher.php, line 171
[main] - APP\webroot\index.php, line 86
Warning (512): SQL Error: 1048: Column 'user_id' cannot be null [CORE\cake\libs\model\datasources\dbo_source.php, line 684]Code | Context
        if ($this->error) {
            $this->showQuery($sql);$sql    =    "INSERT INTO `users` (`user_id`, `user_name`, `access_token_key`, `access_token_secret`, `img_url`, `updated`, `created`) VALUES (NULL, NULL, 'XXXXXXXXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXXXXXXXX', NULL, '2012-10-15 16:15:39', '2012-10-15 16:15:39')"
$error    =    "1048: Column 'user_id' cannot be null"
$out    =    nullDboSource::showQuery() - CORE\cake\libs\model\datasources\dbo_source.php, line 684
DboSource::execute() - CORE\cake\libs\model\datasources\dbo_source.php, line 266
DboSource::create() - CORE\cake\libs\model\datasources\dbo_source.php, line 750
Model::save() - CORE\cake\libs\model\model.php, line 1342
User::update() - APP\models\user.php, line 52
UsersController::twtrCallback() - APP\controllers\users_controller.php, line 72
Dispatcher::_invoke() - CORE\cake\dispatcher.php, line 204
Dispatcher::dispatch() - CORE\cake\dispatcher.php, line 171
[main] - APP\webroot\index.php, line 86
Query: INSERT INTO `users` (`user_id`, `user_name`, `access_token_key`, `access_token_secret`, `img_url`, `updated`, `created`) VALUES (NULL, NULL, 'XXXXXXXXXXXXXXXXXXXXX', 'XXXXXXXXXXXXXXXXXXXXX', NULL, '2012-10-15 16:15:39', '2012-10-15 16:15:39') 
Warning (2): Cannot modify header information - headers already sent by (output started at C:\xampp\htdocs\rakuzon\cake\libs\debugger.php:686) [CORE\cake\libs\controller\controller.php, line 742]Code | Context */
    function header($status) {
        header($status);$status    =    "Location: http://127.0.0.1/rakuzon/main/index"header - [internal], line ??
Controller::header() - CORE\cake\libs\controller\controller.php, line 742
Controller::redirect() - CORE\cake\libs\controller\controller.php, line 721
UsersController::twtrCallback() - APP\controllers\users_controller.php, line 82
Dispatcher::_invoke() - CORE\cake\dispatcher.php, line 204
Dispatcher::dispatch() - CORE\cake\dispatcher.php, line 171
[main] - APP\webroot\index.php, line 86 
https://dev.twitter.com/discussions/11595
ここに対処策が出ていた。 ユーザ情報を引き出す際のURLを以下のように変更したら正常に動作した。
http://twitter.com/account/verify_credentials.json
 ↓
http://api.twitter.com/1/account/verify_credentials.json

また、以下のページによると
https://dev.twitter.com/discussions/10803 
現在以下を使用できないように変更中のようだ。
  • All API endpoints on www.twitter.com and twitter.com.
  • Endpoints on api.twitter.com without /1, /1.1 or /oauth in the fully qualified URL.
私の場合、twitter.comでアクセスし、1や1.1等のバージョンも無かったのでだめだった。
ただ、ソースの他の部分でまだtwitter.comで動いているところもあるので、
バージョンが無いのがダメだったということだろう。
twitter.comのままの部分は直さないと
今後またおかしなエラーが発生してしまうかもしれない。

2012年10月10日水曜日

[CakePHP]あるControllerから別のモデルにアクセスする場合

アソシエイションを設定している場合は、例えば以下のような感じでアクセスできるみたい。
Theme hasAndBelongsToMany Tagの関係を持ち、
ThemesControllerから、Tagのメソッドを使用したい場合。
$this->Theme->Tag->newSave($this->data['Tag']);
特にTagモデルのロード等は行っていない。 これはすごく基本かもしれないけど。

※CakePHPのバージョンはcakephp-cakephp-1.3.15-9-gacd25c3.zip

[CakePHP]$js->submit()のoptionsのbeforeの限界と対策

以下のような形でbeforeオプションを使用し、
ajax送信直前に送ろうとしているformの値自体を操作しようと
したのだけど、うまく送れなかった。
おそらく、beforeが完了してから送信の処理が開始されるので
はなく、beforeと同時並行的に送信するデータ自体は取得して
いるからだろう。

echo $js->submit("登録する",array(
    'id'=>'register',
    'url'=>'add',
    'update'=>'#complete'
    'before'=>'
        var fm = document.getElementById("add");
        $("#add").append("<input type=\"hidden\" name=\"data[Theme][explanation]\"/>");
        fm.elements[\'data[Theme][explanation]\'].value=$("#explanation").val();
    '
));

では、どうすればよいか。
beforeで設定するのではなく、別で$js->submitより上に、
onclickのイベント設定をしてしまえばよい。
こうすれば、フォームの値の操作が完了してから、
送信の処理が開始されるようになる。

$js->get("#register")->event('click','
    var fm = document.getElementById("add");
    $("#add").append("<input type=\"hidden\" name=\"data[Theme][explanation]\"/>");
    fm.elements[\'data[Theme][explanation]\'].value=$("#explanation").val();
');

echo $js->submit("登録する",array(
    'id'=>'register',
    'url'=>'add',
    'update'=>'#complete'
)); 
※CakePHPのバージョンはcakephp-cakephp-1.3.15-9-gacd25c3.zip

[PHP]PHPでJavaScriptを書き出す際のクオテーションについて

PHPでjavascriptを吐き出す場合、シングルクオテーションで
囲っている例が多い。もしかしたらダブルでもよいのかもしれないが、
シングルで囲う場合を考える。

以下phpのスクリプトで2~6行目がJavaScriptである。

$js->get("#register")->event('click','
    var fm = document.getElementById("add");
    $("#tags span").each(function(i){
        $("#add").append("<input type=\"hidden\" name=\"data[Tag]["+i+"][content]\"/>" );
        fm.elements[\'data[Tag][\'+i+\'][content]\'].value=$(this).text();
     });
');

javascript全体がシングルでかこわれている。
この中でさらにクオテーションを使用したい場合、
以下のサイトから、

PHPで javascriptを書き出すときのクォーテーションの使い方

「PHPとしての最初のクォティションと最後のクォティションはそのままで、
内側の同種クォティションはエスケープが基本」

らしいです。

なので、私も

・ダブルはそのまま使う。
・シングルを使いたい場合はエスケープ。

ただし、

javascriptが吐き出す文字列にダブルクオテーションを
付与したい場合は、エスケープしないとだめなようだ。

[CakePHP]inputタグのnameをjQueryで指定できない。

CakePHPでajaxを使ってフォームの内容を送信する際に、
javascriptでinputタグのtype=hiddenのvalueを操作したい場合が
あったのですが、その際に対象のinputエレメントを指定するのに
苦労しました。

inputタグにidをつけてしまえば簡単なのですが、
nameというユニークの属性を持っておきながら、
さらにidを付与するのは少し不細工なので、なんとか
nameで指定するやり方を考えました。

jQueryだと通常、
$(*"input[name=XXX]")
といった形で、name属性で指定できる。

だが、CakePHPのhtmlヘルパーで作成されるinputタグは以下のように
"["と"]"を含むので指定することができない。
色々な方法でエスケープを試みたができなかった。
<input type="text” name=”data[Model][field]“/> 
なので、以下のサイトを参考にした。

cakePHPの Formヘルパーで作られるdata[Model][Field]をJavascript処理する
[CakePHP] 配列のname属性をJavaScriptで参照する方法
var fm = document.getElementById("add");
//中略
fm.elements[\'data[Item][\'+i+\'][code]\'].value=itemLst[i];
上記のような形でnameを変数で動的に変動させることもできるのだ。
上記の例では、formエレメントをidで取得してきているが、
nameをつけている場合には、以下のように書くことができる。
(formのnameが"add"である場合)
document.add.elements[\'data[Item][\'+i+\'][code]\'].value=itemLst[i]; 
※ここでのバックスラッシュはWindwosでいういわゆるYENマークね。
※CakePHPのバージョンはcakephp-cakephp-1.3.15-9-gacd25c3.zip

[CakePHP]HABTMでのデータ保存について(両モデル新規登録・複数行登録)

カレントモデルと同時にHABTM(hasAndBelongsToMany)の関係を持つ
モデルのデータを同時に保存したい場合、色々の問題があり、
それに対して検討した結果を紹介する。

以下で紹介したように、HABTMしているモデルのデータは
idの配列となっている必要がある。

[CakePHP]アソシエイションを持つモデルをsaveall()する際に受け付けるデータ構造

つまり、view側にて、idが取得できる状態が前提となっている
ようだ。なので、id以外の項目のみを送信しても、普通には保存できない。
ただ、一般にウェブサービスで使われている「タグ」などの機能は、
ユーザがその画面でタグ自体を作成することが想定される。
その場合、カレントモデルとHABTMの関係を持つモデルの両方を
新規登録する必要がある。

その場合、どのように実現するか。
いくつかの方法を検証してみた。

1.中間テーブルのsaveallメソッドを使用する。

 

3.7.6.6 hasMany through (The Join Model)

cookbookで紹介されているこのやり方では、
実際はHABTMを利用しているわけではなく、中間テーブルからの
belongsToを利用した保存方法だ。
この方法だと、以下のようにid以外の項目での保存が可能だ。
両方のモデルにidをつけてくれるし、中間テーブルにも
そのidでレコードを挿入してくれる。

※Theme habtm Tagという関係を想定

配列構造①

Array
(
    [Theme] => Array
        (
            [content] => test33です。
            [explanation] => test33です。
        )
    [Tag] => Array
        (
            [content] => 村田さん
        )
)

一つ注意点があり、Themeがカレントだと、なぜか、
ThemesTagからのbelongsToのアソシエイションをbindしなければ
ならない。ThemesTagモデルでアソシエイションを設定したとしても。
でないと、ThemesTagに空のレコードを挿入するのみとなってしまう。
以下のサイトを参考にした。

Habtm | habtm な関係にあるモデルで、両方とも未登録なデータを同時に保存したい

複数レコードの挿入はできるだろうか。
まず以下のような配列構造にしてみた。
結果Tagが保存されなかった。

配列構造②

Array
(
    [Theme] => Array
        (
            [content] => test33です。
            [explanation] => test33です。
        )
    [Tag] => Array
        (
             0=>array('content'=>'村田さん')
             1=>array('content'=>'児島さん'),
             2=>array('content'=>'本'),
        )
)

次に以下のような配列構造にしてみた。
こちらは、ThemesTagsに2行の空行(createdとupdatedは入った)だけ
が挿入されたのみだった。

配列構造③
Array
(
    [1] => Array
        (
            [Theme] => Array
                (
                    [content] => test34です。
                    [explanation] => test34です。
                )
            [Tag] => Array
                (
                    [content] => 村田さん
                )
        )
    [2] => Array
        (
            [Theme] => Array
                (
                    [content] => test34です。
                    [explanation] => test34です。
                )
            [Tag] => Array
                (
                    [content] => 児島さん
                )
        )
)

次に2回に分けてsaveallしてみたらどうかと考え以下のようにしてみた。

コード例①

        $data=array(
                'Theme'=>array(
                    'content'=>'test35です。',
                    'explanation'=>'test35です。'
                ),
                'Tag'=>array(
                    'content'=>'村田さん'
                )
         );
        $data2=array(
                'Theme'=>array(
                ),
                'Tag'=>array(
                    'content'=>'児島さん'
               )
         );
        $this->Theme->saveall($data);
        $data2['Theme']['id']=$this->Theme->id;
        $this->Theme->saveall($data2);

すると、見事に保存することができた。以下のようなSQLが発行された。

1    SHOW FULL COLUMNS FROM `themes`        6    6    4
2    SELECT CHARACTER_SET_NAME FROM INFORMATION_SCHEMA.COLLATIONS WHERE COLLATION_NAME= 'utf8_general_ci';        1    1    1
3    SHOW FULL COLUMNS FROM `users`        8    8    3
4    SHOW FULL COLUMNS FROM `items`        6    6    3
5    SHOW FULL COLUMNS FROM `tags`        4    4    3
6    SHOW FULL COLUMNS FROM `themes_tags`        5    5    3
7    START TRANSACTION        0        0
8    INSERT INTO `themes` (`content`, `explanation`, `modified`, `created`) VALUES ('test35です。', 'test35です。', '2012-10-10', '2012-10-10')        1        0
9    SELECT LAST_INSERT_ID() AS insertID        1    1    0
10    INSERT INTO `tags` (`content`, `modified`, `created`) VALUES ('村田さん', '2012-10-10', '2012-10-10')        1        0
11    SELECT LAST_INSERT_ID() AS insertID        1    1    0
12    INSERT INTO `themes_tags` (`theme_id`, `tag_id`, `modified`, `created`) VALUES (3879, 78, '2012-10-10', '2012-10-10')        1        0
13    SELECT LAST_INSERT_ID() AS insertID        1    1    0
14    COMMIT        0        0
15    START TRANSACTION        0        0
16    SELECT COUNT(*) AS `count` FROM `themes` AS `Theme` WHERE `Theme`.`id` = 3879         1    1    0
17    SELECT COUNT(*) AS `count` FROM `themes` AS `Theme` WHERE `Theme`.`id` = 3879         1    1    0
18    UPDATE `themes` SET `id` = 3879, `modified` = '2012-10-10' WHERE `themes`.`id` = 3879        0        0
19    INSERT INTO `tags` (`content`, `modified`, `created`) VALUES ('児島さん', '2012-10-10', '2012-10-10')        1        0
20    SELECT LAST_INSERT_ID() AS insertID        1    1    0
21    INSERT INTO `themes_tags` (`theme_id`, `tag_id`, `modified`, `created`) VALUES (3879, 79, '2012-10-10', '2012-10-10')        1        0
22    SELECT LAST_INSERT_ID() AS insertID        1    1    0
23    COMMIT

for文で回せばいくつでも保存できそうだ。
ただし、これは、トランザクションが二つにわかれてしまっているが。

ただ、この方法のデメリットとしては、同じ内容のTagを作ってしまう
可能性があるので、それを回避する処理を入れる必要があるという点。

また、以下で議論されているよう連続した保存処理の場合、
Model::create()を毎回実行しないと最後の行しか反映されない、
と思っていたが、このやり方だと、create()しなくてもきちんと保存されていた。
中間テーブルのsaveallを使用しているからだろうか。

CakePHPのsaveメソッドでINSERTするつもりがUPDATEになってしまう場合
連続したsaveメソッドの使い方について

2.habtamable.phpを使用する。


こちらは有志の方が作ったbehaviorだ。
以下でダウンロード可能。

https://github.com/teknoid/cakephp-habtamable-behavior/blob/master/models/behaviors/habtamable.php

使い方はダウロードしたフォルダに英語のreadmeが入っている。

こちらは配列構造①の形で保存可能。

また、その場合、「村田さん」というタグがすでに
存在する場合は、Tagsテーブルに新規レコードを追加せず、
既存のレコードのidを取得し、中間テーブルに挿入してくれる。
また、メリットとして、Themeのsaveall()を使うため、
以下のように、Themeがもつほかのアソシエイションモデルのデータも
同時に保存することができる。

こちらは
Theme hasMany Item
Theme hasAndBelongsToMany Tag
というアソシエイションを持つ場合。

配列構造④

Array
(
    [Theme] => Array
        (
            [content] => test33です。
            [explanation] => test33です。
        )
    [Item] => Array
        (
            [0] => Array
                (
                    [code] => B0017LURGI
                    [point] => 10
                )
            [1] => Array
                (
                    [code] => 4774135038
                    [point] => 9
                )
    [Tag] => Array
        (
            [content] => 村田さん
        )
)

では、複数レコードの保存についてはどうだろうか。
配列構造②のようにしたところ、Tagの保存ができなかった。
配列構造③のようにしたところ、一つ目のデータはきちんと
保存できたのだが、二つ目のデータは空行などのおかしなデータ
しか入らなかった。

コード例①のようにしたところ、ThemesとTagsに対するレコード挿入は
適切に行われたのだが、ThemesTagの2行目を挿入する直前に1行目を
削除してしまうという現象が起きた。

こちらは2度目のsaveall直前で以下のそれぞれを試してもダメだった。

①$this->Theme->create();
②$this->Theme->create(false);
③$this->Theme->create(null);
④$this->Theme->id=null;
⑤$this->Theme->ThemesTag->create();
⑥$this->Theme->ThemesTag->create(false);
⑦$this->Theme->ThemesTag->create(null);
⑧$this->Theme->ThemesTag->id=null;
⑨$this->Theme->ThemesTag->Theme_id=null;


【まとめ】
1.中間テーブルのsaveallメソッドを使用する。
 メリット:
  ・id以外のデータで登録可能
  ・両モデルの新規登録可能
  ・saveallを複数回に分けることにより複数行の挿入が可能。

 デメリット:
  ・複数行挿入する場合、トランザクションが行数分となる。
  ・同じデータがあっても新しい行を挿入してしまう。(回避策をたてる必要あり)

2.habtamable.phpを使用する。
 メリット:
  ・id以外のデータで登録可能
  ・両モデルの新規登録可能
  ・同じデータがある場合は、既存のレコードのIDを使用してくれる。
  ・他の他のアソシエイションを持つモデルのデータも同時に保存できる。

 デメリット:
  ・複数行の登録ができない。


【結局どうしたか】
上記で紹介した二つとも、どっちもどっち、と思ったので、
通常のhabtmの保存方法、idでの保存で、以下を実現する方法を考える
ことにした。
  ・両モデルの新規登録可能
  ・同じデータがある場合は、既存のレコードのIDを使用してくれる。

やり方としては、(Tagが対象モデルと想定)
①viewからはid以外の情報が送られるようにする。
 (新規も既存も混在)

②それをcontroller側で、idの配列に変換する。
 (既存に同じデータがあるものは既存のid、無いものは
  そこでTagsテーブルにsaveし、idを取得する。)

③それをsaveallに渡すデータ配列に組み込む。
 (最終的に以下のような配列構造となるようにする。)

Array
(
    [Theme] => Array
        (
            [content] => 本
            [explanation] => 楽しい本
            [user_id] => 4
        )
    [Tag] => Array
        (
            [0] => 2
            [1] => 68
        )
)

このやり方だと、もし、新規のTagが発生しない場合は、
トランザクションは1回で済むというメリットがある。
新規のTagがある場合は、その数+1回のトランザクションとなる。
「1.中間テーブルのsaveallメソッドを使用する。」のやり方の
場合は、新規があろうとなかろうと、Tag数分のトランザクションが
発生してしまうし、他のアソシエイションのデータについても別トランザクション
で保存する必要があるので、こちらのほうが良いと考えた。

また、CakePHPで通常想定されるのhabtmの保存方法に近いほう
がよいかとも思ったので。

※CakePHPのバージョンはcakephp-cakephp-1.3.15-9-gacd25c3.zip


【追加 2012/11/17】
この記事で何度かトランザクションが分かれてしまうことを問題にして
いるが、下記の記事にあるようbegin()とcommit()で解決できそうだ。
つまり、一つのトランザクションにできそう。
ただ、下記のブログで工夫している、複数のモデルを使うとわかりにくくなる
ことへの対応まではしていない。

CakePHPで複数テーブルに対するトランザクションを使う場合

[CakePHP]saveall()は一段階のアソシエイションモデルしか保存できない。(v1.3)

saveall()にてアソシエイションの関係を持つモデルの情報も
同時に保存できるが、アソシエイションを持つモデルがアソシエイションを
持つモデルは保存できない。

以下のサイトでわかった。
saveAll には4階層までの制限がある?

またどこかの英語のサイトでversion2.0か2.1では、saveall()でも
recursionが有効で、何段階先のアソシエイションでも保存できる
と書いてあった。

※CakePHPのバージョンはcakephp-cakephp-1.3.15-9-gacd25c3.zip

[CakePHP]アソシエイションを持つモデルをsaveall()する際に受け付けるデータ構造

あるモデルのデータを保存する際、Model::saveall()を使用すれば、
アソシエイションを持つモデルのデータも同時に保存できるのは大変便利だ。

ただし、saveall()の引数に渡すデータの配列構造に決まりがあるので、
それをおさえなくてはならない。

view側のformから送信されたデータはcontroller側では、$this->dataで
取得することができる。
大抵これをそのままsave()やsaveall()に
$this->Model->saveall($this->data);
といった形で渡し保存することができる。

ただ、view側のフォームで適切にデータを構成した場合は良いが、
色々な事情で適切な形でデータを送れない場合がある。
そういった場合は、controller側でデータを構成しなおしてあげて
saveall()に渡してあげればよい。
逆に言えば、その構成にさえしてあげれば、view側からどのようにデータを
送信されても保存ができるということだ。

その「適切なデータ構成」をわかっている範囲で紹介する。
以下はThemeControllerから、$this->Theme->saveall($data);する際の
$dataの配列構造を想定している。

・hasMany


以下はTheme hasMany Itemという関係を持つ場合である。
Itemが複数となっている。

Array
(
    [Theme] => Array
        (
            [content] => 本
            [explanation] => 楽しい本
            [user_id] => 4
        )

    [Item] => Array
        (
            [0] => Array
                (
                    [code] => B0017LURGI
                    [point] => 10
                )

            [1] => Array
                (
                    [code] => 4774135038
                    [point] => 9
                )
         )
) 


これを保存すれば、自動的にItemモデルのレコードに
Themeのidを補填してくれる。

・HABTM(hasAndBelongsToMany)


Theme HABTM Tagの関係である。

Array
(
    [Theme] => Array
        (
            [content] => 本
            [explanation] => 楽しい本
            [user_id] => 4
        )

    [Tag] => Array
        (
            [0] => 2
            [1] => 68
        )

)


Tagのほうは、プライマリーキーのidの配列である。(これが超重要)
※連想配列ではなく、ただの配列。
このデータをsaveall()で保存すれば、自動的にThemesTagという
中間テーブルにも両モデルのidを組み合わせたレコードを挿入してくれる。

・hasManyとHABTMの両方を持つ場合。


Theme hasMany Item
Theme HABTM Tag
という関係を持つ場合は、上記で紹介した配列を合体させれば
よいだけとなる。

Array
(
    [Theme] => Array
        (
            [content] => 本
            [explanation] => 楽しい本
            [user_id] => 4
        )

    [Item] => Array
        (
            [0] => Array
                (
                    [code] => B0017LURGI
                    [point] => 10
                )

            [1] => Array
                (
                    [code] => 4774135038
                    [point] => 9
                )

        )

    [Tag] => Array
        (
            [0] => 2
            [1] => 68
        )
)



【課題】
上記のHABTMのモデルのデータ構成は
data[Tag]=array(2,68)
といった形でデータが入っているが、以下のサイトだと
data[Tag][Tag]=array(2,68)
といった形にしないといけないように読める。
どっちでもいけるってことなのかな。
暇な時に試します。

cakePHPの$model->saveにおけるUpdateとInsert

※CakePHPのバージョンはcakephp-cakephp-1.3.15-9-gacd25c3.zip

2012年10月5日金曜日

[CakePHP]jsヘルパーのサブミットを使用するならformタグでaction指定する必要ない。

下記のようにsubmitボタンをjsヘルパーで作成していてurlも指定している場合は、 4行目のactionの指定をしなくても動いた。
    <?php
        echo $form->create(false,array(
            'type'=>'post'
/*            'action'=>'mkHtml'*/
        ));
        echo "商品URL:".$form->input('goodsUrl');
        echo $js->submit("ランキングに入れる",array(
            'url'=>'mkHtml',
            'complete'=>'$("#data").append(XMLHttpRequest.responseText);'
        ));
        echo $form->end();
    ?> 
※CakePHPのバージョンはcakephp-cakephp-1.3.15-9-gacd25c3.zip

[PHP]配列の要素数の取得方法

javascriptみたいに「配列名.length」じゃだめで、
「count($配列名)」とのこと。

下記で勉強。

[PHP] 配列変数の長さを取得する

2012年10月2日火曜日

subversiveで管理したくないディレクトリとファイルを除外する。


EclipseのSVNを使用するためのプラグインであるsubversiveであるが、
log出力先フォルダなどのアプリケーションが生成するリソースは
基本的にはSVN管理をしたくない。
そうでないと、logが出力されただけで、プロジェクト全体がmodifiedの
ステータスになってしまうと、紛らわしい。

そこで、特定のディレクトリやファイルをsvn管理から除外した。

【手順】

1.対象のフォルダを右クリック→「チーム」→「プロパティの設定」を選択。
  以下の画像のように設定。


2.対象のフォルダをコミット


これでlogが出力されてもmodifidにならなくなった。

少し気になるのは以下のサイトでは、一度コミットされているリソースは
一度deleteしてaddしなおさなきゃ有効にならない、って言ってるんだよな。
なんでだろ。できちゃった。

Eclipse Subversive 特定のファイルをignoreする †
svn:ignoreでtmpフォルダ以下を無視するやり方






2012年10月1日月曜日

[CakePHP]XAMPP+eclipse+CakePHPでxDebugを使用する。

このページを見ていけました。
「デバッグのお気に入りの編成を選択して今作ったデバッグの構成を追加」
っていう一文が非常に大事でした。

CakePHP1.3/ 開発環境構築(Windows編)


※それぞれのバージョン
   CakePHP:cakephp-cakephp-1.3.15-9-gacd25c3.zip
  eclipse:pleiades-e3.5-php-jre_20100226 
   PHP:Version 5.3.1