sqliteでage機能のある掲示板を作るときに考えたこと

## テーブル1つでage/sageは実現できるか

### ageって何

※「age」とは投稿のあったスレッドがいちばん上に来る機能のことです。

## できたの？

素人が考えた結果、できました。

こちらの掲示板で使っています。[noReita](https://github.com/sakots/noReita)
[noReitaのサンプル](https://oekakibbs.moe)

## どのように実現したか

まずテーブルにスレッド親かリプライか判断する項目、リプライの親識別用項目、コメントID、スレッド構造IDを用意しました。

### スレッド親投稿の場合

うちの掲示板ではこうしています。

```index.php
$tree = time() * 100000000;

//スレ建て
$thread = 1;
$age = 0;
$parent = NULL;
```

`$tree`がコメントIDになっています。UNIX時間の100000000倍を使いました。これでUNIX時間が8桁の間は大丈夫のはずです。

`$age`がage値になります。リプライがあった場合に使います。`$parent = NULL`がスレッドの親であることを示しています。

スレッドの親なので`$thread`は1です。

### リプライの場合

```index.php
//レスの位置
$tree = time() - $parent - (int)$msgwc["tid"];
$comid = $tree + time();

//リプ処理
$thread = 0;
```

スレッド内でのリプライの順番です。

`$parent`には親スレッドの`$tree`が代入されています。

`$tree = time() - $parent - (int)$msgwc["tid"]`でUNIX時間から親IDを引いています。
そのあと`$comid = $tree + time();`でUNIX時間をまた足しています。

これで何が起こるかというと、「16桁のある順番を持った数字が出来上がる」です。

上8桁がスレッドの順番を、下8桁がリプライの順番をそれぞれ表しています。また、リプライなので`$thread`は0です。

### age処理

リプライの場合の続きです。ちなみに、sage機能（スレッドをageない）も搭載しているのでこうなっています。

```index.php
//メール欄にsageが含まれるならageない
$age = (int)$msgwc["age"];
if (strpos($mail, 'sage') !== false) {
  //sage
  $age = $age;
} else {
  //age
  $age++;
  $agetree = $age + (time() * 100000000);
  $sql_age = "UPDATE tlog SET age = $age, tree = $agetree WHERE tid = $parent";
  $db->exec($sql_age);
}
```

ageると親IDが現在のUNIX時間（の100000000倍よりage数大きい値）で上書きされます。

こうすることによりスレッドの順番を保っています。

## 実際に表示する

このようにしています。（blade使っています）

```index.php
try {
  $db = new PDO(DB_PDO);
  //1ページの全スレッド取得
  $sql = "SELECT * FROM tlog WHERE invz=0 AND thread=1 ORDER BY tree DESC LIMIT $start,$page_def";
  $posts = $db->query($sql);

  $ko = array();
  $oya = array();

  $i = 0;
  $j = 0;
  while ($i < PAGE_DEF) {
    $bbsline = $posts->fetch();
    if (empty($bbsline)) {
      break;
    } //スレがなくなったら抜ける
    $oid = $bbsline["tid"]; //スレのtid(親番号)を取得
    $sqli = "SELECT * FROM tlog WHERE parent = $oid AND invz=0 AND thread=0 ORDER BY comid ASC";
    //レス取得
    $postsi = $db->query($sqli);
    $j = 0;
    $flag = true;
    while ($flag == true) {
      $_pchext = pathinfo($bbsline['pchfile'], PATHINFO_EXTENSION);
      if ($_pchext === 'chi') {
        $bbsline['pchfile'] = ''; //ChickenPaintは動画リンクを出さない
      }
      $res = $postsi->fetch();
      if (empty($res)) { //レスがなくなったら
        $bbsline['ressu'] = $j; //スレのレス数
        $bbsline['res_d_su'] = $j - DSP_RES; //スレのレス省略数
        if ($j > DSP_RES) { //スレのレス数が規定より多いと
          $bbsline['rflag'] = true; //省略フラグtrue
        } else {
          $bbsline['rflag'] = false; //省略フラグfalse
        }
        $flag = false;
        break;
      } //抜ける
      $res['resno'] = ($j + 1); //レス番号
      // http、https以外のURLの場合表示しない
      if (!filter_var($res['a_url'], FILTER_VALIDATE_URL) || !preg_match('|^https?://.*$|', $res['a_url'])) {
        $res['a_url'] = "";
      }
      $res['com'] = htmlspecialchars($res['com'], ENT_QUOTES | ENT_HTML5);

      //オートリンク
      if (AUTOLINK) {
        $res['com'] = auto_link($res['com']);
      }
      //ハッシュタグ
      if (USE_HASHTAG) {
        $res['com'] = hashtag_link($res['com']);
      }
      //空行を縮める
      $res['com'] = preg_replace('/(\n|\r|\r\n|\n\r){3,}/us', "\n\n", $res['com']);
      //<br>に
      $res['com'] = tobr($res['com']);
      //引用の色
      $res['com'] = quote($res['com']);
      //日付をUNIX時間に変換して設定どおりにフォーマット
      $res['created'] = date(DATE_FORMAT, strtotime($res['created']));
      $res['modified'] = date(DATE_FORMAT, strtotime($res['modified']));
      $ko[] = $res;
      $j++;
    }
    // http、https以外のURLの場合表示しない
    if (!filter_var($bbsline['a_url'], FILTER_VALIDATE_URL) || !preg_match('|^https?://.*$|', $bbsline['a_url'])) {
      $bbsline['a_url'] = "";
    }
    $bbsline['com'] = htmlspecialchars($bbsline['com'], ENT_QUOTES | ENT_HTML5);

    //オートリンク
    if (AUTOLINK) {
      $bbsline['com'] = auto_link($bbsline['com']);
    }
    //ハッシュタグ
    if (USE_HASHTAG) {
      $bbsline['com'] = hashtag_link($bbsline['com']);
    }
    //空行を縮める
    $bbsline['com'] = preg_replace('/(\n|\r|\r\n){3,}/us', "\n\n", $bbsline['com']);
    //<br>に
    $bbsline['com'] = tobr($bbsline['com']);
    //引用の色
    $bbsline['com'] = quote($bbsline['com']);
    //日付をUNIX時間にしたあと整形
    $bbsline['past'] = strtotime($bbsline['created']); // このスレは古いので用
    $bbsline['created'] = date(DATE_FORMAT, strtotime($bbsline['created']));
    $bbsline['modified'] = date(DATE_FORMAT, strtotime($bbsline['modified']));
    $oya[] = $bbsline;
    $i++;
  }

  $dat['ko'] = $ko;
  $dat['oya'] = $oya;
  $dat['dsp_res'] = DSP_RES;
  $dat['path'] = IMG_DIR;

  echo $blade->run(MAINFILE, $dat);
  $db = null; //db切断
} catch (PDOException $e) {
  echo "DB接続エラー:" . $e->getMessage();
}
```

## 結論

でかい数字だけで掲示板のスレッド管理ができるというお話でした。

質問やもっとうまくできる方法があればコメントお願いします。