Laravelでインスタもどきの「画像投稿サイト」を作ってみた。

ウェブカツ

こんにちは、ぱるこ(@h9h1h7)です。

ウェブカツの「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設計してみました。

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

やさしい図解で学ぶ ER図 表記法一覧 - Qiita
ER図とは? ER図もしくはERDとは "Entity Relationship Diagram"のこと 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

webpack.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はしなくていいようです。

app.js
import $ from 'jquery';  //jqueryのパッケージをimportして使えるようにする

ちなみに、webpackはまだまだ理解度低いので、udemyのセールの時に買っておいたこちらの授業を勉強中です。
今の所、いい感じ内容ですが、gitの知識はやや必要です・・・!

モジュールバンドラー webpack を1日で習得!フルスクラッチでインストールからカスタマイズまでの手順を理解する
React を題材にし各種形式のモジュールをローダー(babel/css/sass/html/eslint)やプラグイン(JS圧縮のカスタム/CSSのファイル分離と圧縮)でバンドル方法をハンズオンで解説!今回もGitHubにソース完全公開

その2、多対多のリレーション

タグの仕様

・画像投稿の際に、タグを0〜10個まで付けることができる
・タグ検索をすると、そのタグがついた投稿一覧を表示できる

このような仕様にする場合、postsテーブルとtagsテーブルが多対多の関係になります。
その場合、postsテーブルとtagsテーブルを紐付ける「中間テーブル」というものが新たに必要になってくることがわかったので、ER図に改めて書いていきます。

post_tagテーブルを作成する。

新たにテーブルを作る為にマイグレーションを生成します。

php artisan make:migration create_post_tag_table --create=post_tag
post_tag_table.php
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

投稿機能を作成

ルーティング(web.php)
// 投稿新規処理
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');
    }
}
ビュー(view)

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>
コントローラー(LikesController.php)
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メソッドの使い方

【inputメソッド】 (△△△の取得)
$request->input(‘△△△’);
input typeのname’△△△’に指定したデータを取得できる

attachメソッド

attachメソッドを使って、postsテーブルに紐づいている中間テーブルに値を挿入し、tagテーブルにも値を追加することができました。
が、投稿を削除した時に、中間テーブルは削除されていないので、改善が必要です。

syncを使えば良いのか・・?
https://www.wakuwakubank.com/posts/387-laravel-relation-3

タグの実装が今回のLaravelのアウトプットで一番難しかったです。
・画像投稿時にどうやって送信するか?
・配列で受け取ったものをどう処理するのか?
・中間テーブルへの登録の仕方などなど

実装はなんとかできましたが、理解しきれていないので次回のアウトプットでこのモヤモヤを晴らしていきたいです・・!

多対多のリレーションで一番参考になった記事はこちら

その3、お気に入りのajax方法

今回ajaxにて、お気に入り保存をしたい箇所はここです。

無事ajax動いたのですが、コードが合っているかは自信はありませんが、ajaxの実装のみ書き留めます。
ajaxはまだ理解しきれていないので、復習します・・・!(汗)

ルーティング(web.php)
//いいねの処理(未ログイン時)
Route::get('/posts/{post_id}/likes', 'LikesController@store');
// いいねの処理(ログイン時 ajax)
Route::post('/posts/ajaxlike', 'LikesController@ajaxlike');
ビュー(view)
<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>
app.js
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);
  });
});
コントローラー(LikesController.php)
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);
  }
}
Laravelでの注意点

app.jsに、下記の記述が必要です。

$.ajax({
headers: {
‘X-CSRF-TOKEN’: $(‘meta[name=”csrf-token”]’).attr(‘content’)
}

CSRF保護 5.7 Laravel

その4、さくらインターネット(本番環境)へのデプロイ

今回、練習がてらgithubに、定期的にbackupをして開発していきました。
なので、デプロイはgithubからさくらサーバーにデプロイしていく方法で行いました。

こちらを参考にしました。

さくらのレンタルサーバーにLaravelのプロジェクトをgit cloneする|Koushi Kagawa|note
先日はさくらのレンタルサーバにLaravelをインストールする方法を記載しましたが、 今回はすでにローカルで開発しているLaravelのプロジェクトを、さくらのレンタルサーバにgit cloneする方法を記載します。 --- _φ(・_・ --- 今回やること ・さくらのレンタルサーバーにLarav...

上手く行きかけましたが、DB接続が必要なページは、404エラーとなりました。
2日間ほど.envやdatabase.phpを書き直したり、データベースを消したり色々試した結果なんとか表示できました。

.envやdatabase.phpの関連がまだ掴めていないのでここも要復習です><

おまけ

loadingを設置

全ての画像を読み込むと結構時間がかかってしまい、綺麗な状態で表示ができていないので、画像の読み込みが終わるまでは、gif画像を見せるようにしています。よく、いろんなサイトについているやつですね。

仮で犬のgifにしてます。

ちなみに、画像が無いページはそこまで必要ないので設置していません。

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

TIPS & NOTE ページ読み込み中。ローディングアニメーションを入れてみよう。
ページ読み込み中。ローディングアニメーションを入れてみよう。

サイトを知ってもらうにはどうしたら良いか?

投稿した写真をもっと見てもらいたい時や閲覧した方がよかったなと思う写真を気軽に、twitterなどのsnsにもシェアできるようにする為にsnsシェアボタンを設置しました。

また、その際に、表示されるtwitterカードも、投稿内容に合わせて動的にtwitterカードの画像やタイトルが変更できるようにしました。

twitterカードの作成は、サルワカさんがわかりやすいです。

【2020年版】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部」のアウトプットでした。

初心者向けオンラインプログラミング学習
大手スクール生徒や現役エンジニアが多数学び直している「実務レベル」に特化した「稼ぐ」ためのプログラミングスクール
タイトルとURLをコピーしました