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


某QAサイトで一部の方に大人気な質問者がw
なんか、よくわからん理由で xml形式での取得にこだわっているらしいので、XML版投下。

単純に json_encode を simplexml_load_file に変えるだけでは動かない謎仕様w

[ホットペッパーAPI](https://webservice.recruit.co.jp/hotpepper/reference.html)

というのも、レスポンスの構造が json と xml で違っているではありませんか！
図らずもホットペッパーAPIのバグを発見w
リファレンス通りなのは、xml の方で、 json のレスポンスが間違いですね。

2016/08/08
無駄に機能充実w
多少DRYに。

2016/09/15
なんか、別人なんだろうけど、せっかくだから、足してみた。
どんなページを作りたいのかいまいち書かれていないので、本人がやりたいことなのかわからないけど…
[google map apiのマッシュアップ](https://teratail.com/questions/47984)


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

/**
 * エスケープ
 * @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();
?>
<!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: 400px;
			}
		</style>
	</head>
	<body>
		<div class="container">
			<h1>ホットペッパーAPIサンプル</h1>

			<div class="row">
				<div class="col-md-6">
					<form method="get">
						<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 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 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 class="form-group">
							<button type="submit" class="btn btn-primary">検索</button>
						</div>
					</form>
				</div>
			</div>
			<?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: ?>
					<table class="table table-condensed">
						<thead>
							<tr>
								<th>id</th>
								<th>name</th>
								<th>category</th>
								<th>address</th>
							</tr>
						</thead>
						<tbody>
							<?php foreach ($restaurants->shop as $rest) : ?>
								<tr class="shop" data-lat="<?= h($rest->lat); ?>" data-lng="<?= h($rest->lng); ?>">
									<td><?= h($rest->id); ?></td>
									<td>
										<a href="detail.php?id=<?= h($rest->id); ?>"><?= h($rest->name); ?></a>
									</td>
									<td><?= h($rest->genre->name); ?></td>
									<td><?= h($rest->address); ?></td>
								</tr>
							<?php endforeach; ?>
						</tbody>
					</table>

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

					</div>
				</div>
			</div>
		</div>
		<script src="http://maps.google.com/maps/api/js?sensor=false"></script>
		<script type="text/javascript">
			function initMap() {
				var dom = document.getElementById('map');
				var map = new google.maps.Map(dom, {
					center: {
						lat: 35.685175
						, lng: 139.7527995
					},
					scrollwheel: false,
					zoom: 8
				});

				var shops = document.getElementsByClassName('shop');
				if (shops.length === 0) {
					return;
				}

				var bounds = new google.maps.LatLngBounds();

				for (var i = 0; i < shops.length; i++) {
					var marker = new google.maps.Marker({
						position: {
							lat: parseFloat(shops[i].getAttribute('data-lat'))
							, lng: parseFloat(shops[i].getAttribute('data-lng'))
						},
						map: map
					});
					bounds.extend(marker.position);
				}
				map.fitBounds(bounds);
			}
			initMap();
		</script>
	</body>
</html>
```

```html+php:detail.php
<?php
/**
 * detail.php
 *
 * @since 2016/09/15
 */
require_once './Hpepper.class.php';

/**
 * エスケープ
 * @param string $string
 * @return string
 */
function h($string)
{
	return htmlspecialchars($string, ENT_QUOTES, 'utf-8');
}

// レストラン検索
$restaurants = Hpepper::getRestaurants();
?>
<!DOCTYPE HTML>
<html lang="ja">
	<head>
		<meta charset="UTF-8">
		<title></title>
		<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
		<style type="text/css">
			#map {
				height: 300px;
			}
		</style>
	</head>
	<body>
		<div class="container">
			<div class="row">
				<div class="col-md-6 col-md-offset-3">
					<p><?= h($restaurants->shop->genre->name); ?></p>
					<p><?= h($restaurants->shop->name); ?></p>
					<p><?= h($restaurants->shop->address); ?></p>
					<p>
						<img src="<?= h($restaurants->shop->logo_image); ?>" alt="" />
					</p>
				</div>
			</div>
			<div class="row">
				<div class="col-md-6 col-md-offset-3">
					<div id="map" data-lat="<?= h($restaurants->shop->lat); ?>" data-lng="<?= h($restaurants->shop->lng); ?>">

					</div>
				</div>
			</div>
			<div class="row">
				<div class="col-md-6 col-md-offset-3">
					<?php var_dump($restaurants); ?>
				</div>
			</div>
		</div>
		<script src="http://maps.google.com/maps/api/js?sensor=false"></script>
		<script type="text/javascript">
			function initMap() {
				var dom = document.getElementById('map');
				var position = {
					lat: parseFloat(dom.getAttribute('data-lat'))
					, lng: parseFloat(dom.getAttribute('data-lng'))
				};
				var map = new google.maps.Map(dom, {
					center: position,
					scrollwheel: false,
					zoom: 14
				});
				var marker = new google.maps.Marker({
					position: position,
					map: map
				});
			}
			initMap();
		</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
		);
		$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);
	}

}
```

![localhost_2016-09-15_05-15-15.png](https://qiita-image-store.s3.amazonaws.com/0/46841/423abb58-7553-ee20-5eb8-1bc1275ab75f.png "localhost_2016-09-15_05-15-15.png")