六帖のかたすみ

DVを受けていた男性。家を脱出して二周目の人生を生きています。自閉症スペクトラム(受動型)です。http://rokujo.org/ に引っ越しました。

PHP, jQuery + ajax 連携練習

最寄駅の時刻表を見て、いちいちNaviTimeやらを使って新宿駅に到着する時間を調べるのが面倒。
そんなニーズに応えるため、この時刻表(最寄駅は変えてあります)のページに細工をして、マウスカーソルを合わせると新宿駅到着時刻が出てくるようにした。
クライアント側ではページロード後に、時刻表から発時刻をサーバーにajaxで送って、新宿駅到着時間を返してもらう。サーバー側では、発駅発時刻を入力すると新宿駅までの経路を判定して文字列を返すPHPスクリプトを書く(実はこっちの方が面倒)。TrainNaviで作ったデータベースをそのまんま使用した。
さて、すべての時間を一気にajaxで問い合わせると、リクエストが200はあるから貧弱な自宅PCが悲鳴を上げそうだ。しかし、asyncをやめて順次問い合わせにすると、ページが表示されるまでとても時間がかかってしまう。というわけで、asyncにしつつ、1つ処理が終わったら次の処理を実行するようなチェーンの仕組みが必要だ。
概念図

処理開始→ajax処理   →ajax処理 →ajax処理・・・
          ↓       ↑ ↓      ↑
          処理終了→↑処理終了→↑
          ↓           ↓
          コールバック コールバック

出来上がったコード(開始駅を変えてあります)

//ajax処理をするメイン関数
function ktmain(start, end, time, service, cbObj, nextObj){
	$.ajax({
		async: true,
		type: "POST",
		url: "ktajax.php",
		data: { start : start, end : end, time : time, service : service }
	}).done(function( msg ) {
		//コールバック(DOMの要素にmsgをセット)
		cbObj.callback(msg);
		//次なるktmainをコール あらかじめnextObjに引数などを保存してある
		if(nextObj) nextObj.next();
	});
}

//コールバック用オブジェクト
var KTCallBackObject = (function (){
    function KTCallBackObject(dom) {
        this.dom = dom;
    };
    KTCallBackObject.prototype.callback = function(msg){
    	//title属性をmsgに変更
    	this.dom.title = msg;
    }
    return KTCallBackObject;
})();


//メソッド実行用オブジェクト
var KTNextObject = (function (){
    function KTNextObject(start, end, time, service, cbObj) {
        this.start = start;
        this.end = end;
        this.time = time;
        this.service = service;
        this.cbObj = cbObj;
        this.nextObj = null;
    };
    KTNextObject.prototype.next = function(){
    	//次の関数をコール
    	ktmain(this.start, this.end, this.time, this.service, this.cbObj, this.nextObj);
    }
    return KTNextObject;
})();

//メソッドチェーンを作成する
function MakeMethodChain(base, start, end, service){
	var nextObjFirst, nextObjBefore;
	for(var i=1; i<=21; i++){
		var snum = ("0" + i).slice(-2);
		var selector = "." + base + snum;
		var root = $(selector);
		if(root.size()){
			//時刻
			var hour = root.find(".diaHour").html();
			var items = root.find("a");
			for(var j=0; j<items.size(); j++){
				//分
				var minute = items[j].innerHTML.replace(/\s| /g,"").substr(0, 2);
				var time = hour + ":" + minute;
				//コールバック用オブジェクト
				var cbObj = new KTCallBackObject(items[j]);
				//次のメソッド実行用オブジェクト
				var nextObj = new KTNextObject(start, end, time, service, cbObj);
				//関連付けてチェーン作成
				if(nextObjBefore) nextObjBefore.nextObj = nextObj;
				if(!nextObjFirst) nextObjFirst = nextObj;
				nextObjBefore = nextObj;
			}
		}
	}
	//先頭のメソッド実行用オブジェクトを返す
	return nextObjFirst;
}
function DOMManupilation(){
	var start = $("#station-name").find("a").html();
	var end = (start == "足柄") ? "新宿" : "足柄";
	//メソッドチェーンを作って、順次実行
	//平日
	var mcWeekday = MakeMethodChain("t1-time", start, end, 1);
	//休日
	var mcHoliday = MakeMethodChain("t3-time", start, end, 2);
	//fire
	//平日・休日 2列並列順次実行
	mcWeekday.next();
	mcHoliday.next();
}


$(document).ready(DOMManupilation);

これで、処理終了ごとにktmainがコールされることになり、しかもページの表示を妨げない。実行すると、4時台、5時台、・・・と処理が進んでいって、終電のtitle属性の表示が変わったのは30秒ほど後。やっぱりasyncなしじゃきついね。

サーバー側(開始駅を変えてあります)やっつけ

<?php
require_once 'funcs.php';

$start = $_POST["start"];
$end = $_POST["end"];
$service = $_POST["service"];
$time = $_POST["time"];

if(strlen($time) == 4) $time = "0" . $time;


if($start == "足柄"){
	AshigaraShinjuku($service, $time);
}

function AshigaraShinjuku($service, $time){
	$mysqli = OpenDb();
	//足柄発の電車
	$query = "SELECT * FROM tnroute WHERE linename = '小田急小田原線' AND startstation = '足柄' AND endstation='螢田' AND starttime='$time' AND service=$service";
	$result = ExecQuery($mysqli, $query);
	if($result->num_rows == 0) return;
	$row = $result->fetch_assoc();
	$trainname = $row['trainname'];

	$trainkind = TrainKind($mysqli, $service, $trainname);
	$train = Train($mysqli, $service, $trainname);
	$dest = $train[count($train)-1]['endstation'];
	echo "$trainkind $dest"."\n";
	echo "螢田 $time"."\n\n";

	if($dest == "新松田" || $dest == "相模大野"){
		//終点で乗り換える
		$endtime = TimeTrim($train[count($train)-1]['endtime']);
		echo "$dest $endtime"."\n\n";
		$trainname = SearchNorikae($mysqli, $endtime, $dest, $service, true);
		$trainkind = TrainKind($mysqli, $service, $trainname);
		$train = Train($mysqli, $service, $trainname);
		$current = $dest;
		$dest = $train[count($train)-1]['endstation'];
		$loc = StartStation($train, $current);
		$starttime = TimeTrim($train[$loc]['starttime']);
		echo "$trainkind $dest"."\n";
		echo "$current $starttime"."\n\n";

		if(strpos($trainkind, "はこね") !== FALSE)
		{
			$endtime = TimeTrim($train[count($train)-1]['endtime']);
			echo "$dest $endtime"."\n\n";

			//ロマンスカーを使った場合は、もう一度検索
			echo "(ロマンスカーを使わない場合)\n\n";
			$trainname = SearchNorikae($mysqli, $starttime, $current, $service, true);
			$trainkind = TrainKind($mysqli, $service, $trainname);
			$train = Train($mysqli, $service, $trainname);
			$dest = $train[count($train)-1]['endstation'];
			$loc = StartStation($train, $current);
			$starttime = TimeTrim($train[$loc]['starttime']);
			echo "$trainkind $dest"."\n";
			echo "$current $starttime"."\n\n";
		}
	}
	else if($dest == "町田"){
		//相模大野で乗り換える
		$loc = StartStation($train, "小田急相模原");
		if($loc == -1) $loc = StartStation($train, "海老名");
		$endtime = TimeTrim($train[$loc]['endtime']);
		$new_trainname = SearchNorikae($mysqli, $endtime, "相模大野", $service, true);
		if($new_trainname != $trainname)
		{
			echo "相模大野 $endtime"."\n\n";
			$trainname = $new_trainname;
			$trainkind = TrainKind($mysqli, $service, $trainname);
			$train = Train($mysqli, $service, $trainname);
			$current = "相模大野";
			$dest = $train[count($train)-1]['endstation'];
			$loc = StartStation($train, $current);
			$starttime = TimeTrim($train[$loc]['starttime']);
			echo "$trainkind $dest"."\n";
			echo "$current $starttime"."\n\n";
		}
		else
		{
			//乗り換えないでよかった
			//$endtime = TimeTrim($train[count($train)-1]['endtime']);
			//echo "$dest $endtime"."着\n\n";
		}
	}
	else if(ExistStation($train, "新松田") && $dest != "成城学園前" && $time != "00:12"){
		//新松田で乗り換える
		$loc = StartStation($train, "新松田");
		$endtime = TimeTrim($train[$loc]['starttime']);
		echo "新松田 $endtime"."\n\n";
		$trainname = SearchNorikae($mysqli, $endtime, "新松田", $service, true);
		$trainkind = TrainKind($mysqli, $service, $trainname);
		$train = Train($mysqli, $service, $trainname);
		$current = "新松田";
		$dest = $train[count($train)-1]['endstation'];
		$loc = StartStation($train, $current);
		$starttime = TimeTrim($train[$loc]['starttime']);
		echo "$trainkind $dest"."\n";
		echo "$current $starttime"."\n\n";
	}
	$endtime = TimeTrim($train[count($train)-1]['endtime']);
	echo "$dest $endtime"."\n\n";

	if($dest == "経堂" || $dest == "成城学園前" || ($dest == "町田" && $time == "00:12")){
		echo "新宿に着けません";
	}

	else if($dest != "新宿"){
		//新宿につかなかったのでもう一本
		$trainname = SearchNorikae($mysqli, $endtime, $dest, $service, true);
		$trainkind = TrainKind($mysqli, $service, $trainname);
		$train = Train($mysqli, $service, $trainname);
		$current = $dest;
		$dest = $train[count($train)-1]['endstation'];
		$loc = StartStation($train, $current);
		$starttime = TimeTrim($train[$loc]['starttime']);
		echo "$trainkind $dest"."\n";
		echo "$current $starttime"."\n\n";
		$endtime = TimeTrim($train[count($train)-1]['endtime']);
		echo "$dest $endtime"."\n\n";
	}
}

function TrainKind($mysqli, $service, $trainname){
	$query = "SELECT * FROM tntrain WHERE linename = '小田急小田原線' AND trainname = '$trainname' AND service=$service";
	$result = ExecQuery($mysqli, $query);
	$row = $result->fetch_assoc();
	return $row['trainkind'];
}

function Train($mysqli, $service, $trainname){
	$query = "SELECT * FROM tnroute WHERE linename = '小田急小田原線' AND trainname = '$trainname' AND service=$service";
	$result = ExecQuery($mysqli, $query);
	$train = array();
	while($row = $result->fetch_assoc()){
		$train[] = $row;
	}
	return $train;
}

function ExistStation($train, $station){
	for($i=0; $i<count($train); $i++){
		if($train[$i]['endstation'] == $station) return $i;
	}
	return false;
}

function StartStation($train, $station){
	for($i=0; $i<count($train); $i++){
		if($train[$i]['startstation'] == $station) return $i;
	}
	return -1;
}

function TimeTrim($time){
	return substr($time, 0, 5);
}

function SearchNorikae($mysqli, $time, $station, $service, $up){
	$query = "SELECT * FROM tnroute WHERE linename = '小田急小田原線' AND startstation = '$station' AND starttime>'$time' AND service=$service";
	if($up){
		if($station == "新松田"){
			$query .= " AND (endstation = '渋沢' OR endstation = '本厚木')";
		}else if($station == "相模大野"){
			$query .= " AND endstation = '町田'";
		}else if($station == "町田"){
			$query .= " AND endstation = '新百合ヶ丘'";
		}
	}
	$query .= " ORDER BY starttime ASC";
	$result = ExecQuery($mysqli, $query);
	$row = $result->fetch_assoc();
	return $row['trainname'];
}

?>