あーる学習帳

自分が勉強したことや気になることなど、99%自分用です。コードを書いてるのでPCから閲覧を推奨。

開発日記:GithubとSourcetreeでソースを公開

スプラトゥーン2ガチホコでついにSに昇格できたので気持ちよく勉強していきます。

 

いろいろいじった住所録ソフトをいよいよGithub上に公開していく。

今回は以下の記事を参考に、SourcetreeというソフトとGithubを組み合わせる。

qiita.com

Githubでアカウントを取得し、Sourcetreeのインストールが終わった段階から話を進める。SourcetreeとGithubを連携させる手順は以下の通り。

  1. Sourcetreeのツールバー、「ツール」>「オプション」を選択
  2. 「認証」タブでGithubのアカウントのIDやパスワードを入力
  3. Github側でSourcetreeと連携させたいリポジトリを作成、そのURLを取得
  4. Sourcetreeの新しいタブで「Clone」を選択し、必要事項を入力。なお、ローカル側に連携するフォルダを作成する必要がある。
  5. フォルダを作成後、そこにGit管理するソースやフォルダを放り込めばOK。なお、また新しいリポジトリを作成した場合は3~5を繰り返す。

ざっくりとこんな感じ。メモ用なので…。

自分の場合は開発をVagrant仮想マシンで行い、ファイル転送ソフトCyberduckでそのマシンと自分のPCの間でファイルのやりとりをしていた。

Cyberduckから直接アップできるのが一番手っ取り早いが、今回はCyberduckからローカルへソースコードをダウンロード、そこからGithubへアップする流れとする。

連携するフォルダへソースを放り込めば、Sourcetreeの方でもそれを感知する。それをコミットやプルという形でGithubへアップ。以上が一連の流れである。

用語などについてはまだまだ不完全、Readme.mdについても独自の書き方があるようなので、折を見て勉強していこう。

開発日記:Githubで公開?

一昨日まで作っていた住所録について、Githubに公開してみました。

URLはまだ貼りませんが、

  1. Githubでアカウント作成&リポジトリ作成
  2. Sourcetreeをパソコンにインストール
  3. Cyberduckから開発したPHPファイル群をローカル環境に入れて、ローカル環境からSourcetreeへ反映、そのあとGithubにコミット

というややこしい流れを組んでおります。というかこのやり方だとMySQLのテーブルがGithubに行かないけどいいんだろうか…。

とりあえずはReadme.mdの書き方、MySQLのテーブルの扱いなどもまとめて後日改めて紹介します。

2日続けてブログ書かないとモチベーションが一気に下がるかもしれないという危機感があったので、とりあえず今日はこれだけ。

PHP開発日記:GETで検索(1ページにつきn件表示)

昨日からの課題だった、「1ページにつきn件の検索結果を表示、画面下部のリンクで切り替え」という機能について研究。

今日変更したphpファイルの変更箇所は以下の通り。

 

config.php

//検索設定
define('SEARCH_DISP_LIMIT',100); 

1ページあたりの表示件数を定数として設定。上記の設定では1ページあたり100件の検索結果が表示される。

 

db_helper.php

function search_address($dbh,$addno,$add1,$add2,$add3,$pagecount){
 $result=;
 $sql="select addno,add1,add2,add3 from address where 1=1";
 if ($addno!=='') {
  $sql=$sql." and addno like :addno";
 }
 if ($add1!=='指定しない') {
  $sql=$sql." and add1 like :add1";
 }
 if ($add2!=='') {
  $sql=$sql." and add2 like :add2";
 }
 if ($add3!=='') {
  $sql=$sql.=" and add3 like :add3";
 }
 $sql=$sql." order by addno limit :pagecount , :pagelimit";
 $stmt=$dbh->prepare($sql);
 if ($addno!=='') {
  $stmt->bindvalue(':addno','%'.$addno.'%',PDO::PARAM_STR);
 }
 if ($add1!=='指定しない') {
  $stmt->bindvalue(':add1','%'.$add1.'%',PDO::PARAM_STR);
 }
 if ($add2!=='') {
  $stmt->bindvalue(':add2','%'.$add2.'%',PDO::PARAM_STR);
 }
 if ($add3!=='') {
  $stmt->bindvalue(':add3','%'.$add3.'%',PDO::PARAM_STR);
 }
 $stmt->bindvalue(':pagecount',$pagecount,PDO::PARAM_INT);
 $stmt->bindvalue(':pagelimit',SEARCH_DISP_LIMIT,PDO::PARAM_INT);
 $stmt->execute();
 while ($row=$stmt->fetch(PDO::FETCH_ASSOC)) {
  $result
=$row;
 }
 return $result;
}

function count_address($dbh,$addno,$add1,$add2,$add3,$pagecount){
 $count=0;
 $sql="select count(addno) as count from address where 1=1";
 if ($addno!=='') {
  $sql=$sql." and addno like :addno";
 }
 if ($add1!=='指定しない') {
  $sql=$sql." and add1 like :add1";
 }
 if ($add2!=='') {
  $sql=$sql." and add2 like :add2";
 }
 if ($add3!=='') {
  $sql=$sql.=" and add3 like :add3";
 }
 $stmt=$dbh->prepare($sql);
 if ($addno!=='') {
  $stmt->bindvalue(':addno','%'.$addno.'%',PDO::PARAM_STR);
 }
 if ($add1!=='指定しない') {
  $stmt->bindvalue(':add1','%'.$add1.'%',PDO::PARAM_STR);
 }
 if ($add2!=='') {
  $stmt->bindvalue(':add2','%'.$add2.'%',PDO::PARAM_STR);
 }
 if ($add3!=='') {
  $stmt->bindvalue(':add3','%'.$add3.'%',PDO::PARAM_STR);
 }
 $stmt->execute();
 while ($row=$stmt->fetch(PDO::FETCH_ASSOC)) {
  $count=$row;
 }
 return $count;
}

search_address関数で検索結果を、count_address関数で全体の件数を取得。search_addressについて、SQLの最後にlimit x , yを使っているのがポイント(検索結果のx件目からy件表示する)。count_addressでは全体のレコード数をcountを使って取得している。

 

search.php

if (isset($_GET['addno']) or isset($_GET['add1']) or isset($_GET['add2']) or isset($_GET['add3'])) {
 $addno=$_GET['addno'];
 $addno=html_escape($addno);
 $add1=$_GET['add1'];
 $add1=html_escape($add1);
 $add2=$_GET['add2'];
 $add2=html_escape($add2);
 $add3=$_GET['add3'];
 $add3=html_escape($add3);
 $page=$_GET['page'];
 $page=(int)html_escape($page);
 $result=[];
 $tmpcount=0;
 $count=0;
 $dbh=get_db_connect();
 // 検索、レコード取得
 $result=search_address($dbh,$addno,$add1,$add2,$add3,$page);
 $tmpcount=count_address($dbh,$addno,$add1,$add2,$add3);
 $count=intval($tmpcount['count']);

 $_GET['page']を新しく設定。これで何ページ目にあたるデータを表示するのかを指定する。数値型に変換して$pageに格納する。

 

serach_view.php

<!-- 表示 -->
<?php if(isset($addno) or isset($add1) or isset($add2) or isset($add3)): ?>
 <?php if ($count>0): ?>
  <?php if($count>$page+SEARCH_DISP_LIMIT): ?>
   <p><?php echo $count; ?>件ヒット!(<?php echo $page+1;?>~<?php echo $page+SEARCH_DISP_LIMIT;?>件目)</p>
  <?php else: ?>
   <p><?php echo $count; ?>件ヒット!(<?php echo $page+1;?>~<?php echo $count;?>件目)</p>
  <?php endif; ?>
  <table border="1">
   <tr>
    <th>郵便番号</th>
    <th>都道府県</th>
    <th>市区町村</th>
    <th>町域</th>
   </tr>
   <?php foreach ($result as $row): ?>
    <tr>
     <td><?php echo $row['addno']; ?></td>
     <td><?php echo $row['add1']; ?></td>
     <td><?php echo $row['add2']; ?></td>
     <td><?php echo $row['add3']; ?></td>
    </tr>
   <?php endforeach; ?>
  </table>
<!-- n件ごとに1ページ表示 -->
  <?php for($i=0;$i<=$count;$i+=SEARCH_DISP_LIMIT) {?>
   <?php $pagecount+=1 ?>
    <a href="search.php?addno=<?php echo $addno; ?>&add1=<?php echo $add1; ?>&add2=<?php echo $add2; ?>&add3=<?php echo $add3; ?>&page=<?php echo $i;?>"><?php echo $pagecount; ?></a>
   <?php } ?>
  <?php else: ?>
   <p>該当するデータはありません。</p>
 <?php endif; ?>
<?php endif; ?> 

検索結果を表示する表を作った後、forループでaタグを量産していく。aタグのリンク先として現在のsearch.phpを指定するが、このときにGETで渡す値もタグにべた書きする。こうすることでリンクが改行されることなく並ぶ。

 

これらの変更を行い、検索画面は以下のようになった。

f:id:R_de_aru:20180713235106p:plain

検索条件を指定せずに検索。本来ならば12万件以上のデータを表示しようとするが、今回の変更によって「12万行のうちの1~100件目」のみを表示するようになったのでとても処理が軽い。

ページ下部は以下のようになっている。

f:id:R_de_aru:20180713235231p:plain

検索結果が多すぎるのでリンクが無数に並んでいるが、やりたかったことは達成した。このうち「2」のリンクをクリックする。

f:id:R_de_aru:20180713235315p:plain

「2」のリンクをクリックすると画面が切り替わり、101件目~200件目を表示するようになった。

 

今回の住所録機能についてはこれで完成とする。

意外とサクサク進んでいる。明日からはTwitterのAPIを使うための勉強を行う予定。というかJSON?が必要らしいんだけど何それレベルなのでまずはそこから。Javascriptがこの環境で使えるのかどうか…。

PHP開発日記:GETで検索(全件表示)

今夜は昨日作ったaddressテーブルからGETでデータを取得する部分を作成しました。

index.phpからコントローラであるsearch.phpに飛ばし、そこからフォームや結果を表示するためのsearch_view.phpに飛ぶ構成です。

 

search.php

<?php
 //設定&関数読み込み
 require_once('config.php');
 require_once('./helper/db_helper.php');
 require_once('./helper/extra_helper.php');

 if (isset($_GET['addno']) or isset($_GET['add1']) or isset($_GET['add2']) or isset($_GET['add3'])) {
  $addno=$_GET['addno'];
  $addno=html_escape($addno);
  $add1=$_GET['add1'];
  $add1=html_escape($add1);
  $add2=$_GET['add2'];
  $add2=html_escape($add2);
  $add3=$_GET['add3'];
  $add3=html_escape($add3);
  $result=;
  $dbh=get_db_connect();
  $result=search_address($dbh,$addno,$add1,$add2,$add3);
}

 //都道府県一覧を取得して配列にセット
 $ken=;
 $dbh=get_db_connect();
 $ken=get_ken($dbh);

 //検索・表示用PHPファイル呼び出し
 include_once('./view/search_view.php');

viewファイルからGETでここにデータを飛ばし、$_GET['addno']、$_GET['add1']、$_GET['add2']、$_GET['add3']のいずれかが存在していたら検索する。

また、viewファイルで使う都道府県一覧もここで取得する。

html_escapeはextra_helper.phpから呼び出した関数である。

 

search_view.php

<!DOCTYPE html>
<head>
 <meta charset="utf-8">
 <title>住所検索</title>
</head>
<body>
 <!-- 検索条件入力 -->
 <h3>検索条件指定</h3>
 <form action="" method="GET">
  <p>郵便番号:<input type="text" name="addno"> ※「0000000」の形で入力してください</p>
  <p>都道府県:
   <select name="add1">
    <option value="指定しない">指定しない</option>
    <?php foreach($ken as $data): ?>
     <option value="<?php echo $data['add1'];?>"><?php echo $data['add1'];?></option>
    <?php endforeach; ?>
   </select>
  </p>
  <p>市区町村:<input type="text" name="add2"></p>
  <p>町域:<input type="text" name="add3"></p>
  <input type="submit" value="検索!">
 </form>
 <br>
 <!-- 表示 -->
 <?php if(isset($addno) or isset($add1) or isset($add2) or isset($add3)): ?>
  <table border="1">
   <tr>
    <th>郵便番号</th>
    <th>都道府県</th>
    <th>市区町村</th>
    <th>町域</th>
   </tr>
   <?php foreach ($result as $row): ?>
    <tr>
     <td><?php echo $row['addno']; ?></td>
     <td><?php echo $row['add1']; ?></td>
     <td><?php echo $row['add2']; ?></td>
     <td><?php echo $row['add3']; ?></td>

    </tr>
   <?php endforeach; ?>
  </table>
 <?php endif; ?>
</body> 

検索条件を入力する部分と検索結果を表示する部分を1ページで表示する。検索結果についてはGETでこのページを呼び出したときのみ表示。

都道府県名については別途関数で取得してコンボボックスへ。

 

db_helper.php(追加部分)

function get_ken($dbh){
 $ken=;
 $sql="select max(x.kenid) as id ,max(x.add1) as add1 from address as x group by x.add1 order by x.kenid asc";
 $stmt=$dbh->prepare($sql);
 $stmt->execute();
 while ($row=$stmt->fetch(PDO::FETCH_ASSOC)) {
  $ken
=$row;
 }
 return $ken;
}

function search_address($dbh,$addno,$add1,$add2,$add3){
 $result=;
 $sql="select addno,add1,add2,add3 from address where 1=1";
 if ($addno!=='') {
  $sql=$sql." and addno like :addno";
 }
 if ($add1!=='指定しない') {
  $sql=$sql." and add1 like :add1";
 }
 if ($add2!=='') {
  $sql=$sql." and add2 like :add2";
 }
 if ($add3!=='') {
  $sql=$sql.=" and add3 like :add3";
 }
 $sql=$sql." order by addno";
 //プリペア、置換
 $stmt=$dbh->prepare($sql);
 if ($addno!=='') {
  $stmt->bindvalue(':addno','%'.$addno.'%',PDO::PARAM_STR);
 }
 if ($add1!=='指定しない') {
  $stmt->bindvalue(':add1','%'.$add1.'%',PDO::PARAM_STR);
 }
 if ($add2!=='') {
  $stmt->bindvalue(':add2','%'.$add2.'%',PDO::PARAM_STR);
 }
 if ($add3!=='') {
  $stmt->bindvalue(':add3','%'.$add3.'%',PDO::PARAM_STR);
 }
 $stmt->execute();
 while ($row=$stmt->fetch(PDO::FETCH_ASSOC)) {
  $result
=$row;
 }
 return $result;
}

どちらもaddressテーブルを検索して結果を配列として取得している。なお、addressテーブルについてはフィールド「kenid(データ型:int)」を追加して都道府県別に番号を振った。

これが検索条件を入れる前の状態。

f:id:R_de_aru:20180712225210p:plain

検索条件を入力するとこうなる。

f:id:R_de_aru:20180712225245p:plain

明日からはこの状態をベースに、「1ページに検索結果を10件表示し、GoogleやYahooの検索のように2ページ目や3ページ目を表すボタンを押したら11~20件目、21~30件目が表示される」という仕様を作っていく。

 

PHP開発日記:CSVの文字コードとの闘い

昨晩から日本全国の住所が入ったCSVファイルと格闘中。

目下の問題点は「文字化け」。昨日以下のコードで試しに画面に出してみたが、見事に文字化けしていた。

//csv読み込み処理

<?php
 $file=@fopen('./csv/KEN_ALL.CSV','r') or die('ファイルを開けませんでした');
 //var_dump($file);
 flock($file,LOCK_SH);
 //とりあえず画面に出してみよう
 while (!feof($file)) {
  $line=fgets($file);
  echo '<p>'.$line.'</p>';
 }
 flock($file,LOCK_UN);
 fclose($file); 

 何か方法はないかと調べていたところ、Githubに以下の書き込みを発見。

gist.github.com

 

GithubのURLを貼り付けるとコードがそのまま見れる…!!今回はこのコードをお借りする。

試しにエンコーディング処理まで書いてvar_dump()。

<?php

 setlocale(LC_ALL, 'ja_JP.UTF-8');

 require_once('config.php');
 require_once('./helper/db_helper.php');
 require_once('./helper/extra_helper.php');

 $csv = array();
 $file = './csv/KEN_ALL.CSV';


 //エンコーディング処理
 $data = file_get_contents($file); // string型に変換
 $data = mb_convert_encoding($data, 'UTF-8', 'sjis-win'); //この関数を通すことで文字化けが解消される
 var_dump($data);

f:id:R_de_aru:20180711203945p:plain

見事、文字化けせずに出力。これをCSVの1行に沿った形で改行して表示したい。

<?php

 setlocale(LC_ALL, 'ja_JP.UTF-8');

 require_once('config.php');
 require_once('./helper/db_helper.php');
 require_once('./helper/extra_helper.php');

 $csv = array();
 $file = './csv/KEN_ALL.CSV';
 //エンコーディング処理
 $data = file_get_contents($file); // string型に変換
 $data = mb_convert_encoding($data, 'UTF-8', 'sjis-win'); //この関数を通すことで文字化けが解消される
 //初期化・一時ファイル作成
 $temp = tmpfile();
 $csv = array();
 $count=0;
 fwrite($temp, $data); //$tempファイルに、$dataの内容を書き込む。
 rewind($temp); //ファイルポインタを先頭の位置に戻す
 while *1 !== FALSE) {
  $data = implode(",", $data); //一行づつ、配列を","で連結して、文字列として返す
  $csv[] = htmlentities($data);
  echo $csv[$count].'<br>';
 $count+=1;
 }

これでやっと以下の形で表示された。

f:id:R_de_aru:20180711211033p:plain

次は二次元配列として、「$ken[0][8]='札幌市中央区'」のような形でカンマ区切りのフィールドごとに配列に入れたい。

<?php

 setlocale(LC_ALL, 'ja_JP.UTF-8');

 require_once('config.php');
 require_once('./helper/db_helper.php');
 require_once('./helper/extra_helper.php');

 $csv = array();
 $kendata=array();
 $file = './csv/KEN_ALL.CSV';
 //エンコーディング処理
 $data = file_get_contents($file); // string型に変換
 $data = mb_convert_encoding($data, 'UTF-8', 'sjis-win'); //この関数を通すことで文字化けが解消される
 //初期化・一時ファイル作成
 $temp = tmpfile();
 $csv = array();
 $count=0;
 fwrite($temp, $data); //$tempファイルに、$dataの内容を書き込む。
 rewind($temp); //ファイルポインタを先頭の位置に戻す
 while *2 !== FALSE) {
  $data = implode(",", $data); //一行づつ、配列を","で連結して、文字列として返す
  $csv= explode(",", $data); //カンマ区切りで配列の要素に分解
  $kendata[$count]=$csv;
  echo $kendata[$count][6].$kendata[$count][7].$kendata[$count][8].'<br>';
  $count+=1;
  if($count>10){

   break;
  }
 }
 echo "<br>end";

1時間くらい粘った…。

f:id:R_de_aru:20180711214419p:plain

これで二次元配列として住所を取り込むことができるようになった。

今回の二次元配列の構成は以下の通り。

$kendata[$count]→csvの1行

$kendata[$count][2]→郵便番号

$kendata[$count][3]→都道府県(半角カナ)

$kendata[$count][4]→市区町村(半角カナ)

$kendata[$count][5]→町域(半角カナ)

$kendata[$count][6]→都道府県(漢字)

$kendata[$count][7]→市区町村(漢字)

$kendata[$count][8]→町域(漢字)

ちなみに「implodeしないでexplodeだけでいいのでは…?」と思ったが、implodeをコメントアウトするとうまく動かなかった。

これからさらにMySQLへの取り込みを行う。

db_helper.phpに以下の関数を作成。

function insert_ken($dbh,$addno,$add1,$add1kana,$add2,$add2kana,$add3,$add3kana){
 //sql作成
 $sql ="insert into address";
 $sql+="(addno,add1,add1kana,add2,add2kana,add3,add3kana) values ";
 $sql+="(:addno,:add1,:add1kana,:add2,:add2kana,:add3,:add3kana)";
 //プリペア、引数置換
 $stmt=$dbh->prepare($sql);
 $stmt->bindvalue(':addno',$addno,PDO::PARAM_STR);
 $stmt->bindvalue(':add1',$add1,PDO::PARAM_STR);
 $stmt->bindvalue(':add1kana',$add1kana,PDO::PARAM_STR);
 $stmt->bindvalue(':add2',$add2,PDO::PARAM_STR);
 $stmt->bindvalue(':add2kana',$add2kana,PDO::PARAM_STR);
 $stmt->bindvalue(':add3',$add3,PDO::PARAM_STR);
 $stmt->bindvalue(':add3kana',$add3kana,PDO::PARAM_STR);
 if ($stmt->execute()) {
  return true;
 } else {
  return false;
 }
}

作成したはいいが、引数が長い…。配列の形で渡せないかどうかをテストしてみる。

db_hepler.phpに以下の関数を作り、csvget.phpから呼び出してみる。

function test($array){
 var_dump($array);

//csvget.php

<?php

 //~中略

 test($kendata[10]); 

☆結果

array(15) { [0]=> string(5) "01101" [1]=> string(5) "064 " [2]=> string(7) "0640822" [3]=> string(21) "ホッカイドウ" [4]=> string(36) "サッポロシチュウオウク" [5]=> string(44) "キタ2ジョウニシ(20-28チョウメ)" [6]=> string(9) "北海道" [7]=> string(18) "札幌市中央区" [8]=> string(39) "北二条西(20~28丁目)" [9]=> string(1) "1" [10]=> string(1) "0" [11]=> string(1) "1" [12]=> string(1) "0" [13]=> string(1) "0" [14]=> string(1) "0" } 

二次元配列が普通の一次元配列になっている。ということで、insert_ken()関数を以下のように引数を配列にする形で書き換え。

function insert_ken($dbh,$kendata){
 //SQL作成
 $sql="insert into address  (addno,add1,add1kana,add2,add2kana,add3,add3kana)
values (:addno,:add1,:add1kana,:add2,:add2kana,:add3,:add3kana)";
 //プリペア、引数置換
 $stmt=$dbh->prepare($sql);
 $stmt->bindValue(':addno',$array[2],PDO::PARAM_STR);
 $stmt->bindValue(':add1',$array[3],PDO::PARAM_STR);
 $stmt->bindValue(':add1kana',$array[4],PDO::PARAM_STR);
 $stmt->bindValue(':add2',$array[5],PDO::PARAM_STR);
 $stmt->bindValue(':add2kana',$array[6],PDO::PARAM_STR);
 $stmt->bindValue(':add3',$array[7],PDO::PARAM_STR);
 $stmt->bindValue(':add3kana',$array[8],PDO::PARAM_STR);
 if($stmt->execute()){
  return true;
 } else {
  return false;
 }

 作成したはいいが、二次元配列でforeachすると二次元配列の方を取得してしまう…。とりあえず今回は引数の長いバージョンで処理を行うことにする。この方がbindValueの処理も確実にできそうだし。

csvget.phpで配列に都道府県名を渡す処理の下にMySQLへのInsert処理を加える。

//MySQL取り込み処理
$count=0;
foreach ($kendata as $row) {
 if (insert_ken($dbh,$row[2],$row[3],$row[4],$row[5],$row[6],$row[7],$row[8])===false) {
  die('Error!('.$row[6].$row[7].$row[8].')');
 }
$count+=1;
}

echo "OK!";

いろいろ手直しをして、csvget.phpは以下のように生まれ変わった。

<?php

 setlocale(LC_ALL, 'ja_JP.UTF-8');

 ini_set('memory_limit', '512M');

 require_once('config.php');
 require_once('./helper/db_helper.php');
 require_once('./helper/extra_helper.php');

 //CSV取り込み処理
 $csv = array();
 $kendata=array();
 $file = './csv/KEN_ALL.CSV';
 //エンコーディング処理
 $data = file_get_contents($file); // string型に変換
 $data = mb_convert_encoding($data, 'UTF-8', 'sjis-win'); //この関数を通すことで文字化けが解消される
 //初期化・一時ファイル作成
 $temp = tmpfile();
 $csv = array();
 $count=0;
 fwrite($temp, $data); //$tempファイルに、$dataの内容を書き込む。
 rewind($temp); //ファイルポインタを先頭の位置に戻す
 while *3 !== FALSE) {
  $data = implode(",", $data); //一行づつ、配列を","で連結して、文字列として返す
  $csv= explode(",", $data); //一行ずつ、カンマ区切りしたフィールドを配列の要素として扱う
  $kendata[$count]=$csv;
  $count+=1;
 }
 echo $count.'件のデータを配列に格納完了!!<br>';

 //MySQL取り込み処理
 $count=0;
 $dbh=get_db_connect();
 foreach ($kendata as $row) {
  if ($row[8]!=='以下に掲載がない場合') {
   if (insert_ken($dbh,$row[2],$row[6],$row[3],$row[7],$row[4],$row[8],$row[5])===false) {
    die('Error!('.$row[6].$row[7].$row[8].')');
   }
   $count+=1;
  }
 }

echo $count.'件のデータをMySQLに取り込み完了!!<br>';

途中でメモリが足りなくなる事態が発生したので、ini_set('memory_limit', '512M');で一時的にphpが使えるメモリを増やした。

この処理を走らせることで、KEN_ALL.CSVから124228件のデータを配列に格納し、122354件のデータをMySQLに取り込むことができた。ちなみに、どうも郵便番号に重複があるらしく、郵便番号を主キーとしていると途中でエラーを出した。これについてはMySQLで「alter table address drop primary key;」を実行、主キーを削除して対応した。

今日はここまで。20時から風呂を挟んで3時間くらいやってました。明日は検索部分を作る予定。

*1:$data = fgetcsv($temp, 0, ","

*2:$data = fgetcsv($temp, 0, ","

*3:$data = fgetcsv($temp, 0, ","

PHP開発日記:しばらく住所録を作ります

作りたいものは1つ決まっているのですが、それまでにいくつかの過程を踏まなければならないようです。

  • PHPの基礎的な書き方(昨日までの学習)
  • TwitterAPIと会員登録システムの組み合わせ
  • 簡易的な検索エンジン
  • その他必要に応じて付け足し

APIの話は一朝一夕ではできる気がしないので、まずは「簡易的な検索エンジン」から取り組んでいきます。

日本郵便の公式ホームページで日本全国の住所録をcsv形式で手に入れることができるので、これを使って日本全国の住所を検索するシステムを作ります。

 要件定義

  1. 日本郵便のホームページから全国の住所一覧を入手、それをMySQLに取り込み。
  2. 検索条件を入力するため、「都道府県を選択するコンボボックス」「市町村以下をフリーワード検索するための入力欄」を用意する。
  3. 検索結果を表示するページではGoogleやYahooのように、検索してヒットした件数が多くなる場合は10件ごとに1ページ表示すること。検索してヒットしなかった場合、エラーを出す。
  4. 条件を入力するページと結果を表示するページは同じページとする(Googleで画面上部に検索ボックス、その下に結果が表示されるようなイメージ)

 まずは日本全国の住所を入手。

www.post.japanpost.jp

ここから全国の住所が入った「ken_all.zip」を入手し、解凍して「ken_all.csv」を取り出す。カンマで区切られた各項目の説明は以下の通り。

郵便番号データの説明 - 日本郵便より引用。

  • 全国地方公共団体コード(JIS X0401、X0402)……… 半角数字
  • (旧)郵便番号(5桁)……………………………………… 半角数字
  • 郵便番号(7桁)……………………………………… 半角数字
  • 都道府県名 ………… 半角カタカナ(コード順に掲載) (注1)
  • 市区町村名 ………… 半角カタカナ(コード順に掲載) (注1)
  • 町域名 ……………… 半角カタカナ(五十音順に掲載) (注1)
  • 都道府県名 ………… 漢字(コード順に掲載) (注1,2)
  • 市区町村名 ………… 漢字(コード順に掲載) (注1,2)
  • 町域名 ……………… 漢字(五十音順に掲載) (注1,2)
  • 一町域が二以上の郵便番号で表される場合の表示 (注3) (「1」は該当、「0」は該当せず)
  • 小字毎に番地が起番されている町域の表示 (注4) (「1」は該当、「0」は該当せず)
  • 丁目を有する町域の場合の表示 (「1」は該当、「0」は該当せず)
  • 一つの郵便番号で二以上の町域を表す場合の表示 (注5) (「1」は該当、「0」は該当せず)
  • 更新の表示(注6)(「0」は変更なし、「1」は変更あり、「2」廃止(廃止データのみ使用))
  • 変更理由 (「0」は変更なし、「1」市政・区政・町政・分区・政令指定都市施行、「2」住居表示の実施、「3」区画整理、「4」郵便区調整等、「5」訂正、「6」廃止(廃止データのみ使用)

この中で「3.郵便番号」「7.都道府県名(漢字)」「4.都道府県名(半角カナ)」「8.市区町村名(漢字)」「5.市区町村名(半角カナ)」「9.町域名(漢字)」「6.町域名(半角カナ)」を取り出し、取り込み用のPHPファイルを作ってMySQLに取り込む。余力があればcsvファイルをアップロードするボタンを作って、「ローカル環境から仮想環境へファイルをアップロード→それを取り込み」という機能もいいかもしれない。取り込むかどうかの判断が難しそうだけど…。

なお、町域名に「以下に掲載がない場合」と入っているレコードも存在している。これについては取り込みの段階で弾くようにしたい。

取り込み先となるMySQLのデータベースとテーブルを作成する。

データベース名:address

テーブル名:address

テーブル構成

addno varchar(7) primary key…郵便番号、主キーとする

add1 varchar(255)…都道府県名(漢字)

add1kana varchar(255)…都道府県名(半角カナ)

add2 varchar(255)…市区町村名(漢字)

add2kana varchar(255)…市区町村名(半角カナ)

add3 varchar(255)…町域名(漢字)

add3kana varchar(255) …町域名(半角カナ)

 インデックスは考えずに作成。

次にPHP側を作成。

昨日までの学習内容を参考にconfig.phpを作成し、定数の設定を行う。

 <?php

//Config.php

// 住所録用定数
define('DSN','mysql:dbname=address;host=localhost');
define('DB_USER','user');
define('DB_PASS','aaaa');
define('SITE_URL','http:192.168.33.11:8000');

error_reporting(E_ALL & ~E_NOTICE);

 今回はセッションは使わない予定なので、今のところは設定していない。

これまでと同様、MySQL接続用のヘルパーファイルも作成。

 <?php

 //db_helper.php
 function get_db_connect(){
  try {
   $dsn=DSN;
   $user=DB_USER;
   $password=DB_PASS;
   $dbh=new PDO($dsn,$user,$password);
  } catch (PDOException $e) {
   echo $e->getMessage();
   die();
  }
  $dbh->setAttribute(PDO::ATTR_ERRMODE,ERRMODE_EXCEPTION);
  return $dbh
}

 HTML表示用の関数も別のヘルパーファイルに作成する。

<?php

//extra_helper.php
 function html_escape($word){
  return htmlspecialchars($word,ENT_QUOTES,'UTF-8');
 } 

とりあえずはこれだけ、必要があれば適宜追加していく。

まずはcsv取り込み用のページを作る。

定数に以下のフラグとなる定数を設定し、index.phpを開いたときにこの定数をチェック→csv取り込みに飛ぶか検索に飛ぶかを判断する。

//config.php

//住所録設定関連
define('GETCSV','ON');  //ON→CSV取り込み、OFF→検索

index.phpには以下のように記述。

 <?php

 //index.php

 //設定&関数読み込み
 require_once('config.php');
 require_once('./helpers/db_helper.php');
 require_once('./helpers/extra_helper.php');

 if (GETCSV==='ON') {
  header('Location:'.SITE_URL.'csvget.php');
  exit();
 } else {
  header('Location:'.SITE_URL.'search.php');
  exit();
 }

フラグに応じてリダイレクト先を変えるようにした。

いよいよcsvget.phpを作成する。csvget.phpディレクトリに「csv」というフォルダを作り、そこに先ほど取得したken_all.csvをアップロードしてある。

<?php
 require_once('config.php');
 require_once('./helper/db_helper.php');
 require_once('./helper/extra_helper.php');
 //csv読み込み処理
 $file=@fopen('./csv/KEN_ALL.CSV','r') or die('ファイルを開けませんでした');
 //var_dump($file);
 flock($file,LOCK_SH);
 //とりあえず画面に出してみよう
 while (!feof($file)) {
  $line=fgets($file);
  echo '<p>'.$line.'</p>';
 }
 flock($file,LOCK_UN);
 fclose($file); 

ファイル名については大文字小文字の判定までしているので、ファイル名に合わせて書く。登録の前にとりあえず画面に表示を…と思ったが、文字化けでひどい有様に。

f:id:R_de_aru:20180710233003p:plain

今まで文字コードUTF-8だったけど、今回はなんだか違うような気がする…。

明日はこの文字化けの解消法を探る。画面に出すところまではできているので、文字化けさえ解消できれば登録もすぐ出来そう。

PHP開発日記:3度目の仮想環境

今回からは開発日記という形で、大層なことはないですがその日やったことを書いていきたいと思います。

 

まず新しく開発をするにあたって、例によって仮想環境を用意します。

前回の構築の際に1つメモを忘れていたエラーがあったのですが、今回もそれに当たりました。せっかくなので忘れないように書いておきます。

手順としては、

  1. 開発で使う仮想マシンのためのフォルダを作成
  2. PowerShellで「vagrant init bento/centos-6.8」を実行、Vagrantの設定ファイルを作成
  3. 使用するIPを設定、上書き保存
  4. PowerShellで「Vagrant Up」を実行
  5. 「/sbin/mount.vboxsf: mounting failed with the error: No such device」が発生
  6. 仮想マシンのホストとゲストのバージョンがずれていると推測し、「vagrant plugin install vagrant-vbguest」をPowerShellで実行。プラグインをインストール。
  7. インストールが終わったら「vagrant vbguest」を実行。これでプラグインが適用されるはず
  8. 再度「Vagrant up」したが、また「/sbin/mount.vboxsf: mounting failed with the error: No such device」が発生

手順8まできたところです。PowerShellを遡って読んでいくと、

The distribution packages containing the headers are probably:
kernel-devel kernel-devel-2.6.32-642.el6.x86_64
modprobe vboxguest failed 

 こんな文章を見つけました。前回はこの「kernel-devel kernel-devel-2.6.32-642.el6.x86_64」について調べたら仮想環境の構築がうまくいった覚えがあります。

検索して見つけた以下のコマンドを実行。

qiita.com

vagrant ssh

sudo yum install http://vault.centos.org/6.4/cr/x86_64/Packages/kernel-devel-2.6.32-431.el6.x86_64.rpm

exit

 「vagrant ssh」で仮想マシンにログインし、その次のコマンドで不足していたものをインストール。終わったらexitで仮想マシンからログアウト。自分のPCの再起動も必要だと読んだので、「vagrant halt」で仮想マシンをOFFに。その後自分のPCを再起動。それでも同じエラーが出てダメだった。

他にコマンドがないかを調べたところ、以下のブログを発見。

tulist.club

この記事の追記部分を参考にして「vagrant ssh」からの「yum install update kernel kernel-devel」を実行しようとしたら、「You need to be root to perform this command.」というエラーでできなかった。このゲストは管理者権限を持っていないからコマンドの実行ができないとのこと。

ならばならば、「su -」と入力して管理者としてログイン(要パスワード)。このとき、コマンドプロンプトでコマンドの前に付く「$」が「#」になれば管理者としてログインできているとのこと。

この状態で「yum install update kernel kernel-devel」を実行したらいろいろとインストールされた。これはいける!と思い、「exit(管理者権限を終了して一般ゲストへ)」からの「exit(一般ゲストでもログアウトしてホストOSへ)」を実行、そして「vagrant reload」。するとエラーも発生せず、仮想マシンの起動が正常に終了した。

ここまで長かったが、前回までの構築よりはスムーズにいった…。

 

あとはPuTTYでこのマシンへ入り、ドットインストールのスクリプトで環境構築を行う。

//OSアップデート

sudo yum -y update

//スクリプト入手のため、gitをインストール

sudo yum -y install git

//gitを使ってアプリケーション設定用スクリプトをダウンロード

git clone https://github.com/dotinstalleres/centos6.git

//centosフォルダへ移動

cd centos

//スクリプト実行

./run.sh

//設定反映

exec $shell -l

これでやっと仮想環境の設定が完了。今度からはこのページを見ながらしよう…。