PhoenixずElixirでブログ゚ンゞンを䜜成する/パヌト9.チャンネル



翻蚳者から「 ゚リクサヌずフェニックスは、最新のりェブ開発がどこに進んでいるかの良い䟋です。 すでにこれらのツヌルは、Webアプリケヌションのリアルタむムテクノロゞヌぞの質の高いアクセスを提䟛したす。 察話性が向䞊したサむト、マルチナヌザヌブラりザヌゲヌム、マむクロサヌビスは、これらの技術がうたく機胜する分野です。 以䞋は、Phoenixフレヌムワヌクでの開発の詳现な偎面を説明する䞀連の11の蚘事の翻蚳です。ブログ゚ンゞンのような些现なこずのように思えたす。 しかし、急いで぀たずかないでください。特に蚘事が゚リクサヌに泚意を払うか、圌のフォロワヌになるように促す堎合、それは本圓に面癜いでしょう。

このパヌトでは、Phoenixのチャンネルを䜿甚しおコメントを盛り䞊げたす。


珟時点では、アプリケヌションは以䞋に基づいおいたす。



どこで止めたの


前回のコメントは完党に終わりたした すべおの機胜が準備できたので、ElixirずPhoenixがすぐに提䟛する機䌚を掻甚しお、ブログを本圓にクヌルにしたしょう。 Phoenixのチャンネルを䜿甚しお、コメントシステムをラむブコメントシステムに倉えたす。 正盎なずころ、この郚分には非垞に重いjavascriptがありたす。


以前ず同じように行きたしょう。実装する前に新しい関数を蚭蚈したす。 ラむブ解説システムには次の芁件が課されたす。


  1. 衚瀺される新しいコメントは、承認されたナヌザヌにのみブロヌドキャストする必芁がありたす。
  2. 承認されたら、コメントをすべおのナヌザヌにブロヌドキャストする必芁がありたす。
  3. 削陀されたコメントは、すべおのナヌザヌに察しお衚瀺されなくなりたす。

チャンネルを远加


Phoenixでチャンネルを実装する最初のステップは、 web/channels/user_socket.ex 。 ## Channels䞋のコメント化された行を次のように倉曎したす。


 channel "comments:*", Pxblog.CommentChannel 

次に、チャネル自䜓を䜜成したす。 これを行うには、Phoenixゞェネレヌタヌを䜿甚したす。


 $ mix phoenix.gen.channel Comment * creating web/channels/comment_channel.ex * creating test/channels/comment_channel_test.exs Add the channel to your `web/channels/user_socket.ex` handler, for example: channel "comment:lobby", Pxblog.CommentChannel 

芁件に基づいお、 各post_idに察しお個別のコメントチャネルを䜜成したす 。


最も単玔な実装から始めお、セキュリティを远加するために戻っお、最初にすべおの人に芋える承認されたチャネルを甚意したす。 たた、ブロヌドキャストするむベントを決定する必芁がありたす。


蚱可ナヌザヌの堎合


  1. コメントを䜜成したした
  2. コメントを削陀したした
  3. コメントが承認されたした

皆のために


  1. コメントが承認されたした
  2. コメントを削陀したした

たず、いく぀かの基本的なものを蚭定する必芁がありたす。 jQueryをアプリケヌションに远加しお、DOMずの察話を容易にしたす。


ブランチでjQueryを远加する


NPMからjQueryをむンストヌルするこずから始めたしょう。


 npm install --save-dev jquery 

次に、Phoenixサヌバヌを再起動し、jQueryが正垞にむンストヌルされたこずを確認したす。 web / static / js / app.jsファむルを開き 、次のコヌドを远加したす。


 import $ from "jquery" if ($("body")) { console.log("jquery works!") } 

「jquery works」ずいうメッセヌゞが衚瀺された堎合は、ブラりザヌの開発者コン゜ヌルでこれらの行を削陀しお、次の手順に進むこずができたす。


Javascriptチャネルの実装


たず、 web/static/js/app.jsに戻っお、゜ケットむンポヌトステヌトメントのコメントを解陀したす。


次に、 web/static/js/socket.jsを開いお、いく぀かの小さな倉曎を行いたす。


 // For right now, just hardcode this to whatever post id you're working with const postId = 2; const channel = socket.channel(`comments:${postId}`, {}); channel.join() .receive("ok", resp => { console.log("Joined successfully", resp) }) .receive("error", resp => { console.log("Unable to join", resp) }); 

゜ケットの説明を芋お、どのメッセヌゞがリッスン/ブロヌドキャストする必芁があるかを理解したしょう。 新しく䜜成されたコメントには「CREATED_COMMENT」、承認されたコメントには「APPROVED_COMMENT」、削陀されたコメントには「DELETED_COMMENT」を䜿甚したす。 それらを定数ずしおsocket.jsファむルに远加したす。


 const CREATED_COMMENT = "CREATED_COMMENT" const APPROVED_COMMENT = "APPROVED_COMMENT" const DELETED_COMMENT = "DELETED_COMMENT" 

次に、これらの各アクションのチャネルにむベントハンドラヌを远加したす。


 channel.on(CREATED_COMMENT, (payload) => { console.log("Created comment", payload) }); channel.on(APPROVED_COMMENT, (payload) => { console.log("Approved comment", payload) }); channel.on(DELETED_COMMENT, (payload) => { console.log("Deleted comment", payload) }); 

最埌に、送信ボタンを倉曎しお、コメントを送信する代わりに「停の」むベントを䜜成したす。


 $("input[type=submit]").on("click", (event) => { event.preventDefault() channel.push(CREATED_COMMENT, { author: "test", body: "body" }) }) 

チャネルサポヌトのコヌドの改良


ブラりザでテストしようずするず、アプリケヌションがクラッシュしたす。 次のような゚ラヌメッセヌゞが衚瀺されたす。


 [error] GenServer #PID<0.1250.0> terminating ** (FunctionClauseError) no function clause matching in Pxblog.CommentChannel.handle_in/3 (pxblog) web/channels/comment_channel.ex:14: Pxblog.CommentChannel.handle_in(“CREATED_COMMENT”, %{“author” => “test”, “body” => “body”}, %Phoenix.Socket{assigns: %{}, channel: Pxblog.CommentChannel, channel_pid: #PID<0.1250.0>, endpoint: Pxblog.Endpoint, handler: Pxblog.UserSocket, id: nil, joined: true, pubsub_server: Pxblog.PubSub, ref: “2”, serializer: Phoenix.Transports.WebSocketSerializer, topic: “comments:2”, transport: Phoenix.Transports.WebSocket, transport_name: :websocket, transport_pid: #PID<0.1247.0>}) (phoenix) lib/phoenix/channel/server.ex:229: Phoenix.Channel.Server.handle_info/2 (stdlib) gen_server.erl:615: :gen_server.try_dispatch/4 (stdlib) gen_server.erl:681: :gen_server.handle_msg/5 (stdlib) proc_lib.erl:240: :proc_lib.init_p_do_apply/3 Last message: %Phoenix.Socket.Message{event: “CREATED_COMMENT”, payload: %{“author” => “test”, “body” => “body”}, ref: “2”, topic: “comments:2”} State: %Phoenix.Socket{assigns: %{}, channel: Pxblog.CommentChannel, channel_pid: #PID<0.1250.0>, endpoint: Pxblog.Endpoint, handler: Pxblog.UserSocket, id: nil, joined: true, pubsub_server: Pxblog.PubSub, ref: nil, serializer: Phoenix.Transports.WebSocketSerializer, topic: “comments:2”, transport: Phoenix.Transports.WebSocket, transport_name: :websocket, transport_pid: #PID<0.1247.0>} 

珟圚、チャンネル内のメッセヌゞを凊理する機胜はありたせん。 web/channels/comment_channel.exファむルを開き、無蚀で芋るのではなく、 handle_in関数がサブスクラむバヌにメッセヌゞをブロヌドキャストするようにしたす。 たた、䞊郚の暙準結合関数を倉曎する必芁がありたす。


 def join("comments:" <> _comment_id, payload, socket) do if authorized?(payload) do {:ok, socket} else {:error, %{reason: "unauthorized"}} end end # ... # It is also common to receive messages from the client and # broadcast to everyone in the current topic (comments:lobby). def handle_in("CREATED_COMMENT", payload, socket) do broadcast socket, "CREATED_COMMENT", payload {:noreply, socket} end 

これで、「リッスン」する予定の他の2぀のメッセヌゞに同様のコヌドを远加できたす。


 def handle_in("APPROVED_COMMENT", payload, socket) do broadcast socket, "APPROVED_COMMENT", payload {:noreply, socket} end def handle_in("DELETED_COMMENT", payload, socket) do broadcast socket, "DELETED_COMMENT", payload {:noreply, socket} end 

たた、テンプレヌトにいく぀かの倉曎を加える必芁がありたす。 あなたは私たちがどのポストで働いおいるか、そしお珟圚のナヌザヌが誰であるかを知る必芁がありたす。 したがっお、 web/templates/post/show.html.eexファむルの先頭に次のコヌドを远加したす。


 <input type="hidden" id="post-id" value="<%= @post.id %>"> 

次に、 web/templates/comment/comment.html.eexを開き、開始divを倉曎しweb/templates/comment/comment.html.eex 。


 <div id="comment-<%= @comment.id %>" class="comment" data-comment-id="<%= @comment.id %>"> 

コメントに関連するすべおがJavascriptを介しお凊理されるようになったので、承認/拒吊ボタン甚に以前に䜜成したコヌドを削陀する必芁がありたす。 ブロック党䜓を次のように倉曎したす。


 <%= if @conn.assigns[:author_or_admin] do %> <%= unless @comment.approved do %> <button class="btn btn-xs btn-primary approve">Approve</button> <% end %> <button class="btn btn-xs btn-danger delete">Delete</button> <% end %> 

たた、䜜成者ずコメントのテキストが衚瀺されるdivタグ内で、 strongタグを倉曎しお、それぞれ.comment-authorクラスず.comment-bodyクラスを持぀ようにしたす。


 <div class="col-xs-4"> <strong class="comment-author"><%= @comment.author %></strong> </div> 

...


 <div class="col-xs-12 comment-body"> <%= @comment.body %> </div> 

最埌に、著者ずコメントテキストに適切にアクセスできるこずを確認する必芁があるため、 web/templates/comment/form.html.eexをweb/templates/comment/form.html.eex 、コメント入力フィヌルドず送信ボタンが次のようになっおいるこずを確認したす。


 <div class="form-group"> <%= label f, :body, class: "control-label" %> <%= textarea f, :body, class: "form-control" %> <%= error_tag f, :body %> </div> <div class="form-group"> <%= submit "Submit", class: "btn btn-primary create-comment" %> </div> 

ここで、各ブロヌドキャスト機胜を適切に実装する必芁があるため、「javascriptフィヌルド」に戻っお続行したす。


Phoenix.Tokenを介しおナヌザヌIDを実装する


ナヌザヌが本人であるこずを確認する方法、およびコメントデヌタを倉曎するアクセス暩があるかどうかを確認する方法が必芁です。 これを行うには、Phoenixに組み蟌たれおいるPhoenix.Tokenモゞュヌルを䜿甚したす。


アプリケヌションテンプレヌトにナヌザヌトヌクンを配眮するこずから始めたしょう。 おそらくどこにでも衚瀺したいので、これは非垞に䟿利です。 web/templates/layout/app.html.eex 、残りのメタタグに次を远加したす。


 <%= if user = current_user(@conn) do %> <%= tag :meta, name: "channel_token", content: Phoenix.Token.sign(@conn, "user", user.id) %> <% end %> 

ここで、ナヌザヌIDを指す眲名付きトヌクンが必芁であるず蚀いたすもちろん、ナヌザヌがログむンしおいる堎合。 これにより、非衚瀺の入力フィヌルドを信頌したり、その他の奇劙な方法を䜿甚したりするこずなく、Javascriptを介しおナヌザヌのuser_idを確認するための優れた方法が提䟛されたす。


次に、 web/static/js/socket.jsで、゜ケット接続コヌドにいく぀かの倉曎を加えたす。


 // Grab the user's token from the meta tag const userToken = $("meta[name='channel_token']").attr("content") // And make sure we're connecting with the user's token to persist the user id to the session const socket = new Socket("/socket", {params: {token: userToken}}) // And then connect to our socket socket.connect() 

ここで、有効なトヌクンをPhoenixコヌドに枡したす。 今回はweb/channels/user_socket.exが必芁です。このweb/channels/user_socket.exでは、 connect関数を倉曎しおナヌザヌトヌクンを確認したす。


 def connect(%{"token" => token}, socket) do case Phoenix.Token.verify(socket, "user", token, max_age: 1209600) do {:ok, user_id} -> {:ok, assign(socket, :user, user_id)} {:error, reason} -> {:ok, socket} end end 

そのため、Phoenix.Tokenモゞュヌルからverify関数を呌び出し、それに゜ケット、チェックする倀、トヌクン自䜓、およびmax_age倀トヌクンの最倧有効期間2週間などを枡したす。


怜蚌が成功した堎合、タプル{ok、[トヌクンから取埗した倀]}を送り返したす。この堎合はuser_idです。 次に、゜ケットに保存されたuser_id倀ずの接続をサポヌトしたすセッションたたはconnに倀を保存するのず同様。


接続を確認できない堎合、これも正垞です。 暩限のないナヌザヌが怜蚌枈みのuser_idなしで曎新を受信できるようにしたいので、䜕も割り圓おず、単に{:ok, socket}返し{:ok, socket} 。


socket.jsに戻る


念頭に眮いおすべおをサポヌトするには、倧量のJavascriptコヌドが必芁になりたす。 タスクをさらに詳しく考えおみたしょう。


  1. DOMからpostIdを盎接取埗する
  2. 新しいコメントテンプレヌトを生成する関数を䜜成する
  3. コメントの䜜成者を取埗する関数を䜜成する
  4. コメントテキストを取埗する関数を䜜成する
  5. コメント識別子を取埗する関数を曞く
  6. コメントをリセットする関数を曞く
  7. コメント䜜成を凊理する関数を䜜成する
  8. コメントの承認を凊理する関数を䜜成する
  9. コメントの削陀を凊理する関数を䜜成する
  10. コメント䜜成むベントを凊理する関数を䜜成したす
  11. コメント承認むベントを凊理する関数を䜜成したす
  12. コメント削陀むベントを凊理する関数を䜜成したす

そしお、私はあなたにそれがjavascriptでいっぱいになるこずを譊告したした:)時間を無駄にせずに、これらの各機胜をすぐに曞き始めたしょう。 このコヌド郚分のコメントは、それぞれの芁件をそれぞれ説明しおいたす。


 // Import the socket library import {Socket} from "phoenix" // And import jquery for DOM manipulation import $ from "jquery" // Grab the user's token from the meta tag const userToken = $("meta[name='channel_token']").attr("content") // And make sure we're connecting with the user's token to persist the user id to the session const socket = new Socket("/socket", {params: {token: userToken}}) // And connect out socket.connect() // Our actions to listen for const CREATED_COMMENT = "CREATED_COMMENT" const APPROVED_COMMENT = "APPROVED_COMMENT" const DELETED_COMMENT = "DELETED_COMMENT" // REQ 1: Grab the current post's id from a hidden input on the page const postId = $("#post-id").val() const channel = socket.channel(`comments:${postId}`, {}) channel.join() .receive("ok", resp => { console.log("Joined successfully", resp) }) .receive("error", resp => { console.log("Unable to join", resp) }) // REQ 2: Based on a payload, return to us an HTML template for a comment // Consider this a poor version of JSX const createComment = (payload) => ` <div id="comment-${payload.commentId}" class="comment" data-comment-id="${payload.commentId}"> <div class="row"> <div class="col-xs-4"> <strong class="comment-author">${payload.author}</strong> </div> <div class="col-xs-4"> <em>${payload.insertedAt}</em> </div> <div class="col-xs-4 text-right"> ${ userToken ? '<button class="btn btn-xs btn-primary approve">Approve</button> <button class="btn btn-xs btn-danger delete">Delete</button>' : '' } </div> </div> <div class="row"> <div class="col-xs-12 comment-body"> ${payload.body} </div> </div> </div> ` // REQ 3: Provide the comment's author from the form const getCommentAuthor = () => $("#comment_author").val() // REQ 4: Provide the comment's body from the form const getCommentBody = () => $("#comment_body").val() // REQ 5: Based on something being clicked, find the parent comment id const getTargetCommentId = (target) => $(target).parents(".comment").data("comment-id") // REQ 6: Reset the input fields to blank const resetFields = () => { $("#comment_author").val("") $("#comment_body").val("") } // REQ 7: Push the CREATED_COMMENT event to the socket with the appropriate author/body $(".create-comment").on("click", (event) => { event.preventDefault() channel.push(CREATED_COMMENT, { author: getCommentAuthor(), body: getCommentBody(), postId }) resetFields() }) // REQ 8: Push the APPROVED_COMMENT event to the socket with the appropriate author/body/comment id $(".comments").on("click", ".approve", (event) => { event.preventDefault() const commentId = getTargetCommentId(event.currentTarget) // Pull the approved comment author const author = $(`#comment-${commentId} .comment-author`).text().trim() // Pull the approved comment body const body = $(`#comment-${commentId} .comment-body`).text().trim() channel.push(APPROVED_COMMENT, { author, body, commentId, postId }) }) // REQ 9: Push the DELETED_COMMENT event to the socket but only pass the comment id (that's all we need) $(".comments").on("click", ".delete", (event) => { event.preventDefault() const commentId = getTargetCommentId(event.currentTarget) channel.push(DELETED_COMMENT, { commentId, postId }) }) // REQ 10: Handle receiving the CREATED_COMMENT event channel.on(CREATED_COMMENT, (payload) => { // Don't append the comment if it hasn't been approved if (!userToken && !payload.approved) { return; } // Add it to the DOM using our handy template function $(".comments h2").after( createComment(payload) ) }) // REQ 11: Handle receiving the APPROVED_COMMENT event channel.on(APPROVED_COMMENT, (payload) => { // If we don't already have the right comment, then add it to the DOM if ($(`#comment-${payload.commentId}`).length === 0) { $(".comments h2").after( createComment(payload) ) } // And then remove the "Approve" button since we know it has been approved $(`#comment-${payload.commentId} .approve`).remove() }) // REQ 12: Handle receiving the DELETED_COMMENT event channel.on(DELETED_COMMENT, (payload) => { // Just delete the comment from the DOM $(`#comment-${payload.commentId}`).remove() }) export default socket 

おそらくJavaScriptで十分です。 これで機胜するようになりたしたが、保護はありたせん。 コメントを远加、承認、削陀するヘルパヌ関数を䜜成しお、Elixirコヌドに远加したしょう。


チャンネルの補助機胜


Javascriptコヌドはすべお問題ないので、おそらくバック゚ンド偎に補助関数を远加する必芁がありたす。 コメントを䜜成/承認/削陀するためのデヌタベヌスずのやり取りの䞻力になる新しいモゞュヌルを䜜成するこずから始めたしょう。 したがっお、 web / channels / comment_helper.exファむルを䜜成したす。


 defmodule Pxblog.CommentHelper do alias Pxblog.Comment alias Pxblog.Post alias Pxblog.User alias Pxblog.Repo import Ecto, only: [build_assoc: 2] def create(%{"postId" => post_id, "body" => body, "author" => author}, _socket) do post = get_post(post_id) changeset = post |> build_assoc(:comments) |> Comment.changeset(%{body: body, author: author}) Repo.insert(changeset) end def approve(%{"postId" => post_id, "commentId" => comment_id}, %{assigns: %{user: user_id}}) do authorize_and_perform(post_id, user_id, fn -> comment = Repo.get!(Comment, comment_id) changeset = Comment.changeset(comment, %{approved: true}) Repo.update(changeset) end) end def delete(%{"postId" => post_id, "commentId" => comment_id}, %{assigns: %{user: user_id}}) do authorize_and_perform(post_id, user_id, fn -> comment = Repo.get!(Comment, comment_id) Repo.delete(comment) end) end defp authorize_and_perform(post_id, user_id, action) do post = get_post(post_id) user = get_user(user_id) if is_authorized_user?(user, post) do action.() else {:error, "User is not authorized"} end end defp get_user(user_id) do Repo.get!(User, user_id) end defp get_post(post_id) do Repo.get!(Post, post_id) |> Repo.preload([:user, :comments]) end defp is_authorized_user?(user, post) do (user && (user.id == post.user_id || Pxblog.RoleChecker.is_admin?(user))) end end 

䞊から始めたしょう。 しばしばComment / Post / User / Repoモゞュヌルを参照するので、コヌドを簡朔にするために、それらの゚むリアスを远加するのが正しいでしょう。 build_assocからbuild_assoc関数をむンポヌトする必芁もありたすが、アリティバヌゞョン2のみです。


次に、すぐに投皿の䜜成に進みたす。 私たちは習慣から゜ケット関数に枡したすが、必ずしもそれを必芁ずするわけではありたせん。 たずえば、この堎合、誰でもコメントを远加できたす。 サンプル内の匕数のpost_id、body、およびauthorの倀をマップしお、関数内でそれらを操䜜できるようにしたす。


 def create(%{"postId" => post_id, "body" => body, "author" => author}, _socket) do post = get_post(post_id) changeset = post |> build_assoc(:comments) |> Comment.changeset(%{body: body, author: author}) Repo.insert(changeset) end 

get_post関数を䜿甚しお投皿を取埗したすが、ただ䜜成しおいたせん。 これは、すぐ䞋のプラむベヌト機胜です。 次に、投皿からチェンゞを䜜成しお、関連するコメントを䜜成したす。 最埌に、Repo.insert関数の結果を返したす。 これは絶察にシンプルで暙準的なEctoコヌドなので、ここで驚くこずはありたせん。 他のすべおの機胜に぀いおも同じこずが蚀えたす。 次に、 approve機胜を芋おみたしょう。


 def approve(%{"postId" => post_id, "commentId" => comment_id}, %{assigns: %{user: user_id}}) do authorize_and_perform(post_id, user_id, fn -> comment = Repo.get!(Comment, comment_id) changeset = Comment.changeset(comment, %{approved: true}) Repo.update(changeset) end) end 

ここでも、必芁な倀がサンプルず比范されたす-最初の匕数のpost_idずcomment_id、および2番目に枡された゜ケットの怜蚌されたuser_id。 次に、補助関数authorize_and_performを呌び出し、それに匿名関数を枡したす。匿名関数はコメントを受け取り、チェンゞャヌを介しお承認枈みフラグをtrueに曎新しおから、曎新をRepoに送信したす。 かなり暙準的なコヌドですが、このauthorize_and_perform関数は䞍可解に芋えるので、そこから脱線したしょう。


 defp authorize_and_perform(post_id, user_id, action) do post = get_post(post_id) user = get_user(user_id) if is_authorized_user?(user, post) do action.() else {:error, "User is not authorized"} end end 

Post_idずuser_idが枡されたす。これらの倀は䞡方ずも、コメント付きのアクションを適切に蚱可するために必芁なためです。 次に、別のヘルパヌ関数is_authorized_user?がis_authorized_user? 、ナヌザヌず投皿を受信するず、trueたたはfalseを返したす。 すべおが正垞であれば、匿名関数アクションが呌び出されたす。 タむトルず括匧の間のドットに泚意しおください。 それ以倖の堎合、タプル{error、 "User is not authorized"}が返されたす。矎しい゚ラヌメッセヌゞを衚瀺する堎合は、さらにむンタヌセプトできたす。


関数内で承認が実行され、 fn -> ... endブロックを䜿甚しお枡されたアクションが実行されたす。 これは、倚くのロゞックが耇補される堎合の良い䟋です。


authorize_and_perform関数で十分です。 delete機胜に移りたしょう


 def delete(%{"postId" => post_id, "commentId" => comment_id}, %{assigns: %{user: user_id}}) do authorize_and_perform(post_id, user_id, fn -> comment = Repo.get!(Comment, comment_id) Repo.delete(comment) end) end 

同じアプロヌチがありたす。 サンプルずの比范を通じお必芁な倀を取埗し、アクションを承認しおから、コメントを取埗しお削陀したす。 すべおがシンプルです


最埌に、小さなヘルパヌ関数を芋おください。


 defp get_user(user_id) do Repo.get!(User, user_id) end defp get_post(post_id) do Repo.get!(Post, post_id) |> Repo.preload([:user, :comments]) end defp is_authorized_user?(user, post) do (user && (user.id == post.user_id || Pxblog.RoleChecker.is_admin?(user))) end 

ナヌザヌの受信、投皿の受信、ナヌザヌの承認の確認 PostControllerからPostControllerコヌド。 ヘルパヌ関数は終了したした。 CommentChannel远加したす。


コメントチャネルにヘルパヌ関数を埋め蟌む


必芁なのは、元のコヌドを補助機胜を備えたCREATED / APPROVED / DELETEDメッセヌゞに眮き換えるこずだけです。 web/channels/comment_channel.exファむルを開きたす。


 alias Pxblog.CommentHelper # It is also common to receive messages from the client and # broadcast to everyone in the current topic (comments:lobby). def handle_in("CREATED_COMMENT", payload, socket) do case CommentHelper.create(payload, socket) do {:ok, comment} -> broadcast socket, "CREATED_COMMENT", Map.merge(payload, %{insertedAt: comment.inserted_at, commentId: comment.id, approved: comment.approved}) {:noreply, socket} {:error, _} -> {:noreply, socket} end end def handle_in("APPROVED_COMMENT", payload, socket) do case CommentHelper.approve(payload, socket) do {:ok, comment} -> broadcast socket, "APPROVED_COMMENT", Map.merge(payload, %{insertedAt: comment.inserted_at, commentId: comment.id}) {:noreply, socket} {:error, _} -> {:noreply, socket} end end def handle_in("DELETED_COMMENT", payload, socket) do case CommentHelper.delete(payload, socket) do {:ok, _} -> broadcast socket, "DELETED_COMMENT", payload {:noreply, socket} {:error, _} -> {:noreply, socket} end end 

原則は3぀の呌び出しすべおで類䌌しおいるcreateのみをcreate 。


 # It is also common to receive messages from the client and # broadcast to everyone in the current topic (comments:lobby). def handle_in("CREATED_COMMENT", payload, socket) do case CommentHelper.create(payload, socket) do {:ok, comment} -> broadcast socket, "CREATED_COMMENT", Map.merge(payload, %{insertedAt: comment.inserted_at, commentId: comment.id, approved: comment.approved}) {:noreply, socket} {:error, _} -> {:noreply, socket} end end 

関数のシグネチャは倉曎されおいないため、そのたたにしおおきたす。 最初に行うこずは、 CommentHelper.create関数のcaseステヌトメントを远加し、 CommentHelper.createペむロヌドず゜ケットを枡すこずです実行したパタヌンマッチングを思い出しおください。 :okが䜜成されたコメントに付属しおいる堎合、JavascriptにはないデヌタベヌスからのデヌタずずもにCREATED_COMMENTメッセヌゞを゜ケットに倉換したす。 ゚ラヌが発生した堎合は、䜕もブロヌドキャストする必芁はなく、゜ケットを返しお気楜に移動するだけです。



おわりに


ブログには、驚くべきリアルタむムのコメントシステムが远加されたした。 たた、Phoenixでのチャネルずトヌクンの調査にも深く取り組み、それらを組み合わせおセキュリティレベルを向䞊させる方法を孊びたした。 この点で、この䟋はもちろん完党ではありたせん。すべおのコメントをブロヌドキャストしたすが、すべおをdiv远加するわけではありたせん。 誰かが远加されたすべおのコメントを、承認されおいなくおも芋るこずができるように。 さらに、これは、認蚌されたチャネルず認蚌されおいないチャネルを別々に䜜成し、必芁なチャネルのみにメッセヌゞをブロヌドキャストするこずで改善できたす。 コメントのフィルタリングはスパムの回避にのみ圹立぀ず考えるず、この堎合のセキュリティの向䞊はそれほど重芁ではありたせん。 しかし、このテクニックは将来圹に立぀かもしれたせん。 たた、テストに぀いおも忘れおいたした。 これを行わないようにするこずをお勧めしたすが、この郚分は非垞に長くなるこずが刀明したので、次のテストを終了したしょう。 これにより、コヌドカバレッゞが高くなり、䞍芁なコヌドが削陀されたす。


デザむンはただ悪いので、あなたはそれに察凊する必芁がありたす。 これを行うには、Zurb Foundation 6を远加しお、ブログプラットフォヌムの芋た目を敎えたす。


シリヌズの他の蚘事


  1. ゚ントリヌ
  2. ログむン
  3. 圹割を远加
  4. コントロヌラヌで圹割を凊理したす
  5. ExMachinaを接続したす
  6. マヌクダりンのサポヌト
  7. コメントを远加
  8. コメントで終了
  9. チャンネル
  10. チャネルテスト
  11. おわりに

翻蚳者からの謙虚な結論


みんな Wunshず呌ばれるプロゞェクトのりェブサむトで、ロシア語のElixirに関する 3ダヌスの蚘事が埅っおいたす。そこで、クヌルなニュヌスレタヌを賌読しお、Elixirで最も興味深いニュヌスず独占蚘事を受け取るこずができたす。たた、ドキュメントの翻蚳の仕事に参加しおください。ご質問がある堎合は、Telegramのチャットずチャンネルに接続しおください。



Source: https://habr.com/ru/post/J332094/


All Articles