【PHP】phpによるcsvの複数行の削除方法が知りたい。

php

さて、最近ちょいちょいPHPに興味を再び持ち始めています。なぜかサーバーサイドでデータを弄るのが面白いと思ってしまった。
ちょっと初心に戻って、PHPではよく使われると思われる。CSVの操作方法もメモる。

今回は、数あるデータのうちチェックボックスでチェックしたデータを削除するロジックをメモっておこう。早い話が削除というより「fopen() する前のファイルの内容を読んで新しく書き戻す」という作業。

*とりあえず調べたりして、やってみたのでもっと良い方法なども全然あるんじゃいかな~と思ってる。

全体の流れとしては、下記のような流れになる。

  • フォームのチェックボックスからチェックを入れたデータを選択して送信。
  • CSVファイルを読み込んでチェックしたデータ以外を配列に保存する。
  • CSVデータを読み込み。

フォームのチェックボックスからチェックを入れたデータを選択して送信

フォームのコードは下記のようにした。

 
>

for文の初期値について

上記のコードを見て分かると思いますが、for文の初期値が「$i=1」と1から始まっています。これはCSVのデータをだいたいEXCELか何かで開くと思いますが、1行目ってだいたい各項目のタイトルが入ってたりしますよね。
なので、常にCSVの1行目は各項目のタイトルが記入されていることが前提なので初期値を1にしました。

チェックボックスのvalue属性に設定する値について

何番目のデータか・・というのは、フォームのチェックボックスのvalue属性にfor文の連番を設定することで、削除したいデータが何番目のデータか分かるようにしています。
よく、CSVデータにIDを入れるパターンもありますが、IDが連番ではなくアルファベットと混在(data1,data2みたいな)だったりするとめんどくさいので・・

CSVファイルを読み込んでチェックしたデータ以外を配列に保存する。

PHPのコードは下記のようにした。*あくまでチェックしたデータを削除するのが目的なので、最低限の説明のみ。

//1 必要な配列を用意
$newdata=array();
$datalist=array();
$checks=array();

if($_SERVER["REQUEST_METHOD"]=="POST" && !empty($_POST)){
	
	//2 フォームよりチェックしたデータを受け取る
	$checks=$_POST["checker"];
	
	//3 CSVファイルを読み込み・書き込み用でオープンする
	$fp=fopen($file,"r+");
	flock($fp,LOCK_EX);
	$count=0;
	while($data=fgetcsv($fp)){
		//4 in_array関数でチェックする
		if(!in_array($count,$checks)){
			$newdata[]=$data;
		}
		$count++;
	}
	//5 ftruncateでファイルサイズをゼロにする
	ftruncate($fp,0);
	fseek($fp,0);
	
	//6 新しく作った配列データをCSVに書き込む
	foreach($newdata as $list){
		fputcsv($fp,$list);
	}
	flock($fp,LOCK_UN);
	fclose($fp);
	
}

1:必要な配列を用意する

  • 「$newdata」はチェックボックスより受け取ったデータ以外を保存するための配列。
  • 「$datalist」はhtml上でCSVデータを表示させる際にデータを保存しておく配列。
  • 「$checks」はフォームよりより受け取った番号を保存するための配列

2:フォームよりチェックしたデータを受け取る

何番目のデータを削除したかの番号をPOSTで受け取る。フォームには「name=”checker[]”」と指定して複数のデータを受け取れるようにしておく。 
「name=”chekcer”」だと最後にチェックされた値しか保存できません。

3:CSVファイルを読み込み・書き込み用でオープンする

CSVファイルを「読み込み/書き込み」モードで開く。 「r+」にしているが「w+」もある。しかし「r+」と「w+」は動作が異なるので注意が必要。
古い記事ですがブログ:Apocripha.netの「[PHP] fopen() “r+” “w+” どちらも『読み込み/書き込み共用です』 どない違うねんという話」が参考になります。*時間あれば検証してみたいですが。

4:in_array関数でチェックする

in_array関数は配列に値があるかチェックする関数です。
上記の場合はカウンター変数として「$count」を用意してfgetcsvで読み込んだCSVファイルの数をwhile文でまわして「$count++」でカウントアップしています。

カウンター変数と「$checks」の配列に保存された番号が一致していれば同じデータを判断できる。
今回の場合は同じデータは削除したいデータなのでif文で論理演算子の「否定」をしているので「カウント変数と$checksの番号と一致していないデータを新しい配列に保存する」という意味になる。

5:ftruncateでファイルサイズをゼロにする

ftruncateはファイルを指定した長さに丸める関数です。

4番目で新しいデータを作った後は最初に読みこんだCSVファイルに書き込めばいいのですが、fopenした段階ではまだ以前のデータが残っています。
CSVファイル自体は再利用したいので、ftruncateで0を指定にしてファイルを空っぽにしています。

ファイルを空っぽにした後はデータを先頭から追加していきたのでファイルポイントを先頭に戻すためにfseek関数を使用して先頭に戻しています。
参考サイト:せとっちの防忘録「ファイルの中身を空にするftruncate

6:新しく作った配列データをCSVに書き込む

CSVファイルの中身を空っぽにしてファイルポイントを先頭に戻したら、新しく作ったデータをforeach文を利用して1行ずつ、fputcsv関数を利用してCSVにデータを書き込みます。
その後はファイルロックを解除してfcloseでファイルを閉じたら終わりです。

CSVデータを読み込み。

これまでの動作を行ったら後はまた、CSVデータを読み込む処理を行えばいいだけです。
*今回は削除が目的なので読み込みの説明は書きませんがPHPの全体のコードを載せておく。

デモはこちら

$file="shoplist.csv";
$newdata=array();
$datalist=array();
$checks=array();
if($_SERVER["REQUEST_METHOD"]=="POST" && !empty($_POST)){
	$checks=$_POST["checker"];
	var_dump($checks);
	
	//
	$fp=fopen($file,"r+");
	flock($fp,LOCK_EX);
	$count=0;
	while($data=fgetcsv($fp)){
		if(!in_array($count,$checks)){
			$newdata[]=$data;
		}
		$count++;
	}
	//
	ftruncate($fp,0);
	fseek($fp,0);
	
	foreach($newdata as $list){
		fputcsv($fp,$list);
	}
	flock($fp,LOCK_UN);
	fclose($fp);
	
}
//
$datalist=show($file);


function show($filename){
	$fp=  fopen($filename,"r");
	$datas=array();
	while ($data=fgetcsv($fp)) {

	list($id,$name,$pos1,$pos2,$url,$plot,$detail)=$data;
	$list=array(
    "id"=>$id,
    "name"=>$name,
    "pos1"=>$pos1,
    "pos2"=>$pos2,
    "url"=>$url,
    "plot"=>$plot,
    "detail"=>$detail
    );
  //一行データをまとめる配列に追加する
  $datas[]=$list;
}
fclose($fp);

return $datas;
}