プログラミングでWEBサービスを作りました。
プログラミングでWEBサービスを作成。 愛犬の写真を投稿して、他の飼い主さんと会話のやりとりをします。うちのラブラドールがめっちゃかわいいので、作ってみました笑 ダミーのメールアドレスでも良いので、ぜひ遊んでみてください!(PHP・MySQL・Ajaxなど使用)https://t.co/Tv35jtKAa4 pic.twitter.com/lg7d5ncMOQ
— いどはる idoharu (@idoharu_com) March 8, 2020
プログラミングでWEBサービスを作成。 愛犬の写真を投稿して、他の飼い主さんと会話のやりとりをします。うちのラブラドールがめっちゃかわいいので、作ってみました笑 ダミーのメールアドレスでも良いので、ぜひ遊んでみてください!(PHP・MySQL・Ajaxなど使用)https://uchinoinnu.don-do.comユーザー登録かんたんなので、良ければいじってみてください。
サービス「ウチのイッヌ」のURLと、コード(Github)
愛犬の元気な姿を残しておきたい、また、他の愛犬家さんとも交流できるようなサービスを作ってみたいと考えました。「イヌ派・ネコ派」と言うように、動物の中でもイヌやネコはコアなファンが多いペット。
そして少子高齢化や、コロナによる新しい生活様式の中、ペット需要が伸びています。
新型コロナウイルスの感染拡大に伴う「巣ごもり生活」の影響で、ペット需要が拡大している。そんな中、イヌを飼っている私としては、イヌに特化した情報共有サービスを作りたいと思い作成しました。ペットショップでは売り上げが前年を上回るケースが相次いでいるほか、あるペット用品の通販サイトでは飼い始めに購入することが多い犬用ケージの5月の売上高が前年同月の3倍に急増した。
外でのレジャーが楽しめずペットに喜びを求める人が増えているようだ。
ペット需要拡大、在宅のお供に 安易な購入には懸念も 2020/6/13 11:30 日本経済新聞 電子版
全体像は、こんな感じ(Cacooにて画面全体の構成を作成)。
トップページ・サインイン・サインアウト・マイページ。イッヌ登録ページ・プロフィール編集ページやパスワードリマインダーなど、WEBサービスに必須な機能を備えています。
当記事では、以下7点について書いています。
- サービス概要:ユーザー & 愛犬を登録。掲示板や、お気に入り登録など
- サービスを作った理由:愛犬との思い出を残し、共有できたら楽しそう
- 使用技術
- サービスの作成にどのように取り組んだか:大まかなワイヤーフレームをもとに、PHP・MySQLでベースを作成
- 工夫したところ・難しかったところ:表示画面の切り替え・セキュリティ対策・Ajaxの遷移画面のやりとり
- やってみて感じたこと、得られたこと:動作テストをしながら、不具合に対応していくこと
- 今後の課題として取り組んでいきたいこと:toBや業務システムを意識したサービスの作成
サービス概要:ユーザー & 愛犬を登録。掲示板、お気に入り登録など
サービス概要は、以下の通り。- ユーザー登録(サインイン・サインアウト・退会機能)プロフィール登録・編集
- パスワード変更・パスワードリマインダー機能
- 愛犬(イッヌ)登録(編集・削除、一覧・詳細表示、検索機能)
- お気に入り登録(Ajax)
ユーザー登録(サインイン・サインアウト・退会機能)プロフィール登録・編集
ユーザー管理の一連の処理。
パスワード変更・パスワードリマインダー機能
パスワードを忘れた場合、再発行できます。メールアドレス宛に発行された認証キーを、画面に入力。
パスワードが再発行されます。
愛犬(イッヌ)登録(編集・削除、一覧・詳細表示、検索機能)
コンテンツ管理の一連の処理。
お気に入り登録(Ajax)
お気に入り登録すると、マイページに反映されます。    なお、サインインしたときはAjax処理を使えるようにし、サインアウト時はdisabledにして使えないようにして、分岐を分けています。
サービスを作った理由:愛犬との思い出を残し、共有できたら楽しそう
自分の愛犬は家族だなぁと思います。
朝はあいさつに来てくれるし、表情を見ていろいろ察して動いてくれる(飼い主の都合の良い解釈しすぎ?^ ^;)。思い出がたくさんあります。
イヌに愛情を持つ人が多いと思ったので、共有できたらなあと思ったためです。
また、少子高齢化とコロナの影響によってペットの需要は増えています。コミュニケーションの手段として、オンラインで愛犬の情報交換ができるものを作りたいと考え、制作しました。
使用技術
以下、6点- PHP(フルスクラッチ)・MySQL。セキュリティ・PHPのロジックを意識
- Ajax
- jQuery:レスポンシブ時、ハンバーガーメニュー
- 外部設計〜内部設計を意識。DBは正規化しつつ、テーブル・カラムを作成
- 単体テスト・連結テスト・総合テスト
- 実機テスト、開発ツール確認(Chrome・Safari・Edge)、開発ツールにてレスポンシブ縦横幅を確認(iPhone6/7/8系・Plus系・X系)
PHP(フルスクラッチ)・MySQL。セキュリティ・PHPのロジックを意識
セキュリティで意識したこと:以下2点- プリペアドステートメントを使用
- htmlspecialchars関数によるサニタイズ
PHPのロジック:ファイル冒頭にてPHPを記述・HTML側では変数を出力
PHPとHTMLの住み分けを意識。
(画面切り替えの都合上、完全に住み分けていない部分もあります)
Ajax
お気に入りボタンをクリック時に、非同期でDB内のレコードを編集jQuery:レスポンシブ時、ハンバーガーメニュー
レスポンシブ時(ブレークポイント:〜767px)の、ハンバーガーメニュー設置外部設計〜内部設計を意識。DBは正規化しつつ、テーブル・カラムを作成
サービスの要件・概要を考え、ワイヤーフレームを作成。そこから、プログラムの全体像を意識しつつ、作成していきます。サービスに必須の外部設計〜内部設計を意識して作りました。
なお、DBの設計では、正規化を行っています。
単体テスト・連結テスト・総合テスト
単体テスト、連結テスト(状況に応じて、同値クラスや境界値のテストなど)を行いつつ、コードを作成・編集。ある程度作ったら、サービスを使いつつ総合テストを行いました。
厳密にシナリオ作成やテストケースの作成をしたわけではありませんが、バグが出そうな重要な箇所を重点的にテスト。
実機テスト、開発ツール確認(Chrome・Safari・Edge)、レスポンシブ縦横幅を確認(iPhone6系・Plus系・X系)
スマホの実機は、iPhoneXとAndroidにて確認。開発ツールでの確認は、Chrome・Safari・Edgeにて、見え方や動き方を確認しました。
なお、開発ツールのレスポンシブは、iPhone6/7/8系・Plus系・X系にて、縦横幅の違いを確認しています(タブレットには対応していないので、一部崩れます。時間の関係で未改修)。
サービスの作成にどのように取り組んだか:大まかなワイヤーフレームをもとに、PHP・MySQLでベースを作成
大まかにワイヤーフレームを作成(全14ページ)。
機能とモノを洗い出し、DB・テーブル設計。
PHPのロジックを作りつつ、変更しては動かし、都度修正していきました。
工夫したところ・難しかったところ:表示画面の切り替え・セキュリティ対策、Ajaxの遷移画面のやりとり
工夫したところ、難しかったところなど4点紹介します。
- PHPのロジック:表示画面の切り替え
- SQLインジェクション・XSS対策:プレースホルダ・htmlspecialchars関数の使用
- Ajax:画面遷移と、Ajaxの値のやりとりが難しかった
- PHPのロジックとHTMLの分担を意識:冒頭PHPと後半HTML(インデント を揃え、コメントを書きなど)
PHPのロジック:表示画面の切り替え
ユーザーの使い勝手を考えつつ、分岐を作るのが大変さを感じつつ、達成感を得られたところ。たとえば、コンテンツ(イッヌ)詳細ページにて。
コンテンツ(イッヌ)のユーザーがアクセスしたら、編集ページへ。
コンテンツ(イッヌ)のユーザーでない人がアクセスしたなら、掲示板画面へ遷移するボタンが表示されるように組みました。
<div class="btn-container">
<?php
// ユーザーによる、場合分け遷移ボタン
// ユーザーのイッヌの場合、編集画面へ遷移するボタンを表示
if(!empty($_SESSION) && $_SESSION['user_id'] === $viewData['user_id']){
?>
<a href="registDog.php<?php echo '?d_id='.$d_id; ?>">
<input value="編集する!" class="btn btn-primary">
</a>
<?php
// ユーザーのイッヌでない場合、掲示板画面へ遷移するボタンを表示
}elseif(!empty($_SESSION) && $_SESSION['user_id'] !== $viewData['user_id']){
?>
<input type="submit" value="声をかけてみる!" name="submit" class="btn btn-primary">
<?php
// サインインしていない場合、サインインページへ遷移するボタンを表示
}else{
?>
<a href="signin.php">
<input value="サインインページへ" class="btn btn-primary">
</a>
<?php
// 場合分け遷移ボタン終了
}
?>
</div>
あと、コンテンツ編集ページのみ、コンテンツ削除ページへのボタンを出現させる処理を行いました
(コンテンツ登録ページには、削除ページへのボタンを出現させない)。
また、掲示板も、状況に応じて掲示板画面の出現を切り替えています。
<div class="area-board" id="js-scroll-bottom">
<?php
// メッセージがある場合
if(!empty($viewData['msg'])){
// メッセージ情報を展開
foreach($viewData['msg'] as $key => $val){
// 送信者メッセージかつ、送信者メッセージが相手のものの場合
if(!empty($val['from_user']) && $val['from_user'] == $partnerUserId){
?>
<!-- 掲示板左側のメッセージ情報 -->
<div class="msg-cnt msg-left">
<div class="avatar">
<img src="<?php echo sanitize(showImg($partnerUserInfo['pic'])); ?>" alt="" class="avatar">
</div>
<p class="msg-inrTxt">
<span class="triangle"></span>
<?php echo sanitize($val['msg']); ?>
</p>
<div style="font-size:.5em;"><?php echo sanitize($val['send_date']); ?></div>
</div>
<?php
// 上記以外。送信者メッセージが自分のものの場合
}else{
?>
<!-- 掲示板右側のメッセージ情報 -->
<div class="msg-cnt msg-right">
<div class="avatar">
<img src="<?php echo sanitize(showImg($myUserInfo['pic'])); ?>" alt="" class="avatar">
</div>
<p class="msg-inrTxt">
<span class="triangle"></span>
<?php echo sanitize($val['msg']); ?>
</p>
<div style="font-size:.5em;text-align:right;"><?php echo sanitize($val['send_date']); ?></div>
</div>
<?php
}
}
// 1と合致。つまり、掲示板が無い場合
}elseif($viewData === 1){
?>
<p style="text-align:center;line-height:20;">掲示板はありません。</p>
<?php
// 掲示板があり、メッセージが無い場合。
}else{
?>
<p style="text-align:center;line-height:20;">メッセージ投稿はまだありません</p>
<?php
}
?>
</div>
SQLインジェクション・XSS対策:プレースホルダ・htmlspecialchars関数の使用
SQL発行時はプレースホルダを使用。以下は、ユーザー情報取得時の例。<?php
function getUser($u_id){
debug('ユーザー情報を取得します。');
try {
$dbh = dbConnect();
// usersテーブルから、レコードを取得
$sql = 'SELECT id, username, age, tel, zip, addr, email, password, signin_time, pic, delete_flg, create_date, update_date FROM users WHERE id = :u_id AND delete_flg = 0';
// プレースホルダ割り当て
$data = array(':u_id' => $u_id);
// クエリ実行
$stmt = queryPost($dbh, $sql, $data);
// クエリ結果のデータを、1レコード返却
if($stmt){
return $stmt->fetch(PDO::FETCH_ASSOC);
// クエリ成功しなかった場合
}else{
return false;
}
} catch (Exception $e) {
error_log('エラー発生:'.$e->getMessage());
}
}
また、XSS攻撃対策として、ユーザー情報を出力する部分でhtmlspecialchars関数を使用。
<?php
// サニタイズ
function sanitize($str){
return htmlspecialchars($str,ENT_QUOTES);
}
ポスト送信内容に害のあるコードが入っていても、スクリプトを無害化するようにしています。
Ajax:画面遷移と、Ajaxの値のやりとりが難しかった
Ajaxの値のやりとりって、PHPとちょっと雰囲気が違いますからね^ ^;コンテンツ(イッヌ)詳細ページdogDetail.phpにて、クリックしたhtmlのdata属性から商品IDを取得。
<?php
echo '<i class="fa fa-paw icn-recommend js-click-recommend ';
// DBにてお気に入り登録している場合、お気に入りアイコンに色をつける
if(isRecommend($_SESSION['user_id'], $viewData['id'])){ echo 'active'; }
// data属性からイッヌのIDを取得
echo '" aria-hidden="true" data-dogid="'.sanitize($viewData['id']).'">お気に入り登録!</i>';
商品IDをAjaxでDBへ登録。もしくは、すでに登録されていたら、DBから削除。
<script>
// お気に入り登録・削除
var $recommend,
recommendDogId;
$recommend = $('.js-click-recommend') || null; // null値にて、変数の中身が空と明示(undefinedが入らないようにする)
// dogDetail.phpのdata属性・キーdogidから、イッヌのIDを取得
recommendDogId = $recommend.data('dogid') || null;
// !== null を判定することで、0もtrueとする。数値の0がfalseと判定されてしまわないため。例外的ではあるが、dogsテーブルのidカラム(イッヌのid)が0の場合の想定をしたため、undefinedとnullの両方を判定した
if(recommendDogId !== undefined && recommendDogId !== null){
$recommend.on('click',function(){
var $this = $(this);
$.ajax({
type: "POST",
url: "ajaxRecommend.php", // 送信先ファイル
data: { dogId : recommendDogId} // キーdogId : 値recommendDogId(イッヌのid)
}).done(function( data ){
// console.log('Ajax Success'); // 開発・動作確認用コード
$this.toggleClass('active'); // お気に入りの色を変える
}).fail(function( msg ) {
// console.log('Ajax Error'); // 開発・動作確認用コード
});
});
}
</script>
data-に値を持たせます。Ajax処理のファイルで受け取り、処理。
あとは、データがあるか無いかで、INSERT INTOとDELETEの処理を分けています。
複数のファイルで、データをやりとりし、AjaxとPHPの処理が行われるので理解に時間がかかりました。
PHPロジックは画面の上部に書いて、HTMLは下部
PHPで書く前半部分と、HTMLで書く後半部分との役割分担を意識しました。HTML内で、PHPを書くような感じで。HTML内でのPHPは、なるべく表示させるのみにすることを意識しました。
そして、インデントで整理。コメントは多めに書き、自分でも見直した時に分かりやすいよう心がけています。
ページ最後の空行も、意識しつつ作成。
やってみて感じたこと、得られたこと:動作テストをしながら、不具合に対応していくこと
コードを作り、編集して都度動かすと、予想していない不具合が出て来ます。新たな発見があって楽しかったです。
単体テスト、連結テストを行いつつ作り、最後に実際にサービスを使いつつ総合テストを行いました。
(厳密にシナリオ作成やテストケースの作成をしたわけではありませんが、バグが出そうな重要な箇所を重点的にテスト)
ユーザーにとってどうなんだろうと考えながらの作業を通じて、いろいろ調べることによって、新たなコードの使い方を気づかせるものにもなります。
今後の課題として取り組んでいきたいこと:toBや業務システムを意識したサービスの作成
今回、toC用として遊んでもらえるよう意識しました。
使い勝手を考えつつサービスを作ると、いろいろな発見があり楽しいです。
今後はサービス利用者の対象を少し変更し、toBを意識しつつ、業務システムにも使えそうなサービスを作っていきたいです。
以上となります。最後まで読んでいただきありがとうございます。