CSVをPHPでオブジェクト配列として扱う、CSVのActiveRecord的な何かを書いた。


だいぶ前にもあったけど、CSVを読み込んだり書き出したり、うにゃうにゃすることがよくあるので、簡単に扱えるものでも書こうかと思って書きました。
ただ、ここで扱うCSVは1行目にfieldName(半角英数)が入ることを前提としています。


利用方法は以下のような感じ。

//ライブラリを読み込む
require_once("csv_active_record.php");
//オブジェクト生成
//第1引数はCSVファイルヘのパス、第2引数は文字コード(省略した場合、デフォルトでUTF-8)
$object=new CSVActiveRecord("test.csv", "UTF-8");

オブジェクトを生成した時点で、CSVがオブジェクト配列として返されます。


◆CSV

"id","name","price","comment"
"1","梅おにぎり","105","梅干し入りのおにぎり"
"2","お〜い、お茶","120","喉が渇いたときはやっぱりお茶"
"4","大根サラダ","180","野菜もちゃんと食べないとね"
"5","プリン","88","デザートはやっぱりプリンでしょ"


◆返されたオブジェクト配列

var_dump($object->csvObject);
 ↓
  array(4) {
    [0]=>
    object(stdClass)#2 (4) {
      ["id"]=>
      string(1) "1"
      ["name"]=>
      string(15) "梅おにぎり"
      ["price"]=>
      string(3) "105"
      ["comment"]=>
      string(30) "梅干し入りのおにぎり"
    }
    [1]=>
    object(stdClass)#3 (4) {
      ["id"]=>
      string(1) "2"
      ["name"]=>
      string(18) "お〜い、お茶"
      ["price"]=>
      string(3) "120"
      ["comment"]=>
      string(42) "喉が渇いたときはやっぱりお茶"
    }
    [2]=>
    object(stdClass)#4 (4) {
      ["id"]=>
      string(1) "4"
      ["name"]=>
      string(15) "大根サラダ"
      ["price"]=>
      string(3) "180"
      ["comment"]=>
      string(39) "野菜もちゃんと食べないとね"
    }
    [3]=>
    object(stdClass)#5 (4) {
      ["id"]=>
      string(1) "5"
      ["name"]=>
      string(9) "プリン"
      ["price"]=>
      string(2) "88"
      ["comment"]=>
      string(45) "デザートはやっぱりプリンでしょ"
    }
  }


var_dump($object->fieldNameArray);
 ↓
  array(4) {
    [0]=>
    string(2) "id"
    [1]=>
    string(4) "name"
    [2]=>
    string(5) "price"
    [3]=>
    string(7) "comment"
  }

CSVのアップデート・行の追加・削除

csvObjectを変更して、$object->save()関数を呼び出すことでCSVファイルに保存することができます。
(CSVファイルはSJISで書き出すようにしています)

$object->csvObject[2]->["name"]="NewName";
$object->save();

また、$object->update()関数でもアップデートできます。
この場合、第1引数はレコードの行番号で、第2引数はアップデートするパラメータの連想配列です。

$object->update(1, array(
	"name"=>"NewName"
));

行の追加をするときは$object->insert()でできます。
このとき、insertするパラメータは連想配列で与えてください。

$object->insert(array(
	"id"=>"5",
	"name"=>"プリン",
	"price"=>"88",
	"comment"=>"デザートはやっぱりプリンでしょ"
));

行の削除もできます。もちろん$object->delete()です。引数には行番号を与えてください。

$object->delete(2);

CSVを条件付きでSELECTする

insert, update, deleteときたら「select」だ。
おまけ的に$object->select()も作ってみた。

第1引数にfieldName、第2引数に条件を与えると、条件にあったオブジェクト配列を返してくれます。

$selectedRecords=$object->select("price", ">110");

ライブラリのソース

<?php

class CSVActiveRecord {
	private $fileURL;
	private $charset;
	public $csvRecords;
	public $fieldNameArray;
	public $csvObject;
	public $selectObject;
	
	function __construct($fileURL, $charset="UTF-8"){
		$this->fileURL=$fileURL;
		$this->charset=$charset;
		mb_regex_encoding($this->charset);
		$result=$this->read();
		return $result;
	}
	
	//CSVの読み込み(オブジェクト配列を返す)
	public function read(){
		$this->csvRecords=array();
		$fp = @fopen($this->fileURL, "r+");
		while(($line = @fgets($fp))){
			$line = mb_convert_encoding($line, $this->charset, "SJIS");
			$line = mb_ereg_replace("\"", "", $line);
			$line = mb_ereg_replace("\n", "", $line);
			if(mb_substr_count($line, ",") > 1){
				$this->csvRecords[] = mb_split(",", $line);
			}
		}
		@fclose($fp);
		$result=$this->mapping();
		return $result;
	}
	
	//オブジェクトにマッピングする
	public function mapping(){
		if(!$this->csvRecords){
			return false;
		}
		$this->csvObject=null;
		$this->fieldNameArray=$this->csvRecords[0];
		foreach($this->csvRecords as $key => $csvRecord){
			if($key!="0"){
				$objectArray[]=$this->transformArray($this->fieldNameArray, $csvRecord);
			}
		}
		return $this->csvObject=$objectArray;
	}
	
	//CSVを書き出す・保存
	public function save(){
		if(!$this->csvObject){
			return false;
		}
		$csv="";
		//フィールドネームレコード
		foreach($this->fieldNameArray as $fieldName){
			$csv.="\"".$fieldName."\",";
		}
		$csv.="\n";
		foreach($this->csvObject as $record){
			foreach($record as $value){
				$csv.="\"".$value."\",";
			}
			$csv.="\n";
		}
		$csv = mb_ereg_replace(",\n", "\n", $csv);
		$csv = mb_convert_encoding($csv, "SJIS", $this->charset);
		// CSVファイルを呼び出す(※なければ新たに作成)
		$fp = @fopen($this->fileURL, "w+");
		// CSVファイルに$dataを追加挿入
		@fputs($fp, $csv);
		// CSVファイルを閉じる
		@fclose($fp);
		chmod($this->fileURL, 0666);
		$result=$this->read();
		return $result;
	}
	
	//新たな行を追加する
	public function insert($param=array()){
		if(!is_array($param) || !count($param)){
			return false;
		}
		foreach($this->fieldNameArray as $NameArray){
			$record[$NameArray]=$param[$NameArray];
		}
		$this->csvObject[]=$record;
		$result=$this->save();
		return $result;
	}
	
	//特定の行をアップデート
	public function update($recordNum, $param=array()){
		if(!is_array($param) || !count($param)){
			return false;
		}
		foreach($param as $fieldName => $value){
			$this->csvObject[$recordNum]->$fieldName=$value;
		}
		$result=$this->save();
		return $result;
	}
	
	//特定の行を削除
	public function delete($recordNum){
		unset($this->csvObject[$recordNum]);
		$result=$this->save();
		return $result;
	}
	
	//条件付きでrecordをselect
	public function select($fieldName, $condition){
		$selectRecords=array();
		$newfunc = create_function('$a', 'if($a'.$condition.') return true; else return false');
		foreach($this->csvObject as $record){
			if($newfunc($record[$fieldName])){
				$selectRecords[]=$record;
			}
		}
		return $this->selectObject=$selectRecords;
	}
	
	//配列をobjectに変換
	private function transformArray($fieldNameArray, $array){
		foreach($array as $key => $value){
			$fieldName=$fieldNameArray[$key];
			$object->$fieldName=$value;
		}
		return $object;
	}
	
}
?>

と、ここまで書いたけど、少し気に入らない部分が出てきた。
時間があったら書き直そう。

タイトルにはActiveRedord的な何かと書いたけど違うよなーたぶん。