Help us understand the problem. What is going on with this article?

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

概要

今回は、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サイトの体裁を整える事ができます。

↓公式ドキュメント

Screen Shot 2019-05-06 at 1.23.33.png

↓実装したコード

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

↓結果
Screen Shot 2019-05-06 at 1.24.16.png

そうして、

↓記事の一覧表示

61f4b02b0ef38083298f9dc935485986.gif

↓記事の作成ページ

6c71ee68a3ea04585e7db5ac002943d5.gif

↓検索機能

30b232fc80076504327daaadcf0f1f9f.gif

↓ログインページ

fd1a1f08010b99a2020da7db27f224f2.gif

↓マイページ

e526903fd6e94b9bb1eae5e0d3d64768.gif

即席なので、かなり粗だらけですが、ゆくゆくは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図を再掲します。

Screen Shot 2019-05-06 at 3.57.13.png

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

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

↓この時点

5c1119e716b18c51ea2becac9789f71f.gif

つまり、記事の新規作成の場合、記事の作成より先に、画像が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でのデプロイ

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away