WEBからアクセスし、CSVデータから検索ワードを含むもののみ表形式で高速抽出し出力します(ページネーションバージョン)

当ソフトウェアは利用方法及びソースが読める方のみご利用いただけます。

<?php

/**
 * 汎用高速CSV検索くん(ページネーションバージョン)
 *
 * CSVデータから検索ワードを含むものを表として高速に抽出します
 *
 * 当ファイルを search.php として保存してください。PGは1ファイルのみです。
 * 同フォルダに検索対象 search_db.csv を utf-8 ,改行LF ,区切りtab,タイトルあり
 * で設置してください
 *
 * @version   1.0
 * @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 = 20; // 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, $name)
{
    $hitRowNos[] = array();
    $isFirst = true;
    $i = 0;
    foreach ($fp as $cols) {
        if ($isFirst) {
            // タイトル行
            $isFirst = false;
        } else {
            if (strpos(implode("\t", $cols), $name) !== false) {
                $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\">&laquo;&nbsp;先頭</a></li>";
        $html .= "<li><a href=\"./{$scriptName}?name={$name}&p={$prevPage}\">&#139;&nbsp;前へ</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}\">次へ&nbsp;&#155;</a></li>";
        $html .= "<li><a href=\"./{$scriptName}?name={$name}&p={$maxPages}\">最後&nbsp;&raquo;</a></li>";
    }
    $html .= "</ul></div>";

    return $html;
}
function h($str)
{
    return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
// ------------------------------------------------------------------------
$name = "";
if (isset($_GET['name'])) {
    $name = h($_GET['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, $name);
$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>
            検索ワード(含む) : 
            <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: center;
            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>

簡単らくらくリモートワークセット

新型コロナウィルス対策として自宅でリモートワークを行うとき、会社の電話外線の着信・発信ができればと思われること御座いませんか?
「簡単らくらくリモートワークセット」はVPN(バーチャルプライベートネットワーク)とVoIP(IP電話)を実現するハードウェアとソフトウェアを組み合わせたセット商品です。

また、クラウドではなくオンプレミス(御社内でハードウェアとソフトウェアを運用)のシステムである為、システムを利用する為の月額利用料が発生しません。(インターネットプロバイダー料金、実際に通信を行った費用は必要となります)