みんなの「作ってみた」

【10日間でポートフォリオ作成に挑戦】9日目:フロントエンドの実装〜各種機能の修正

2019/05/05

ryoutaku
ryoutaku
2018年12月から3年間「毎日技術ブログ書く」と宣言して現在も継続中。IBMのWatsonきっかけでエンジニアに憧れて、28歳未経験で転職。バックエンドの開発を担当(Ruby,AWS)社会にインパクトを与えるプロダクトの開発に携わりたい一心で、愚直にアウトプットを継続。今の関心事は自然言語処理。

概要

今回は、2019年のGW期間(10日間)を全て費やして取り組むポートフォリオの製作過程を取りまとめた内容を投稿させて頂きます。(投稿は毎日行う予定)

全体通した取り組みの詳細については、前回までの記事をご参照ください。

【10日間でポートフォリオ作成に挑戦】1日目:要件定義〜記事投稿のCRUD
【10日間でポートフォリオ作成に挑戦】2日目:アクセス制限〜コメントのCRUD機能
【10日間でポートフォリオ作成に挑戦】3日目:ページネーション~CKEditorの導入
【10日間でポートフォリオ作成に挑戦】4日目:テーブル分割〜CKEditorのフォームへの反映
【10日間でポートフォリオ作成に挑戦】5日目:CKEditorへ画像アップロード機能を追加
【10日間でポートフォリオ作成に挑戦】6日目:テストコードの実装
【10日間でポートフォリオ作成に挑戦】7日目:検索機能〜いいね機能の実装
【10日間でポートフォリオ作成に挑戦】8日目:記事ストック機能〜ユーザーフォロー機能の実装

今日一日の作業内容

ここからは、今日1日で取り組んだ作業内容をご説明します。

フロントエンドの実装

先日、サーバーサイドのの実装が概ね完了したので、全く未着手だったフロントエンドの実装を行って行きます。
と言っても、私自身、実務でフロントの実装は全く経験していないのと、残り二日という短い期間なので、手早く実装できる手段を採用します。

そこで利用するのが、前回紹介したCSSフレームワークのMaterializeCSSです。
本当は、主流のBootstrapを利用したかったのですが、全く触った事が無いので、今回は諦めました。

使い方としては、公式のドキュメントから、使いたいパーツやデザインを探してきて、そのデザインを適用させる為のclassを、任意の箇所に記述するだけで、実装が完了します。
なので、全くCSSを触らなくても、WEBサイトの体裁を整える事ができます。

↓公式ドキュメント

↓実装したコード

= f.submit value: t('common.button.submit'), class: 'waves-effect waves-light btn orange'
↓結果

そうして、

↓記事の一覧表示

↓記事の作成ページ

↓検索機能

↓ログインページ

↓マイページ

即席なので、かなり粗だらけですが、ゆくゆくはCSSフレームワークは使わずに仕上げていきたいと考えています。Vue.jsも使ってみたいですし!

今日の失敗

ここからは今日の失敗をまとめていきます

ページネーションの表示数をベタ打ち

記事の一覧を表示する箇所ではページネーションを導入していたのですが、1ページの表示件数を指定する記述は、各コードにそれぞれ記述していました。

記事の一覧は、下記のコードで表した通り、「通常の一覧表示・検索結果の一覧・ストックの一覧・自身の投稿記事の一覧」の4箇所が該当します。

controllers/posts_controller.rb
def index
  @posts = Post.page(params[:page]).per(10).order(id: "DESC").includes(:user)
end

def search
  @search = Post.ransack(params[:q])
  @posts = @search.result.page(params[:page]).per(10)
  @keyword = params[:q][:title_cont]
end
controllers/users_controller.rb
def show
  @posts = Post.where(user_id: params[:id]).page(params[:page]).per(10).order(id: "DESC")
  @user = User.find(params[:id])
end

def post_stocks
  post_stocks = current_user.post_stocks.pluck(:post_id)
  @posts = Post.where(id: post_stocks).page(params[:page]).per(10).order(id: "DESC")
end

流石に4箇所全てにベタ打ちは冗長なので、変数に置き換える事にしました。
今回は、コントローラーが別れているので、application_controllerに変数を定義しています。

controllers/application_controller.rb
PER = 10

これで、もし表示件数を変更する必要が出て来ても、application_controllerの記述だけ変更すれば良いので、メンテナンス性は高まります。

画像アップロード機能の実装方法に無理がある

念のため、振り返りでER図を再掲します。

注目して頂きたいのが、記事に添付した画像を、どの様に管理しているか?です。
ER図では、PostDescriptionと関連付けさせたimageテーブルで管理する様にしています。

そして、今回の記事の編集はCKEditorを利用していますが、CKEditorは、画像アップロードの操作をした時点で、画像を保存します。

↓この時点

つまり、記事の新規作成の場合、記事の作成より先に、画像がDBに保存されます。
なので、画像保存時に、記事との関連付けを行う事が出来ません(まだ存在しないデータと関連付けは出来ない)

CKEditorは、アップロードした画像のパスも含めて、入力した情報をHTML形式に変換して保存してくれるので、関連付けしていなくとも、編集や詳細表示は問題なく出来ます。

支障をきたすのが、記事の一覧表示で、画像を表示させる場合です。
関連付けしていないので、PostからもImageからも、互いにどのデータと紐づいているか判別出来ません。

それを解消させる為に、下記の様な手段を取りました。

1:postにimage_idカラムを新たに追加し、モデルには下記のアソシエーションを記述

models/post.rb
has_one :image, dependent: :destroy

2:画像保存後に、保存されたレコードのIDをセッションで保持

controllers/images_controller.rb
def create
  image = current_user.description_images.build(
      image: params[:upload],
      image_relation: params[:image_relation]
  )
  if image.save
    render json: {
        url: image.image[:standard].url,
        uploaded: true
    }
     #画像のDBへの保存が完了したタイミングで、IDをセッションに保持
     # 複数枚画像を投稿した際は、最初の画像を登録する様にnilガードで記述
    session[:image_id] ||= image.id
  else
    render json: {
        error: {
            message: image.errors.full_messages
        },
        uploaded: false
    }
  end
end

3:記事保存時に、セッションに保持していたImageのIDも、外部キーとして一緒に保存する

controllers/posts_controller.rb
def create
  @post = current_user.posts.build(post_params)
  @post[:image_id] = session[:image_id]
  if @post.save
    session[:image_id] = nil
    redirect_to post_path(@post), notice: t('common.message.post_create')
  else
    render :new
  end
end

こうする事で、あとは下記のコードで、記事一覧の中で画像も表示させる事が可能になります。

views/posts/index.html.haml
.nav-wrapper.container
  %h5.header.orange-text
    = t('common.header.post_index')
  .row
    -# 要素の数だけ繰り返し
    = render @posts
views/posts/_post.html.haml
= link_to post_path(post) do
  .col.s6
    .post.card
      .card-image
        - if post.image_id.present?
          -# この記述で対象のpostと関連付いた画像を表示させる
          -# (:standard)は任意で変更できる様にした画像リサイズのオプション
          = image_tag post.image.image_url(:standard)
        - else
          = image_tag 'no_image.png'
      .card-title
        = post.title
      .card-user
        = "投稿者:#{post.user.name}"

もっと良い方法がRailsやCKEdtorにはあるのかもしれませんが、あまり調査に時間を掛ける余裕も無かったため、一旦この方法で実装しました。

これについては、追い追い調査して、効果的な方法を探りたいと考えています。

明日の予定

  • AWSへのデプロイ
  • HTTPS化
  • フロントの調整

AWSへのデプロイが1ヶ月ぶりの作業になるのと、HTTPS化が初の試みなので、本当に明日1日だけで完成できるのか?かなり不安が残ります。

いずれにしても、この10日間の開発で終わりにするのではなく、今後も継続して開発は続けて行き、機能を更にブラッシュアップさせて行こうと考えています。

※追記:10日目を投稿しました
【10日間でポートフォリオ作成に挑戦】10日目:AWSでのデプロイ