Qiitaに投稿していました:美術が「2」だったEngineerでも作れるCSS アニメーション

qiita.com

最近は、こんな感じのCSSで遊ぶお仕事をしている。

front-endの開発がどんどん面白い方向へと進んでいるので、今後は現場で培った(あるいは、ハマった)ことをアウトプットしていきたい。

Kindle Fire HD(Ver. 7.4.6)をRoot化

たぶん成功した。
Windowsでやると楽っぽいけど、Windows 8.1に上げている最中だったので開発機(Ubuntu 13.04)で実行。

自分のメモのために書くので、実際にこれを見てやる人は他の記事とかも調べつつ、自己責任でお願いします。

(1) 必要なものをインストールしまくる

JDK

OpenJDK 7で。

sudo apt-get install openjdk-7-jdk
Android SDK

http://developer.android.com/sdk/index.html
↑ここからダウンロードして、どこかのフォルダへ解凍。
とりあえず、僕は
/opt/adt-bundle-linux-x86_64-20130917
に置いた。
んで、/opt/adt-bundle にシンボリックリンクを貼った。

・・・だが・・・
実は、aptでインストールできたんじゃなかろうか・・・

apt-cache search android-tools
android-tools-fsutils - Android ext4 utilities with sparse support
android-tools-adb - Android Debug Bridge CLI tool
android-tools-fastboot - Android Fastboot protocol CLI tool

まいっか。
必要ならアップデートしたほうがいいのかな?
開発しなければいらないかもしれないが。

sdkのディレクトリまでいき

tools/android update sdk --no-ui

で、とりあえず何でもかんでも持ってくる。

Root_with_Restore_by_Bin4ry_v31

わかりにくいけど
http://forum.xda-developers.com/showthread.php?t=1886460
から。

こいつは、実際に実行するわけではない(batファイルだし・・・)ので、ダウンロード・解凍だけ。

僕は
/opt/adt-bundle-linux-x86_64-20130917/sdk/platform-tools/Bin4ryv31
に解凍した。

解凍したら、RunMe.batをテキストエディタで開いておく

(2) KindleとPCをつなぐ

USBでつなぐ
あ、ADB許可することを忘れずに。
セキュリティ→ADBを有効にする

(3) RunMe.batを見ながらコマンドを叩きまくる!

ここから先はターミナルを開いて、sdkのplatform-toolsディレクトリまで移動してから実行していく

http://www.zero-gadget.info/2013/10/Kindle-Fire-HD-Root-PlayStore.html
を見ると1番を選ぶようなので、それに沿って叩く。

if %type% == 1 GOTO NEWSTANDARD

と書いてあるので、:NEWSTANDARDのところへいく

echo Please connect Device with enabled USB-Debugging to your Computer
stuff\adb.exe wait-for-device

と書いてあるので、とりあえずwait-for-deviceを実行。

./adb wait-for-device

まあ、すぐに戻ってくるよね。

echo Doing a Backup first, please confirm this on your device!
stuff\adb.exe backup -f stuff\mysettings.ab com.android.settings

と書いてあるので、backup実行。

./adb backup -f Bin4ryv31/stuff/mysettings.ab com.android.settings

echo Please select the RESTORE MY DATA option now on your device!
stuff\adb.exe restore stuff\special.ab
echo Please press any Key when restore is done.

と書いてあるので、restoreを実行

./adb restore Bin4ryv31/stuff/special.ab 

Kindleに「データ復元する?」って出てるので「データ復元」。すぐに終わった。

echo Going to reboot now ...
stuff\adb.exe reboot

と書いてあるので、rebootする

./adb reboot

再起動が終わるまでしばらく待つ。

stuff\adb.exe wait-for-device
stuff\adb.exe shell "mount -o remount,rw /system"
stuff\adb.exe push stuff\su /data/local/tmp/su
stuff\adb.exe push stuff\Superuser.apk /data/local/tmp/Superuser.apk
stuff\adb.exe push stuff\busybox /data/local/tmp/busybox
stuff\adb.exe shell "chmod 06755 /data/local/tmp/busybox"
stuff\adb.exe pull /system/bin/ric . > NUL

と書いてあるので、順に実行していく。

./adb shell "mount -o remount,rw /system"
./adb push Bin4ryv31/stuff/su /data/local/tmp/su
./adb push Bin4ryv31/stuff/Superuser.apk /data/local/tmp/Superuser.apk
./adb push Bin4ryv31/stuff/busybox /data/local/tmp/busybox
./adb shell "chmod 06755 /data/local/tmp/busybox"
./adb pull /system/bin/ric . > /dev/null

なんと!「/system/bin/ricなんてねーよって」言われるが、慌てずに先を読むと

if EXIST ric (set ric=1) else (echo .)
set foo=1
GOTO NORMAL

と書いてあるので、それぞれの変数を覚えておき、:NORMALへいく。

IF %ric% == 1 GOTO RICSTUFF

と書いてあるが、ricはなかったので次の行へ。

echo Going to copy files to it's place
stuff\adb.exe shell "/data/local/tmp/busybox mount -o remount,rw /system && /data/local/tmp/busybox mv /data/local/tmp/su /system/xbin/su && /data/local/tmp/busybox mv /data/local/tmp/Superuser.apk /system/app/Superuser.apk && /data/local/tmp/busybox cp /data/local/tmp/busybox /system/xbin/busybox && chown 0.0 /system/xbin/su && chmod 06755 /system/xbin/su && chmod 655 /system/app/Superuser.apk && chmod 755 /system/xbin/busybox && rm /data/local.prop && reboot"

と書いてあるので、実行。

./adb shell "/data/local/tmp/busybox mount -o remount,rw /system && /data/local/tmp/busybox mv /data/local/tmp/su /system/xbin/su && /data/local/tmp/busybox mv /data/local/tmp/Superuser.apk /system/app/Superuser.apk && /data/local/tmp/busybox cp /data/local/tmp/busybox /system/xbin/busybox && chown 0.0 /system/xbin/su && chmod 06755 /system/xbin/su && chmod 655 /system/app/Superuser.apk && chmod 755 /system/xbin/busybox && rm /data/local.prop && reboot"

再起動がかかるので待つ。

IF %foo% == 1 GOTO REENTER

と書かれているので、:REENTERへいく

stuff\adb.exe wait-for-device
echo Restoring previous Backup! Please select the RESTORE MY DATA option now on your device!
stuff\adb.exe restore stuff\mysettings.ab

と書かれているので、wait-for-deviceしたあとrestore。

./adb wait-for-device
./adb restore Bin4ryv31/stuff/mysettings.ab 

Kindleに「データ復元する?」って出てるので「データ復元」。すぐに終わった。

echo Please press any Key when restore is done.
pause
echo Going to reboot last time now ...
stuff\adb.exe reboot

と書かれているので、rebootする。


たぶん、これでできてるはず・・・

追記・・・

http://www.zero-gadget.info/2013/10/Kindle-Fire-HD-Root-PlayStore.html
のとおりにアプリを入れて、
Google playも入りました!

続・iPhoneで見ると、<symbol>で作った絵が見えない?

d:id:myamyugon:20130819:1376838056

で「もっと調べる」と書いた件。
わかった。インラインSVGで書いていたのが原因。
インラインSVGだとけっこうブラウザごとに対応の差がありそうだ。

<!doctype html>
<html lang="ja">
  <head>

    <svg
      xmlns="http://www.w3.org/2000/svg"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      version="1.1"
      viewBox="0 0 480 320"
    >
      <defs>
        <symbol id="hoge">

        </symbol>
      </defs>
      <use xlink:href="#hoge" x="100" y="100" />
    </svg>

  </body>
</html>

この場合、iOS safariではsymbol→useした絵が表示されない。
SVGを別ファイルにして、img要素で参照した場合は表示された。

しかし!!!

img要素で参照した場合、svgファイル内でimageで参照している画像が表示されない!><
まじかーーーー!!!

結局svgを使う場合、クロスブラウザをある程度考えるのであれば、object要素で読み込むのが今のところ一番確実かも。

<!doctype html>
<html lang="ja">
  <head>

    <object data="hoge.svg" type="image/svg+xml" 
      width="640" height="427"></object>

  </body>
</html>

iPhoneで見ると、<symbol>で作った絵が見えない?

最近ハマったSVGの話。
defsの中にsymbol書いて、その中に図形を描いて、それをuseで表示しようと思ったら、iPhoneでは見えなかった。
↓実際はちょっと違うけど、こんなの。

<defs>
  <symbol id="hoge">
    <circle cx="0" cy="0" id="point" r="5" fill="blue" />
  </symbol>
</defs>
<use xlink:href="#hoge" x="10" y="10" />

iPhone safariのバグなのだろうか・・・?
書き方が悪いのだろうか・・・?

※ と、書き始めて「まだ試せることあるんじゃね?」と思ったのえ、いろいろ試してから続報を書きます。
#こういうの書いておかないと、調べるの忘れてまたハマりそうな気がするので(笑)

続報:GoogleBotがどんなリンクを辿るのか調べてみた

前回の投稿「GoogleBotがどんなリンクを辿るのか調べてみた - やわらかたまご - molaovo -」から少し時間が経過して・・・

GoogleBotはJavaScriptを少しは動かしているかもしれない。

そんな驚きの結果が出てしまったので、続報を書く。


前回の記事でアクセスされていなかった、/区切りの配列にしてjoinするようないじわるonclickの別ドメイン版↓

<a onclick="window.open(['http:','','hoge.example.com','foo','bar','test.html'].join('/'), '_blank');return false;" href="javascript:void(0)">別ドメインのリンク</a>

こいつに対して、↓この子の足あとがあった。
apacheのaccess_logより抽出。まだインデックスはされてなかった。。。)

"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"

・・・まじで!?
頭いいな、こいつ。

てことは、高負荷とかそういうのを気にしてクローラーを避けるためにJSで画面遷移させるようなページは、あまり意味が無いのかもしれないなぁ。
ちゃんとリンクにrel="nofollow"つけろってことか。
あるいは、何度も来ないようにHTML側にもnofollowつけたり、robot.txtに書いたり。

当たり前の話だけど、来て欲しいページと来てほしくないページ、それぞれしっかり対策しないとダメだな。

JavaScriptを生み出す言語がたくさんあってよくわからん

なんか、ちょろっと調べただけでもけっこうあってビビった。

  • JSXDeNAが開発
  • Haxe:JSだけでなく、ActionScriptPHPC++なんかにも変換可能。速度は、オプション次第ではJSXを超えるらしい。すげぇ。
  • CoffeeScript:タブでインデント。書きやすいけど、今後どうなるかが心配との声も。
  • TypeScript:MicroSoftが開発。WebMatrixで使えるはずなんだけど、なぜか僕の環境で拡張機能がうまく動かない。
  • DartGoogleが開発。
  • Kotlin:どちらかというと、Javaを書きやすくする目的の言語。JSにもなるっぽい。
  • ClojureScriptClojureで書いたものをJSへ。Clojureがハードル高いよ。。。(´・ω・`)
  • FayHaskellをJSへ。

個人的にはCoffeeScriptの書き方が好きなんだけど、単純にJSに書き換えるだけで、速度が上がるとかコード量が減るとかではない。
実行速度を求めると、JSX、Haxeあたりがいいっぽい。(検証はしてないけど。)

ClojureScriptとかFayは完全に趣味の世界な気がする。
動くけどJSのコード量はけっこう多そうだし、速度もどうなんだろ??


なんか、「あった!」ってのを調べただけでお腹いっぱいになりそうだけど、
それぞれで同じ処理を書いてみて、後で感想をまとめてみようかな。
開発環境、書き方、jQuery等との連携、自作のJSライブラリとの連携
あたりの視点で。

何がいいんだろうなぁ・・・。

GoogleBotがどんなリンクを辿るのか調べてみた

仕事で検索エンジンのクローラーの挙動について話題になったことがあったので、興味本位に調べてみた。

実験内容は、

「どんな書き方をすると、検索エンジンのクローラーはリンクを辿って次のページをGETするのか?」

やり方は簡単で、いろんなパターンの書き方をしたaタグを並べておいて、GoogleBotにそのページだけ食べさせる。
access_logやインデックスの結果から、どこが辿られたのかを調べる。
# 変なもの食べさせてごめんなさい。消化不良起こさないでね。。。

結果は・・・

<a href="url">ほげほげ</a>

辿られる。これは当然。

<a href="url" rel="nofollow">ほげほげ</a>

辿られない。nofollowは見てくれるんだね。

<a href="javascript:window.open('url', '_blank');void(0);">ほげほげ</a>

辿られる。・・・え!?

<a href="javascript:void(0)" onclick="window.open('url', '_blank');return false;">ほげほげ</a>

辿られる。・・・まじで!?


というわけで、aタグにjavascriptのコードを書いてもGoogleBotはとりあえず読みにくる。
ただ、

  • hoge.html(ファイル名だけ) → 辿られない
  • /foo/hoge.html(/から書いてた場合) → 辿られる
  • http://~/foo/hoge.html(URLをフルで書く) → 辿られる
  • rel="nofollow"をつける → 辿られない

となったので、/が入ってるかどうかでURLかも?判断するみたい。
window.openじゃなく、location.href=でやっても同様の結果だったよ。

あと、こんな意地悪もしてみたけど

<a href="javascript:void(0)" onclick="window.open(['','foo','hoge.html'].join('/'),'_blank');return false;">ほげほげ</a>

さすがにこれは辿らないみたい。
URLをhttp:から書いても辿られなかった。

てことは、JSを動かすわけではないんだね。
hrefだけでなく、onclickも見るって感じ。
検索結果に画面をプレビューする機能ではJSを動かした結果を出していたので、そっちのBotはJSがきいてるみたい。
通常の検索用Botは動かしはしないね。


・・・あれ?じゃあ、さ。
↓JSでこんな関数を書いといてさ

function openHoge(path)
{
  var url = '/foo' + path;
  window.open(url, '_blank');
}

↓こういうHTMLにしたらさ

<a href="javascript:void(0)" onclick="openHoge('/bar/hoge.html');return false;">ほげほげ</a>

もしかして、 /bar/hoge.html を辿りに来るのか!?

・・・ありえる・・・これはよくないなー。。。
もうちょっと実験してみようかな。

jQueryでAjaxでファイルアップロード

昔、諦めたんだけど、実はできたんだね。FormDataを使えばいいらしい。
※ 使用したjQueryは1.8.3。


ひとまず、こんなformを書いて

<form id="hogeForm" method="post" action="/hoge/upload" enctype="multipart/form-data">
  <div><input type="file" name="hogeFile"></div>
  <div>ファイルの説明:<input type="text" name="hogeText"></div>
  <input id="hogeSubmit" type="submit" value="うpする">
</form>

js側はこんな感じで

$(function(){
  $("#hogeForm").submit(function(){
    var $form, fd;
    $form = $(this);
    fd = new FormData($form[0]);
    $.ajax($form.attr("action"), {
      type: 'post',
      processData: false,
      contentType: false,
      data: fd,
      dataType: 'html',
      success: function(data){
        console.log(data);
      }
    });
    return false;
  });
});

以上おしまい。
サーバー側はよしなに作ればいい。

processData: falseとcontentType: falseが鍵らしい。

CakePHP hasAndBelongsToMany(HABTM)で保存できた!

前に書いた
CakePHP hasAndBelongsToMany(HABTM)に超苦戦中!!!>< - やわらかたまご - molaovo -
の続編。

なんとか保存までできた。
CakePHPに苦戦して泣いている僕に救いの手を差し伸べてくれた@kanonjiさん、本当に感謝です!助かりました。

保存できない原因は?

なんと!HABTMで接続しているPKがchar(6)だったから。
どうやら、PKは数値型(Auto Increment)かcharならば36bytes(値はCakePHPが自動的にUUIDをセットする)でなければいけないようで。
CakePHPの規約 — CakePHP Cookbook v2.x documentation
それを読み飛ばしてフレームワークを使っていたのが悪かった。
困ったら、ちゃんとドキュメントを見よう。

経緯

Blog記事を書く

Twitterで見つけてもらう

サンプルと動作結果を教えてもらう



動作結果とサンプルとを見ながら自分のプロジェクトと見比べて、カラム名変えたりしていろいろ試すがうまくいかない

あれ?そういえばidとか数字だよなぁ・・・となんとなく思って検索してみる

「HABTMアソシエーションはchar(2)なんてサポートしてねーよ」って答えてる人を見つける。


TagテーブルのPKをid int auto incrementにする

CakePHPのDB設計に関する注意

そんなわけで、今回のことを踏まえて注意点をメモっておくと。

テーブルには必ず1カラムのPKを数値型かchar(36)かで付ける

数値型の場合はauto incrementにしておく。たとえばそれが紐付けテーブルで、IDなんていらないとしても・・・だ。

CakePHP モデルの相互に作用する全てのテーブル(join テーブルは除く)は、それぞれの列を一意に識別する単一フィールドのプライマリーキーが必要です。 単一のプライマリーキーを持たないテーブルをモデリングする場合、テーブルに単一フィールドのプライマリーキーを追加することが CakePHP の規約です。 このようなテーブルのモデルを使いたい場合は、単一フィールドのプライマリーキーを追加する必要があります。

テーブル・カラムの命名規則をCakePHPに合わせる

別に合わせなくてもなんとかなるところはあるものの、合わせておくとコードを書かなくて済む。

  • テーブル名は複数形
    • 単語区切りは_で。最後の単語が複数形になる感じで。
  • Modelは単数形
    • 単語区切りはキャメル記法で。
  • 接続用のテーブル名はアルファベット順の複数形_複数形
  • 接続用のカラム名は接続するテーブル名の単数形_id

参考:CakePHPの規約 — CakePHP Cookbook v2.x documentation

複雑にしない

CakePHPに限った話ではないけれど、できる限りシンプルにする。

でもさー・・・

今回、僕が手を出しているプロジェクトは設計から新規だから、テーブルの作り直しは問題ないのだけれど、
例えば、既存のシステムをCakePHPに置き換える場合に、これはけっこう大変なんじゃないかと。
まあ、ほとんどのところでSQLを書いてしまえば問題ないけれども、ならばCakePHP使わなくてもいいんじゃないか?とも思えてしまう。

CakePHP hasAndBelongsToMany(HABTM)に超苦戦中!!!><

色々調べながらやっているのだけれど、どうもうまくいかない。
表示はさくっといくのだけれど、保存ができない。

解決はしていないが、苦悩の様子を書いておこうと思う。

【参考にしたサイト】

Model定義してsaveとかsaveAllとかすればいいだけとか、定義に注意すればいけるとか、そういうのはよく見るんだけどなぁ。

【環境】

まず、やってみる

HABTMを確認するための最低限の定義。こんなテーブルを作る。
f:id:myamyugon:20121204225036p:plain

SET SESSION FOREIGN_KEY_CHECKS=0;

/* Drop Tables */

DROP TABLE HOGES_TAGS;
DROP TABLE HOGE;
DROP TABLE TAG;

/* Create Tables */

CREATE TABLE HOGE
(
	ID INT NOT NULL AUTO_INCREMENT,
	TITLE VARCHAR(40) NOT NULL,
	CONTENTS TEXT,
	PRIMARY KEY (ID)
);


CREATE TABLE HOGES_TAGS
(
	HOGE_ID INT NOT NULL,
	TAG_ID CHAR(6) NOT NULL
);


CREATE TABLE TAG
(
	TAG_ID CHAR(6) NOT NULL,
	TAG_TEXT VARCHAR(20) NOT NULL,
	PRIMARY KEY (TAG_ID)
);

/* Create Foreign Keys */

ALTER TABLE HOGES_TAGS
	ADD FOREIGN KEY (HOGE_ID)
	REFERENCES HOGE (ID)
	ON UPDATE RESTRICT
	ON DELETE RESTRICT
;

ALTER TABLE HOGES_TAGS
	ADD FOREIGN KEY (TAG_ID)
	REFERENCES TAG (TAG_ID)
	ON UPDATE RESTRICT
	ON DELETE RESTRICT
;

タグデータを入れておく。

INSERT INTO tag (`TAG_ID`, `TAG_TEXT`) VALUES ('TAG001', 'たぐたぐ1'), ('TAG002', 'たぐたぐ2'), ('TAG003', 'たぐたぐ3');

テーブルに対するModelを作る。
入力チェックは何も入れない。最低限の定義で。

Hoge.php

<?php 
App::uses('AppModel', 'Model');

class Hoge extends AppModel
{
	public $name = 'Hoge';
	public $useTable = "HOGE";
	public $primaryKey = "ID";
	
	public $hasAndBelongsToMany = array(
		'Tag' => array(
			'className' => 'Tag',
			'joinTable' => 'HOGES_TAGS',
			'with' => 'HogesTags',
			'foreignKey' => 'HOGE_ID',
			'associationForeignKey' => 'TAG_ID',
			'unique' => true,
		),
	);
}

Tag.php

<?php
App::uses('AppModel', 'Model');

class Tag extends AppModel
{
	public $name = 'Tag';
	public $useTable = "TAG";
	public $primaryKey = "TAG_ID";
	public $displayField = 'TAG_TEXT';
}

HogesTags.php

<?php 
App::uses('AppModel', 'Model');

class HogesTags extends AppModel
{
	public $name = "HogesTags";
	public $useTable = "HOGES_TAGS";
	

	public $belongTo = array(
		'Hoge' => array(
			'className' => 'Hoge',
			'foreignKey' => 'ID',
		),
		'Tag' => array(
			'className' => 'Tag',
			'foreignKey' => 'TAG_ID',
			'fields' => array('TAG_ID', 'TAG_TEXT'),
		),
	);
}

#HogesTagsのbelogToや、HogeのHABTMのwithなんかは入れても入れなくても挙動は変わらなかったなぁ・・・

Hogeを編集するControllerも作る。
$scaffoldするだけにしてみる。

HogesController.php

<?php
class HogesController extends AppController 
{
	public $name = 'Hoges';
	public $scaffold;
}

そして、画面を表示。
http://localhost/hoges/

表示できたら、New HogeからHoge追加。
f:id:myamyugon:20121204233859p:plain

ちゃんと追加できてる。
f:id:myamyugon:20121204234034p:plain

Editで編集画面を開いてみると、選択されていない。なんで!?
f:id:myamyugon:20121204234230p:plain

hoges_tagsテーブルに直接データを突っ込んで編集画面を開くと、選択された状態になる。

INSERT INTO hoges_tags (`HOGE_ID`, `TAG_ID`) VALUES ('1', 'TAG001'), ('1', 'TAG003');

f:id:myamyugon:20121204234614p:plain

編集画面でこのまま保存すると、消える。
(´・ω・`)

うーむ・・・どういうことだ。
ちなみに、$scaffoldを使わずにbakeで生み出したControllerでも同様。saveとかsaveAllとか試したけど、ダメ。。。

Testで保存のテスト

ここでちょっとテストに色々書いて試してみる。
bakeでHogeのTestを作って、そこにtestHoge1メソッドを追加。

debug("ほげほげ");
$id = '1';
$this->Hoge->id = $id;
$this->assertNotEqual($this->Hoge->exists(), false, "ID=".$id."が存在しない");
debug($this->Hoge->read(null, $id));

http://localhost/test.php にアクセスしてテストを実行、デバッグログでHogeの中身を見ると

array(
	'Hoge' => array(
		'ID' => '1',
		'TITLE' => 'ほげタイトル',
		'CONTENTS' => 'ほげのコンテンツ。
ほげほげほげ。
'
	),
	'Tag' => array(
		(int) 0 => array(
			'TAG_ID' => 'TAG001',
			'TAG_TEXT' => 'たぐたぐ1'
		),
		(int) 1 => array(
			'TAG_ID' => 'TAG003',
			'TAG_TEXT' => 'たぐたぐ3'
		)
	)
)

なるほど。これをそのまま保存してみよう。

debug("そのまま保存してみる");
$data = $this->Hoge->read(null, $id);
$this->assertNotEqual($this->Hoge->save($data), false, "保存に失敗しちゃった。");
debug($this->Hoge->read(null, $id));

すると・・・

array(
	'Hoge' => array(
		'ID' => '1',
		'TITLE' => 'ほげタイトル',
		'CONTENTS' => 'ほげのコンテンツ。
ほげほげほげ。
'
	),
	'Tag' => array(
		(int) 0 => array(
			'TAG_ID' => 'TAG003',
			'TAG_TEXT' => 'たぐたぐ3'
		)
	)
)

あれ?タグ、足りなくね???

実行されたSQLを見ていくと・・・

Hoge本体を保存して

UPDATE `caketest`.`HOGE` SET `ID` = 1, `TITLE` = 'ほげタイトル', `CONTENTS` = 'ほげのコンテンツ。\r\nほげほげほげ。\r\n' WHERE `caketest`.`HOGE`.`ID` = '1'

タグの紐付けを削除して

DELETE `HogesTags` FROM `caketest`.`HOGES_TAGS` AS `HogesTags` WHERE `HogesTags`.`HOGE_ID` = 1 AND `HogesTags`.`TAG_ID` IN ('TAG001', 'TAG003')

紐付けをINSERTしなおす・・・っと。

INSERT INTO `caketest`.`HOGES_TAGS` (`TAG_ID`, `HOGE_ID`) VALUES ('TAG001', 1)

んで、もいっこ・・・って・・・え・????

UPDATE `caketest`.`HOGES_TAGS` SET `TAG_ID` = 'TAG003', `HOGE_ID` = 1 WHERE `caketest`.`HOGES_TAGS`.`HOGE_ID` = '1'

お前、なぜUPDATEする。なぜだ。

でも、これなら1つは保存されるはず。
画面では1つも保存されずに完全に消えるのだ。
なんでだろ・・・??

ひとまず、この時点で問題は2つできた。
1つは、取ってきたまま保存してもうまくいかないこと。
もう1つはControllerから保存した場合と挙動が違うこと。

まずは、取ってきたまま保存を成功させることを目標にしよう。

取ってきたまま保存できるようにする

これは本当に悩んだ。どうやってもできない。
何度も同じサイトを見て・・・気づいた!

CakePHP の HABTMって楽ですよ : かばだんなさん かく語りぬ2

紐付けテーブルに、idなんてものがある・・・
もしや・・・

SET SESSION FOREIGN_KEY_CHECKS=0;
DROP TABLE HOGES_TAGS;
CREATE TABLE HOGES_TAGS
(
	ID INT NOT NULL AUTO_INCREMENT,
	HOGE_ID INT NOT NULL,
	TAG_ID CHAR(6) NOT NULL,
	PRIMARY KEY (ID)
);
ALTER TABLE HOGES_TAGS
	ADD FOREIGN KEY (HOGE_ID)
	REFERENCES HOGE (ID)
	ON UPDATE RESTRICT
	ON DELETE RESTRICT
;
ALTER TABLE HOGES_TAGS
	ADD FOREIGN KEY (TAG_ID)
	REFERENCES TAG (TAG_ID)
	ON UPDATE RESTRICT
	ON DELETE RESTRICT
;

INSERT INTO hoges_tags (`ID`, `HOGE_ID`, `TAG_ID`) VALUES (NULL, '1', 'TAG001'), (NULL, '1', 'TAG003');

さっきのテストを実行!!
・・・
!!

array(
	'Hoge' => array(
		'ID' => '1',
		'TITLE' => 'ほげタイトル',
		'CONTENTS' => 'ほげのコンテンツ。
ほげほげほげ。'
	),
	'Tag' => array(
		(int) 0 => array(
			'TAG_ID' => 'TAG001',
			'TAG_TEXT' => 'たぐたぐ1',
			'HogesTags' => array(
				'ID' => '1',
				'HOGE_ID' => '1',
				'TAG_ID' => 'TAG001'
			)
		),
		(int) 1 => array(
			'TAG_ID' => 'TAG003',
			'TAG_TEXT' => 'たぐたぐ3',
			'HogesTags' => array(
				'ID' => '2',
				'HOGE_ID' => '1',
				'TAG_ID' => 'TAG003'
			)
		)
	)
)

そのまんま保存できたー!
でも・・・id邪魔だなぁ・・・

Controllerから保存した場合と挙動が違う

これは、テストの挙動と画面から実際に飛んでくるリクエストと比較したらわかった。

そのまんま保存できるのならば、その形式でsaveに渡せば保存はできるはず!
と、テストに以下の記述をしてやってみた。

debug("こうやれば保存できそう。");
$postData = array(
	'Hoge' => array(
		'ID' => $id,
		'TITLE' => 'ほげほげ編集タイトル',
		'CONTENTS' => 'ほげのコンテンツ編集後'
	),
	'Tag' => array(
		array('TAG_ID' => 'TAG002'),
		array('TAG_ID' => 'TAG003'),
	),
);
$this->assertNotEqual($this->Hoge->save($postData), false, "保存に失敗しちゃった。");
debug($this->Hoge->read(null, $id));

できた!実行するたびidがインクリメントされるけど、期待通り保存できた。

array(
	'Hoge' => array(
		'ID' => '1',
		'TITLE' => 'ほげほげ編集タイトル',
		'CONTENTS' => 'ほげのコンテンツ編集後'
	),
	'Tag' => array(
		(int) 0 => array(
			'TAG_ID' => 'TAG002',
			'TAG_TEXT' => 'たぐたぐ2',
			'HogesTags' => array(
				'ID' => '5',
				'HOGE_ID' => '1',
				'TAG_ID' => 'TAG002'
			)
		),
		(int) 1 => array(
			'TAG_ID' => 'TAG003',
			'TAG_TEXT' => 'たぐたぐ3',
			'HogesTags' => array(
				'ID' => '6',
				'HOGE_ID' => '1',
				'TAG_ID' => 'TAG003'
			)
		)
	)
)

Controllerで受け取るリクエストのデータがこの形式ならば問題ないはず!
と、Controllerの中身をちゃんと書いてlogに出してみると・・・こんな結果になる。

2012-12-02 04:07:48 Debug: Array
(
    [Hoge] => Array
        (
            [ID] => 1
            [TITLE] => aaa
            [CONTENTS] => asasas
        )

    [Tag] => Array
        (
            [Tag] => Array
                (
                    [0] => TAG001
                    [1] => TAG002
                )

        )

)

違うじゃん!これじゃだめじゃん!
だからかー。

というわけで・・・

ドウシヨウ...
(´・ω・`)

紐付けの部分をしっかり実装するしかないかなー。
それとも、何か見逃している?

はてな記法

はてなブログでもはてな記法が使えるらしい。

小見出し

見出しの見出し

更に見出し

見出しの見出しの見出し

引用記法
ここに引用文が入ります。

// javaのソースとか
public class Hoge {
  public Hoge() {
    System.out.println("hogehoge");
  }
}


異次元への入り口(笑)↓ズームしていくとある。

           r'丁´ ̄ ̄ ̄ ̄`7¬‐,-、           /
        r'| |          |  |/  >、     /
        ! | |          |  |レ'´/|       |   待 て
        | | |   /\   |  |l  /⊂う    |
        | | |__∠∠ヽ_\ |  リ /  j     ヽ   あ わ て る な
        |´ ̄   O   ̄ ̄ ̄ ̄ ̄`!      〉
        l'"´ ̄ ̄ヾ'"´ ̄ ̄`ヾ::幵ー{       /   こ れ は 孔 明 の 罠 だ
        ⊥,,,,,_、    ___,,,,,ヾ| l::::::|      |
         lヾ´ f}`7   ヘ´fj ̄フ  | l::i'⌒i    |    そ ん な 事 は 無 理 だ
         l ,.ゝ‐イ    `‐=ニ、i | l´ ( }    ヽ
         l     {         U | l 、_ノ    ∠ヘ
        l   / ̄  ''ヽ、   | l ヽ_       \,_________
           !  ハ´ ̄ ̄ ̄`ト、  |亅〃/\
        ,人 f ´ ̄ ̄ ̄``ヾ  j ,!// {_っ )、
      // `ト、__iiiii______,レ'‐'//  _,/ /スァ-、
    ,.イl{ { 々 !/´しllllト、 ̄`ヽ、 // /´,.-、 /彑ゝ-{スァ-、
  ,.イ彑l l > ゞ く l 〃 l|ハ.lヽ、 ハVゝヽ二ノ/ゝ-{、彑ゝ-{、彑ァ-、
,.イ彑ゝ-'l l ( (,) レシ′   !l `ソァ'´    _ノ7{、彑ゝ-{、彑ゝ-{、彑{
ュゝ-{、彑l l  ` -イヘ      !l // /⌒ヽヾ/_ゝ-{、彑ゝ-{、彑ゝ-{、
 {、彑ゝ-'l l f⌒Yハ ',    !l/ / ヽ_う ノ /-{、彑ゝ-{、彑ゝ-{、彑ゝ
彑ゝ-{、彑l l{ に!小 ヽ   /!l /   ,/ /彑ゝ-{、彑ゝ-{、彑ゝ-{、