# 雛形を作成する
掲示板画面全体のデザインと、投稿用フォーム、仮データのモックアップ表示ができている状態を目指します。

# 本セクションの流れ
- 掲示板全体のデザインを作る。
- 投稿用フォームを作成する。
- 投稿内容表示のモックアップを作る。
# 1. 掲示板全体のデザインを作る
まずは、画面全体のデザインと簡単な構成要素を作っていきます。

現在のディレクトリ構造は、このようになっています。
.
├── docker-compose.yml
├── php
│ └── Dockerfile
└── public
└── index.php
2
3
4
5
6
7
~~/public/index.php の内容をすべて削除し、以下のコードを記述します。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="noindex" />
<title>ひとこと掲示板</title>
<link rel="stylesheet" href="./assets/main.css" />
</head>
<body>
<div class="page-cover">
<p class="page-title">ひとこと掲示板</p>
<div class="form-cover">
<!-- 投稿内容入力フォーム -->
</div>
<hr class="page-divider" />
<div class="message-list-cover">
<!-- この中に投稿された内容のリストを表示 -->
</div>
<hr class="page-divider" />
</div>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
meta タグや CSS の読み込みについてはポートフォリオ制作:雛形を作成するをご参照ください。
bodyタグの中には、hrタグを挟んでdiv要素が 2 つあります。1 つめの<div class="form-cover">要素には入力フォーム、2 つめの <div class="message-list-cover"> 要素には、投稿された内容のリスト表示を行なっていきます。
スタイルを加えるために、 ~~/public/assets ディレクトリを作成、その中に ~~/public/assets/main.css となるように css ファイルを作成し以下のコードを記述します。
@charset "utf-8";
@import url(https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;500;700;900&display=swap);
@import url(https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@300;400;500;700;900&display=swap);
/* ===== BASIC BEGIN ===== */
html,
body {
background-color: #eef3f4;
font-family: Roboto, 'Noto Sans JP', sans-serif;
color: #585858;
margin: 0;
padding: 0;
}
a {
color: #3280e7;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
button,
input,
textarea {
font-family: inherit;
box-sizing: border-box;
}
button {
background-color: initial;
border: initial;
cursor: pointer;
outline: initial;
padding: initial;
appearance: initial;
-webkit-appearance: none;
border-radius: initial;
font-size: inherit;
}
p {
margin: 0;
padding: 0;
}
/* ===== BASIC END ===== */
/* ===== PAGE PARTS BEGIN ===== */
.page-cover {
max-width: 800px;
margin: 0 auto;
}
.page-title {
text-align: center;
padding: 10px 0;
margin: 0 auto;
font-weight: bold;
font-size: 25px;
}
.page-divider {
width: 80%;
max-width: 500px;
margin: 0 auto 20px auto;
height: 2px;
background-color: rgba(0, 0, 0, 0.2);
border: none;
}
/* ===== PAGE PARTS END ===== */
/* ===== NOTIFICATION AREA BEGIN ===== */
.action-success-area {
width: max-content;
margin: 10px auto;
padding: 5px 15px;
color: #388e3c;
border: solid 2px #388e3c;
border-radius: 4px;
}
.action-failed-area {
width: max-content;
margin: 10px auto;
padding: 5px 15px;
color: #bb1850;
border: solid 2px #bb1850;
border-radius: 4px;
}
/* ===== NOTIFICATION AREA END ===== */
/* ===== NEW MESSAGE FORM AREA BEGIN ===== */
.form-cover {
margin: 0 auto;
width: 90%;
}
.form-input-title {
font-size: 0.875rem;
padding: 10px;
}
.form-input-error {
color: #bb1850;
font-size: 0.875rem;
padding: 0 10px;
}
.input-author-name {
font-size: 16px;
width: 100%;
height: 2em;
border: 1px solid rgba(236, 234, 234, 0.5);
border-radius: 5px;
box-sizing: border-box;
}
.input-message {
font-size: 16px;
width: 100%;
border: 1px solid rgba(236, 234, 234, 0.5);
border-radius: 5px;
height: 100px;
width: 100%;
box-sizing: border-box;
resize: vertical;
min-height: 100px;
max-height: 500px;
}
.input-submit-button {
background-color: #333333;
border: 2px solid #333333;
color: #ffffff;
margin: 15px auto;
padding: 5px;
display: block;
border-radius: 5px;
width: 100%;
max-width: 200px;
}
.input-submit-button:hover {
background-color: #ffffff;
border: 2px solid #59b1eb;
color: #59b1eb;
}
/* ===== NEW MESSAGE FORM AREA END ===== */
/* ===== MESSAGE LIST AREA BEGIN ===== */
.message-list-cover {
margin: 0 auto;
width: 90%;
}
.message-item {
background-color: #ffffff;
margin-bottom: 16px;
padding: 8px 16px;
overflow-wrap: break-word;
border-radius: 4px;
}
.message-title {
display: flex;
flex-direction: column;
gap: 0 10px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
margin-bottom: 4px;
}
.message-title .spacer {
flex-grow: 1;
}
@media screen and (min-width: 500px) {
.message-title {
flex-direction: row;
align-items: end;
}
}
.message-line {
min-height: 1em;
}
.message-delete-button {
color: #3280e7;
min-width: max-content;
}
.message-delete-button:hover {
text-decoration: underline;
}
/* ===== MESSAGE LIST AREA END ===== */
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
掲示板開発で扱う全てのスタイルが記述されています。
ファイルの作成が完了したら、http://localhost:8080/ (opens new window) にアクセスして確認します。
以下のデザインがは反映されていれば無事 css が読み込まれています。

# 2. 投稿用フォームを作成する
ベースとなるデザインができたので、「投稿用入力フォーム」と「投稿内容表示」部分を作っていきます。

まずは、ひとこと投稿用フォームを作成していきます。

~~/public/index.php に以下のコードを記述します。
<body>
<div class="page-cover">
<p class="page-title">ひとこと掲示板</p>
<hr class="page-divider" />
<div class="form-cover">
<form action="/" method="post">
<div class="form-input-title">投稿者ニックネーム</div>
<input type="text" name="author_name" maxlength="40" value="" class="input-author-name" />
<div class="form-input-error">
</div>
<div class="form-input-title">投稿内容<small>(必須)</small></div>
<textarea name="message" class="input-message"></textarea>
<div class="form-input-error">
</div>
<input type="hidden" name="action_type" value="insert" />
<button type="submit" class="input-submit-button">投稿する</button>
</form>
</div>
</div>
<hr class="page-divider" />
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ここでは、投稿用フォームを設置するために form タグを記述しています。
基礎編:リクエストとレスポンス にもあるように、HTTP は「HTTP リクエスト」と「HTTP レスポンス」を扱います。
Web ブラウザから Web サーバに対しての命令(リクエスト)を行う際には、HTTP リクエスト送信されます。そのときにサーバーに要求する処理の種類が HTTP メソッド です。HTTP リクエストメソッドを PHP ではこれらのリクエストに含まれるデータを処理することができます。
ここでは、HTML の form タグを利用し、HTTP リクエストメソッドを行います。
form の中にある method 属性はフォームのデータを送信する通信方式を指定します。
通信方式は「GET」や「POST」を使用することができます。
今回は「POST」による送信を行うことで、リソースの送信を行います。
参考:PHP 公式ドキュメント「フォームの処理」 (opens new window)
POST によるデータ通信
パラメータは、URL ではなく、メッセージボディに設定されるため、パスワードやユーザ情報などの機密性の高いデータを送信するのに用いられます。またサイズの大きいデータを扱う場合も用いられます。
action には送信先の URL を指定することができます。例えば、 <form action="https://example.com"> とすると、データを絶対 URL の https://example.com (opens new window) に送信します。
今回は index.php 内でデータを扱い、DB への保存処理を展開していくため、action="/" としてフォームが置かれた自身のページ ~~/public/index.php に送信しています。
値を空、もしくは属性自体がない場合でも、フォームが置かれた自身のページに送信されます。
form タグの中には以下の 4 つのものを作成しています。
- 投稿者ニックネーム入力欄
- 投稿内容入力欄
- 発火するアクションを指定するための要素
- 投稿するボタン
それぞれ説明していきます。
# 投稿者ニックネーム入力欄について
<div class="form-input-title">投稿者ニックネーム</div>
<input type="text" name="author_name" maxlength="40" value="" class="input-author-name" />
2
inputタグはテキスト入力欄や実行ボタン等、 フォーム(<form>)を構成する部品を作成するタグです。
各属性について
【type】
type 属性の値により、1 行テキストボックス・チェックボックス・ラジオボタン・実行ボタン・リセットボタン等の部品となります。
この属性を指定しない場合の既定の型は"text"になります。
今回は、投稿者のニックネームが入力されることを想定しているのでtype="text"を指定します。
他にも type 属性には以下の値が設定できます。
type="text"1 行テキストボックスをつくります。通常のテキストを入力するフィールドです。2 行以上にわたる長い文章を入力させたい場合には、
<textarea>を使用します。type="password"パスワード入力ボックスをつくります。入力したテキストがアスタリスク(*)などに置き換えて表示されます。ただし、データが暗号化されるわけではないので、送信されたデータを受け取ることができれば内容を見ることができます。利用の際には注意してください。
type="checkbox"チェックボックスをつくります。チェックボックスは複数選択が可能です。checked 属性を指定すると、その項目についてはあらかじめチェックの付いた状態となります。
type="button"汎用ボタンをつくります。
type="submit"送信ボタンをつくります。
type="hidden"
隠しデータをサーバーに送信する際に使用します。このタイプのデータは、ブラウザ画面上には表示されません。value 属性で指定した値がサーバーへ送信されます
引用: HTML タグリファレンス_INPUT (opens new window)
【name】
name 属性 は「要素の名前」を指定することができます。
PHP で扱う $_GET や $_POST でデータを受け取る際に、name 属性がリクエストパラメータ名になり、value 属性の値やテキストボックスに入力された値がそのパラメータの値になります。
【value】
input 要素の値を指定する属性。テキスト入力欄などにおいては初期入力値、チェックボックスやラジオボタンにおいては選択した時にだけ送信する値、送信ボタンなどにおいてはボタン名を表します。
ここでは、投稿された投稿者名を $_POST['author_name'] のようにして受け取っています。
【maxlength】
valueの最大長 (文字数)を指定します。
# 投稿内容入力欄について
<div class="form-input-title">投稿内容<small>(必須)</small></div>
<textarea name="message" class="input-message"></textarea>
2
<textarea>は複数行の入力フィールドを作成するタグです。<textarea>~</textarea>内に記述されたテキストは、入力フィールドの初期値として表示されます。
複数行のテキストを表すことが可能で、レビューのコメントやお問い合わせフォーム等のように、ユーザーが大量の自由記述テキストを入力できるようにするときに便利です。
各属性について
【name】
<form> における name と同じ役割です。
【rows,cols】
<textarea>が占める実際の大きさを指定することができます。サイズを指定することによる欠点として、文字数による指定になるため、文字のフォントによってはデザインが汚くなってしまいます。それを避けるため、今回は CSS で指定しています。
引用:HTML タグリファレンス_TESTAREA (opens new window)
# 発火するアクションを指定するための要素について
<input type="hidden" name="action_type" value="insert" />
この<input>要素はブラウザに表示する必要は無いため、type="hidden"を指定し、value 属性で指定した値をがサーバーへ送信されます。これにより、ボタンがクリックされた時に、$_POSTという変数に値を格納することができます。name="action_type"、value="insert"としているので、$_POST['action_type']という変数に"insert"という文字列を格納しています。
# 投稿するボタンについて
<button type="submit" class="input-submit-button">投稿する</button>
<button>タグは、ボタンを作成する際に使用され、<input>と同様に name や value 等の属性が指定できます。type 属性には 3 以下の3種類のいずれかを指定することができます。
【submit】
フォームのデータをサーバーへ送信します。これはこの属性が <form> に関連付けられたボタンに指定されていない場合、またはこの属性が空であったり不正な値であったりした場合の既定値です(初期値)。
form 内で type='submit' が指定してある button 要素を押すと submit イベントが発火するという仕様があるため、この<form> ~</form>で囲まれたブロックの中にあるテキストボックスやラジオボタンなどに入力された値を、submit ボタンはサーバー側や別ページに送信することができます。今回はこのボタンがクリックされた時に、ユーザが入力した投稿者ニックネーム、投稿内容、$_POST 変数に格納する文字列がサーバへ送られることになります。
【reset】
指定されたボタンはすべてのコントロールを初期値に初期化します。 <input type="reset"> と同様です。
button タグ単体の場合は既定の動作がなく、押されても何も行いません。上記要素のイベントを待ち受けし、イベントが発生すると起動されるクライアント側スクリプトを設定することができます。
引用:mdn web docs:ボタン要素 (opens new window)
input タグで作成したボタンとの違い
<input type='submit' name='action' value='送信'>のようにしても送信ボタンを作成することができますが、input 要素のボタンとは異なり、button 要素では子要素を持つことができます。
また、input タグと違い、button タグには疑似要素が使うことができ、CSS でのデザインに自由度が高くなります。したがって、送信ボタンには button タグを利用するのが良いでしょう。
投稿用入力フォーム表示結果

# 3. 投稿内容表示のモックアップを作る
実際に投稿が完了したデータをデータベースから取得し、画面に表示した際のイメージを再現するために、仮のデータを表示するモックアップを作成しイメージを膨らませていきます。
~~/public/index.php 以下のコードを記述します。
<div class="message-list-cover">
<small>
1 件の投稿
</small>
<div class="message-item">
<div class="message-title">
<div>イチロー</div>
<small>2022-01-01 00:00:00</small>
<div class="spacer"></div>
<form action="/" method="post" style="text-align:right">
<input type="hidden" name="id" value="" />
<input type="hidden" name="action_type" value="delete" />
<button type="submit" class="message-delete-button">削除</button>
</form>
</div>
<p class="message-line">明けましておめでとうございます</p>
</div>
</div>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
以下のように仮データが表示されます。

# 最終的なコードとファイル構成
~~/public/index.php
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="noindex" />
<title>ひとこと掲示板</title>
<link rel="stylesheet" href="./assets/main.css" />
</head>
<body>
<div class="page-cover">
<p class="page-title">ひとこと掲示板</p>
<hr class="page-divider" />
<div class="form-cover">
<form action="/" method="post">
<div class="form-input-title">投稿者ニックネーム</div>
<input type="text" name="author_name" maxlength="40" value="" class="input-author-name" />
<div class="form-input-error">
</div>
<div class="form-input-title">投稿内容<small>(必須)</small></div>
<textarea name="message" class="input-message"></textarea>
<div class="form-input-error">
</div>
<input type="hidden" name="action_type" value="insert" />
<button type="submit" class="input-submit-button">投稿する</button>
</form>
</div>
<hr class="page-divider" />
<div class="message-list-cover">
<small>
1 件の投稿
</small>
<div class="message-item">
<div class="message-title">
<div>イチロー</div>
<small>2022-01-01 00:00:00</small>
<div class="spacer"></div>
<form action="/" method="post" style="text-align:right">
<input type="hidden" name="id" value="" />
<input type="hidden" name="action_type" value="delete" />
<button type="submit" class="message-delete-button">削除</button>
</form>
</div>
<p class="message-line">明けましておめでとうございます</p>
</div>
</div>
</div>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
~~/public/assets/main.css
@charset "utf-8";
@import url(https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;500;700;900&display=swap);
@import url(https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@300;400;500;700;900&display=swap);
/* ===== BASIC BEGIN ===== */
html,
body {
background-color: #eef3f4;
font-family: Roboto, 'Noto Sans JP', sans-serif;
color: #585858;
margin: 0;
padding: 0;
}
a {
color: #3280e7;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
button,
input,
textarea {
font-family: inherit;
box-sizing: border-box;
}
button {
background-color: initial;
border: initial;
cursor: pointer;
outline: initial;
padding: initial;
appearance: initial;
-webkit-appearance: none;
border-radius: initial;
font-size: inherit;
}
p {
margin: 0;
padding: 0;
}
/* ===== BASIC END ===== */
/* ===== PAGE PARTS BEGIN ===== */
.page-cover {
max-width: 800px;
margin: 0 auto;
}
.page-title {
text-align: center;
padding: 10px 0;
margin: 0 auto;
font-weight: bold;
font-size: 25px;
}
.page-divider {
width: 80%;
max-width: 500px;
margin: 0 auto 20px auto;
height: 2px;
background-color: rgba(0, 0, 0, 0.2);
border: none;
}
/* ===== PAGE PARTS END ===== */
/* ===== NOTIFICATION AREA BEGIN ===== */
.action-success-area {
width: max-content;
margin: 10px auto;
padding: 5px 15px;
color: #388e3c;
border: solid 2px #388e3c;
border-radius: 4px;
}
.action-failed-area {
width: max-content;
margin: 10px auto;
padding: 5px 15px;
color: #bb1850;
border: solid 2px #bb1850;
border-radius: 4px;
}
/* ===== NOTIFICATION AREA END ===== */
/* ===== NEW MESSAGE FORM AREA BEGIN ===== */
.form-cover {
margin: 0 auto;
width: 90%;
}
.form-input-title {
font-size: 0.875rem;
padding: 10px;
}
.form-input-error {
color: #bb1850;
font-size: 0.875rem;
padding: 0 10px;
}
.input-author-name {
font-size: 16px;
width: 100%;
height: 2em;
border: 1px solid rgba(236, 234, 234, 0.5);
border-radius: 5px;
box-sizing: border-box;
}
.input-message {
font-size: 16px;
width: 100%;
border: 1px solid rgba(236, 234, 234, 0.5);
border-radius: 5px;
height: 100px;
width: 100%;
box-sizing: border-box;
resize: vertical;
min-height: 100px;
max-height: 500px;
}
.input-submit-button {
background-color: #333333;
border: 2px solid #333333;
color: #ffffff;
margin: 15px auto;
padding: 5px;
display: block;
border-radius: 5px;
width: 100%;
max-width: 200px;
}
.input-submit-button:hover {
background-color: #ffffff;
border: 2px solid #59b1eb;
color: #59b1eb;
}
/* ===== NEW MESSAGE FORM AREA END ===== */
/* ===== MESSAGE LIST AREA BEGIN ===== */
.message-list-cover {
margin: 0 auto;
width: 90%;
}
.message-item {
background-color: #ffffff;
margin-bottom: 16px;
padding: 8px 16px;
overflow-wrap: break-word;
border-radius: 4px;
}
.message-title {
display: flex;
flex-direction: column;
gap: 0 10px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
margin-bottom: 4px;
}
.message-title .spacer {
flex-grow: 1;
}
@media screen and (min-width: 500px) {
.message-title {
flex-direction: row;
align-items: end;
}
}
.message-line {
min-height: 1em;
}
.message-delete-button {
color: #3280e7;
min-width: max-content;
}
.message-delete-button:hover {
text-decoration: underline;
}
/* ===== MESSAGE LIST AREA END ===== */
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
ファイル構成
.
├── docker-compose.yml
├── php
│ └── Dockerfile
└─ public
├── assets
│ └── main.css
└── index.php
2
3
4
5
6
7
8