PDOでO/Rマッピング

前回,PDOを使ってみた で,PDOを使ってSQLiteデータベースにアクセスする例を示しました.そこで,memberテーブルを操作するMemberクラスなるものを作りましたが,Memberクラスが肥大化してしまっており,これをなんとかしたいのです.

これはゼミで私がPDOを取り上げたのがキッカケで,PDOについて説明するついでに,なんか便利なクラスを作っちゃえーということで,いつもは手続き指向でPHPをちょこまか書いている私ですが,PHPのオブジェクト指向も体感してみるか,という軽い気持ちで取り組んだらハマってしまったという類いのものです.

Memberクラス(以前の状態)

別のテーブル,例えば students テーブルか何かを作成した時に,また,find()メソッドやらsave()メッソドやらを実装するのは非効率的.

そこで,汎用クラスいわゆるMemberクラスのスーパークラスを考えます.ここでは,かっこ良く(?)「PDOModel」としました.find()メソッドやsave()メソッドは,ここに実装することになります.しかし,save()メソッドは,INSERT文やUPDATE文を使用しますので,フィールド名の情報が必要です.ですのでサブクラスとなるMemberでフィールド名の情報,ここでは $fields というフィールド名をキーとし値をデータ型名とする配列を利用します.後,必要な情報としてはクラス名とテーブル名ですので,それも定数として定義しておきます.

Memberクラスと汎用クラス

ご注意として,今回はとりあえず動けば良いという考えのもとシンプルな実装にしています.ですので,例外処理とか気にせずに記載していますので,これをそのまま業務アプリに適用しようとすると問題があります.業務アプリ等は既に出回っているフレームワークをご利用頂ければと思います.

ここで使用しているソースは,こちらに置いています.
tamochia/pdo_model | GitHub

今回はあくまでPHPでなんちゃってActiveRecordを実装してみようっていうだけのことですから,はい...

member_test.php ー Memberクラスをテストするプログラム

PDOModelクラス,Memberクラスをテスト(利用)するプログラムとしては,次のようなイメージで考えています.

// bootstrap
PDOModel::configuration('pdo_config.xml');
PDOModel::connection();

// ID:0002のレコードを更新する
$obj = Member::find('0002');
$obj->name = 'Hayato Satsuma';
$obj->height = '168.0';
$obj->save();

// ID:0005のレコードを削除する
$obj = Member::find('0005');
$obj->delete();

// heightが170より大きいレコードを取得
$obj = Member::where('height > 170');
print_r($obj);

// 新規レコードを追加する
$obj = new Member();
$obj->id = '0005';
$obj->name = 'Takamori Saigo';
$obj->height = '169.5';
$obj->weight = '50.9';
$obj->save();

// 全レコードを取得
$obj = Member::findAll();
print_r($obj);

細かく見て行きます.まずは,データベースとの接続の部分から.

// bootstrap
PDOModel::configuration('pdo_config.xml');
PDOModel::connection();

この2行で,MySQLデータベースなりSQLiteデータベースなりに接続し,PDOハンドルを取得させます.

configuration()メソッドでは,コンフィグXMLファイルを指定することで,使用するデータベースを汎用的にセレクトさせるようにします.ここでいう「pdo_config.xml」の中身は,こんな感じです.

<?xml version="1.0" encoding="UTF-8"?>
<config>
	<dsn>sqlite:./sample.sqlite3</dsn>
	<user></user>
	<password></password>
</config>

MySQLの場合はこんな感じ.

<?xml version="1.0" encoding="UTF-8"?>
<config>
	<dsn>mysql:dbname=sample;host=localhost;charset=utf8</dsn>
	<user>foo</user>
	<password>hogehoge</password>
</config>

データベースを移行した際は,このコンフィグXMLファイルを入れ替えるだけで良いことになります.

connection()メソッドで,データベースと接続して,PDOオブジェクト生成するのですが,そこは内部的に処理して,クラス外では見せなくしています.

任意のレコード,例えばid=’0002’のレコードのデータを変更(更新)する際は,まず,find()メソッドで目的のレコードオブジェクトを取得してから,そのオブジェクトのプロパティ値を変え,最後にsave()メソッドでデータベースに反映させます.

// ID:0002のレコードを更新する
$obj = Member::find('0002');
$obj->name = 'Hayato Satsuma';
$obj->height = '168.0';
$obj->save();

削除する際も同様で,まずfind()メソッドで目的のオブジェクトを取得してから,delete()メソッドで削除させます.

// ID:0005のレコードを削除する
$obj = Member::find('0005');
$obj->delete();

ある条件,例えばWHERE句の部分を記述することにより,汎用的に目的のレコード群(結果セット)を取得したい場合は,where()メソッドを使用します.

// heightが170より大きいレコードを取得
$obj = Member::where('height > 170');
print_r($obj);

新規にレコードを追加する場合は,newでMemberインスタンスを生成し,それから各プロパティ値をセットし,最後にsave()メソッドを実行させます.

// 新規レコードを追加する
$obj = new Member();
$obj->id = '0005';
$obj->name = 'Takamori Saigo';
$obj->height = '169.5';
$obj->weight = '50.9';
$obj->save();

もちろんfindAll()メソッドも準備します.

// 全レコードを取得
$obj = Member::findAll();
print_r($obj);

それでは,まずMemberクラスの実装から...

<?php                                                                                                                                                   
require_once "pdo_model.php";

class Member extends PDOModel {
    const CLASS_NAME = 'Member';
    const TABLE_NAME = 'member';
    protected static $fields = array (
        'id' => PDO::PARAM_STR,
        'name' => PDO::PARAM_STR,
        'height' => PDO::PARAM_STR,
        'weight' => PDO::PARAM_STR
    );
}
:

PDOModelクラスは,Memberクラスのスーパークラスです.PDOModelの方で,サブクラス名を既存メソッドか何かで取得させたかったのですが,やり方がわからなかったので,CLASS_NAME という定数で渡しています.同じ様にテーブル名も必要なので,TABLE_NAME という定数も用意しました.
PDOModelクラスの方で,bindParam()メソッドを利用しますので,フィールド名とその型の情報が必要となります.そこで,$fieldsというstaticな配列プロパティも用意しました.

pdo_model.php ー PDOModelクラス

後は,PDOModelクラスの実装となります.まずは,用意するプロパティは次のような感じ.これも色々精査する必要があるんだけど,とりあえず動けば良いという前提で今回はTRYしています.

<?php                                                                                                                                                   
class PDOModel {
    private static $db;  // PDOのハンドル
    private static $table;  // テーブル名
    private static $pdo_params;  // PDOコンストラクタのパラメータ
    public $id;

次に,各メソッドを見て行きます.まずは,データベースとの接続に関するクラスメソッドから.

    // DB設定XMLファイルの読み込みとパラメータセット
    public static function configuration($xml) {
        $conf = simplexml_load_file($xml);
        self::$pdo_params = get_object_vars($conf);
    }

    // データベースへの接続,PDOインスタンスの生成
    public static function connection() {
        $_dsn = self::$pdo_params['dsn'];
        $_user = self::$pdo_params['user'];
        $_password = self::$pdo_params['password'];
        try {
            self::$db = new PDO($_dsn, $_user, $_password);
        } catch(PDOException $e) {
            printf("Error: %s\n", $e->getMessage());
            self::$db = null;
        }
    }

ここで注目すべきは,configuratio()メソッドのXMLパースの部分.本当はRailsのようにYAMLファイルをコンフィグファイルとして利用したかったのですが,PHPのYAMLパースは標準で実装されていない(?)ぽかったので,XMLとしました.でもsimplexml_load_file()メソッドを使うだけで良かったです.そこで取得したデータを $pdo_params 配列に格納し,その情報を connection() メソッドで利用します.

	// 結果セット,レコードオブジェクトの配列を返す
	protected static function getRecords(PDOStatement $stmt) {
		$rets = array();
		while($ret = $stmt->fetchObject(static::MODEL_CLASS)) $rets[] = $ret;
		return $rets;
	}
	
	// 単一のレコードオブジェクトを返す
	protected static function getRecord(PDOStatement $stmt) {
		$ret = $stmt->fetchObject(static::MODEL_CLASS);
		return $ret;
	}

	// すべてのレコードオブジェクト配列を返す
	public static function findAll() {
		$sql = "SELECT * FROM ".static::TABLE_NAME;
		$stmt = self::$db->prepare($sql);
		$stmt->execute();
		return self::getRecords($stmt);
	}

	// 任意のidのレコードオブジェクトを返す
	public static function find($id) {
		$sql = "SELECT * FROM ".static::TABLE_NAME." WHERE id = :id";
		$stmt = self::$db->prepare($sql);
		$stmt->bindParam(':id', $id, PDO::PARAM_STR);
		$stmt->execute();
		return self::getRecord($stmt);
	}

	// 任意のWHERE検索にてレコードオブジェクト配列を返す
	public static function where($cond) {
		$sql = "SELECT * FROM ".static::TABLE_NAME." WHERE ".$cond;
		$stmt = self::$db->query($sql);
		return self::getRecords($stmt);
	}
	
	// 対象オブジェクトレコードの削除
	public function delete() {
		$sql = "DELETE FROM ".static::TABLE_NAME." WHERE id = :id";
		$stmt = self::$db->prepare($sql);
		$stmt->bindParam(':id', $this->id, PDO::PARAM_STR);
		$stmt->execute();
	}	

ここは,前回の PDOを使ってみた で説明したとおりで,そのまま実装しています.delete()メソッド以外はすべてクラスメソッドとしています.delete()メソッドはインスタンスメソッドになりますので,WHERE句でのid特定については,69行目のbindParamの第2引数にて「$this->id」を持ってきています.

工夫が必要だったのは次のsave()メソッドの部分です.

	// 対象オブジェクトレコードの保存(新規及び更新)
	public function save() {
		$obj = self::find($this->id);
		if($obj == null) {
            // 新規レコード追加(存在しないidの場合)
			$flist = implode(array_keys(static::$fields), ",");
			$vfunc = function($v){return(":".$v);};
			$vlist = implode(array_map($vfunc, array_keys(static::$fields)), ",");
			$insert_sql = "INSERT INTO ".static::TABLE_NAME." (".$flist.") VALUES (".$vlist.")";
			$stmt = self::$db->prepare($insert_sql);
		}
		else {
            // 既存レコードの更新
			$sfunc = function($v){return($v."=:".$v);};
			$slist = implode(array_map($sfunc, array_keys(static::$fields)), ",");
			$update_sql = "UPDATE ".static::TABLE_NAME." SET ".$slist." WHERE id=:id";
			$stmt = self::$db->prepare($update_sql);
		}
		foreach(static::$fields as $key => $value)
			$stmt->bindParam(":".$key, $this->{$key}, $value);
		$stmt->execute();
	}	
}
?>

bindParamメソッドでは,テーブルの各フィールド名の情報が必要です.そこで,サブクラスとなるMemberクラスの$fieldsプロパティからフィールド名とそのデータ型を取得します.それには,array_keys(static::$fields) を利用します.

しかし,INSERT文のVALUES句において,

VALUES(:id, :name, :height, :weight)

のように,「コロン+フィールド名」のカンマ付きリスト文字列が必要です.それには,次のようにarray_map()関数を使って作成しています.

$vfunc = function($v){return(":".$v);};
$vlist = implode(array_map($vfunc, array_keys(static::$fields)), ",");

また,UPDATE文のSET句おいては,

SET id = :id, name = :name, height = :height, weight = :weight

という「フィールド名+”=:”+フィールド名」のカンマ付きリスト文字列が必要ですので,先述と同様にarray_map()関数を使って次のように作成しました.

$sfunc = function($v){return($v."=:".$v);};
$slist = implode(array_map($sfunc, array_keys(static::$fields)), ",");

無理矢理感がありますが,とりあえず短い記述になるようまとめました.これですべてのテーブルに対応できるようにしています.

広告

PDOを使ってみた

PHPとMySQLとの接続について,これまでは,mysql_connect()関数を使用してきたのですが,最近,PHP 5.5より非推奨らしいということで,いずれmysql_connect()は使えなくなってしまうらしいのです.では,代わりに何があるのか調べてみました.

方法として3種類あるそうな.

  • mysql — これまでの拡張モデル(PHP 5.5より非推奨)
  • mysqli — 改良版拡張モジュール(PHP 5.0以上,MySQL 4.1以上)
  • PDO_MySQL — PHP Data Object データアクセスの抽象レイヤ(PHP 5.1以上)

参考 PHP: MySQL 用 PHP ドライバの概要 – Manual
http://jp2.php.net/manual/ja/mysql.php

これまで,PHPは,MySQLとの接続には「mysql_connect()」,PostgreSQLとの接続には「pg_connect()」関数をそれぞれ利用していました.そのため,データベースを移植した場合は,プログラムを大幅に書き換える必要がありました.そこで,どのデータベースを利用してもプログラムを変更する事無く使えるよう,抽象的レイヤPDOを利用できるようになりました.

今回は,SQLite3のデータベースを準備.

$ sqlite3 sample.sqlite3
SQLite version 3.7.10 2012-01-16 13:28:40
Enter ".help" for instructions
Enter SQL statements terminated with a ";"

sqlite> select * from member;
0001|Taro Kagoshima|171.5|65.2
0002|Hanako Ohsumi|158.5|50.0

sqlite> .schema member
CREATE TABLE member (
id char(4) not null,
name char(100),
height char(10),
weight char(10),
primary key(id)
);

sqlite>.quit

では,PDOを利用したサンプルプログラムを見てください.

$dsn = 'sqlite:./sample.sqlite3';
$user = null;
$password = null;

try {
  // データベースへの接続,PDOインスタンスの生成
  $db = new PDO($dsn, $user, $password);
} catch(PDOException $e) {
  printf("Error: %s\n", $e->getMessage());
  die();
}

$sql = "SELECT * FROM member WHERE id = ?";
$stmt = $db->prepare($sql);
$stmt->execute(array('0002'));

$rows = $stmt->fetch(PDO::FETCH_ASSOC);

print_r($rows);
print_r($rows['name']);

実行結果はこうなります.

$ php -f test1.php
Array
(
    [id] => 0002
    [name] => Hanako Ohsumi
    [height] => 158.5
    [weight] => 50.0
)
Hanako Ohsumi

PDOインスタンス生成の際に,データソース名($dsn),ユーザ名,パスワードを指定します.今回はSQLiteの例を示しましたが,MySQLの場合は次のような感じになります.

$dsn = 'mysql:dbname=sample;host=localhost;charset=utf8';
$user = 'foo';
$password = 'hogehoge';

この部分だけ書き換えれば,後のプログラムはいじることなくデータベースを移行できます.

PDOオブジェクト($db)のprepare()メソッドの引数にSQLを指定して呼び出すことにより,PDOStatementオブジェクト($stmt)を生成します.そのPDOStatementオブジェクトのexecute()メソッドの引数に配列,これにはプリペアステートメントの「?」にあたる部分の値を配列形式で指定します.今回は,「?」が1つだけなので,要素数1の配列になります.fetch()メソッドにPDO::FETCH_ASSOCを指定することにより,フィールド名をキーとする連想配列でレコードを取得することが可能です.

他にも,

$id = '0002';
$sql = "SELECT * FROM member WHERE id = ?";
$stmt = $db->prepare($sql);
$stmt->bindParam(1, $id, PDO::PARAM_STR);
$stmt->execute();

や,次のような指定が可能です.

$id = '0002';
$sql = "SELECT * FROM member WHERE id = :id";
$stmt = $db->prepare($sql);
$stmt->bindParam(':id', $id, PDO::PARAM_STR);
$stmt->execute();

fetch()メソッドをfetchObject()メソッドに書き換え,引数にクラス名を指定することで,その出力がそのクラスのオブジェクトになります.

class Member {
}

$dsn = 'sqlite:./sample.sqlite3';
$user = null;
$password = null;

try {
// データベースへの接続, PDOインスタンスの生成
    $db = new PDO($dsn, $user, $password);
} catch(PDOException $e) {
    printf("Error: %s\n", $e->getMessage());
    die();
}

$id = '0002';
$sql = "SELECT * FROM member WHERE id = :id";
$stmt = $db->prepare($sql);
$stmt->bindParam(':id', $id, PDO::PARAM_STR);
$stmt->execute();

$rows = $stmt->fetchObject('Member');

print_r($rows);
print_r($rows->name);

結果としてはこうなります.

Member Object
(
    [id] => 0002
    [name] => Hanako Ohsumi
    [height] => 158.5
    [weight] => 50.0
)
Hanako Ohsumi

せっかくMemberクラスを作成したので,次のような感じで,find()メソッドを作成し,引数にIDの値を指定することにより,結果セットを取得したいのですが,どうすれば良いでしょうか.

$obj = Member::find('0002');
print_r($obj);

そのためには,Memberクラスにstaticな関数を定義します.ついでにデータベース接続関係もinit()メソッドとして定義します.ここまでのソースを次ぎに示します.

<?php                                                                                                                                                   
class Member {
    private static $db;

    // データベースへの接続,PDOインスタンスの生成
    public static function init() {
        $_dsn = 'sqlite:./sample.sqlite3';
        $_user = null;
        $_password = null;
        try {
            self::$db = new PDO($_dsn, $_user, $_password);
        } catch(PDOException $e) {
            printf("Error: %s\n", $e->getMessage());
            self::$db = null;
        }
    }

    public static function find($id) {
        $sql = "SELECT * FROM member WHERE id = :id";
        $stmt = self::$db->prepare($sql);
        $stmt->bindParam(':id', $id, PDO::PARAM_STR);
        $stmt->execute();
        $ret = $stmt->fetchObject("Member");
        return $ret;
    }
}

Member::init();
$obj = Member::find('0002');
print_r($obj);
?>

同様に,すべてのレコードオブジェクトを出力するfindAll()メソッドも実装してしまいましょう.今回の結果セットは配列になりますので,返り値が配列になっています.

<?php                                                                                                                                                   
class Member {
 :
 (略)
 :
     public static function findAll() {
          $sql = "SELECT * FROM member";
          $stmt = self::$db->prepare($sql);
          $stmt->execute();
          $rets = array();
          while($ret = $stmt->fetchObject('Member'))
               $rets[] = $ret;
          return $rets;
     }       
}

Member::init();
$obj = Member::findAll();
print_r($obj);
?>

結果は次の様になります.

Array
(
    [0] => Member Object
        (
            [id] => 0001
            [name] => Taro Kagoshima
            [height] => 171.5
            [weight] => 65.2
        )

    [1] => Member Object
        (
            [id] => 0002
            [name] => Hanako Ohsumi
            [height] => 158.5
            [weight] => 50.0
        )
)

ついでに,削除も考えてみます.

$obj = Member::find('0002');
$obj->delete();
print_r($obj);

こんな風に,削除できるようにするには,次のようなインスタンスメソッドを準備します.

class Member {
 :
 (略)
 :
     // 任意のIDのレコードを削除する
     public function delete() {
          $sql = "DELETE FROM member WHERE id = :id";
          $stmt = self::$db->prepare($sql);
          $stmt->bindParam(':id', $this->id, PDO::PARAM_STR);
          $stmt->execute();
     }    

}

Member::init();

$obj = Member::find('0002');
$obj->delete();

$obj = Member::findAll();
print_r($obj);
?>

そうなってくると,次のような感じで,任意のレコードの更新や,新規レコードの生成など,ActiveRecordのような感じでやってみたくなります.

更新は...

$obj = Member::find('0002');
$obj->name = 'Hana Ohsumi';
$obj->height = '160.0';
$obj->save();

新規作成は...

$obj = new Member;
$obj->id = '0005';
$obj->name = 'Hayato Satsuma';
$obj->height = '168.0';
$obj->save();

いずれにしてもsave()メソッドを作ることになります.色々やり方はあると思いますが,ここは単純に,対象オブジェクトで指定したidが,既に使われているidであれば更新(update)で,存在しないidであれば追加(insert)とします.すると,saveメソッドは次のような実装になります.

    public function save() {
        $obj = self::find($this->id);
        // 新規レコード追加(存在しないidの場合)
        if($obj == null) {
            $stmt = self::$db->prepare("INSERT INTO member (id, name, height, weight) values (:id, :name, :height, :weight)");
            $stmt->bindParam(':id', $this->id, PDO::PARAM_STR);
            $stmt->bindParam(':name', $this->name, PDO::PARAM_STR);
            $stmt->bindParam(':height', $this->height, PDO::PARAM_STR);
            $stmt->bindParam(':weight', $this->weight, PDO::PARAM_STR);
            $stmt->execute();
        }
        // 既存レコードの更新
        else {
            $stmt = self::$db->prepare("UPDATE member SET name = :name, height = :height, weight = :weight WHERE id = :id");
            $stmt->bindParam(':name', $this->name, PDO::PARAM_STR);
            $stmt->bindParam(':height', $this->height, PDO::PARAM_STR);
            $stmt->bindParam(':weight', $this->weight, PDO::PARAM_STR);
            $stmt->bindParam(':id', $this->id, PDO::PARAM_STR);
            $stmt->execute();
        }
    }

それでは,ここまでのまとめです.ここでは新たに(今後のために)PDOStatementオブジェクトを引数に持ち,単一のレコードオブジェクトを返すgetRecord()メソッドと,複数の結果セット,即ちレコードオブジェクト配列を返すgetRecords()メソッドを加えました.そのため,find()メソッドとfindAll()メソッドに少し修正を加えています.

<?php
class Member {
	private static $db;

	// データベースへの接続,PDOインスタンスの生成
	public static function init() {
		$_dsn = 'sqlite:./sample.sqlite3';
		$_user = null;
		$_password = null;
		try {
			self::$db = new PDO($_dsn, $_user, $_password);
		} catch(PDOException $e) {
			printf("Error: %s\n", $e->getMessage());
			self::$db = null;
		}
	}

	// 結果セット,レコードオブジェクトの配列を返す
	protected static function getRecords(PDOStatement $stmt) {
		$rets = array();
		while($ret = $stmt->fetchObject("Member")) $rets[] = $ret;
		return $rets;
	}
	
	// 単一のレコードオブジェクトを返す
	protected static function getRecord(PDOStatement $stmt) {
		$ret = $stmt->fetchObject("Member");
		return $ret;
	}

	// 任意のIDの単一レコードを返す
	public static function find($id) {
		$sql = "SELECT * FROM member WHERE id = :id";
		$stmt = self::$db->prepare($sql);
		$stmt->bindParam(':id', $id, PDO::PARAM_STR);
		$stmt->execute();
		return self::getRecord($stmt);
	}
	
	// すべてのレコードを返す
	public static function findAll() {
		$sql = "SELECT * FROM member";
		$stmt = self::$db->prepare($sql);
		$stmt->execute();
		return self::getRecords($stmt);
	}

	// 任意のIDのレコードを削除する
	public function delete() {
		$sql = "DELETE FROM member WHERE id = :id";
		$stmt = self::$db->prepare($sql);
		$stmt->bindParam(':id', $this->id, PDO::PARAM_STR);
		$stmt->execute();
	}	

	// レコードの保存
    public function save() {
        $obj = self::find($this->id);
        // 新規レコード追加(存在しないidの場合)
        if($obj == null) {
            $stmt = self::$db->prepare("INSERT INTO member (id, name, height, weight) values (:id, :name, :height, :weight)");
            $stmt->bindParam(':id', $this->id, PDO::PARAM_STR);
            $stmt->bindParam(':name', $this->name, PDO::PARAM_STR);
            $stmt->bindParam(':height', $this->height, PDO::PARAM_STR);
            $stmt->bindParam(':weight', $this->weight, PDO::PARAM_STR);
            $stmt->execute();
        }
        // 既存レコードの更新
        else {
            $stmt = self::$db->prepare("UPDATE member SET name = :name, height = :height, weight = :weight WHERE id = :id");
            $stmt->bindParam(':name', $this->name, PDO::PARAM_STR);
            $stmt->bindParam(':height', $this->height, PDO::PARAM_STR);
            $stmt->bindParam(':weight', $this->weight, PDO::PARAM_STR);
            $stmt->bindParam(':id', $this->id, PDO::PARAM_STR);
            $stmt->execute();
        }
    }

}
// bootstrap
Member::init();

// idが0002のレコードの内容を更新する
$obj = Member::find('0002');
$obj->name = 'Hana Ohsumi';
$obj->height = '160.0';
$obj->save();

// 新しいレコードを追加する
$obj = new Member;
$obj->id = '0005';
$obj->name = 'Hayato Satsuma';
$obj->height = '168.0';
$obj->save();

// 現在のすべてのレコードを表示する
$obj = Member::findAll();
print_r($obj);
?>

これで利用する側としては,SQLを一切書くこと無く,ある程度のCRUD処理が可能となります.まだまだ色々とバグがありそうですが...

VimによるMarkdown編集環境

MarkdownはいわゆるLightweight markup langage(軽量マークアップ言語)の一つです.

lightweight markup langage には,色々と種類があります.
http://en.wikipedia.org/wiki/Lightweight_markup_language

これまで,GitHubを利用している時ぐらいしか使っていませんでしたが,最近では色々な場面で,なんちゃら.md という名のファイル名に出くわします.ここはもう少し突っ込んで記述法を確認してみるかと思った次第です.

記述は簡単なのですが,さてどうやってプレビューするか.HTMLに変換してブラウザで,が一般的なのでしょうが,色々と面倒だな...と.

そういうことで調べてみるとありました,「previm」です.

kannokanno/previm | GitHub
https://github.com/kannokanno/previm

Vimのプラグインらしいです.まあVimをよく利用しますし,保存の度にブラウザの内容が更新されるということで,動きも軽そうなのでこれに決めました.

実は今ままで恥ずかしながらプラグインを利用したことがなかったのですね.そうピュアVimな人だったのです.

ということで,まずは,プラグイン管理を行うNeoBundleをインストールしてみます.

すべてはここに記載されています.
Shougo/neobundle.vim | GitHub
https://github.com/Shougo/neobundle.vim

指定通り,.vimrcに追記して,Vimを起動すると,インストールするかと聞いてきますので,yとすれば勝手にインストールされます.
既に,デフォルトでプラグインがごろごろとインストールされていきます.
そこに,flazz/vim-colorschemasというのがあって,その中に私が普段Emacsで利用しているカラースキーマのSolarizedがありましたので,これを適用しました..vimrcに次の様に追記します.

"Solarized
set background=light
let g:solarized_termcolors=256
colorscheme solarized

NeoBundleの記述の後に記載しないと,Solarized?そんなスキーマは無いよと怒られます.それとMacのターミナルで動かす場合は,デフォルトだとシアンとかマゼンダとか16色レベルでギラギラとした色になって,逆に目が疲れてしまうということになってしまいますので,colorschemaの記述の前に「let g:solarized_termcolors=256」を追記しておきましょう.

markdown-env01

 

さて,いよいよPreVimの出番です.

PreVimについては,次を参照してもらえればOK.

kannokanno/previm | GitHub
https://github.com/kannokanno/previm

ほんと,GitHubはありがたい.

ということで,「.vimrc」に次の3つを記述.

NeoBundle 'kannokanno/previm'
NeoBundle 'plasticboy/vim-markdown'
NeoBundle 'tyru/open-browser.vim'

vim-markdownは,VimのMarkdowmモードで,キーワードハイライトや色々と補完やらしてくれるみたい.open-browserは,これを入れておくと,VimからデフォルトのWebブラウザが開けちゃうみたい.

Vimを再起動すると,上記3つのプラグインをインストールするのかと聞いてきます.

使い方にも記述されている通り,「.vimrc」にこれを追記しておきます.

"PreVim
augroup PrevimSettings
    autocmd!
    autocmd BufNewFile,BufRead *.{md,mdwn,mkd,mkdn,mark*} set filetype=markdown
augroup END

そうしないと,拡張子「.md」をMarkdownと認識してくれないみたい.

Markdownのファイルを編集して,Vimのコマンドモードで「:PrevimOpen」やると,次のようにブラウザが開いて,きちんと整形してくれます.嬉しいことに,「:w」するたびに自動で表示も更新されます.

markdown-env02

なぜか画像が表示されないんですけど...

とりあえず,今回はこの辺で...