(XML版)ホットペッパーAPIからレストラン検索するサンプル その2


##各種リファレンス

###GoogleMaps

[Google Maps API> ウェブ向け > Maps JavaScript API > スタートガイド](https://developers.google.com/maps/documentation/javascript/tutorial?hl=ja)
[Google Maps API> ウェブ向け > Maps JavaScript API > マーカー](https://developers.google.com/maps/documentation/javascript/markers?hl=ja)

###JavaScript

[JavaScript リファレンス](https://developer.mozilla.org/ja/docs/Web/JavaScript)

###PHP

[PHP関数リファレンス](http://php.net/manual/ja/funcref.php)

[foreach](http://php.net/manual/ja/control-structures.foreach.php)
[if](http://php.net/manual/ja/control-structures.if.php)
[制御構造に関する別の構文](http://php.net/manual/ja/control-structures.alternative-syntax.php)
[return](http://php.net/manual/ja/function.return.php)
[htmlspecialchars](http://php.net/manual/ja/function.htmlspecialchars.php)
[require_once](http://php.net/manual/ja/function.require-once.php)
[number_format](http://php.net/manual/ja/function.number-format.php)
[isset](http://php.net/manual/ja/function.isset.php)
[is_object](http://php.net/manual/ja/function.is-object.php)
[代数演算子](http://php.net/manual/ja/language.operators.arithmetic.php)
[filter_input](http://php.net/manual/ja/function.filter-input.php)
[filter_input_array](http://php.net/manual/ja/function.filter-input-array.php)
[sprintf](http://php.net/manual/ja/function.sprintf.php)
[simplexml_load_file](http://php.net/manual/ja/function.simplexml-load-file.php)
[is_null](http://php.net/manual/ja/function.is-null.php)
[ceil](http://php.net/manual/ja/function.ceil.php)
[http_build_query](http://php.net/manual/ja/function.http-build-query.php)
[implode](http://php.net/manual/ja/function.implode.php)


##ソースコード

```html+php:index.php
<?php
/**
 * index.php
 *
 * @since 2016/08/06
 */
require_once './Hpepper.class.php';

/**
 * エスケープ
 * これを通さずに出力すると、XSS脆弱性で任意のJavaScriptが実行されてしまう。
 * 
 * @param string $string
 * @return string
 */
function h($string)
{
	return htmlspecialchars($string, ENT_QUOTES, 'utf-8');
}

// 料理カテゴリ
$food_category = Hpepper::getFoodCategory();

// 予算
$budget = Hpepper::getBudget();

// クレジットカード
$credit_card = Hpepper::getCreditCard();

// 大サービスエリア
$large_service_area = Hpepper::getLargeServiceArea();

// サービスエリア
$service_area = Hpepper::getServiceArea();

// レストラン検索
$restaurants = Hpepper::getRestaurants();

// ドキュメントの頭で、echo しない。
// ここで echo すると、<html> tagよりも前で文字列を出力してしまい、html 文法に違反する。
?>
<!DOCTYPE HTML>
<html lang="ja">
	<head>
		<meta charset="UTF-8">
		<title>ホットペッパーAPIテスト（アホでも設置するだけで動く）</title>
		<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
		<style type="text/css">
			.map {
				height: 300px;
			}
			.shop {
				margin-bottom: 2em;
			}
			.shop > div {
				border: 1px solid #CCC;
				padding: 1em;
			}
		</style>
	</head>
	<body>
		<div class="container">
			<h1>ホットペッパーAPIサンプル</h1>

			<form method="get">
				<div class="row">
					<div class="col-md-4">
						<div class="form-group">
							<label for="food_category">料理カテゴリ</label>
							<select class="form-control" name="food" id="food_category">
								<option value="">料理カテゴリ</option>
								<?php foreach ($food_category->food_category as $food_category) : ?>
									<?php if ($food_category->code == filter_input(INPUT_GET, 'food')): ?>
										<option value="<?= h($food_category->code); ?>" selected="selected">
											<?= h($food_category->name); ?>
										</option>
									<?php else: ?>
										<option value="<?= h($food_category->code); ?>">
											<?= h($food_category->name); ?>
										</option>
									<?php endif; ?>
								<?php endforeach; ?>
							</select>
						</div>

						<div class="form-group">
							<label for="budget">予算</label>
							<select class="form-control" name="budget" id="service_area">
								<option value="">予算</option>
								<?php foreach ($budget->budget as $budget) : ?>
									<?php if ($budget->code == filter_input(INPUT_GET, 'budget')): ?>
										<option value="<?= h($budget->code); ?>" selected="selected">
											<?= h($budget->name); ?>
										</option>
									<?php else: ?>
										<option value="<?= h($budget->code); ?>">
											<?= h($budget->name); ?>
										</option>
									<?php endif; ?>
								<?php endforeach; ?>
							</select>
						</div>

						<div class="form-group">
							<label for="credit_card">クレジットカード</label>
							<select class="form-control" name="credit_card" id="credit_card">
								<option value="">クレジットカード</option>
								<?php foreach ($credit_card->credit_card as $credit_card) : ?>
									<?php if ($credit_card->code == filter_input(INPUT_GET, 'credit_card')): ?>
										<option value="<?= h($credit_card->code); ?>" selected="selected">
											<?= h($credit_card->name); ?>
										</option>
									<?php else: ?>
										<option value="<?= h($credit_card->code); ?>">
											<?= h($credit_card->name); ?>
										</option>
									<?php endif; ?>
								<?php endforeach; ?>
							</select>
						</div>
					</div>

					<div class="col-md-4">
						<div class="form-group">
							<label for="large_service_area">大サービスエリア</label>
							<select class="form-control" name="large_service_area" id="large_service_area">
								<option value="">大サービスエリア</option>
								<?php foreach ($large_service_area->large_service_area as $large_service_area) : ?>
									<?php if ($large_service_area->code == filter_input(INPUT_GET, 'large_service_area')): ?>
										<option value="<?= h($large_service_area->code); ?>" selected="selected">
											<?= h($large_service_area->name); ?>
										</option>
									<?php else: ?>
										<option value="<?= h($large_service_area->code); ?>">
											<?= h($large_service_area->name); ?>
										</option>
									<?php endif; ?>
								<?php endforeach; ?>
							</select>
						</div>

						<div class="form-group">
							<label for="service_area">サービスエリア</label>
							<select class="form-control" name="service_area" id="service_area">
								<option value="">サービスエリア</option>
								<?php foreach ($service_area->service_area as $pref) : ?>
									<?php if ($pref->code == filter_input(INPUT_GET, 'service_area')): ?>
										<option value="<?= h($pref->code); ?>" selected="selected">
											<?= h($pref->name); ?>
										</option>
									<?php else: ?>
										<option value="<?= h($pref->code); ?>">
											<?= h($pref->name); ?>
										</option>
									<?php endif; ?>
								<?php endforeach; ?>
							</select>
						</div>
					</div>

					<div class="col-md-4">
						<div class="form-group">
							<label for="name_kana">店名 or ふりがな</label>
							<input class="form-control" type="text" name="name_kana" id="name_kana" value="<?= h(filter_input(INPUT_GET, 'name_kana')); ?>" />
						</div>

						<div class="form-group">
							<label for="keyword">フリーワード</label>
							<input class="form-control" type="text" name="keyword" id="keyword" value="<?= h(filter_input(INPUT_GET, 'keyword')); ?>" />
						</div>

					</div>
				</div>

				<div class="row">
					<div class="col-md-12">
						<div class="form-group">
							<button type="submit" class="btn btn-primary">検索</button>
						</div>
					</div>
				</div>
			</form>
			<?php if (is_object($restaurants)): ?>

				<p>検索結果 <?= h(number_format((int) $restaurants->results_available)); ?> 件</p>

				<?php if (isset($restaurants->error)): ?>

					<div>
						<?php foreach ($restaurants->error as $err) : ?>
							<h2><?= $err->code; ?></h2>
							<p><?= $err->message; ?></p>
						<?php endforeach; ?>
					</div>

				<?php else: ?>

					<?php $i = 0; ?>
					<?php foreach ($restaurants->shop as $rest) : ?>
						<?php if ($i % 2 == 0): ?>
							<div class="row">
							<?php endif; ?>
							<div class="col-md-6 shop">
								<div>
									<dl>
										<dt>ID</dt>
										<dd><?= h($rest->id); ?></dd>

										<dt>店名</dt>
										<dd><?= h($rest->name); ?></dd>

										<dt>ジャンル</dt>
										<dd><?= h($rest->genre->name); ?></dd>

										<dt>所在地</dt>
										<dd><?= h($rest->address); ?></dd>
									</dl>
									<div class="map" data-lat="<?= h($rest->lat); ?>" data-lng="<?= h($rest->lng); ?>">
									</div>
								</div>
							</div>
							<?php if ($i % 2 == 1 || count($restaurants->shop) - 1 == $i): ?>
							</div>
						<?php endif; ?>
						<?php $i++; ?>

					<?php endforeach; ?>

					<div class="row">
						<div class="col-md-12">
							<ul class="pagination pagination-sm no-margin">
								<?= Hpepper::pagination($restaurants->results_available); ?>
							</ul>
						</div>
					</div>

				<?php endif; ?>
			<?php endif; ?>

		</div>

		<script type="text/javascript" src="//www.google.com/jsapi"></script>
		<script type="text/javascript">
			var map;
			function initMap() {
				
				// map というクラスが付いている要素を複数取得
				// 同名IDはドキュメント内に複数設定できないため、クラスでアクセスする。
				// MAPがドキュメント内に一つだけ表示するなら、getElementById() を使えば良い。
				// @see https://developer.mozilla.org/ja/docs/Web/API/Document/getElementsByClassName
				var maps = document.getElementsByClassName('map');

				// 複数要素全てを処理する
				// @see https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/for
				for (var i = 0; i < maps.length; i++) {

					// それぞれの要素につけてれている data-* 属性から lat, lng を取得する
					// @see https://developer.mozilla.org/ja/docs/Web/API/Element/getAttribute
					var latlng = {
						// @see https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/parseFloat
						lat: parseFloat(maps[i].getAttribute('data-lat'))
						, lng: parseFloat(maps[i].getAttribute('data-lng'))
					};

					// MAPオブジェクトをそれぞれ生成する
					// @see https://developers.google.com/maps/documentation/javascript/tutorial?hl=ja
					map = new google.maps.Map(maps[i], {
						center: latlng,
						scrollwheel: false,
						zoom: 16
					});
					
					// 各MAPオブジェクトにマーカーを設定する
					// @see https://developers.google.com/maps/documentation/javascript/markers?hl=ja#add
					new google.maps.Marker({
						position: latlng,
						map: map
					});
				}

			}
		</script>
		<script src="//maps.googleapis.com/maps/api/js?key=GOOGLEMAP_API_KEY&callback=initMap&language=ja"></script>
	</body>
</html>
```


```php:Hpepper.class.php
<?php

/**
 * Hpepper.class.php
 *
 * @since 2016/08/06
 */
class Hpepper
{

	/**
	 * アクセスキー
	 * @var string
	 */
	private static $token = 'APIKEY';

	/**
	 * 1ページに表示する件数
	 * @var int
	 */
	private static $rowCount = 10;

	/**
	 * API問い合わせ
	 * @param string $uri
	 * @param array $query
	 * @return object
	 */
	private static function request($uri, $query = array())
	{
		$query += array(
			'format' => 'xml'
			, 'key' => self::$token
		);
		
		// @see http://php.net/manual/ja/function.http-build-query.php
		$url = sprintf("%s?%s", $uri, http_build_query($query));
		$obj = simplexml_load_file($url);
		return $obj;
	}

	/**
	 * 予算リストを取得
	 * @return object
	 */
	public static function getBudget()
	{
		$uri = "http://webservice.recruit.co.jp/hotpepper/budget/v1/";
		return self::request($uri);
	}

	/**
	 * 大サービスエリア
	 * @return object
	 */
	public static function getLargeServiceArea()
	{
		$uri = "http://webservice.recruit.co.jp/hotpepper/large_service_area/v1/";
		return self::request($uri);
	}

	/**
	 * サービスエリアマスタAPI
	 * @return object
	 */
	public static function getServiceArea()
	{
		$uri = "https://webservice.recruit.co.jp/hotpepper/service_area/v1/";
		return self::request($uri);
	}

	/**
	 * 料理カテゴリ
	 * @return object
	 */
	public static function getFoodCategory()
	{
		$uri = "http://webservice.recruit.co.jp/hotpepper/food_category/v1/";
		return self::request($uri);
	}

	/**
	 * クレジットカードマスタAPI
	 * @return object
	 */
	public static function getCreditCard()
	{
		$uri = "http://webservice.recruit.co.jp/hotpepper/credit_card/v1/";
		return self::request($uri);
	}

	/**
	 * レストラン検索
	 * @return object
	 */
	public static function getRestaurants()
	{
		$uri = "http://webservice.recruit.co.jp/hotpepper/gourmet/v1/";
		$query = array(
			'count' => self::$rowCount
		);
		if (!is_null(filter_input_array(INPUT_GET))) {
			$query += filter_input_array(INPUT_GET);
		}
		return self::request($uri, $query);
	}

	/**
	 * ページネーション
	 * @param int $total
	 * @return string
	 */
	public static function pagination($total = 0)
	{
		if ($total == 0) {
			return;
		}
		$start = filter_input(INPUT_GET, 'start', FILTER_VALIDATE_INT);
		if ($start == 0) {
			$start = 1;
		}

		$html = '<li%s><a href="?%s">%s</a></li>';

		//現在の頁
		$curPage = ceil($start / self::$rowCount);
		$iStart = (0 < $curPage - 3) ? $curPage - 3 : 1;

		$pages = ceil($total / self::$rowCount);
		$iMax = ($iStart + 6 > $pages) ? $pages : $iStart + 6;

		$arr = array();
		$params = filter_input_array(INPUT_GET);

		// 先頭ページ
		if ($start != 1) {
			$params['start'] = 1;
			$query = http_build_query($params, '', '&amp;');
			$arr[] = sprintf($html, '', $query, '最初');
			$params['start'] = $start - self::$rowCount;
			$query = http_build_query($params, '', '&amp;');
			$arr[] = sprintf($html, '', $query, '前へ');
		}

		for ($i = $iStart; $i <= $iMax; $i++) {
			$params['start'] = ($i - 1) * self::$rowCount + 1;
			$class = ($params['start'] == $start) ? ' class="active"' : '';
			$query = http_build_query($params, '', '&amp;');
			$arr[] = sprintf($html, $class, $query, $i);
		}

		// 最終ページ
		$maxCount = ($pages - 1) * self::$rowCount + 1;
		if ($start < $maxCount) {
			$params['start'] = $start + self::$rowCount;
			$query = http_build_query($params, '', '&amp;');
			$arr[] = sprintf($html, '', $query, '次へ');
			$params['start'] = $maxCount;
			$query = http_build_query($params, '', '&amp;');
			$arr[] = sprintf($html, '', $query, '最終');
		}

		return implode(PHP_EOL, $arr);
	}

}

// PHPのみで構成されるファイルには終了タグ（?>）は不要。
// 終了タグの存在はかえってバグの元になる。
```

##スクリーンショット

![hpapi.jlamp.net_2016-10-10_18-47-51.png](https://qiita-image-store.s3.amazonaws.com/0/46841/157e67ff-df43-ea6b-1d00-fa21c050b313.png "hpapi.jlamp.net_2016-10-10_18-47-51.png")