\n```\n\n### 試したこと・調べたこと\n- [x] teratailやGoogle等で検索した\n- [x] ソースコードを自分なりに変更した\n- [x] 知人に聞いた\n- [x] その他\n\n##### 上記の詳細・結果\n検索してみたのですが Q&A掲示板についての記事がなく掲示板のコメント返信機能に必要な部分のみ参考にしております。\n\n##### 追記\nデータ通信量によるかと思いますが回答専用テーブルを用意して質問専用テーブルと分けたほうが良いのではないかと考えております。\n7~10日で質問を cron で削除する前提であるものの同時刻に複数の掲示板が動作した場合負荷がかかり遅延に繋がるのではないかと思います。\n\n質問&回答の両用テーブルの場合共通のコードなどはまとめることが可能だと思うのですがそれ以外のメリットがあまり無さそうです。\n\n\n### 補足\n※参考サイト\nhttps://qiita.com/ryouya3948/items/6928c89607cf4eaa72a0","answerCount":8,"upvoteCount":0,"datePublished":"2024-08-26T10:29:52.952Z","dateModified":"2024-11-08T11:38:17.000Z","acceptedAnswer":{"@type":"Answer","text":"設計上、10日ほどで古いものを削除する運用であることから、データが増えた場合の性能低下を考慮する必要性が薄いです。そのため、作りやすい方で良いと思います。\n動かないものよりは動いているものの方が大きな価値がありますし、とっかかりは実装が容易な方が良いです。\n(セキュリティに問題があり、利用者に影響がでるなどの場合はその限りではないので、ここは慎重に検討する必要はあります。)\n\nteratailのように、運営時間に応じてデータが増えていくサイトを目指すのであれば私であればテーブルを分けます。理由は主に下記です。\n- テーブルを分けなかった場合、一番アクセスの多いであろう質問一覧画面を表示するためにwhere句で `item_type = 1` の絞り込みが必要になり、DBを効率的に扱えない\n- 感覚上テーブルを正規化して作りにくくなったことはあまりない\n- 分けることによって質問だけ、回答だけのデータ属性を拡張しやすくなる\n","dateModified":"2024-08-31T22:30:52.000Z","datePublished":"2024-08-27T16:24:44.240Z","upvoteCount":0,"url":"https://teratail.com/questions/nzq61t2fhxomzs#reply-jrf4z4olpydnhy"},"suggestedAnswer":[{"@type":"Answer","text":"「X-Twitterのように質問・回答関係なくタイムラインとして時系列で並べたい」\n「質問にも回答、どちらにも返信ができる仕様としたい」\n「質問も回答も、基本的なデータ構造が同じ」\n「質問・回答、どちらも対象に同じ条件で検索をさせたい」\nという場合は、同じテーブルにして質問か回答かのフラグを付ける形が良いと思います。\n\n質問と回答で持つカラムの内容が大きく異なる - 例えば、質問の方にはタグ、カテゴリ、閲覧PV数、いいね数、など回答には必要のないカラムが多数あるといった場合は、分けても良いと思います。\n\nこれは感想ですが、7-10日で質問を削除してしまうと、Q&Aがウェブ上に残らないのは少しもったいないような感じました。現にこの回答はご質問から8日後にしていますので、あと2日で消えてしまうと思うと…","dateModified":"2024-09-02T23:28:16.593Z","datePublished":"2024-09-02T23:28:16.593Z","upvoteCount":0,"url":"https://teratail.com/questions/nzq61t2fhxomzs#reply-2lkjhix9yfy0wd","comment":[{"@type":"Comment","text":"findyさんアドバイスありがとうございます。\nカラムはスタンプとタイトルを除いて同じなので両用テーブルの方が良さそうですね。\nもう少し期間を延ばせるように工夫してみたいと思います。","datePublished":"2024-09-03T03:27:28.405Z","dateModified":"2024-09-03T03:27:28.405Z"}]},{"@type":"Answer","text":"分けた方がいいかなーとおもってはいます","dateModified":"2024-08-28T02:06:58.222Z","datePublished":"2024-08-28T02:06:58.222Z","upvoteCount":0,"url":"https://teratail.com/questions/nzq61t2fhxomzs#reply-1gcudx2rg0hq8g","comment":[{"@type":"Comment","text":"yuske39さん回答ありがとうございます。皆様のアドバイスからしっかり考えてみます。","datePublished":"2024-08-28T05:37:07.508Z","dateModified":"2024-08-28T05:37:07.508Z"}]},{"@type":"Answer","text":"質問と回答に保存する内容が極端に違うのであれば分けてもいいでしょうけど、たとえば回答に対してレスができる枝分岐していくような仕組みを模索するなら一緒の方が管理が楽だと思います","dateModified":"2024-08-28T00:34:48.048Z","datePublished":"2024-08-28T00:34:48.048Z","upvoteCount":1,"url":"https://teratail.com/questions/nzq61t2fhxomzs#reply-1hprx2p8f56u7k","comment":[{"@type":"Comment","text":"yambejpさんアドバイスありがとうございます。\n質問と回答が結びついており回答に対してレスができる枝分岐していくような形なので両用テーブルで作成してみます。","datePublished":"2024-08-28T05:52:56.398Z","dateModified":"2024-08-28T05:52:56.398Z"}]},{"@type":"Answer","text":"[香車]東上☆Aho☆海美「\n『7~10日で質問が削除される掲示板』には、ワシは回答しないであろう。\n」","dateModified":"2024-08-27T10:37:11.296Z","datePublished":"2024-08-27T10:37:11.296Z","upvoteCount":0,"url":"https://teratail.com/questions/nzq61t2fhxomzs#reply-82zsbrw3gmkn57","comment":[{"@type":"Comment","text":"umimiさん回答ありがとうございます。\n期間は回答率などを見て調整する予定です。古い質問が蓄積されていくよりも循環させたほうが良いと思い一定期間での削除を考えました。","datePublished":"2024-08-27T14:58:33.373Z","dateModified":"2024-08-27T14:58:33.373Z"},{"@type":"Comment","text":"似たような質問があった時、『過去レス #nnnn にあります』みたいな書込をしますが、質問が消される掲示板では、それが出来ません。\n\n『循環』とは、具体的には、どのような行為を指しますか ?","datePublished":"2024-08-29T11:16:50.613Z","dateModified":"2024-08-29T11:16:50.613Z"},{"@type":"Comment","text":"umimiさん回答ありがとうございます。\nすべてのユーザーに平等な機会が与えられるべきだと考えました。過去レスが残ってしまうと掲示板開設初期から質問をしているユーザーに回答が集まることが予測されます。\n\n雑談掲示板も別途作成予定であるため、そちらにも影響が出てしまうのではないかと考え、既存ユーザーと新規ユーザーで格差が出ない仕組みを考えております。","datePublished":"2024-08-31T13:29:12.453Z","dateModified":"2024-08-31T13:29:12.453Z"}]},{"@type":"Answer","text":"ひとつのテーブルでいいと思います\n質問にもあるようにコードをまとめられますから","dateModified":"2024-08-27T04:14:50.331Z","datePublished":"2024-08-27T04:14:50.331Z","upvoteCount":0,"url":"https://teratail.com/questions/nzq61t2fhxomzs#reply-3zpwm884awabp1","comment":[{"@type":"Comment","text":"関係的に1:1でないこととテーブルを分けることになんの関連性があるんだろうか?\n例えばTwitterとかは、投稿1つに対して複数の返信がありえて、再帰的にこれが起きるけど、\n返信テーブルが無限にあるという考えなのだろうか?(システムに制限があるのか、無限に返信出来るかどうかは知らんが)\n\nグッドボタンが5つも押されているので疑問を吐露","datePublished":"2024-08-27T11:42:34.596Z","dateModified":"2024-08-27T12:05:50.664Z"},{"@type":"Comment","text":"utm.さん回答ありがとうございます。\n投稿1つに対して複数の返信があるというシステムであると思います。\n質問に対して回答がありその回答にも複数の返信があるという形を想定しているので、負荷を考えるとテーブルを分ける方が良いかもしれません…","datePublished":"2024-08-27T15:02:37.622Z","dateModified":"2024-08-27T15:02:37.622Z"},{"@type":"Comment","text":"わかりました。\n投稿者に任せますが、\nテーブルを分けると、おそらく想定よりコードが煩雑になる気がします。\n\n正直な話、\n1. 多分想定しているよりは、検索等のクエリによる負荷(遅延)はかからないかと思います。\n2. 同じテーブルに投稿をまとめた場合、リプレイスやリファクタリングでテーブルを分けるのは簡単ですが、その逆は困難です。\n\n個人的に、ヌルなカラムが増えることより、コードで見た時のやりやすさを取りたい気持ちがあります。\n\nあ、そういえば最初の回答で書き忘れましたが、\nDB設計に正解はありませんから、正直なんでもいいとも思います。","datePublished":"2024-08-27T15:09:12.525Z","dateModified":"2024-08-27T15:09:12.525Z"},{"@type":"Comment","text":"UIに使う列はどうせインデックス張るし、絞込みを行うとDBを効率的に扱えないってミスリードな気がするんですが。\n(パフォーマンスについては測定しろ推測するなですが)","datePublished":"2024-08-27T23:51:29.391Z","dateModified":"2024-08-27T23:51:29.391Z"},{"@type":"Comment","text":"utm.さんアドバイスありがとうございます。\nテーブルを分けた場合も質問専用テーブルと結び付ける必要がありそうなのでカラムもコードも増えそうです。\nクエリによる負荷(遅延)が想定よりかからないのであれば両用テーブルにしてみます。","datePublished":"2024-08-28T05:44:31.714Z","dateModified":"2024-08-28T05:44:31.714Z"},{"@type":"Comment","text":"ひとつのテーブルにしても結びつけは必要ですけどね。。。","datePublished":"2024-08-28T07:31:35.150Z","dateModified":"2024-08-28T07:31:35.150Z"},{"@type":"Comment","text":"utm.さんアドバイスありがとうございます。\n結びつけるようにして考えてみます。","datePublished":"2024-08-31T13:30:28.746Z","dateModified":"2024-08-31T13:30:28.746Z"}]},{"@type":"Answer","text":"タグにも本文にも登場していないですが、そもそもテーブルとは\nDB(おそらくmysql辺り?)という前提で良いんでしょうか\n\n\n----\n\n仮にmysqlであればpaizaのオンライン実行環境みたいなところで\nテーブル作成・仮データ投入・想定される用途でのデータ取得(質問の一覧取得, 特定の質問と紐づく回答の取得)\nみたいなのを試してみると良いんじゃないでしょうか。\n\n\n一般的な開発のやり方としては、実装を進める前の設計フェーズにて机上で検討してみるというのは当然やるべきで大切なことですが\nなんとなく、質問者さんはDB設計のセオリーとか常識みたいなのを習得する前段階なのでは?と見受けられるので\nまずは色々手を動かして試してみて、異なる設計方針でのメリットデメリットを体感してみるのも良いと思います。\n\n今回の件では、「両用テーブル」方針と「専用テーブルに分割」方針で\nある一方は割と自然なデータ定義&素直なデータ取得クエリになり、\nもう一方はかなり不自然なデータ定義&面倒なデータ取得クエリになる。\n……と思うので、実際に分かり易く体感できるのでないかと。","dateModified":"2024-08-27T04:10:49.842Z","datePublished":"2024-08-27T04:10:49.842Z","upvoteCount":0,"url":"https://teratail.com/questions/nzq61t2fhxomzs#reply-7btu3dx7f4r88g","comment":[{"@type":"Comment","text":"pecmmさん回答ありがとうございます。\nデータベースの SQL になります。paiza で動かしてみるというのは想定しておりませんでした、以後使ってみようと思います。","datePublished":"2024-08-27T15:06:24.462Z","dateModified":"2024-08-27T15:06:24.462Z"}]},{"@type":"Answer","text":"Q&Aは必ず関連しているから、両用という考えになるのかと思いますが、関係的に1:1とは限りませんから別テーブルにすべきだと思います。\nまた、関係を示す情報も別テーブルとする方が、拡張性は高いと思います。","dateModified":"2024-08-27T01:15:02.454Z","datePublished":"2024-08-27T01:15:02.454Z","upvoteCount":5,"url":"https://teratail.com/questions/nzq61t2fhxomzs#reply-ozk665fipnnb1j","comment":[{"@type":"Comment","text":"回答ありがとうございます。\n別テーブルにするという方向でコードを考えてみます。","datePublished":"2024-08-27T03:27:13.231Z","dateModified":"2024-08-27T03:27:13.231Z"},{"@type":"Comment","text":"関係を示す別テーブル(中間テーブル)は、この場合は不要ではないてしょうか?\n基本的にN対Nで、例えば質問テーブルに回答IDがない場合があり、回答テーブルに質問IDがない場合がある。という設計であれば有効かと思いますが……\n※必要になるかもしれないからとりあえず作る、という設計には否定的な立場での意見です。","datePublished":"2024-08-27T05:46:30.088Z","dateModified":"2024-08-27T05:46:30.088Z"},{"@type":"Comment","text":"XiiTuziさん回答ありがとうございます。\n基本的に1つの質問に対して回答は複数くるのではないかと想定しているため両用テーブルにして質問か回答かを識別させた方が良いかもしれません。","datePublished":"2024-08-27T14:55:47.262Z","dateModified":"2024-08-27T14:55:47.262Z"},{"@type":"Comment","text":"質問テーブルと回答テーブルを分ける設計については全く異論ありません。\nsaziさんの回答にある最後の一行に対して、私の想定していない意図があればお聞きしたいと思った次第です。\n(私の勘違いの可能性もありますし)","datePublished":"2024-08-28T01:11:55.557Z","dateModified":"2024-08-28T01:12:12.528Z"},{"@type":"Comment","text":"@XiiTuziさん\n> ※必要になるかもしれないからとりあえず作る、という設計には否定的な立場での意見です。\n要件が明確であれば提案はしませんが、Qに対して過去のAが参考になる(所謂FAQ)のような場合を想定していました。\n私の回答スタンスは情報を提供し、取捨選択は質問者にしてもらうというものなので、こういった回答が多いです。","datePublished":"2024-09-02T01:03:23.624Z","dateModified":"2024-09-02T01:03:23.624Z"},{"@type":"Comment","text":"かなり今更ですが、通知が来ないので見逃していました。\nこちらから質問したにも関わらず、すみません。\n\nFAQというのは私の想定範囲外で、理解しました。\n\n回答の「スタンス」について異を唱えるつもりはありませんでしたが、私の発言を見直すとそのように取れますね。不快に感じたのであれば謝罪します。(日本語ネイティブではないので、言葉の表現がおかしいのはご容赦ください)","datePublished":"2024-11-08T02:38:17.426Z","dateModified":"2024-11-08T02:38:17.426Z"}]}],"breadcrumb":{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"https://teratail.com","name":"トップ"}},{"@type":"ListItem","position":2,"item":{"@id":"https://teratail.com/tags/phpMyAdmin","name":"phpMyAdminに関する質問"}},{"@type":"ListItem","position":3,"item":{"@id":"https://teratail.com/questions/nzq61t2fhxomzs","name":"Q&A掲示板のテーブルは質問と回答で分けるべきでしょうか?"}}]}}}
実現したいこと
ファイルアップロード(画像、動画、PDF)が可能なログイン無しのQ&A掲示板を作成したいです。
発生している問題・分からないこと
現在質問画面と質問一覧表示画面を作り終えて回答画面を作成中です。
質問画面と質問一覧表示画面のテーブル構造は下記になります。
回答画面には追加でカラムに質問と回答を紐付けするための回答IDとその回答に対する返信のIDの2つを追加する予定なのですが、質問&回答の両用テーブルで使用するか回答専用テーブルを用意するどちらのほうが良いと思われますでしょうか?
両用にする場合はQ&A掲示板に投稿されたものが質問か回答かを識別するカラムを追加して、質問を「1」、回答を「2」のように意味付けて識別する形で考えております。
どちらもメリット、デメリットがあると思うのでそちらについてもお聞きしたいです。
※質問画面と質問一覧表示画面のテーブル構造
ID (質問番号)・・・・無限スクロールで表示しているため
TS (投稿日時)・・・・公開期間終了後に質問を cron で削除するため
question (質問内容)・・・・質問投稿文
title (質問タイトル)・・・・質問タイトル
namae (質問者名)・・・・任意なのでない場合は匿名で表示
stamp (リアクションスタンプ)・・・・用意された画像で気持ちを伝える
unique_id (質問UUID)・・・・アップロードされたファイル名の改名に使用
ip (IPアドレス)・・・・セキュリティ保護のために保存
attach1 (アップロードされたファイル)・・・・1、2、3で分けているのは1つ目の画像を一覧表示&1つ目の動画を横スクロールで表示するため
attach2 (IPアドレス)・・・・略
attach3 (IPアドレス)・・・・略
usericon (アイコン画像)・・・・アップロードされた画像を表示、アップロードがない場合は代替え画像を表示する
該当のソースコード
PHP
1<?php
2/*
3Template Name: input
4固定ページ: 入力画面
5*/
6get_header();
7?>
8<?php
9if (!$noindexaccess) {
10exit('不正アクセス');
11}
12if (empty($_SESSION['token'])) {// 悪意のある攻撃者があらかじめ作成したコードが実行されてしまうのを防ぐ
13$_SESSION['token'] = bin2hex(random_bytes(16));
14}
15$attach = [];
16if (!empty($_SESSION['attach'])) {
17foreach ($_SESSION['attach']['data'] as $i => $data) {
18if (!empty($data)) {
19$base64 = base64_encode($data);
20}
21$type = $_SESSION['attach']['type'][$i];
22switch ($type) {
23case 'image/jpeg':
24case 'image/png':
25$attach[] = '<img style="height: 100px;" src="data:'.$type.';base64,'.$base64.'">';
26break;
27case 'video/mp4':
28$attach[] = '<video style="height: 100px;" controls src="data:'.$type.';base64,'.$base64.'">';
29break;
30case 'application/pdf':
31$attach[] = '<iframe style="height: 100px;" src="data:'.$type.';base64,'.$base64.'"></iframe>';
32break;
33default:
34$attach[] = '';
35break;
36}
37}
38}
39$stamp_checked = [];
40$stamp_checked[$stamp] = 'checked';
41$upload_dir = wp_upload_dir();
42$camera_url = $upload_dir['baseurl'].'/camera.png';
43?>
44<h2>入力画面</h2>
45<?php
46foreach ($errors as $error) {
47echo "<p>{$error}</p>";
48}
49?>
50<div class="board_form_partial" id="js_board_form_partial">
51<form method="post" enctype="multipart/form-data">
52<input type="hidden" name="token" value="<?php echo $_SESSION['token']; ?>">
53<div class="image-partial">
54<h2>動画・画像をアップロード(Upload video・image)<span class="required">※ファイルサイズ15MB以内、JPG/GIF/PNG/MP4</span></h2>
55<div class="image-selector-button">
56<label>
57<div class="image-camera-icon">
58<img src="<?php echo $camera_url; ?>" class="changeImg" style="height: 100px;">
59</div>
60<input type="file" class="attach" name="attach[]" accept=".png, .jpg, .jpeg, .pdf, .mp4" style="display: none;">
61</label>
62<input type="hidden" class="attachdel" name="attachdel[]">
63<div class="viewer"><?php echo $attach[0]; ?></div>
64<button type="button" class="attachclear">clear</button>
65</div>
66<div class="image-selector-button">
67<label>
68<div class="image-camera-icon">
69<img src="<?php echo $camera_url; ?>" class="changeImg" style="height: 100px;">
70</div>
71<input type="file" class="attach" name="attach[]" accept=".png, .jpg, .jpeg, .pdf, .mp4" style="display: none;">
72</label>
73<input type="hidden" class="attachdel" name="attachdel[]">
74<div class="viewer"><?php echo $attach[1]; ?></div>
75<button type="button" class="attachclear">clear</button>
76</div>
77
78<div class="image-selector-button">
79<label>
80<div class="image-camera-icon">
81<img src="<?php echo $camera_url; ?>" class="changeImg" style="height: 100px;">
82</div>
83<input type="file" class="attach" name="attach[]" accept=".png, .jpg, .jpeg, .pdf, .mp4" style="display: none;">
84</label>
85<input type="hidden" class="attachdel" name="attachdel[]">
86<div class="viewer"><?php echo $attach[2]; ?></div>
87<button type="button" class="attachclear">clear</button>
88</div>
89</div>
90<style>
91.hideItems {
92display: none;
93}
94</style>
95<div class="title-partial parts"> <!-- title-partial + parts -->
96<h2>名前(name)<span class="required">※必須</span></h2>
97<div class=parts>
98<input class=input type="text" name="namae" id="name" data-length="32" placeholder="未入力の場合は、匿名で表示されます" value="<?php echo $namae; ?>">
99<div></div>
100</div>
101</div>
102
103<div class="body-partial parts"><!-- body-partial + parts -->
104<h2>コメント(comment)<span class="required">※必須</span></h2>
105<div class=parts>
106<textarea class=input name="message" id="message" data-length="40" placeholder="荒らし行為や誹謗中傷や著作権の侵害はご遠慮ください"><?php echo $message; ?></textarea>
107<div></div>
108</div>
109</div>
110
111<div class="stamp-partial">
112<h2>スタンプを選ぶ(必須)</h2>
113<input type="radio" name="stamp" value="1" id="stamp_1" <?php echo $stamp_checked['1']; ?>><label for="stamp_1"></label>
114<input type="radio" name="stamp" value="2" id="stamp_2" <?php echo $stamp_checked['2']; ?>><label for="stamp_2"></label>
115<input type="radio" name="stamp" value="3" id="stamp_3" <?php echo $stamp_checked['3']; ?>><label for="stamp_3"></label>
116<input type="radio" name="stamp" value="4" id="stamp_4" <?php echo $stamp_checked['4']; ?>><label for="stamp_4"></label>
117<input type="radio" name="stamp" value="5" id="stamp_5" <?php echo $stamp_checked['5']; ?>><label for="stamp_5"></label>
118<input type="radio" name="stamp" value="6" id="stamp_6" <?php echo $stamp_checked['6']; ?>><label for="stamp_6"></label>
119<input type="radio" name="stamp" value="7" id="stamp_7" <?php echo $stamp_checked['7']; ?>><label for="stamp_7"></label>
120<input type="radio" name="stamp" value="8" id="stamp_8" <?php echo $stamp_checked['8']; ?>><label for="stamp_8"></label>
121</div>
122<div class="post-button"><!-- ボタンを押せなくする -->
123<button type="submit" id="submit_button" name="mode" value="confirm">表示画面へ進む</button>
124</div>
125<!-- type、name、id、valueの順番 -->
126</form>
127
128<script>
129function validation_submit(f) {
130const submit = document.getElementById("submit_button");
131/* 判定は逆なので、逆に渡す */
132submit.disabled = f ? false : true;
133};
134function validation_text(parts) {
135/* このpartsグループの、inputを抽出 */
136let text = parts.getElementsByClassName('input')[0];
137/* 最小チェック */
138if (text.value.length == 0) {
139return false;
140}
141/* 最大チェック */
142if (text.value.length >= text.dataset.length) {
143return false;
144}
145return true;
146};
147/* バリデーション条件判断部分 */
148function validation() {
149let parts = document.getElementsByClassName('parts');
150let submit = true;
151for (let i = 0; i < parts.length; i++) {
152if (validation_text(parts[i]) != true) {
153submit = false;
154}
155}
156validation_submit(submit);
157};
158/* 例えばのチェック */
159function init() {
160/* カメラ画像をファイルアップロード時に非表示にする */
161/* 省略 */
162/* カメラ画像をファイルアップロード時に非表示にする */
163const attach = document.querySelectorAll('.attach');
164const del = document.querySelectorAll('.attachdel');
165const clear = document.querySelectorAll('.attachclear');
166const viewer = document.querySelectorAll('.viewer');
167const changeImg = document.querySelectorAll('.changeImg'); // 入力されたら消す画像
168for (let i = 0; i < attach.length; i++) {
169attach[i].addEventListener('change', () => {
170if (attach[i].files[0].size > 15 * 1024 * 1024) {
171alert('ファイルサイズが 15MBバイトを超えています');
172return;
173}
174del[i].value = "";
175viewer[i].innerHTML = "";
176if (attach[i].files.length !== 0) {
177const reader = new FileReader();
178reader.onload = () => {
179var child = null;
180if (reader.result.indexOf("data:image/jpeg;base64,") === 0 ||
181reader.result.indexOf("data:image/png;base64,") === 0) {
182child = document.createElement("img");
183} else if (reader.result.indexOf("data:video/mp4;base64,") === 0) {
184child = document.createElement("video");
185child.setAttribute("controls", null);
186} else if (reader.result.indexOf("data:application/pdf;base64,") === 0) {
187child = document.createElement("iframe");
188} else {
189alert("対象外のファイルです");
190alert(reader.result);
191attach[i].value = "";
192}
193if (child !== null) {
194child.style.height = "350px";
195child.style.width = "528px";
196child.src = reader.result;
197viewer[i].appendChild(child);
198changeImg[i].classList.add('hideItems'); // もともとの画像を消す
199}
200};
201reader.readAsDataURL(attach[i].files[0]);
202}
203});
204clear[i].addEventListener('click', () => {
205attach[i].value = "";
206del[i].value = "1";
207viewer[i].innerHTML = "";
208changeImg[i].classList.remove('hideItems');
209});
210}
211/* 文字数表示 */
212document.addEventListener('input', e => {
213if (!['name', 'message'].includes(e.target.id)) return;
214const
215t = e.target,
216m = t.nextElementSibling,
217n = t.value.length - (t.dataset.length | 0),
218c = document.createElement('span');
219c.append(Math.abs(n));
220m.style.color = n > 0 ? 'red' : 'black';
221m.replaceChildren(n > 0 ? '' : '残り', c,
222`文字${n > 0 ? '超過してい' : '入力でき'}ます。`);
223/* 毎回判定によるボタン制御 */
224validation();
225});
226/* 初回判定のボタン制御 */
227validation();
228};
229document.addEventListener("DOMContentLoaded", init);
230</script>
試したこと・調べたこと
上記の詳細・結果
検索してみたのですが Q&A掲示板についての記事がなく掲示板のコメント返信機能に必要な部分のみ参考にしております。
追記
データ通信量によるかと思いますが回答専用テーブルを用意して質問専用テーブルと分けたほうが良いのではないかと考えております。
7~10日で質問を cron で削除する前提であるものの同時刻に複数の掲示板が動作した場合負荷がかかり遅延に繋がるのではないかと思います。
質問&回答の両用テーブルの場合共通のコードなどはまとめることが可能だと思うのですがそれ以外のメリットがあまり無さそうです。
補足
※参考サイト
https://qiita.com/ryouya3948/items/6928c89607cf4eaa72a0
Q&Aは必ず関連しているから、両用という考えになるのかと思いますが、関係的に1:1とは限りませんから別テーブルにすべきだと思います。
また、関係を示す情報も別テーブルとする方が、拡張性は高いと思います。
質問と回答に保存する内容が極端に違うのであれば分けてもいいでしょうけど、たとえば回答に対してレスができる枝分岐していくような仕組みを模索するなら一緒の方が管理が楽だと思います
「X-Twitterのように質問・回答関係なくタイムラインとして時系列で並べたい」
「質問にも回答、どちらにも返信ができる仕様としたい」
「質問も回答も、基本的なデータ構造が同じ」
「質問・回答、どちらも対象に同じ条件で検索をさせたい」
という場合は、同じテーブルにして質問か回答かのフラグを付ける形が良いと思います。
質問と回答で持つカラムの内容が大きく異なる - 例えば、質問の方にはタグ、カテゴリ、閲覧PV数、いいね数、など回答には必要のないカラムが多数あるといった場合は、分けても良いと思います。
これは感想ですが、7-10日で質問を削除してしまうと、Q&Aがウェブ上に残らないのは少しもったいないような感じました。現にこの回答はご質問から8日後にしていますので、あと2日で消えてしまうと思うと…
ベストアンサー
設計上、10日ほどで古いものを削除する運用であることから、データが増えた場合の性能低下を考慮する必要性が薄いです。そのため、作りやすい方で良いと思います。
動かないものよりは動いているものの方が大きな価値がありますし、とっかかりは実装が容易な方が良いです。
(セキュリティに問題があり、利用者に影響がでるなどの場合はその限りではないので、ここは慎重に検討する必要はあります。)
teratailのように、運営時間に応じてデータが増えていくサイトを目指すのであれば私であればテーブルを分けます。理由は主に下記です。
- テーブルを分けなかった場合、一番アクセスの多いであろう質問一覧画面を表示するためにwhere句で
item_type = 1 の絞り込みが必要になり、DBを効率的に扱えない
- 感覚上テーブルを正規化して作りにくくなったことはあまりない
- 分けることによって質問だけ、回答だけのデータ属性を拡張しやすくなる
[香車]東上☆Aho☆海美「
『7~10日で質問が削除される掲示板』には、ワシは回答しないであろう。
」
ひとつのテーブルでいいと思います
質問にもあるようにコードをまとめられますから
タグにも本文にも登場していないですが、そもそもテーブルとは
DB(おそらくmysql辺り?)という前提で良いんでしょうか
仮にmysqlであればpaizaのオンライン実行環境みたいなところで
テーブル作成・仮データ投入・想定される用途でのデータ取得(質問の一覧取得, 特定の質問と紐づく回答の取得)
みたいなのを試してみると良いんじゃないでしょうか。
一般的な開発のやり方としては、実装を進める前の設計フェーズにて机上で検討してみるというのは当然やるべきで大切なことですが
なんとなく、質問者さんはDB設計のセオリーとか常識みたいなのを習得する前段階なのでは?と見受けられるので
まずは色々手を動かして試してみて、異なる設計方針でのメリットデメリットを体感してみるのも良いと思います。
今回の件では、「両用テーブル」方針と「専用テーブルに分割」方針で
ある一方は割と自然なデータ定義&素直なデータ取得クエリになり、
もう一方はかなり不自然なデータ定義&面倒なデータ取得クエリになる。
……と思うので、実際に分かり易く体感できるのでないかと。
15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.29%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる
質問する