WebEngine

だらだらと綴る技術系メモ

PHPのforeachの罠

foreachの$valueは参照渡しではない

$arrayに格納されている要素を、すべて空文字列に置き換えたいとします。
方法はいろいろありますが、今回はforeachの話なので、foreachを使います。

<?php

$array = array("php", "python", "ruby", "javascript");

foreach ($array as $value) {
  $value = "";
}

var_dump($array);

実行結果は次のようになります。

array(4) {
  [0]=>
  string(3) "php"
  [1]=>
  string(6) "python"
  [2]=>
  string(4) "ruby"
  [3]=>
  string(10) "javascript"
}

要素はそのままになっていますね。これは、$valueを直接変更したからです。
PHPの場合、foreachで取り出した要素は、その要素をコピーする形になります。
$valueは、$arrayの要素と同じアドレスを見ていない、というわけです。

解決策1

上記では、値渡しになっているので、参照渡しにすれば良い、という考え方です。

<?php

$array = array("php", "python", "ruby", "javascript");

foreach ($array as &$value) {
  $value = "";
}
unset($value);

$valueの前に&をつけて参照渡しにしています。
こうすることで、すべての要素を空文字列にすることができました。
注意点として、処理後、unset($value)で参照渡しをクリアしてやることが大事です。
処理したあとも、参照渡しである状態が保持されつづけると、思わぬバグの温床となります。

解決策2

参照渡しにするのではなく、$arrayの方を直接書き換える、という手法になります。

<?php

$array = array("php", "python", "ruby", "javascript");

foreach ($array as $key => $value) {
  $array[$key] = "";
}

キーを取得して、そのキーで要素を指定することによって配列を操作しています。
僕はこちらの方をよく使っています。


広告を非表示にする

PDOのfetch系メソッド

前提

  • PHP 5.6
  • mysql Ver 14.14 Distrib 5.7.18
  • Google Chromeでしかテストしていません
  • PDOのfetch系メソッドをまとめただけです


テストデータを用意

※ fetch系メソッドだけ調べたい方は下の方まで飛ばしてください

ターミナルでMySQLを起動。それから以下の命令を実行します。
create database fetch_test default charset utf8;
もちろんphpMyAdminからでもOKです。

show databases;で作成できたか確認できます。databasesと複数形であることに注意しましょう。

確認ができたら、use fetch_testでDBを選択します。

文房具を管理するitemsテーブルを作成します。

create table items (
  id int auto_increment not null primary key,
  name varchar(30),
  price int(30)
);


show tables で確認できます。 こちらもDBを表示した時と同じくtablesと複数形であることに注意です。

ここでようやくテストデータの挿入です。
適当にinsertしていきます。

insert into items (name, price) values ('鉛筆', 100);
insert into items (name, price) values ('ノート', 120);
insert into items (name, price) values ('消しゴム', 50);


終わったらselect * from items;でチェックしておきましょう。

id name price
1 鉛筆 100
2 ノート 120
3 消しゴム 50


メソッドを切り替えてfetch系メソッドを検証するクラスをつくる

同じディレクトリ下に

という3つのファイルを作成します。

database.php

<?php
class Database {

  public static function connectDB() {

    $pdo = '';

    try {
      $pdo = new PDO(
        // 環境によってはhost部分をlocalhostにしてください
        'mysql:dbname=fetch_test;host=127.0.0.1;charset=utf8',
        'root',
        '',
        [
          PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
          PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
        ]
      );
    } catch (PDOException $e) {
      header('Content-Type: text/plain; charset=UTF-8', true, 500);
      exit($e->getMessage());
    }

    return $pdo;
  }

}

静的メソッドでデータベースに接続するだけのクラスです。本来はDB名、ユーザ名、パスワードなど は別ファイルに定数として分割するものですが、ここでは省略します。

PDO::ATTR_DEFAULT_FETCH_MODEはPDOのfetch系メソッドで引数が省略された場合などのフェッチスタイルを決定します。
ここではPDO::FETCH_ASSOCで設定しています。これは、カラム名をキーとする連想配列で取得するモードになります。

また、コメントアウトにもあるとおり、環境によってはhostの部分を127.0.0.1からlocalhostにしてください。僕の環境では逆で ないと動きませんでした。

fetchTester.php

<?php
require_once 'database.php';

class FetchTester {

  private $pdo;
  private $table = 'items';

  function __construct() {
    $this->pdo = Database::connectDB();
  }

  public function fetch() {
    $stmt = $this->selectTable();
    while ($row = $stmt->fetch()) {
      var_dump($row);
    }
  }

  public function fetchObject() {
    $stmt = $this->selectTable();
    while ($row = $stmt->fetchObject()) {
      var_dump($row);
    }
  }

  public function fetchColumn($fetchColumnNumber) {
    $stmt = $this->selectTable();
    while (false !== $val = $stmt->fetchColumn($fetchColumnNumber)) {
      var_dump($val);
    }
  }

  public function fetchAll() {
    $stmt = $this->selectTable();
    $rows = $stmt->fetchAll();
    var_dump($rows);
  }

  private function selectTable() {
    $sql = 'select * from ' . $this->table;
    $stmt = $this->pdo->query($sql);

    return $stmt;
  }
}

fetch系メソッドをそれぞれクラス固有のメソッドに分けています。唯一、前処理に当たるselectTableのみprivateで定義し、 残りのアクセス権はindex.phpで使えるようにpublicにしてあります。

index.php

<?php
require_once 'FetchTester.php';

$fetch_tester = new FetchTester();

// この部分のメソッドを変更して使う
$fetch_tester->fetchAll();

実際に実行するメソッドを実装するファイルです。動作を確かめる際、このファイルへアクセスします。
ここではfetchAllになっていますが、動作を確認したいfetch系メソッド名に切り替えることで、その動きをvar_dump形式で確認することができます。var_dumpが嫌だ、という方はechoなりprintなりに書きかえてもらって構いません。値をreturnする形式にして汎用性を高めても良いです。

1. fetch

では、順番に見ていきましょう。
まず基本形のfetchメソッドです。

$fetch_tester->fetch();

出力結果は以下のようになります。

array(3) {
  ["id"]=>
  string(1) "1"
  ["name"]=>
  string(6) "鉛筆"
  ["price"]=>
  string(3) "100"
}
array(3) {
  ["id"]=>
  string(1) "2"
  ["name"]=>
  string(9) "ノート"
  ["price"]=>
  string(3) "120"
}
array(3) {
  ["id"]=>
  string(1) "3"
  ["name"]=>
  string(12) "消しゴム"
  ["price"]=>
  string(2) "50"
}

fetchは取得し終えるとfalseを返すので、それを条件分岐として利用することができます。
fetchTester.phpでは以下の部分です。

while ($row = $stmt->fetch()) {
  // 処理
}


2. fetchObjcet

連想配列ではなく、オブジェクトとしてデータを取得します。

$fetch_tester->fetchObject();

結果は以下のような感じ。

object(stdClass)#4 (3) {
  ["id"]=>
  string(1) "1"
  ["name"]=>
  string(6) "鉛筆"
  ["price"]=>
  string(3) "100"
}
object(stdClass)#5 (3) {
  ["id"]=>
  string(1) "2"
  ["name"]=>
  string(9) "ノート"
  ["price"]=>
  string(3) "120"
}
object(stdClass)#4 (3) {
  ["id"]=>
  string(1) "3"
  ["name"]=>
  string(12) "消しゴム"
  ["price"]=>
  string(2) "50"
}


3. fetchColumn

引数で指定した番号のカラムを文字列で取得します。省略時は0を指定したとみなされます。

$fetch_tester->fetchColumn();


今回使用しているfetchTester.phpでは、fetchTesterクラスのfetchColumnメソッドの引数から、PDO のfetchColumnメソッドの引数へとカラム番号を受け渡す形になるよう実装してあります。
while内で、false !==としているのは値に0が入る場合を考慮してです。参考にさせていただいたサイトのソースコードが しっかりしていると思ったので、そのまま採用させていただきました。

public function fetchColumn($fetchColumnNumber = 0) {
  $stmt = $this->selectTable();
  while (false !== $val = $stmt->fetchColumn($fetchColumnNumber)) {
    var_dump($val);
  }
}


以下は、引数に2を指定した場合の結果。

string(3) "100"
string(3) "120"
string(2) "50"


4. fetchAll

2次元配列として全データを取得します。

$fetch_tester->fetchAll();

ブラウザには以下のように出力されます。

array(3) {
  [0]=>
  array(3) {
    ["id"]=>
    string(1) "1"
    ["name"]=>
    string(6) "鉛筆"
    ["price"]=>
    string(3) "100"
  }
  [1]=>
  array(3) {
    ["id"]=>
    string(1) "2"
    ["name"]=>
    string(9) "ノート"
    ["price"]=>
    string(3) "120"
  }
  [2]=>
  array(3) {
    ["id"]=>
    string(1) "3"
    ["name"]=>
    string(12) "消しゴム"
    ["price"]=>
    string(2) "50"
  }
}


参考


広告を非表示にする

値が重複することを考えて連想配列をソート[PHP]

2017.5.9修正

この記事のタイトルは「キーに優先順位をつけて連想配列をソート」というものでしたが、読み返したところ誤解を招く表現であると考え、「値が重複することを考えて連想配列をソート」というものに変更させていただきました。(ソースコード内の関数名も変更させていただきました)

前提


usort関数を使います

ユーザ定義の関数でソートを行える関数です。
以下はPHP公式マニュアルから抜粋したサンプルコードです。

<?php
function cmp($a, $b)
{
    if ($a == $b) {
        return 0;
    }
    return ($a < $b) ? -1 : 1;
}

$a = array(3, 2, 5, 6, 1);

usort($a, "cmp");

foreach ($a as $key => $value) {
    echo "$key: $value\n";
}
?>


usort関数にはソートしたい配列と自分で定義した関数を渡します。(つまり引数の関数は、コールバック関数になるわけです)

このユーザ定義の関数では、最初の引数と2つ目の引数の比較結果を返す必要があります。最初の引数のほうが2番目の引数より大きいときは正の数を、2番目の引数と等しいときは0を、そして2番目の引数より小さいときは負の数を返す必要があります。

上記のソースコードでは

  • 変数aと変数bが同じならば0を返す
  • 変数aが変数bがより小さいならば-1(負の数)を返す
  • 変数aが変数bより大きいならば1(正の数)を返す

ということをやっているわけです。

ただ、usort関数にわざわざ関数名をつけて分割して渡してやるパターンは、ブログが長くなってしまいますので、無名関数で渡したいと思います。

<?php

$a = array(3, 2, 5, 6, 1);

usort($a, function($a, $b) {
  if ($a == $b) {
    return 0;
  }
  return ($a < $b) ? -1 : 1;
});

foreach ($a as $key => $value) {
  echo "$key: $value\n";
}

こんな感じで本題のソースコードは書いていきたいと思います。


本題

<?php

// 普通はこれがデータベースなどから取得したデータ
$ary = array(
  array(
    'id' => 1001,
    'name' => 'alice',
    'birthday' => 20010408
  ),
  array(
    'id' => 1002,
    'name' => 'bell',
    'birthday' => 20010408
  ),
  array(
    'id' => 1003,
    'name' => 'clara',
    'birthday' => 20001227
  )
);

function checkDuplicationSort($ary) {
  usort($ary, function($a, $b) {
    if ($a['birthday'] === $b['birthday']) {
      if (strcmp($a['name'], $b['name']) === 0) {
        return ($a['id'] > $b['id']);
      } else {
        return strcmp($a['name'], $b['name']);
      }
    } else {
      return ($a['birthday'] > $b['birthday']);
    }
  });

  return $ary;
}

// -- main --
$sorted_ary = checkDuplicationSort($ary);
echo "<pre>";
print_r($sorted_ary);
echo "</pre>";

※ 最近環境を変えたのでxdebugの設定をしておらず、上記のように<pre>タグを使ってデバッグしています。邪魔な人は消してください。

見てのとおり、if文で、誕生日が同じならば名前でソート、名前が同じならばIDでソートするようにしているだけです。(つまりゴリ押し)
誕生日、名前、IDの順に調べていっているんですね。まあ、データベースから取得してきた場合、IDがダブることはないだろう、ということで IDが最終チェック項目にしてあります。

結果は下記のようになります。

Array
(
    [0] => Array
        (
            [id] => 1003
            [name] => clara
            [birthday] => 20001227
        )

    [1] => Array
        (
            [id] => 1001
            [name] => alice
            [birthday] => 20010408
        )

    [2] => Array
        (
            [id] => 1002
            [name] => bell
            [birthday] => 20010408
        )

)

まず、もっとも早く生まれたclaraさんが最初に、そのあとのaliceさんとbellさんは誕生日が 一緒なので名前でソートされます。a、bとアルファベット順に並び替えられるわけです。

オブジェクトもソートしたい

Studentクラスをyomiでソートします。

<?php

class Student {
  public $id;
  public $name;
  public $yomi;

  function __construct($id, $name, $yomi) {
    $this->id = $id;
    $this->name = $name;
    $this->yomi = $yomi;
  }
}

// -- main --
$students = array();
// PHPでは[] = hoge のようにすることで配列末尾にどんどん要素を追加していけます
$students[] = new Student(1000, "Alice", "アリス");
$students[] = new Student(1001, "Bell", "ベル");
$students[] = new Student(1002, "Clara", "クララ");

usort($students, function($a, $b) {
  return strcmp($a->yomi, $b->yomi);
});

echo "<pre>";
var_dump($students);
echo "</pre>";


結果は次のようになります。

array(3) {
  [0]=>
  object(Student)#1 (3) {
    ["id"]=>
    int(1000)
    ["name"]=>
    string(5) "Alice"
    ["yomi"]=>
    string(9) "アリス"
  }
  [1]=>
  object(Student)#3 (3) {
    ["id"]=>
    int(1002)
    ["name"]=>
    string(5) "Clara"
    ["yomi"]=>
    string(9) "クララ"
  }
  [2]=>
  object(Student)#2 (3) {
    ["id"]=>
    int(1001)
    ["name"]=>
    string(4) "Bell"
    ["yomi"]=>
    string(6) "ベル"
  }
}

ちゃんと「あいうえお順」に並び替えられていることがわかります。
ソートの重複をチェックしたければ連想配列のときと同様、usortの無名関数内でif文を適用してやれば良いだけです。

ComposerでSmartyを入れて遊ぶだけ

Composer

最近のPHPerには必須のツール。PHP系ライブラリをインストールする際、依存関係を標準的に管理してくれる。(ある ライブラリをインストールした際、あのライブラリも要るのかあ、という状況を解決してくれる)

Smarty

PHPのテンプレートエンジン。nodeでいうEJSなどである。

ComposerをMacにインストール

環境はMac OS Sierraでやっていきます。 まず、Composerのインストーラcurlで取得、その後インストーラPHPで実行します。

curl -sS https://getcomposer.org/installer | php

次にcomposer.pharを移動します。

mv composer.phar /usr/local/bin/composer

入れたComposerのバージョンを確認するにはcomposer -Vで調べます。


Smartyを入れる

なんか適当にディレクトリをつくって、その中に潜りましょう。

mkdir smarty_app
cd smarty_app


先にcomposer.jsonを書いてインストールしても良いですが、面倒なのでcomposer requrieというコマンドを 使います。このコマンドを使った場合、composer.jsonは勝手に作成されます。

composer require smarty/smarty


現時点でのsmarty_appの中の構成はこのような感じになっているはずです。

composer.json    composer.lock   vendor

vendorの中を見ると、ちゃんとsmartyが入っています。

必要最小限の構築

次にtemplatesというディレクトリと、templates_cというディレクトリを作成します。
そして、このtemplates_cの権限を変えます。

mkdir templates
mkdir templates_c
chmod 777 templates_c

このtemplates_cというディレクトリは必須です。ここではあんまり良くないですが、777で許可しています。
イメージとしては、templatesというディレクトリに入っているテンプレートが、自動的にコンパイルされて templates_cに入ります。(cはコンパイルのcみたいですね)

使ってみる

index.phpsmarty_app直下につくります。

<?php
require_once 'vendor/autoload.php';

ini_set('date.timezone', 'Asia/Tokyo');
define('MY_TITLE', 'TEST');

$smarty = new Smarty();

// 使うテンプレートが入っているディレクトリを指定
$smarty->setTemplateDir('./templates/');

$smarty->assign('hello', 'Hello, Smarty!!');
$smarty->assign('today', new DateTime());
$smarty->assign('animal', array('rabbit','cat','dog'));

$smarty->display('index.tpl');


つづいて、templates内にhader.tplindex.tplを作成。

header.tpl

<!DOCTYPE html>
<meta charset="utf-8">
<title> {$page_title} </title>


index.tpl

{*
  コメントアウト
*}

{include file='header.tpl' page_title={$smarty.const.MY_TITLE}}

{* 普通の変数 *}
<p>{$hello}

{* メソッド *}
<p>{$today->format('Y/m/d (D)')}

{* 予約変数 *}
<dl>
  <dt>現在のタイムスタンプ
  <dd>{$smarty.now}

  <dt>現在処理中のテンプレートファイル名
  <dd>{$smarty.template}

  <dt>Smarty version
  <dd>{$smarty.version}
<dl>

{* 配列を逆順でループ(step=-1だから) *}
<ul>
{section name=i loop=$animal step=-1}
  <li>{$animal[i]}
{/section}
</ul>


一応smarty_app内の構造を記しておきます。

|--composer.json
|--composer.lock
|--index.php
|--templates
|  |--header.tpl
|  |--index.tpl
|--templates_c
|--vendor

つくったら、PHPのビルトインサーバを立てるなりして、http://localhost/~なんかでアクセスします。
成功していればブラウザ上に、Smartyのバージョンや現在の日付なんかが表示されるはずです。
templates_c内を見ると、コンパイルされたファイルがあるのもわかるはずです。

参考

jQueryでAjaxを扱う

前提


Ajaxを使うメリット

非同期通信ができることです。 具体例を挙げると

  • ユーザのストレスを軽減できる
  • サーバへの負荷を軽減できる

というメリットがあります。もちろんデメリットもありますが、ほかのサイトでいろいろ考察されているので、 ここでは流します。


いろいろ書き方があるみたいだけど

ajaxで検索すると、結構いろんな書き方が紹介されています。

$.ajax({
    url: "test.html",
    success: function(data) {
       console.log("success");
    },
    error: function(data) {
       console.log("error");
    }
});
$.ajax({
    url: "test.html",
}).success(function(data) {
    console.log("success");
}).error(function(data) {
    console.log("error");
});

調べてみると上記2つの書き方は旧式の書き方のようです。
今回は下の書き方で統一します。

$.ajax({
    url: "test.html",
}).done(function(data) {
    console.log("success");
}).fail(function(data) {
    console.log("error");
});

これがベストプラクティス、というわけではないようですが、今回はとりあえずこの記述法で 実装します。


サンプルコード

AjaxCSVJSON形式のデータを取得し、出力するというサンプルをつくってみたいと思います。 HTMLファイルにjavascriptの関数が入っていたりするので、よかったらリファクタリングにも挑戦してみてくださいね。(人任せ)

1.index.html

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>ajax</title>
    <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
    <script src="script.js"></script>
  </head>
  <body>
    <input type="button" onClick="output(0)" value="output_data0">
    <input type="button" onClick="output(1)" value="output_data1">
    <input type="button" onClick="output_json()" value="output_data_json">
    <div id="output_zone"></div>
  </body>
</html>


2.script.js

function output(num) {
  clear();

  $.ajax({
    url: "data" + num + ".csv"
  }).done(function(result) {
    var tmp_ary = result.split("\n");
    // console.log(tmp_ary);
    for (var i = 0; i < tmp_ary.length; i++) {
      $("#output_zone").append(tmp_ary[i]).append("<br>");
    }
  }).fail(function(data) {
    console.log(data);
  });

}

function output_json() {
  clear();

  $.getJSON("data.json", function(json) {
    for (var i = 0; i < json.student.length; i++) {
      var student = json.student[i];
      $("#output_zone").append(student.name + " : " + student.age).append("<br>");
    }
  });

}

function clear() {
  $("#output_zone").empty();
}


3.data0.csv

Takeda,20
Sakurai,45
Hashimoto,36


4.data1.csv

Takeda Yuri, woman
Sakurai Kaoru, woman
Hashimoto Takuya, man


5.data.json

{"student" : [
  {
    "name" : "Takeda",
    "age" : 20
  },
  {
    "name" : "Sakurai",
    "age" : 45
  },
  {
    "name" : "Hashimoto",
    "age" : 36
  }
]}


HTMLファイルであるindex.htmlドラッグアンドドロップとかしても動きません。これはクロスドメイン制約があるためです。


ドキュメントルートに設置するか、PHPのビルトインサーバを立てるかして、http://localhost/hogehoge/~というような感じでアクセスしましょう。

左のボタンを押せばdata0.csvの情報が、中央のボタンを押せばdata1.csvの情報が出力されるはずです。
そして、右のボタンを押せばJSON形式であるdata.jsonが出力されます。

広告を非表示にする

Node.jsでいろんな形式のデータを扱う

注意事項

以下のファイルの文字コードはすべてUTF-8で実装しているので、Windows環境などでは、日本語が 文字化けする可能性があります。それらを踏まえた上で、テスト用のテキストなどに日本語 を使うことは避けていますが、やはり、環境によっては上手くいかないことがあることを ご理解ください。


対象となるデータ形式

  1. JSON
  2. XML / RSS
  3. CSV


1. JSON

javaScriptrといえばJSONですね。 下のようなdata.jsonを用意しました。

{                                                                 
  "title" : "-- programming --",
  "languages" : [
    {"id":1000, "name":"Java"},
    {"id":1001, "name":"Ruby"},
    {"id":1002, "name":"PHP"},
    {"id":1003, "name":"Python"},
    {"id":1004, "name":"JavaScript"}
  ]
}

そして、こちらが上のJSONファイルを読み込んだり、パースしたりする json_read.jsです。

var fs = require('fs');                                                             

var json_data = fs.readFileSync("data.json", "utf-8");
var obj = JSON.parse(json_data);

console.log(obj.title);
for (var i in obj.languages) {
  console.log(obj.languages[i].name);
}

node json_read.jsで実行すると、プログラミング言語がずらっと表示されるはずです。 非常に簡単ですね。

javaScriptのオブジェクトをJSONのデータに変換するには

var json_data = JSON.Stringify(obj);

とします。JSON.parse(json_data);と対になっていると考えると理解しやすいです。


2. XML / RSS

node.jsでXMLを扱うには、xml2jscheerioというモジュールを使います。
ここでは、jQueryライクにDOMへアクセスできるcheerioの方を使います。

cheerio

npm install cheerio

でインストールすることができます。

XMLは、data.xmlというファイルを用意しました。

<item>
  <name>pen</name>
  <price>120</price>
</item>
<item>
  <name>note</name>
  <price>150</price>
</item>
<item>
  <name>book</name>
  <price>540</price>
</item>

これを扱うファイルをxml_read.jsとして作成します。

var fs = require('fs'),
    che = require('cheerio');

var xml_data = fs.readFileSync("data.xml", "utf-8");
$ = che.load(xml_data);

$("item").each(function(i, el) {
  var name = $(this).children("name").text();
  var price = $(this).children("price").text();

  console.log(name + " : " + price);
});

node xml_read.jsと実行すると、モノと、その値段が表示されるはずです。
直観的にわかるかもしれませんが、cheerioモジュールのload()XMLファイルをパースしたものが、 $に入っています。そのあとは、jQueryのような構文で各要素の中身を取得していく感じになります。


3. CSV

node.jsでCSV形式のファイルを扱うにはcomma-separated-valuesというモジュールが要ります。

npm install comma-separated-values

でインストールできます。 以下はdata.csvになります。

ID, Name, Birthday
1000, Alice, 19990201
1001, Bell, 20010609
1002, Clara, 20001230

CSVファイルを扱うファイルが以下のcsv_read.jsです。

var fs = require('fs'),
    CSV = require('comma-separated-values');

var csv_data = fs.readFileSync("data.csv", "utf-8");
var data = new CSV(csv_data, {header:false});
var rec = data.parse();

for (var i = 0; i < rec.length; i++) {
  console.log(rec[i][0] + " : " + rec[i][1]);
}

node csv_read.jsで実行すると、IDと名前が表示されます。
1行目のヘッダ部分がいらない、という場合には、パース直後に、 rec.shift();と書けば、配列の先頭要素が削除されるので上手い具合になります。


まとめ

どのファイル形式を扱うにしても

  1. モジュールを読み込む
  2. fsのreadFileSync()を使ってデータを読み込む
  3. 読み込んだモジュールを使ってデータをパースする
  4. パースしたデータから必要な部分を取得したり、出力したりする

といった大まかな流れは変わりません。

また、基本的にnode.jsには便利なモジュールが多く、それらはほとんどnpmで取得できます。なにか行いたい 場合、先に使えそうなモジュールがないか調べるのも手であることを覚えておきましょう。


参考


Node.jsでSQLite3を使ってみる

環境

  • Ubuntu16.04
  • Node.js v7.7.1
  • npm 4.1.2


SQLite3をnpmでインストール

node.jsはサーバサイドでjavascriptを扱えるようにしたもの。当然、データベースも扱うことが できます。
今回はSQLiteというデータベースを使ってみます。ほかの選択肢としてはmongoDBなんかがあるみたいですね。

SQLiteはnpmを使ってインストールすることができます。

npm install sqlite3

実行時のディレクトリにnode_modulesというディレクトリができて、さらになかにsqlite3というディレクトリ が入ったはずです。


データを挿入する

SQLiteを直接立ち上げて、コマンドでテーブル定義などを行っても良いですが、今回は sqlite-insert.jsというスクリプトファイルを作成し、それを実行する過程でテーブル 定義などを行いつつ、データも挿入することにします。

var sqlite = require('sqlite3').verbose();                                          
var db = new sqlite.Database('test.sqlite');

db.serialize(function() {

  // テーブルがなければ作成
  db.run('CREATE TABLE IF NOT EXISTS students(name TEXT, age INT)');
 
  // プリペアードステートメントでデータ挿入
  var stmt = db.prepare('INSERT INTO students VALUES(?,?)');
  stmt.run(["Tanaka", 12]);
  stmt.run(["Sato", 13]);
  stmt.run(["Nakamura", 15]);
  stmt.finalize();

 });

db.close();

studentsテーブルは名前と年齢を管理するテーブルです。本当はIDなんかをつけた方が 良いのですが、今回はテストなので省略します。
実行はnode sqlite-insert.jsでできます。

プリペアードステートメントについては、以前MySQLで書いた記事があるので、そちらを参考にしてみてください。


データを取得する

つづいて、データベースからデータを取得するスクリプトを書いてみます。
ファイル名はsqlite-fetch.jsとでもしておきましょう。

var sqlite = require('sqlite3').verbose();                                          
var db = new sqlite.Database('test.sqlite');
 
db.serialize(function() {

  db.each("SELECT * FROM students", function(err, row) {
    console.log(row.name + ":" + row.age);
  });

});

db.close();

node sqlite-fetch.jsで実行します。
まあ、見ての通り、ターミナルにデータを表示するだけです。取得の際コールバック関数を 使っているのがnodeっぽいですよね。

ちなみに、いつのまにかtest.sqliteというファイルができているかと思いますが、そこに挿入した データが入っています。


参考