こんにちは、ぱるこです。
「Laravel」のアウトプットとして、画像投稿サイトを作成してみました。
その作成記録をまとめていきます。
完成品
サイト名「フィルムカメラ日和。」

デザイン
一覧画面 詳細画面 タグ検索 タグ検索結果 お気に入り結果 プロフィール画面 プロフィール編集 ログアウト 削除 投稿 新規登録 ログイン
作成の流れ
制作にかかった時間
1.要件定義 | 1h |
2.ワイヤーフレーム | 4h |
3.デザイン(spのみ) | 4h |
4.環境構築(laravel.mix使用) | 4h |
5.コーディング(flocss・scss) | 6h |
6.Laravel組み込み(bladeを使用) | 49h |
7.本番環境にデプロイ+修正 | 8h |
合計 | 76h |
サービス概要
「フィルムカメラ日和。」は、フィルムカメラで撮った写真専用の投稿&閲覧サイトです。
会員登録無しのままでも、閲覧や検索が可能です。
投稿やお気に入り登録をする場合は会員登録の必要があります。
(※ご利用ガイドは準備中です。)
導入
なぜ、このサイトを作ったのかというと、ズバリ作成者本人が、最近「フィルムカメラ」を始めたからです。(今は勉強の気分転換の散歩中に撮るくらいですが・・・)
一眼レフやミラーレスといったデジタルカメラではなく、「フィルムカメラ」です。
フィルムカメラは、現像するまで写真を見れないというもどかしさがありますが、それが楽しみでもあります。
まだフィルムカメラを初めてまだ1ヶ月も経っていない私の場合、なかなかフレームの中に収めることができません。
が、そんなど素人の私でもフィルムカメラで撮ると、ほっこりとした温もりのある写真を撮ることができます。
私ごとですが、ちょっと病んでしまった時にフィルムカメラで撮った写真を見ると、なんだか元気が出ます。
そんなフィルムカメラをもっと知って欲しいという想いと、現像後のもう一つの楽しみのためのサイトを作りたいと思い、フィルムカメラに特化した投稿サイトを作成しました。
インスタやfacebookと何が違うのか?(差別化)
いいねやコメントができる両者と差別化して、今回作ったサイトは、いいねやコメントはできません。
インスタは最近いいねが非表示になっており、本人からは見ることができますが、それもこのサイトではあえて表示しません。
なぜ、いいねやコメントなしにしたのか?
いいねやコメントの有る無しにとらわれることなく、フィルムカメラで撮った写真を、「自由に」そして「気軽に」投稿できるサイトにしたかったからです。
ちなみに、投稿画像の近くにあるハートは、いいねではなく、お気に入り保存のための機能です。ログインした本人しか、見れません。
ターゲット
・カメラやフィルムカメラに興味があるひと・趣味にしている人
・いいねやコメントにとらわれることなく投稿したい人
・フィルムのほっこりとした雰囲気に癒されたい人
機能一覧
・写真投稿一覧
・ログイン
・ログアウト
・写真の投稿(タイトル、写真1枚、タグ付け(0〜10個まで)、画像の圧縮処理)
・マイページ
・プロフィール編集
・お気に入り保存
・タグ検索
・snsシェア
テーブル設計
今回は、ER図と呼ばれる手法を用いて、DB設計してみました。

図を用いているので、関係性がわかりやすいですね!次回からもこの方法を使っていきたいと思います。
こちらの記事を参考にさせて頂きました。

制作時につまづいた箇所
その1、環境構築(laravel.mix)
前回まではgulpを用いてsassの環境構築をしていたのですが、今回はlaravel.mixを用いて、sassの環境構築を行いました。
今回、flocssを利用してcssを記述していったので、ワイルドカード(*)を用いたコンパイルを行いたかったのですが、まずここでつまづきました。
今後を見据えて、laravel mixでワイルドカードを使用したsassのコンパイルを調べてみました。gulpで行った時と同様に、まず、ワイルドカードを使用できるように「import-glob-loader」のパッケージをインストールします。
npm install --save-dev import-glob-loader
laravelにもnode_modulesというディレクトリができているので、その中にインストールしたパッケージが格納されます。
(出力先)
public/css/style.css
public/js/app.js
(出力元)
resources/sass/style.scss
resources/js/app.js
mix.webpackConfig({
module: {
rules: [{
// ローダーの処理対象ファイル
test: /\.scss/,
enforce: "pre",
loader: 'import-glob-loader'
}]
}
})
.js('resources/js/app.js', 'public/js')
.sass('resources/sass/style.scss', 'public/css')
.sourceMaps();
ちなみにここで、jsが動かないという状態に陥ってしまいましたが、ただ、jqueryのパッケージがimportされていないだけでした。laravelではjqueryはデフォルトで使用できるので、npm installはしなくていいようです。
import $ from 'jquery'; //jqueryのパッケージをimportして使えるようにする
ちなみに、webpackはまだまだ理解度低いので、udemyのセールの時に買っておいたこちらの授業を勉強中です。
今の所、いい感じ内容ですが、gitの知識はやや必要です・・・!

その2、多対多のリレーション
・画像投稿の際に、タグを0〜10個まで付けることができる
・タグ検索をすると、そのタグがついた投稿一覧を表示できる
このような仕様にする場合、postsテーブルとtagsテーブルが多対多の関係になります。
その場合、postsテーブルとtagsテーブルを紐付ける「中間テーブル」というものが新たに必要になってくることがわかったので、ER図に改めて書いていきます。

post_tagテーブルを作成する。
新たにテーブルを作る為にマイグレーションを生成します。
php artisan make:migration create_post_tag_table --create=post_tag
public function up()
{
Schema::create('post_tag', function (Blueprint $table) {
$table->unsignedbigInteger('post_id');
$table->unsignedbigInteger('user_id');
$table->primary(['post_id','user_id']);
// 外部キー制約
$table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
});
}
マイグレーションの実行します。
php artisan migrate
投稿機能を作成
// 投稿新規処理
Route::post('/posts','PostsController@store');
belongsToManyを用いて、PostモデルとTagモデルに多対多のリレーションを貼ります。
class Post extends Model
{
public function tags()
{
return $this->belongsToMany('App\Tag');
}
}
class Tag extends Model
{
public function posts()
{
return $this->belongsToMany('App\Post');
}
}
name属性にtags[]という配列でタグをpost送信するようにします。
//タグ入力
<div class="p-postEdit-form__input-tags">
@for ($i = 1; $i <= 10; $i++)
<input type="text" name="tags[]" class="p-postEdit-form__input-tag @error('tags[]'.$i) is-error @enderror" value="{{ old('tags[]'.$i) }}" placeholder="#タグ">
@error('tags[]'.$i)
<span class="p-postEdit-form__errorMsg" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
@endfor
</div>
public function store(Request $request)
{
〜省略〜
// タグの登録
$post = new Post;
$tags_name = $request->input('tags');
$tag_ids = [];
foreach ($tags_name as $tag_name) {
if(!empty($tag_name)){
$tag = Tag::firstOrCreate([
'name' => $tag_name,
]);
$tag_ids[] = $tag->id;
}
}
// 中間テーブル
$post->tags()->attach($tag_ids);
return redirect('/')->with('success', '投稿しました');
}
【inputメソッド】 (△△△の取得)
$request->input(‘△△△’);
input typeのname’△△△’に指定したデータを取得できる
DBにデータが存在する場合は取得し、存在しない場合はDBにデータを登録した上でインスタンスを取得してくれる便利メソッド。これを使うと重複してタグの登録ができないようになります。

attachメソッドを使って、postsテーブルに紐づいている中間テーブルに値を挿入し、tagテーブルにも値を追加することができました。
が、投稿を削除した時に、中間テーブルは削除されていないので、改善が必要です。
syncを使えば良いのか・・?
https://www.wakuwakubank.com/posts/387-laravel-relation-3
タグの実装が今回のLaravelのアウトプットで一番難しかったです。
・画像投稿時にどうやって送信するか?
・配列で受け取ったものをどう処理するのか?
・中間テーブルへの登録の仕方などなど
実装はなんとかできましたが、理解しきれていないので次回のアウトプットでこのモヤモヤを晴らしていきたいです・・!
その3、お気に入りのajax方法
今回ajaxにて、お気に入り保存をしたい箇所はここです。
無事ajax動いたのですが、コードが合っているかは自信はありませんが、ajaxの実装のみ書き留めます。
ajaxはまだ理解しきれていないので、復習します・・・!(汗)
//いいねの処理(未ログイン時)
Route::get('/posts/{post_id}/likes', 'LikesController@store');
// いいねの処理(ログイン時 ajax)
Route::post('/posts/ajaxlike', 'LikesController@ajaxlike');
<div class="c-post__likeIcon-wrapper">
@guest
<a class="c-post__likeIcon" data-remote="true" rel="nofollow" href="/posts/{{ $post->id }}/likes">いいね</a>
@endguest
@auth
<a class="c-post__likeIcon js-like-change <?php if($post->likedBy(Auth::user())->count() > 0){{ echo 'loved'; }}?>" data-remote="true" rel="nofollow" data-postid="{{ $post->id }}">いいね</a>
@endauth
</div>
var $like = $('.js-like-change');
var likePostId;
$like.on('click', function () {
var $this = $(this);
likePostId = $this.data('postid');
$.ajax({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
url: '/posts/ajaxlike',
type: 'POST',
dataType: "json",
data: { 'post_id': likePostId },
})
// Ajaxリクエストが成功した場合
.done(function () {
$this.toggleClass('loved');
})
// Ajaxリクエストが失敗した場合
.fail(function (data) {
console.log('エラー');
console.log(data);
});
});
public function ajaxlike(Request $request)
{
$post_id = $request->post_id;
$liked = Like::where('post_id', $post_id)->where('user_id', Auth::user()->id)->count();
if($liked > 0){
$like = Like::where('post_id', $post_id)->where('user_id', Auth::user()->id)->delete();
return response()->json($like);
}else{
$like = new Like;
$like->post_id = $request->post_id;
$like->user_id = Auth::user()->id;
$like->save();
return response()->json($like);
}
}
app.jsに、下記の記述が必要です。
$.ajax({
headers: {
‘X-CSRF-TOKEN’: $(‘meta[name=”csrf-token”]’).attr(‘content’)
}
その4、さくらインターネット(本番環境)へのデプロイ
今回、練習がてらgithubに、定期的にbackupをして開発していきました。
なので、デプロイはgithubからさくらサーバーにデプロイしていく方法で行いました。
こちらを参考にしました。

上手く行きかけましたが、DB接続が必要なページは、404エラーとなりました。
2日間ほど.envやdatabase.phpを書き直したり、データベースを消したり色々試した結果なんとか表示できました。
.envやdatabase.phpの関連がまだ掴めていないのでここも要復習です><
おまけ
loadingを設置
全ての画像を読み込むと結構時間がかかってしまい、綺麗な状態で表示ができていないので、画像の読み込みが終わるまでは、gif画像を見せるようにしています。よく、いろんなサイトについているやつですね。

ちなみに、画像が無いページはそこまで必要ないので設置していません。
ローディングの設置は、いつもお世話になっているこちらのサイトを参考にさせて頂きました。
webだけでなく、フィルムカメラのことにも書かれていて、大好きなサイトです。

サイトを知ってもらうにはどうしたら良いか?
投稿した写真をもっと見てもらいたい時や閲覧した方がよかったなと思う写真を気軽に、twitterなどのsnsにもシェアできるようにする為にsnsシェアボタンを設置しました。
また、その際に、表示されるtwitterカードも、投稿内容に合わせて動的にtwitterカードの画像やタイトルが変更できるようにしました。
twitterカードの作成は、サルワカさんがわかりやすいです。

今回のサイトでは、投稿詳細ページのみheadタグを動的にします。
<meta name="twiiter:card" content="summary_large_image">
<meta name="twitter:site" content="@filmbiyori">
<meta property="og:url" content="{{ config('app.url') }}posts/{{ $post->id }}">
<meta property="og:title" content="フィルムカメラ日和。">
<meta property="og:description" content="{{ $post->title }}">
<meta property="og:iamge" content="{{ config('app.url') }}storage/post_images/{{ $post->photo }}.jpg">
画像やタイトルを表示させるのと同じように、metaタグにも{{ $post->title }}のようにすればOKです!上手く動的に表示することができました。
また、twitterカードを表示させる場合、カードの読み込みエラーになってしまうことがありましたが、キャッシュが原因でした。google cromeのclear casheなどを利用すると良さそうです。
改善したい箇所
・パスワードリセット時のメールの日本語化
・パスワードリセット時のページのスタイル(デフォルトのまま)
・ドメインの変更&お引越し
・投稿画像が溜まったらページネーション実装
・github公開
webサービス部重要!!
Laravelを勉強して率直な感想ですが、いきなりフレームワークをしていたら、本当に中身がわかっていないエンジニアになってしまうなと感じました。なぜならLaravelでは「php artisan make:auth」でいとも簡単に、ログイン〜新規登録ができてしまいます。
非常〜に便利ですが、その反面、中身がわかっていないと、Laravelは怖いなと感じました。
なので、webサービス部で、フルスクラッチで生のPHPを書いて2ヶ月間もがきましたが、本当によかったと思います。と同時に、まだまだ勉強不足を感じています。
この不安を払拭するには、もうたくさん実装してコードを書いていくしかないので、早く転職して経験を積むしかないと、ひしひしと感じています。
以上、「Laravel」のアウトプットでした。
