btcline04
btcline04

Reputation: 25

Save movie info from API to a list in my database

I have a movie app that searches OMDb API database and populates the search results in my app. I've taken those search results and iterated over them to create movie cards for each result.

In this movie cards, I have a button to 'Add Movie To List', which would allow the user to press it and add it to lists that are already created. The user would be able to specify which list, then add it to the database.

I'm not sure how to get that action to work. Can anyone point me in the right direction?

My search.html.erb page:

<div class="card-deck text-center" id="card-deck"> 
  <% if @movies %>
  <% @movies.each do |m| %>
      <div class="card">
        <img src= "<%= m["Poster"] %>" alt="No poster found." class="card-img-top">

        <div class="card-body">
          <h5 class="card-title"><%= m["Title"] %></h5>
          <p class="card-text"><%= m["Year"] %></p>
        </div>

        <div class="card-footer">
          <div class="btn-top">
            <span class="btn-group">
              <%= button_to "Add Movie To List", class: 'btn btn-primary btn-sm btn-space' %>
            </span>
          </div>
        </div>
      </div>
    <% end %>
  <% end %>
</div>

How would I route that button to a certain controller action, then use the info from the API to create a new instance of a movie, then add it to a list?

My searches_controller.rb:

class SearchesController < ApplicationController

  def index
    @list = List.all.select(:id, :name)
    @movie = Movie.new
  end

  def omdb
    @movie = Movie.new
    @list = List.all.select(:id, :name)
    conn = Faraday.new(:url => 'http://www.omdbapi.com')

    @resp = conn.get do |req|
      req.params['apikey'] = ENV['OMDB_API_KEY']
      req.params['s'] = params[:movie]
    end

    body = JSON.parse(@resp.body)
    if @resp.success?
      @movies = body["Search"]
    else
      @error = "There was a timeout. Please try again."
    end
    render 'index'
  end
end

Routes:

Rails.application.routes.draw do
  resources :lists, :movies
  get '/search', to: 'searches#index'
  post '/search', to: 'searches#omdb'
  root 'lists#index'
end

My button on my Search index.html.erb to add movie to list to bring up modal popup:

  <div class="btn-top">
      <button class="btn btn-primary btn-sm add-movie" movie-id="m['imdbID']" type="button" data-toggle="modal" data-target="#movielistmodal">Add Movie To List</button>
  </div>

And my modal body code:

  <div class="modal-body">
    <%= form_for @movie, method: :post, url: 'add_api_movie_to_list' do |f| %>
      <%= hidden_field_tag :movie_id, { id: "movie-id" } %>
      <% @list.each do |list| %>
        <%= f.label :name, list.name %>
        <%= f.radio_button :list_ids, list.id %>
      <% end %>
      <%= f.submit class: 'btn btn-primary btn-space btn-sm' %>
    <% end %>
  </div>
  <div class="modal-footer">
    <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
  </div>

Add API movie to database controller action:

  def add_api_movie_to_list
    conn = Faraday.new(:url => 'http://www.omdbapi.com')

    @resp = conn.get do |req|
      req.params['apikey'] = ENV['OMDB_API_KEY']
      req.params['i'] = params[:movie_id]
    end

    body = JSON.parse(@resp.body)
    movie_params = { title: body["Title"], genre: body["Genre"], year: body["Year"], poster: body["Poster"], plot: body["Plot"], director: body["Director"] }
    movie_params.merge(params[:movie])
    @movie = Movie.new(movie_params)
    if @movie.save
      redirect_to movie_path(@movie)
    else
      flash[:error] = "Unable to save movie. Try again."
      render 'search'
    end
  end

Upvotes: 0

Views: 742

Answers (2)

Zalom
Zalom

Reputation: 754

Since the movies are saved to your database, you can set up a route for a custom action on your movies, in combination with link_to.

An example if you have resources :movies defined you can do something like this ->

In routes.rb

resources :movies do
  post '/add_to_list', action: 'add_to_list'
end

In movies_controller.rb ->

def add_to_list
  @movie = Movie.find(params[:movie_id]
  @movie.add_to_list
end

add_to_list can be defined in your Movie model to whatever you need to do with it.

Inside your view (search.html.erb) instead of button_to, you can use link_to like this ->

<%= link_to 'Add Movie To List', movie_add_to_list_path(m), class: "btn btn-primary btn-sm btn-space", method: :post %>

This is the quick solution on your question: "How would I route that button to a certain controller action".

For this part -> "Then use the info from the API to create a new instance of a movie, then add it to a list?" I will guess a few things:

  • You have some service object that is issuing queries to OMDB API through some gem
  • You save enough information in your Movie model so you can issue another query to OMDB API to get the information on a specific movie (that user will click on in your list)

If this is the case, then you can create a model concern that will contain some logic to interact with OMDB service object

For example, that custom method I put above add_to_list can be defined in that concern.

If you need more info on how to do that, you can contact me.

Upvotes: 0

Gagan Gupta
Gagan Gupta

Reputation: 1227

Things you've to do to achieve this functionality.

  1. Add a bootstrap modal which will be containing the list of movies.
  2. Bring the movie_lists in a variable as @movie_list in def search so that you can access it on the search.html.erb
  3. You'll have to use another OMDb API to get the details of the particular movie you'll saving in the database.
  4. Creating a method which is going to hit a controller method to add movies in the list.
  5. Some JQuery.

I'm assuming you already have 2 models with similar relationships:

Class MovieList
  has_many :movies
end

Class Movie
  belongs_to :movie_list
end

in your movies_controller or any other controller where you've defined def search method.

add this line.

def search
  #your pre existing code
  #add the following line
  @movie_list = MovieList.all.select(:id, :name)
end

in search.html.erb

add html code for modal. in modal body add code for a form where you'll be storing the movie_id in the hidden field and checkboxes for selection of list.

<%= form_for :movie, method: :post, url: 'path_of_the_method' do |f| %>
  <%= hidden_field_tag :movie_id, { id: "movie-id" } %>
  <% @movies_list.each do |item| %>
    <%= f.radio_button :movie_list_id, item.id %>
  <% end %>
  <%= f.submit %>
<% end %>

specify this route in the routes.rb

now,

for each movie in search result there's a button associated to it. Let's override it's functionality.

<%= button_to "Add Movie To List", class: 'btn btn-primary btn-sm btn-space add-movie', 'data-movie-id': m['id'] %>

NOTE: I'm guessing you're getting uniq movie_id for each movie.

JQuery:

for making it work you can define it at the end of search.html.erb

<script>
  $('.add-movie').click(function(e){
    e.preventDefault();
    //Let's assume the id of the Modal (popup) is #list-modal
    $('#movie-id').val($(this).data('movie-id')); //id of the hidden field
    $('#list-modal').modal('show');
  });
</script>

now in controller method you've just defined for saving the form value let's assume the method name is create_movie or if you're using resources you can directly write create

def create_movie
  #API to fetch details of the movie id you've passed in `params[:movie_id]`
  #movie = API call
  #select the attributes you'll be saving in the database. eg:
  hash_params = { title: movie['title'], name: movie['name'] }
  hash_params.merge(params[:movie])!
  movie = Movie.new(hash_params)
  if movie.save
    #redirect with success message
  else
    #redirect with failure message
  end
end

I've tried to explain in the best possible manner. :P You can get a hint with it what you'll have to do. In case you still need help, just let me know.

Upvotes: 1

Related Questions