OpenWeatherMapから取得した気象情報をデータベース（MySQL）に自動的に登録する方法

# 概要

先日投稿した下記記事のアップグレード版として，取得した気象情報を自動的にデータベース（以下略称DBとする）に登録する機能を追加した．今回使用するDBは**MySQL**とする．

[【簡易版】APIを使いOpenWeatherMapから気象情報が取得できるWebサービスをPHPで作成する方法](https://qiita.com/rimorimo/items/35d88ce3c42cfabb4000)

PHPソースコード内でのDB接続にはPDOクラスを使用するが，PDOクラスはよく使用するため外部モジュールに関数化することにした．また，DB登録機能追加版として気象情報表示用ページ`weather_info.php`のソースコードの一部に下記コードを追記した．なお`weather_info.php`の完全版のソースコードは後述にて記載する．

```php
# DB接続処理
try {

    # DB名
    $dbname = 'weather';

    # 最新の気象更新時間と都市名を取得するSQL
    $sql = "select datetime, updated from " . $dbname . ".weather_data order by updated desc limit 1";

    # DB接続関数
    $stmt = db_connect($dbname, $sql);

    # 最新の気象更新日時を取得
    foreach ($stmt as $value):
        $latest = $value[0];
    endforeach;

    #null処理
    $stmt = null;
    $sql = null;

    # 日時（DB用の書式）
    $datetime = getDatetime($response, 'Y-m-d H:i:s');

    # システム日時
    $now = date('Y-m-d H:i:s');

    /*
    〜DB登録条件は下記のいずれか〜
    1. DBに最新の気象更新日時が存在しない場合
    2. 最新の気象更新日時とDBに最新の気象更新日時が異なる場合
       かつ最新の気象更新日時がDBに最新の気象更新日時より後の場合
       かつ日時（DB用の書式）とシステム日時が一致しない場合
    */
    if (!isset($latest) || (($datetime != $latest) && ($datetime > $latest) && ($datetime != $now))):

        # 気象情報をDBに登録するsql（文字列はシングルクォーテーションで囲むこと）
        $sql  = "insert into " . $dbname . ".weather_data(datetime, day, city, weather, description, icon, ";
        $sql .= "temp, feels_like, temp_max, temp_min, humidity, pressure, speed, deg) ";
        $sql .= "values('$datetime', '$day', '$city', '$weather', '$description', '$icon', ";
        $sql .= "$temp, $feels_like, $temp_max, $temp_min, $humidity, $pressure, $speed, '$deg')";

        # DB接続関数
        $stmt = db_connect($dbname, $sql);

        # null処理
        $stmt = null;
        $sql = null;
    endif;

# 例外処理
} catch (PDOException $e) {
    header('Content-type: text/html; charset=utf-8', true, 500);
    exit('DB接続失敗' . $e->getMessage() . '-' . $e->getLine());
}
```

# Webサービスの作製

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

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

* **DBの作成 (MySQL)**

```sql
mysql> create database weather;
```

* **パスワードポリシーの確認 (MySQL)**

```sql
mysql> show variables like 'validate_password%';
```

* **必要に応じてパスワード強度の変更 (MySQL)**

```sql
mysql> set global validate_password_policy=LOW;
```

* **権限の付与 (MySQL)**  
**MySQL 5.7の場合**: `USERNAME`, `HOST`, `PASSWORD`を設定

```sql
mysql> grant all on weather.* to 'USERNAME'@'HOST' identified by 'PASSWORD';
```

* **権限の付与 (MySQL)**  
**MySQL 8の場合**: `USERNAME`, `HOST`, `PASSWORD`を設定

```sql
mysql> create user 'USERNAME'@'HOST' identified by 'PASSWORD' with grant option;
mysql> grant all on weather.* to 'USERNAME'@'HOST';
mysql> flush privileges;
```

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

```bash
$ sudo mkdir /Library/WebServer/Documents/WeatherInfo
$ sudo touch /Library/WebServer/Documents/WeatherInfo/weather_info.php
$ sudo mkdir /Library/WebServer/Documents/WeatherInfo/common
$ sudo touch /Library/WebServer/Documents/WeatherInfo/common/header.php

# 新規追加
$ sudo touch /Library/WebServer/Documents/WeatherInfo/common/db_connect.php

# 新規追加
$ sudo touch /Library/WebServer/Documents/WeatherInfo/common/timezone.php

$ sudo mkdir /Library/WebServer/Documents/WeatherInfo/my_module
$ sudo touch /Library/WebServer/Documents/WeatherInfo/my_module/openweathermap.php

# 新規追加
$ sudo touch /Library/WebServer/Documents/WeatherInfo/my_module/weather_table_creator.php

$ sudo mkdir /Library/WebServer/Documents/WeatherInfo/config
$ sudo touch /Library/WebServer/Documents/WeatherInfo/config/opn_wm_api_key.php

# 新規追加
$ sudo touch /Library/WebServer/Documents/WeatherInfo/config/db_info.php

$ sudo mkdir /Library/WebServer/Documents/WeatherInfo/css
$ sudo touch /Library/WebServer/Documents/WeatherInfo/css/weather_info_css.php
```

* **気象情報表示用ページ（DB登録機能追加版）`weather_info.php`のソースコード**  
**約10〜20分間隔でOpenWeatherMapから最新データが配信される．**

```php:weather_info.php
<?php
require_once __DIR__ . '/my_module/openweathermap.php';
require_once __DIR__ . '/common/header.php';
require_once __DIR__ . '/common/db_connect.php';
require_once __DIR__ . '/common/timezone.php';

# ページタイトル
$title = '気象情報';

# ブラウザ自動更新間隔（10分とする）
$reload_interval = 10 * 60;

# 都市名と都市コードの連想配列（新たな都市は都度追加）
$citycode1 = ['札幌' => 2128295, '東京都' => 1850144, '横浜市' => 1848354, '大阪市' => 1853909];
$citycode2 = ['名古屋市' => 1856057, '尾道市' => 1853992, '福岡市' => 1863967, '那覇市' => 1856035];
$citycode3 = ['北見市' => 2129537, '千歳市' => 2130452, '函館市' => 2130188];

# 上記配列をマージ
$citycodes = array_merge($citycode1, $citycode2, $citycode3);

/*
気象情報を取得したい都市名を上記連想配列から選択し入力
今回は北見市とする
*/
$city = $citycodes['北見市'];

# 表示形式と都市コードを指定しJSONを連想配列にデコード
$response = getAssociative('weather', $city);

# 日付の書式1（表示用）
$date1 = getDatetime($response, 'Y年m月d日');

# 日付の書式2（曜日取得用）
$date2 = getDatetime($response, 'Ymd', 'weather');

# 曜日（日本語化）
$day = getWeek($date2);

# 時刻の書式
$time = getDatetime($response, 'H:i');

# 都市名（英語表記）
$city = $response['name'];

# 都市名（日本語化）
$city = getCityName($city);

# 天気情報の配列
$weather_data = $response['weather'];

# 現在の天気（英語表記）
$weather = $weather_data[0]['main'];

# 現在の天気（日本語化）
$weather = getWeatherTranslation($weather);

# 天気の詳細（英語表記）
$description = $weather_data[0]['description'];

# 天気の詳細（日本語化）
$description = getDescriptionTranslation($description);

# 天気アイコン
$icon = $weather_data[0]['icon'];
$img = 'https://openweathermap.org/img/wn/' .$icon .'@2x.png';

# 絶対零度
$absolute_zero = -273.15;

# 温度情報のメイン階層
$temp_data = $response['main'];

# 現在の気温，体感温度，最高気温，最低気温（小数点以下四捨五入かつ摂氏表示）
$temp = round($temp_data['temp'] + $absolute_zero);
$feels_like = round($temp_data['feels_like'] + $absolute_zero);
$temp_max = round($temp_data['temp_max'] + $absolute_zero);
$temp_min = round($temp_data['temp_min'] + $absolute_zero);

# 湿度
$humidity = $temp_data['humidity'];

# 気圧
$pressure = $temp_data['pressure'];

# 風の情報のメイン階層
$wind_data = $response['wind'];

# 風速（小数点第二位で四捨五入）
$speed = round($wind_data['speed'], 1);

# 風向（16方位で日本語化)
$deg = getDirection($wind_data['deg']);

# DB接続処理
try {

    # DB名
    $dbname = 'weather';

    # 最新の気象更新時間と都市名を取得するSQL
    $sql = "select datetime, updated from " . $dbname . ".weather_data order by updated desc limit 1";

    # DB接続関数
    $stmt = db_connect($dbname, $sql);

    # 最新の気象更新日時を取得
    foreach ($stmt as $value):
        $latest = $value[0];
    endforeach;

    #null処理
    $stmt = null;
    $sql = null;

    # 日時（DB用の書式）
    $datetime = getDatetime($response, 'Y-m-d H:i:s');

    # システム日時
    $now = date('Y-m-d H:i:s');

    /*
    〜DB登録条件は下記のいずれか〜
    1. DBに最新の気象更新日時が存在しない場合
    2. 最新の気象更新日時とDBに最新の気象更新日時が異なる場合
       かつ最新の気象更新日時がDBに最新の気象更新日時より後の場合
       かつ日時（DB用の書式）とシステム日時が一致しない場合
    */
    if (!isset($latest) || (($datetime != $latest) && ($datetime > $latest) && ($datetime != $now))):

        # 気象情報をDBに登録するsql（文字列はシングルクォーテーションで囲むこと）
        $sql  = "insert into " . $dbname . ".weather_data(datetime, day, city, weather, description, icon, ";
        $sql .= "temp, feels_like, temp_max, temp_min, humidity, pressure, speed, deg) ";
        $sql .= "values('$datetime', '$day', '$city', '$weather', '$description', '$icon', ";
        $sql .= "$temp, $feels_like, $temp_max, $temp_min, $humidity, $pressure, $speed, '$deg')";

        # DB接続関数
        $stmt = db_connect($dbname, $sql);

        # null処理
        $stmt = null;
        $sql = null;
    endif;

# 例外処理
} catch (PDOException $e) {
    header('Content-type: text/html; charset=utf-8', true, 500);
    exit('DB接続失敗' . $e->getMessage() . '-' . $e->getLine());
}
?>
<!doctype html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="refresh" content="<?= $reload_interval; ?>">
    <!--
    時刻によって背景色と文字色を切り替えるCSSを設定
    PHPを使用するので拡張子はcssではなくphpにする
    -->
    <link rel="stylesheet" href="css/weather_info_css.php">
    <title><?= $title; ?></title>
  </head>
  <body>
    <font>
      <h1><?= $title; ?></h1>
      <div><span><?= $date1; ?></span><span><?= '（' . $day . '）' ?></span></div>
      <div><span><?= $time; ?></span> 時点の気象情報</div>
      <hr style="border:0;border-top:1px none;">
      <div>都市名　　: <span><?= $city; ?></span></div>
      <div>現在の天気: <span><?= $weather; ?></span></div>
      <div>天気の詳細: <span><?= $description; ?></span></div>
      <img src="<?= $img; ?>">
      <div>現在の気温: <span><?= $temp . '℃'; ?></span></div>
      <div>体感気温　: <span><?= $feels_like . '℃'; ?></span></div>
      <div>最高気温　: <span><?= $temp_max . '℃'; ?></span></div>
      <div>最低気温　: <span><?= $temp_min . '℃'; ?></span></div>
      <div>湿度　　　: <span><?= $humidity . '%'; ?></span></div>
      <div>気圧　　　: <span><?= $pressure . 'hPa'; ?></span></div>
      <div>風速　　　: <span><?= $speed . 'm/s'; ?></span></div>
      <div>風向　　　: <span><?= $deg . 'の風'; ?></span></div>
    </font>
  </body> 
</html>
```

* **ヘッダ設定ファイル`header.php`のソースコード**

```php:header.php
<?php
# HTMLを記述するコード内でのヘッダ設定
header('Content-type: text/html; charset=utf-8');
```

* **【新規追加】DB接続モジュール`db_connect.php`のソースコード**

```php:db_connect.php
<?php
require_once __DIR__ . '/../config/db_info.php';

# DB設定
function db_config($dbname) {

    # DB接続情報を外部ファイルから呼び出し（.gitignore済）
    $server = db_info()[0];
    $dsn = 'mysql: host=' . $server . '; dbname=' . $dbname . '; charset=utf8mb4; unix_socket=/tmp/mysql.sock';
    $user = db_info()[1];
    $pass = db_info()[2];

    # 戻り値
    return [$dsn, $user, $pass];
}

# DB名とsqlは引数（プレースホルダはデフォルトfalse）
function db_connect($dbname, $sql, $placeholder = 'false') {

    # DB設定情報を外部ファイルから呼び出し（.gitignore済）
    list($dsn, $user, $pass) = db_config($dbname);

    # PDOクラスのインスタンス作成
    $dbh = new PDO($dsn, $user, $pass);
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    if ($placeholder == 'true'):
        $stmt = $dbh->prepare($sql);
    elseif ($placeholder == 'false'):
        $stmt = $dbh->prepare($sql);
        $stmt->execute();
    endif;

    # null処理
    $dbh = null;

    # 戻り値は$stmt
    return $stmt;
}
```

* **【新規追加】タイムゾーン設定ファイル`timezone.php`のソースコード**

```php:timezone.php
<?php
# タイムゾーンの設定（設定値: 日本）
date_default_timezone_set('Asia/Tokyo');
```

* **メインモジュール`openweathermap.php`**  
**デメリットとして`ifconfig.me`に一旦アクセスするためレスポンスの取得が若干遅くなる．**

```php:openweathermap.php
<?php
require_once __DIR__ . '/../config/opn_wm_api_key.php';

# ネットワークエラー画面を表示する関数（5秒間隔でリロード再接続を試みる）
function networkError($msg, $sec = 5) {
?>
<!doctype html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="refresh" content="<?= $sec; ?>">
    <title><?= $msg; ?></title>
  </head>
  <body>
    <div><?= $msg; ?></div>
    <div><?= $sec; ?>秒後に再接続します</div>
  </body>
</html>
<?php
    exit();
}

/*
OpenWeatherMapのAPIを利用したJSONを取得し連想配列にデコードする関数
引数は表示形式と都市コード
*/
function getAssociative($api_type, $area_id) {

    # グローバルIPアドレスを調べるコマンド
    $command = 'curl -s ifconfig.me | grep \'^[21]\'';

    # 上記コマンドからレスポンス値を取得（グローバルIPアドレス）
    exec($command, $response);

    # グローバルIPアドレスが返ってこないの場合のエラーメッセージ
    $msg = 'Network Error..';

    # グローバルIPアドレスが返ってこないの場合はエラー画面を表示する
    !isset($response[0]) ? networkError($msg) : null;

    /*
    API Keyを外部ファイルから呼び出し（.gitignore済）
    OpenWeatherMapよりAPI Keyを取得して設定することも可能
    */
    $api_key = opn_wm_api_key();

    # ベースURL
    $api_base = 'https://api.openweathermap.org/data/2.5/';

    # ベースURLのパラメータ
    $api_parm = '?id=' . $area_id . '&appid=' . $api_key;

    # URLの連結
    $api_url = $api_base . $api_type . $api_parm;

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

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

    # 戻り値はデコードされた連想配列
    return $associative;
}

# 下記関数への引数（エラー制御演算子付き）
@$response = getAssociative($api_type, $area_id);

# 上記関数から取得した最新のUTCから指定した書式に変換する関数
function getDatetime($response, $format) {

    # 最新のUNIX時刻（UTC）
    $utc = $response['dt'];

    # DateTimeZoneオブジェクトのインスタンスを生成しJSTを設定
    $jst = new DateTimeZone('Asia/Tokyo');

    /*
    DateTimeオブジェクトのインスタンスを生成
    UNIX時刻を日時に変換
    */
    $datetime = new DateTime();

    # UTCをJSTに変換
    $datetime->setTimestamp($utc)->setTimeZone($jst);

    # 書式設定し年月日を取得
    $date = $datetime->format($format);

    return $date;
}

# 日本語表記の曜日を取得する関数
function getWeek($date) {

    # $date2をUNIX時刻に変換
    $day = strtotime($date);

    # 曜日の配列番号を取得
    $day = date('w', $day);

    # 曜日の配列
    $week = ['日', '月', '火', '水', '木', '金', '土'];

    # 連想配列の長さマイナス1
    $length = count($week) - 1;

    # 曜日の日本語表示の分岐
    for ($i = 0; $i <= $length; $i++):
        if ($day == $i):
            return $week[$i];
        endif;
    endfor;
}

# 都市名を日本語化する関数（新たな都市は都度追加）
function getCityName($city) {

    # 都市名の連想配列
    $list1 = ['Sapporo' => '札幌市', 'Tokyo' => '東京都', 'Yokohama' => '横浜市', 'Osaka' => '大阪市'];
    $list2 = ['Nagoya' => '名古屋市', 'Onomichi' => '尾道市', 'Fukuoka' => '福岡市', 'Naha' => '那覇市'];

    # 上記配列をマージ
    $lists = array_merge($list1, $list2);

    # 連想配列の長さマイナス1
    $length = count($lists) - 1;

    # 連想配列のキー
    $keys = array_keys($lists);

    # 連想配列の値
    $values = array_values($lists);

    # 都市名の日本語表示の分岐
    for ($i = 0; $i <= $length; $i++):
        if($city == $keys[$i]):
            return  $values[$i];
        endif;
    endfor;
}

# 現在の天気を翻訳する関数
function getWeatherTranslation($weather) {

    # 翻訳用天気名の連想配列（新たな英語表記の天気名が表示された場合は都度追加）
    $list = ['Clear' => '晴れ', 'Clouds' => 'くもり', 'Rain' => '雨', 'Snow' => '雪', 'Mist' => '霧'];

    # 連想配列の長さマイナス1
    $length = count($list) - 1;

    # 連想配列のキー
    $keys = array_keys($list);

    # 連想配列の値
    $values = array_values($list);

    # 天気名の日本語化
    for ($i = 0; $i <= $length; $i++):
        if ($weather == $keys[$i]):
            return $values[$i];
        endif;
    endfor;
}

# 天気詳細を翻訳する関数
function getDescriptionTranslation($description) {
    switch ($description):
        case 'overcast clouds':
            return 'どんよりした雲<br class="nosp">（雲85~100%）';
            break;
        case 'broken clouds':
            return '千切れ雲<br class="nosp">（雲51~84%）';
            break;
        case 'scattered clouds':
            return '散らばった雲<br class="nosp">（雲25~50%）';
            break;
        case 'few clouds':
            return '少ない雲<br class="nosp">（雲11~25%）';
            break;
        case 'light rain':
            return '小雨';
            break;
        case 'moderate rain':
            return '雨';
            break;
        case 'heavy intensity rain':
            return '大雨';
            break;
        case 'very heavy rain':
            return '激しい大雨';
            break;
        case 'clear sky':
            return '快晴';
            break;
        case 'shower rain':
            return 'にわか雨';
            break;
        case 'light intensity shower rain':
            return '小雨のにわか雨';
            break;
        case 'heavy intensity shower rain':
            return '大雨のにわか雨';
            break;
        case 'thunderstorm':
            return '雷雨';
            break;
        case 'snow':
            return '雪';
            break;
        case 'light snow':
            return '小雪';
            break;
        case 'light shower snow':
            return '弱いにわか雪';
            break;
        case 'mist':
            return '靄';
            break;
        case 'tornado':
            return '強風';
            break;
        default:
            return $description;
    endswitch;
}

# 16方位により風向を取得する関数
function getDirection($deg) {
    if ($deg >= 0 && $deg <= 10):
        return '北';
    elseif ($deg >= 11 && $deg <= 29):
        return '北北東';
    elseif ($deg >= 30 && $deg <= 60):
        return '北東';
    elseif ($deg >= 61 && $deg <= 79):
        return '東北東';
    elseif ($deg >= 80 && $deg <= 100):
        return '東';
    elseif ($deg >= 101 && $deg <= 119):
        return '東南東';
    elseif ($deg >= 120 && $deg <= 150):
        return '南東';
    elseif ($deg >= 151 && $deg <= 169):
        return '南南東';
    elseif ($deg >= 170 && $deg <= 190):
        return '南';
    elseif ($deg >= 191 && $deg <= 209):
        return '南南西';
    elseif ($deg >= 210 && $deg <= 240):
        return '南西';
    elseif ($deg >= 241 && $deg <= 259):
        return '西南西';
    elseif ($deg >= 260 && $deg <= 280):
        return '西';
    elseif ($deg >= 281 && $deg <= 299):
        return '西北西';
    elseif ($deg >= 300 && $deg <= 330):
        return '北西';
    elseif ($deg >= 331 && $deg <= 349):
        return '北北西';
    elseif ($deg >= 350 && $deg <= 360):
        return '北';
    endif;
}
```

* **【新規追加】テーブル作成実行ファイル`weather_table_creator.php`のソースコード**  
**こちらはMySQLにSQL直打ちでも構わない**

```php:weather_table_creator.php
<?php
require_once __DIR__ . '/../common/db_connect.php';

# テーブルを生成する関数
function weather_table_creator() {

    # DB名
    $dbname = 'weather';

    # テーブルを生成するSQL
    $sql  = "create table if not exists " . $dbname . ".weather_data(num int(10) not null auto_increment, ";
    $sql .= "datetime varchar(20), day varchar(2), city varchar(10), weather varchar(10), description varchar(50), ";
    $sql .= "icon varchar(5), temp int(4), feels_like int(4), temp_max int(4), temp_min int(4), humidity int(3), ";
    $sql .= "pressure int(5), speed float, deg varchar(10), ";
    $sql .= "updated datetime not null default current_timestamp on update current_timestamp, primary key(num))";

    db_connect($dbname, $sql);
 }

# 上記関数を実行
weather_table_creator();
```

* **【新規追加】API Key記述ファイル`opn_wm_api_key.php`のソースコード（下記戻り値に`API Key`を設定）**  
**GitHub等でのバージョン管理を考慮して外部ファイルとした．（.gitignoreに記述を推奨）**

```php:opn_wm_api_key.php
<?php
# OpenWeatherMapのAPI Keyを返す関数
function opn_wm_api_key() {
    $api_key = 'API Key';

    return $api_key;
}
```

* **【新規追加】DB設定情報ファイル`db_info.php`のソースコード（下記戻り値に`HOST`, `USERNAME`, `PASSWORD`を設定）**  
**GitHub等でのバージョン管理を考慮して外部ファイルとした．（.gitignoreに記述を推奨）**

```php:db_info.php
<?php
# DB接続情報を返す関数
function db_info() {
    $ip = 'HOST';
    $user = 'USERNAME';
    $pass = 'PASSWORD';

    return [$ip, $user, $pass];
}
```

* **画面装飾ファイル`weather_info_css.php`のソースコード**  
**PHPでCSSを制御しシステム時刻により背景色・文字色を切り替える．**  
**参照:** [PHPでCSSファイルを制御する](https://qiita.com/rimorimo/items/d8a3f2e82d30c02a8ec8#php%E3%81%A7css%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E5%88%B6%E5%BE%A1%E3%81%99%E3%82%8B)

```php:weather_info_css.php
<?php
# タイムゾーンの設定
require_once __DIR__ . '/../common/timezone.php';

# CSSを記述するコード内でのヘッダ設定
header('Content-Type: text/css; charset=utf-8');

# 現在のシステム時刻（ゼロなしの時）
$now = date('G');

# システム時刻5:00〜16:59までとそれ以外の時刻で背景色と文字色を切り替える
$bgcol = $now >= 5 && $now < 17 ? '#87ceeb' : '#000033';
$ftcol = $now >= 5 && $now < 17 ? '#000000' : '#ffffff';
?>
body {
  background-color: <?= $bgcol; ?>;
  color: <?= $ftcol; ?>;
}
```

* **テーブル作成実行ファイル`weather_table_creator.php`の実行**

```bash
$ php /Library/WebServer/Documents/WeatherInfo/my_module/weather_table_creator.php
```

* **上記実行ファイルによって作成されたテーブル構成の確認**

```sql
mysql> desc weather.weather_data;
+-------------+-------------+------+-----+-------------------+-----------------------------+
| Field       | Type        | Null | Key | Default           | Extra                       |
+-------------+-------------+------+-----+-------------------+-----------------------------+
| num         | int(10)     | NO   | PRI | NULL              | auto_increment              |
| datetime    | varchar(20) | YES  |     | NULL              |                             |
| day         | varchar(2)  | YES  |     | NULL              |                             |
| city        | varchar(10) | YES  |     | NULL              |                             |
| weather     | varchar(10) | YES  |     | NULL              |                             |
| description | varchar(50) | YES  |     | NULL              |                             |
| icon        | varchar(5)  | YES  |     | NULL              |                             |
| temp        | int(4)      | YES  |     | NULL              |                             |
| feels_like  | int(4)      | YES  |     | NULL              |                             |
| temp_max    | int(4)      | YES  |     | NULL              |                             |
| temp_min    | int(4)      | YES  |     | NULL              |                             |
| humidity    | int(3)      | YES  |     | NULL              |                             |
| pressure    | int(5)      | YES  |     | NULL              |                             |
| speed       | float       | YES  |     | NULL              |                             |
| deg         | varchar(10) | YES  |     | NULL              |                             |
| updated     | datetime    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+-------------+-------------+------+-----+-------------------+-----------------------------+
16 rows in set (0.00 sec)
```

# 動作確認

**Apacheの起動**

```bash
$ sudo apachectl start
```

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

**システム時刻5:00〜16:59**
![ss1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478088/3e2e39c1-81b1-d309-d640-9bfb6abee44b.png)

**システム時刻17:00〜翌日4:59**
![ss2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478088/f3cdec06-8f48-d6f7-e2dc-8a113d90585d.png)

**システム時刻により背景色・文字色を切り替わることが確認された．**

# 自動登録された気象情報をブラウザで一覧表示してみる

* **一覧表示用ファイルの作成**

```bash
$ sudo touch /Library/WebServer/Documents/WeatherInfo/weather_log.php
```

* **一覧表示用ファイル`weather_log.php`のソースコード**

```php:weather_log.php
<?php
require_once __DIR__ . '/common/header.php';
require_once __DIR__ . '/common/db_connect.php';
require_once __DIR__ . '/common/timezone.php';

$title = '気象データ一覧';

$minutes = 60;

# ブラウザ自動更新間隔（10分とする）
$reload_interval = 10 * 60;

# 項目名一覧
$itm1 = ['No', '更新日時', '曜日', '都市名', '天気', '詳細', 'アイコンID'];
$itm2 = ['気温', '体感気温', '最高気温', '最低気温', '湿度', '気圧', '風速', '風向'];
$item = array_merge($itm1, $itm2);

# DB接続処理
try {

    # DB名
    $dbname = 'weather';

    # プレースホルダなし
    $sql  = "select num, datetime, day, city, weather, description, icon, temp, feels_like, temp_max, ";
    $sql .= "temp_min, humidity, pressure, speed, deg from " . $dbname . ".weather_data order by num desc";

    # DB接続関数
    $stmt = db_connect($dbname, $sql);

    # sqlのヒット件数取得
    $rowcnt = $stmt->rowCount();

    # sqlのカラム数取得
    $colcnt = $stmt->columnCount();

    # 取得値（FETCH_ASSOCだと配列番号指定できない）
    $result = $stmt->fetchAll(PDO::FETCH_BOTH);

    # null処理
    $stmt = null;
    $sql = null;

# 例外処理
} catch (PDOException $e) {
    header('Content-type: text/html; charset=utf-8', true, 500);
    exit('データベース接続失敗。' . $e->getMessage() . '-' . $e->getLine());
}
?>
<!doctype html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="refresh" content="<?= $reload_interval; ?>">
    <title><?= $title; ?></title>
  </head>
  <body>
    <h1><?= $title; ?></h1>
    <p><b>全<?= $rowcnt; ?>件</b></p>
    <table border="1">
      <tr>
<?php
# 項目名の表示
foreach ($item as $clm):
?>
        <th><?= $clm; ?></th>
<?php
endforeach;
?>
      </tr>
<?php
# ヒットしたデータの表示
foreach ($result as $row):
?>
      <tr>
<?php
    for ($i = 0; $i <= $colcnt - 1; $i++):
?>
        <td align="center"><?= $row[$i]; ?></td>
<?php
    endfor;
?>
      </tr>
<?php
endforeach;
?>
    </table>
  </body>
</html>
```

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

* **データ取得日時が降順に表示される**
![list.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478088/1d2496db-148e-e667-b849-f5d3478aa786.png)