LoginSignup
10
10

More than 5 years have passed since last update.

Elixir+Phoenixに触ってみる(デモ作成編2)

Last updated at Posted at 2016-02-09

やること

前回に引き続き、デモアプリを作っていく。
今回は記事に対するコメント機能を追加する。

コメントモデルを追加する

コメントのスキーマはこんな感じ

commnets   
    id: int,
    post_id: int,
    name: string(255),
    content: text,
    inserted_at: datetime,
    updated_at: datetime

コメントの表示及び投稿フォームは記事の詳細ページに配置する予定なので、今回はモデルのみを追加。

$ mix phoenix.gen.model Comment comments post_id:references:posts name:string content:string
* creating priv/repo/migrations/20160209074344_create_comment.exs
* creating web/models/comment.ex
* creating test/models/comment_test.exs

Remember to update your repository by running migrations:

    $ mix ecto.migrate

$ mix ecto.migrate
Compiled web/models/comment.ex
Generated phoenix_sample app

16:52:27.852 [info]  == Running PhoenixSample.Repo.Migrations.CreateComment.change/0 forward

16:52:27.853 [info]  create table comments

16:52:27.861 [info]  create index comments_post_id_index

16:52:27.898 [info]  == Migrated in 0.3s
$ 

モデル同士の関連を定義する場合は、作成時の型としてreferencesを使う。
生成された、web/models/comment.exを確認してみると

schema "comments" do
    field :name, :string
    field :content, :string
    belongs_to :post, PhoenixSample.Post

    timestamps
end

と親に当たる記事に対する参照が定義されていることが確認できる。

コメント表示機能の追加

Postモデルの変更

コメントから記事への参照は定義されているが、逆方向の参照が定義されていないので追加する。
web/models/post.exを以下のように変更

schema "posts" do
    field :title, :string
    field :content, :string

    # add reference to comments
    has_many :comments, PhoenixSample.Comment                                                                                                                                                    

    timestamps
end

テンプレートの変更

コメントは記事の詳細ページにて表示するので、web/templates/post/show.html.eexの最後に以下の記述を追加

<h3>Comments</h3>
<ul>
  <%= for comment <- @post.comments do %>
    <li><%= comment.name %>: <%= comment.content %> </li>
  <%= end %>
</ul>

画面で確認

コメント投稿機能はまだ無いので、DBに直接コメントレコードを追加して、記事詳細ページを開いてみると…
スクリーンショット 2016-02-09 17.51.14.png

Ecto.Association.NotLoadedで検索してみると、ロードしたモデルの関連先のモデルもビューで使用したいときは、その関連先のモデルもコントローラで明示的にロードしてやる必要があるらしい(はっきりとした情報でもないのですが…)
なので、コントローラ(web/controllers/post_controller.ex)の33行目付近に、commentsをロードする記述を追加

def show(conn, %{"id" => id}) do
    post = Repo.get!(Post, id) |> Repo.preload(:comments) # add comment preload 

    render(conn, "show.html", post: post)
end

再度、記事ページにアクセスしてみる。
スクリーンショット 2016-02-09 17.59.19.png

今度は無事成功。

コメント投稿機能の追加

コメント投稿機能を追加する。
コメント投稿フォームは、記事詳細ページに配置し、投稿後は同じ記事詳細ページヘ遷移する。

コントローラの修正

コメントは、独立したリソースではなく記事に従属するリソースなので、コメント追加のためのメソッドもPostコントローラに追加することにする。

web/controllers/post_controller.exにAliasを追加して

alias PhoenixSample.Comment

以下のようなメソッドを追加

def add_comment(conn, %{"id" => id, "comment" => post_params}) do
  post = Repo.get!(Post, id)
  changeset = Comment.changeset(%Comment{post_id: post.id}, post_params)

  case Repo.insert(changeset) do
    {:ok, _post} ->
      conn
      |> put_flash(:info, "Comment create successfully.")
      |> redirect(to: post_path(conn, :show, post))
    {:error, changeset} ->
      conn
      |> put_flash(:info, "Error occured.")
      |> redirect(to: post_path(conn, :show, post))
  end
end

ルートの追加

コメントの投稿には

POST /posts/:id/comment

というルートを割り当てたい。
web/router.exの21行目付近に新しいルートを追加。

scope "/", PhoenixSample do
  pipe_through :browser # Use the default browser stack

  get "/", PageController, :index
  resources "/posts", PostController
  post "/posts/:id/comments", PostController, :add_comment  # add comment route
end

確認のために、ルート一覧を確認

$ mix phoenix.routes
page_path  GET     /                    PhoenixSample.PageController :index
post_path  GET     /posts               PhoenixSample.PostController :index
post_path  GET     /posts/:id/edit      PhoenixSample.PostController :edit
post_path  GET     /posts/new           PhoenixSample.PostController :new
post_path  GET     /posts/:id           PhoenixSample.PostController :show
post_path  POST    /posts               PhoenixSample.PostController :create
post_path  PATCH   /posts/:id           PhoenixSample.PostController :update
           PUT     /posts/:id           PhoenixSample.PostController :update
post_path  DELETE  /posts/:id           PhoenixSample.PostController :delete
post_path  POST    /posts/:id/comments  PhoenixSample.PostController :add_comment # <- 追加されている

テンプレートの修正

テンプレートにコメント投稿フォームを追加する。
まず、newメソッドを参考に、showメソッドを以下のように修正する。

def show(conn, %{"id" => id}) do
  post = Repo.get!(Post, id) |> Repo.preload(:comments)

  comment_changeset = Comment.changeset(%Comment{})

  render(conn, "show.html", post: post, comment_changeset: comment_changeset)
end

そして、web/templates/post/show.html.eexの最後に以下のような記述を追加。

<%= form_for @comment_changeset, post_path(@conn, :add_comment, @post), fn f -> %>
  <div class="form-group">
    <span>名前: <%= text_input f, :name, size: 10 %></span>
    <span><%= text_input f, :content, size: 50 %></span>
    <%= error_tag f, :name %>
    <%= error_tag f, :content %>
  </div>
  <div class="form-group">
    <%= submit "コメントする", class: "btn btn-primary" %>
  </div>
<%= end %>

確認

ブラウザで記事詳細ページを開いてみると
スクリーンショット 2016-02-09 19.31.53.png
このように、コメント投稿フォームが追加されていることが確認できると思います。
試しにコメントを投稿してみると…
スクリーンショット 2016-02-09 19.33.23.png
このように、無事コメントの投稿ができることが確認されました。
やったね。

終わりに

以上で、当初想定していた機能は一通り実装することができました。
Phoenixを触るのは全くの初めての状態から開始でしたが、ところどころ躓いたりもしましたが、概ね問題なくここまで終わらせることができました。
Railsやそれに影響を受けたフレームワークを扱ったことがある人なら、さほど苦労なく導入最初の段階は超えられると思われます。(その先はまだ不明)

今後は、このプロジェクトをベースに様々な機能の実験をしていこうと思います。

10
10
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
10