Completing the votes context for the image uploads

We have almost everything in place, except for the final code necessary to get our image uploads into the context. We've started the work in the create_poll_with_options/3 function of lib/vocial/votes/votes.ex, but now we need to expand that work significantly and add to the function's signature. We'll want to pass in the image_data hash that we constructed as part of the modified template that included the caption and the alt_text for our image, and we'll also want to add a new line to our with statement that saves the uploaded file into the database! At the top of lib/vocial/votes/votes.ex, you'll want to add an alias statement for the image module:

alias Vocial.Votes.Image

Then we'll continue by modifying the function to take in a new argument that is the image-specific data, leaving us with a three-argument create_poll_with_options function:

def create_poll_with_options(poll_attrs, options, image_data) do

Next, we'll want to go into the with statement, modify the upload_file call, and add a new line to it as well, since now we actually care about the returned filename, and will need to persist that to the database!

{:ok, filename} <- upload_file(poll_attrs, poll),
{:ok, _upload} <- save_upload(poll, image_data, filename)

We don't have a save_upload/3 function defined yet but don't worry, we're getting there! The save_upload function will need data from the poll (this will give us the poll_id and user_id data), the image_data map that contains the caption and alt text, and finally the actual filename that we decided on for the user! Since this is a pretty custom data structure we're building out that will essentially be our attributes, we'll start constructing the attrs map that we'll use to create the image struct. We'll also need to catch the scenario where we get a nil filename since that will happen if the user does not upload any images:

defp save_upload(_poll, _image_data, nil), do: {:ok, nil}
defp save_upload(poll, %{"caption" => caption, "alt_text" => alt_text}, filename) do
attrs = %{
url: "/uploads/#{filename}",
alt: alt_text,
caption: caption,
poll_id: poll.id,
user_id: poll.user_id
}
# ...
end

Again, remember the structure we wrote in our with statement? That's why, if we have no upload, we just carry on as if all is well since it is perfectly valid for us to not want to upload an image to our poll! The rest of the code to make this work is pretty standard stuff that you've seen a million times before at this point, so we won't go into excruciating detail on it:

%Image{}
|> Image.changeset(attrs)
|> Repo.insert()

This will successfully insert the poll into the database! We also need to adjust the get_poll and list_polls functions to include :image as part of their preloads:

  def get_poll(id), do: Repo.get!(Poll, id) |> Repo.preload([:options, :image])

def list_polls do
Repo.all(Poll) |> Repo.preload([:options, :image])
end

And there we are! The code to create, upload, and display our images is nearly complete! We just need to modify the code template for showing the poll in lib/vocial_web/templates/vote/_poll.html.eex to include a conditional div to display the associated image!

<%= if @poll.image do %>
<div class="image-display">
<img src="<%= @poll.image.url %>" alt="<%= @poll.image.alt %>" style="width: 350px;" />
<br />
<small><%= @poll.image.caption %></small>
</div>
<% end %>

Voila! We are done! We should be able to navigate back to the New Poll page, fill in the new fields, and see the resulting poll with the image! Let's fill out the poll information, as shown in the following screenshot:

We should expect to see our completed poll with an image in its display.

We're done! We can now move on to the next missing feature in our application: Restricting people from voting more than once!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset