当ソフトウェアは利用方法及びソースが読める方のみご利用いただけます。
<?php
/**
* 汎用高速CSV検索くん(ページネーションバージョン)
*
* CSVデータから検索ワードを含むものを表として高速に抽出します
*
* 当ファイルを search.php として保存してください。PGは1ファイルのみで動きます。
* 同フォルダに検索対象 search_db.csv を utf-8 ,改行LF ,区切りtab,タイトルあり
* で設置してください
*
* @version 1.2
* @category
* @package
* @author Takashi Kawahara <t_kawahara@chess-inc.com>
* @copyright (c) 2022 CHESS INC.
* @license (c) 2022 CHESS INC.Released under the GPLv3 license.
**/
// ------------------------------------------------------------------------
// 設定値
$CSV_DATA_FILE = "search_db.csv"; // CSVファイル1行目はタイトル必須です
$CSV_PARSER = "\t";
$TITLE = "汎用高速CSV検索くん";
$PAGE_RECORDS = 25; // 1ページの表示件数
// ------------------------------------------------------------------------
$scriptName = basename($_SERVER['SCRIPT_NAME']); // search.php
function getSplFileObject($csv_data_file, $csv_parser)
{
$fp = new SplFileObject($csv_data_file, 'r');
$fp->setFlags(SplFileObject::READ_CSV
| SplFileObject::SKIP_EMPTY
| SplFileObject::DROP_NEW_LINE
| SplFileObject::READ_AHEAD);
$fp->setCsvControl($csv_parser);
return $fp;
}
// ヒットした行番号と全件数(タイトル抜き)を取得
function getHitRowNosAndTotal($fp, $names)
{
$hitRowNos[] = array();
$isFirst = true;
$i = 0;
foreach ($fp as $cols) {
if ($isFirst) {
// タイトル行
$isFirst = false;
} else {
$allFind = true;
$line = implode("\t", $cols);
if(!empty($names)){
foreach($names as $nm){
if (strpos($line, $nm) !== false) {
// 発見
}else{
$allFind = false;
break;
}
}
if($allFind){
$hitRowNos[] = $i;
}
}
$i++;
}
}
$total = $i;
return [$hitRowNos, $total];
}
function getTableHtmlFo($fp, $hitRowNos, $hasPage, $nowPage, $page_records)
{
// 現在のページ数取得とスタートページとエンドページの計算
if ($hasPage) {
$startRow = ($nowPage * $page_records) - $page_records;
$endRow = ($nowPage * $page_records) - 1;
} else {
$startRow = $page_records - $page_records;
$endRow = $page_records - 1;
}
$html = "<table>";
$isFirst = true;
$i = 0;
$hitCounter = 0;
foreach ($fp as $cols) {
if ($isFirst) {
// タイトル行
$html .= "<tr>";
foreach ($cols as $col) {
$html .= "<th>{$col}</th>";
}
$html .= "</tr>";
$isFirst = false;
} else {
if (in_array($i, $hitRowNos, true)) {
if ($hitCounter < $startRow) {
} else if ($hitCounter <= $endRow) {
// $startRow <= $hitCounter && $hitCounter <= $endRow
$html .= "<tr>";
foreach ($cols as $col) {
$html .= "<td>{$col}</td>";
}
$html .= "</tr>";
} else {
// $startRow <= $hitCounter && $endRow < $hitCounter
break;
}
$hitCounter++;
}
$i++;
}
}
$html .= "</table>";
return $html;
}
// ページャー用のhtmlを返す
function getPagerHtml($name, $hasPage, $nowPage, $hitTotal, $page_records, $scriptName)
{
$maxPages = intval(ceil($hitTotal / $page_records)); // ceilはfloat注意
$prevPage = $nowPage - 1;
if ($hasPage) {
$nextPage = $nowPage + 1;
} else {
$nextPage = $nowPage + 2;
}
$html = "<div class=\"pager\"><ul>";
// 先頭,前へ
if ($hasPage && $nowPage != 1) {
$html .= "<li><a href=\"./{$scriptName}?name={$name}&p=1\">« 先頭</a></li>";
$html .= "<li><a href=\"./{$scriptName}?name={$name}&p={$prevPage}\">‹ 前へ</a></li>";
}
// 数字ボタンの生成
if ($maxPages <= 7) {
for ($i = 1; $i <= $maxPages; $i++) {
// 合計ページ数が7ページに満たない場合のみ
if ($nowPage === $i || ($nowPage === 0 && $i === 1)) {
$html .= "<li><em>{$i}</em></li>";
} else {
$html .= "<li><a href=\"./{$scriptName}?name={$name}&p={$i}\">{$i}</a></li>";
}
}
} elseif ($nowPage <= 4 && $maxPages > 7) {
// 合計ページが7ページ以上で、最初の4ページまで
for ($i = 1; $i <= 7; $i++) {
if ($nowPage === $i || ($nowPage === 0 && $i === 1)) {
$html .= "<li><em>{$i}</em></li>";
} else {
$html .= "<li><a href=\"./{$scriptName}?name={$name}&p={$i}\">{$i}</a></li>";
}
}
} elseif ($nowPage >= 5 && $nowPage < ($maxPages - 2) && $maxPages > 7) {
// 合計ページが7ページ以上で、5ページ目以降、最後から4ページ前まで
for ($i = ($nowPage - 3); $i <= ($nowPage + 3); $i++) {
if ($nowPage === $i || ($nowPage === 0 && $i === 1)) {
$html .= "<li><em>{$i}</em></li>";
} else {
$html .= "<li><a href=\"./{$scriptName}?name={$name}&p={$i}\">{$i}</a></li>";
}
}
} elseif ($nowPage > ($maxPages - 3)) {
// 合計ページが7ページ以上で、最後の3ページ
for ($i = ($maxPages - 6); $i <= $maxPages; $i++) {
if ($nowPage === $i || ($nowPage === 0 && $i === 1)) {
$html .= "<li><em>{$i}</em></li>";
} else {
$html .= "<li><a href=\"./{$scriptName}?name={$name}&p={$i}\">{$i}</a></li>";
}
}
}
// 次へ,最後
if ($nowPage < $maxPages && $maxPages != 1) {
$html .= "<li><a href=\" ./{$scriptName}?name={$name}&p={$nextPage}\">次へ ›</a></li>";
$html .= "<li><a href=\"./{$scriptName}?name={$name}&p={$maxPages}\">最後 »</a></li>";
}
$html .= "</ul></div>";
return $html;
}
function h($str)
{
return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
// ------------------------------------------------------------------------
$name = "";
$names = [];
if (isset($_GET['name'])) {
$name = h($_GET['name']);
$name = str_replace(' ',' ',$name); // 大文字スペースは半角に
$names = explode(' ',$name);
}
if (isset($_GET['p'])) {
$nowPage = intval(h($_GET['p']));
$hasPage = true;
} else {
$nowPage = 0;
$hasPage = false;
}
// ------------------------------------------------------------------------
$fp = getSplFileObject($CSV_DATA_FILE, $CSV_PARSER);
// ヒットした行番号と全件数(タイトル抜き)を取得
[$hitRowNos, $total] = getHitRowNosAndTotal($fp, $names);
$hitTotal = count($hitRowNos) - 1;
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<link rel="icon" href="data:;base64,=">
<title><?= $TITLE ?></title>
</head>
<body onload="load_focus()">
<h1><?= $TITLE ?></h1>
<form action="<?= $scriptName ?>" method="get">
<p>
検索ワード(AND) :
<input type="text" name="name" id="name" value="<?= $name; ?>" onchange="document.forms[0].submit();" class="form-text" style="margin-right:1rem">
<!-- tabでもsubmit -->
<input type="submit" value="検 索" class="form-submit-button">
</p>
</form>
<?= getTableHtmlFo($fp, $hitRowNos, $hasPage, $nowPage, $PAGE_RECORDS); ?>
<div style=" display: table; width: 100%;">
<p style="display: table-cell; text-align: left;">
</p>
<p style="display: table-cell; text-align: right;">
<?php echo "対象 " . number_format($hitTotal) . " / 全 " . number_format($total) ?>
</p>
</div>
<?= getPagerHtml($name, $hasPage, $nowPage, $hitTotal, $PAGE_RECORDS, $scriptName); ?>
<style>
table {
width: 100%;
text-align: left;
border-collapse: collapse;
border-spacing: 0;
}
table tr:nth-child(2n+1) {
background: #e9faf9;
}
table th {
padding: 10px;
background: #778ca3;
border: solid 1px #666666;
color: #ffffff;
}
table td {
padding: 10px;
border: solid 1px #666666;
}
/* ----------------------------------------------------------------------- */
.form-text {
width: 20rem;
height: 3em;
padding: 0 12px;
border-radius: 4px;
border: none;
box-shadow: 0 0 0 1px #ccc inset;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
}
.form-text:focus {
outline: 0;
box-shadow: 0 0 0 2px rgb(33, 150, 243) inset;
}
.form-submit-button {
display: inline-block;
padding: 8px;
border: none;
border-radius: 4px;
background-color: #555;
color: #fff;
font-weight: bold;
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
cursor: pointer;
border: 2px solid transparent;
}
.form-submit-button:hover {
background-color: #aaa;
}
.form-submit-button:focus {
outline: 0;
background-color: #555;
border: 2px solid rgb(33, 150, 243);
}
/* ----------------------------------------------------------------------- */
div.pager ul {
float: left;
position: relative;
left: 50%;
list-style: none;
margin: 0;
padding: 0;
}
div.pager ul li {
float: left;
position: relative;
left: -50%;
}
div.pager li {
float: left;
margin-right: 5px;
border: 1px #ccc solid;
}
div.pager li.prevPage,
div.pager li.nextPage {
border: none;
}
div.pager li a {
padding: 8px 12px;
display: block;
color: #444;
}
div.pager li a:link,
div.pager li a:visited {
text-decoration: none;
}
div.pager li a:hover,
div.pager li a:active {
background-color: #eee;
text-decoration: none;
}
div.pager li em {
display: block;
padding: 8px 12px;
background: #444;
color: #fff;
font: inherit;
}
</style>
<script>
// 初期オープン時のINPUTのTEXT位置を設定
function load_focus() {
var elm = document.getElementById('name');
var val = elm.value;
elm.value = '';
elm.focus();
elm.value = val;
}
</script>
</body>
</html>