APIを使い郵便番号から住所が検索できるWebサービスをPHPで作成する方法

# 概要

本記事では**郵便番号データ配信サービスzipcloud様**が提供するAPIを用いて，7桁の郵便番号を入力すると住所が検索できるWebサービスの作製法を記載する．

# 事前リサーチ: ブラウザで様々なパターンのJSONを表示検証してみる

下記URLの後ろに様々な文字列を連結してブラウザでアクセスしてみる．
`https://zip-cloud.appspot.com/api/search?zipcode=`

* **実在する郵便番号`1050011`を連結**  
[https://zip-cloud.appspot.com/api/search?zipcode=1050011](https://zip-cloud.appspot.com/api/search?zipcode=1050011)  
**住所詳細が表示された．（ステータスコード: 200）**

```json
{
	"message": null,
	"results": [
		{
			"address1": "東京都",
			"address2": "港区",
			"address3": "芝公園",
			"kana1": "ﾄｳｷｮｳﾄ",
			"kana2": "ﾐﾅﾄｸ",
			"kana3": "ｼﾊﾞｺｳｴﾝ",
			"prefcode": "13",
			"zipcode": "1050011"
		}
	],
	"status": 200
}
```

* **実在しない郵便番号`1234567`を連結**  
[https://zip-cloud.appspot.com/api/search?zipcode=1234567](https://zip-cloud.appspot.com/api/search?zipcode=1234567)  
**住所詳細がnull表示された．（ステータスコード: 200）**

```json
{
	"message": null,
	"results": null,
	"status": 200
}
```

* **7桁以外の番号`105`を連結**  
[https://zip-cloud.appspot.com/api/search?zipcode=105](https://zip-cloud.appspot.com/api/search?zipcode=105)  
**住所詳細がnull表示され，エラーメッセージAが表示された．（ステータスコード: 400）**

```json
{
	"message": "パラメータ「郵便番号」の桁数が不正です。",
	"results": null,
	"status": 400
}
```

* **文字列を`105hoge`を連結**  
[https://zip-cloud.appspot.com/api/search?zipcode=105hoge](https://zip-cloud.appspot.com/api/search?zipcode=105hoge)  
**住所詳細がnull表示され，エラーメッセージBが表示された．（ステータスコード: 400）**

```json
{
	"message": "パラメータ「郵便番号」に数字以外の文字が指定されています。",
	"results": null,
	"status": 400
}
```

* **文字列を連結しない**  
[https://zip-cloud.appspot.com/api/search?zipcode=](https://zip-cloud.appspot.com/api/search?zipcode=)  
**住所詳細がnull表示され，エラーメッセージCが表示された．（ステータスコード: 400）**

```json
{
	"message": "必須パラメータが指定されていません。",
	"results": null,
	"status": 400
}
```

JSONの検証により，コードを記述の際はステータスコード（200, 400の2種）とエラーメッセージ(A, B, Cの3種)により条件分岐を行う必要がある事が判明した．

# Webサービスの作製

**【システム環境】**
OS X El Capitan 10.11以降（他のOSでも可）
Apache 2.4.28以降
PHP 5.5.38以降

**本記事ではディレクトリ操作をMacに合せて記述しているため，他のOS（Linux, Windows等）の使用時は必要に応じてディレクトリ構造を置き換えて考えるものとする．**

* **ディレクトリとファイルの作成**

```bash
$ sudo mkdir /Library/WebServer/Documents/AddressSearch
$ sudo touch /Library/WebServer/Documents/AddressSearch/address_search.php
$ sudo mkdir /Library/WebServer/Documents/AddressSearch/common
$ sudo touch /Library/WebServer/Documents/AddressSearch/common/getCity.php
$ sudo mkdir /Library/WebServer/Documents/AddressSearch/js
$ sudo touch /Library/WebServer/Documents/AddressSearch/js/copyToClipboard.js
$ sudo mkdir /Library/WebServer/Documents/AddressSearch/css
$ sudo touch /Library/WebServer/Documents/AddressSearch/css/address_search.css
```

* **検索用フォーム`address_search.php`のソースコード**

```php:address_search.php
<?php
require_once __DIR__ . '/common/getCity.php';

$title = '住所検索';

$zipcode = isset($_POST['zipcode']) ? $_POST['zipcode'] : null;

/**
 * 郵便番号にスペース（半角または全角）が含まれる場合，
 * スペースを削除して検索結果に反映する
 */
$zipcode = preg_replace('/( |　)/', '', $zipcode);
$address = getCity($zipcode);
?>
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="css/address_search.css">
    <title><?= $title; ?></title>
  </head>
  <body>
    <h1><?= $title; ?></h1>
    <form action="" method="post">
      <input type="text" name="zipcode" maxlength=7 style="width:55px;" value="<?= $zipcode; ?>">
      <input type="submit" value="検索"><br>
      <input id="copyTarget1" type="text" name="place" style="width:480px;"value="<?= $address; ?>">
      <button onclick="copyToClipboard(1)">Copy</button>
    </form>
    <script src="js/copyToClipboard.js"></script>
  </body>
</html>
```

* **メインモジュール`getCity.php`のソースコード**

```php:getCity.php
<?php
# 郵便番号検索APIを利用した住所を取得する関数
function getCity($zipcode) {

    /**
     * 郵便番号にスペース（半角または全角）が含まれる場合，
     * Warning: file_get_contents対策のためスペースを削除して検索結果に反映する
     */
    $zipcode = preg_replace('/( |　)/', '', $zipcode);

    # URLの連結
    $api_url = 'https://zip-cloud.appspot.com/api/search?zipcode=' . $zipcode;

    # URLの読み込み
    $api_url = file_get_contents($api_url);

    # JSON文字列をデコード（第二引数をtrueにすることでJSON文字列を連想配列にする）
    $json = json_decode($api_url, true);

    # ステータスコード
    $status = $json['status'];

    # エラーメッセージ
    $message = $json['message'];

    # 住所詳細の配列
    $results = $json['results'];

    # 取得値の分岐
    if ($status == 400):
        if ($message == '必須パラメータが指定されていません。'):
            $output = '上のテキストボックスに7桁の「郵便番号」をハイフンなしで入力してください．';
            return $output;
        elseif ($message == 'パラメータ「郵便番号」の桁数が不正です。'):
            $output = '「郵便番号」の桁数が不正です．';
            return $output;
        elseif ($message == 'パラメータ「郵便番号」に数字以外の文字が指定されています。'):
            $output = '「郵便番号」に数字以外の文字が指定されています．';
            return $output;
        endif;
    elseif ($status == 200):
        if ($results == null):
            $output = '入力された「郵便番号」に該当する住所が見つかりませんでした．';
            return $output;
        elseif ($results != null):

            # 住所詳細
            for ($i = 0; $i <=2; $i++):
                $address[$i] = array_values($results[0])[$i];
            endfor;

            $output = $address[0] . $address[1] . $address[2];
            return $output;
        endif;
    endif;
}
```

* **検索結果のコピー機能`copyToClipboard.js`のJavaScriptソースコード**

```javascript:copyToClipboard.js
function copyToClipboard(index) {
    var copyTarget = document.getElementById('copyTarget' + index);
    copyTarget.select();
    document.execCommand('Copy');
    alert('Copied: ' + copyTarget.value);
}
```
* **Webページ表示設定ファイル`address_search.css`のCSSソースコード**

```css:address_search.css
body {
  background-color: #000000;
  color: #00ff00;
}
```

# 動作確認

**Apacheの起動**

```bash
$ sudo apachectl start
```

ブラウザ上に**`localhost/AddressSearch/address_search.php`**を入力して動作確認を行う．

**初期状態**
![postal1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478088/eb92b4c0-31d1-2d1b-4286-c553d25e272e.png)

**実在する郵便番号`1050011`を入力**
![postal2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478088/c2fd0399-d1be-693d-f59c-5ab4af0fa90a.png)

**実在しない郵便番号`1234567`を入力**
![postal3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478088/954c2a24-4e9c-05f1-e87a-49dbaa512205.png)

**7桁未満の番号`105`を入力**
![postal4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478088/34ce447c-db9e-784d-bda1-9bf2272feb92.png)

**文字列を`105hoge`を入力**
![postal5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478088/2e09ba0d-acdd-835d-cb11-f5be99495317.png)

今回はAPIを用いた情報取得の類としては簡単な部類の「住所検索」について記述した．次は応用編としてAPIを用いた**OpenWeatherMap**から気象情報を取得する方法に関して記述予定である．