How to pass parameters to the controller with the proper key - ruby-on-rails

I'm trying to figure out how to get the store id on the controller side to delete the store.
Currently with this code my params on the controller side sends the store.id(1) as the value to the key format. I need to retrieve this by store_id: instead.
{..."controller"=>"home", "action"=>"delete_store", "format"=>"1"}
What I need:
{..."controller"=>"home", "action"=>"delete_store", "store_id"=>"1"}
HTML/ERB:
<h4>Your Stores:</h4>
<% #my_stores.each do |store| %>
<p><%= store.name %><%= link_to "X", delete_store_path(store.id), method: :delete %></p>
<% end %>
Controller:
class HomeController < ApplicationController
...
def delete_store
# With current code, I would have to do
# current_user.stores.where(store_id: params[:format] )
# But to make it proper I need this
# current_user.stores.where(store_id: params[:store_id] )
end
end

You can get it in the params with a hidden_field_tag:
<%= hidden_field_tag :store_id, store.id %>
This should allow you to do a lookup like:
params[:store_id]
Here is a link to the apidock for those wanting more info on hidden_field_tag.
http://apidock.com/rails/ActionView/Helpers/FormTagHelper/hidden_field_tag

Related

In Rails, how can I create a button that saves an entry from an external api into my database?

I have an Rails api that consumes an external design search api using the HTTParty gem. With the data that is returned to my view I'd like to be able to save selected entries to my database. Kind of like a bookmark function. Is there anyway of having a button next to each search result item that would achieve this? Any help would really be appreciated. My current code is below.
designs controller:
class DesignsController < ApplicationController
def index
#search = Api.new.find(params[:q])['results']
end
end
Class method:
class Api
include HTTParty
base_uri "http://search.example.com/searchv2"
attr_accessor :name, :limit, :offset
# Find a particular design, based on its name
def find(name)
self.class.get("/designs", query: { q: name }).parsed_response
end
end
View:
<h1>Design Search</h1>
<%= form_tag(designs_path, method: :get) do %>
<%= label_tag(:q, "Search for:") %>
<%= text_field_tag(:q) %>
<%= submit_tag("Search") %>
<% end %>
<h2>Search results:</h2>
<% #search.each do |design| %>
<h3><%= design['name'] %></h3>
<h5><%= image_tag design['thumbnail_url'] %></h5>
<% end %>
Design model:
class Design < ApplicationRecord
end
app/views/designs/index.html.erb:
...
<% #search.each do |design| %>
<h3><%= design['name'] %></h3>
<h5><%= image_tag design['thumbnail_url'] %></h5>
<%= button_to 'Save',
designs_path(
design: design.slice('name', 'thumbnail_url') # etc...
),
method: :post,
data: { disable_with: 'Loading...' }
%>
<% end %>
app/config/routes.rb:
resources :designs, only: [:index, :create]
app/controllers/designs_controller.rb:
class DesignsController < ApplicationController
# ...
def create
#design = Design.new(design_params)
#design.save!
# just an example...
redirect_to designs_path, success: 'Design has been saved!'
end
private
def design_params
params.require(:design).permit(:name, :thumbnail_url) # etc...
end
end
TODOs:
your Design model needs to have attributes that you want to be saved (i.e: :name? and :thumbnail_url?). My assumption was that you already have created/migrated these attributes; if not yet, then please do add them.
because there's still a vulnerability in my code above in that any user can modify the <button>'s <form action='...'> (i.e. they can modify the "name" of the design as they wish when it gets Saved), then my solution above is still not the best solution, though for simplicity and that assuming that this vulnerability is not a problem for you, then the above code already is sufficient.

Rails: How to check which form in a view was submitted

There are 2 forms on one page. I want an if else statement in the controller to use different params and variable values depending on which form was submitted. From doing a google search the best I came across was to have a value on the submit button.
<%= f.submit "Save" :value => "x" %>
If this is a plausible way I cant find how to make an if else statement for checking if the submit value is 'x'.
Something like
if submit.value == 'x'
do something
else
do something else
end
Really not sure. If there is another way of having an if else statement in the controller to catch witch form was submitted by using an id or name or whatever I'm happy to hear it.
The value of the submit button can be accessed with the help of params[:commit], so you can use it to check which form is submitted.
if params[:commit] == 'x'
do something
else
do something else
end
#Pavan has the direct answer, however if you're evaluating form submissions by their respective submit values, you've got a major issue with your pattern.
Form
Forms should be a way to pass values to your controller, which will then populate the model. You should not have to determine actions based on those values, unless you have different functionality...
#app/views/posts/index.html.erb
<% #posts.each do |post| %>
<%= form_for post do |f| %>
<%= f.text_field :name %>
<%= f.submit %>
<% end %>
<% end %>
The above will create multiple forms, all submitting to the posts#update method:
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def update
#post = Post.find params[:id]
#post.update post_params
end
private
def post_params
params.require(:post).permit(:x, :y, :z)
end
end
The values inside those forms don't matter, nor to which post object they were sent; they will all be evaluated in exactly the same way.
--
Actions
The other way around this is to make separate actions for the different forms:
#config/routes.rb
resources :posts do
patch :update_2, on: :member
end
#app/controllers/posts_controller.rb
class PostsController < ApplicationController
def update_2
#post = Post.find params[:id]
#post.param = "value"
#post.update post_params
end
end
#app/views/posts/show.html.erb
<%= form_for #post, url: posts_update_2_path(#post) do |f| %>
<%= f.submit %>
<% end %>
You could use something like
<%=f.submit "Basic update", name: "basic-update" %>
<%=f.submit "Security update", name: "security-update" %>
and then check in your controller:
if params.has_key? "security-update"
#do something
elsif params.has_key? "basic-update"
#do another thing
end

Accessing post id from show in orders/new view and orders/create

I have checkout using braintree api located at orders/new. However, the price that I want to charge the user is determined based on what the post id is. The user is linked to orders/new from the posts/show if that helps at all.
thanks in advance!
Accessing the :post_id in the create method is a little trickier because there is no link to a create view to send the :post_id with.
orders_controller create method
def create
post=Post.find(params[:post_id])
nonce = params[:payment_method_nonce]
render action: :new and return unless nonce
result = Braintree::Transaction.sale(
amount: post.price,
payment_method_nonce: nonce
)
end
end
orders new view that sends user to the create method
<h2>Purchase Ticket (refresh if fields don't load)</h2>
<p>the price is <%= number_to_currency(#post.price) %></p>
<script src="https://js.braintreegateway.com/v2/braintree.js"></script>
<%= render 'payment/form' %>
When using this updated top section of my form
<%= form_tag orders_path, method: 'post' do %>
<%= hidden_field :post_id, #post.id %>
<div id="dropin"></div>
<input type="submit" value="Pay">
<% end %>
<%= #params %>
You can add the post_id into the link on the post#show view that points to the orders#new action:
<%= link_to 'Purchase', new_order_path(post_id: #post.id) %>
In your orders controller you will also need to allow the post_id to be accepted by the strong params section:
def post_params
params.permit(:post_id)
end
I do not know what is already in you orders_params method so I have given you the minimum that should work from the guides.
Then in the OrdersController action you can grab the post_id from the params:
def new
post_id = post_params[:post_id]
# ...
end
UPDATE: expanding answer to encompass additional create action criteria.
Maybe the most straight forward option is to add a hidden field in the form to store the post_id:
# app/views/payments/_form.html.erb
<%= form_tag orders_path, method: 'post' do %>
<%= hidden_field_tag :post_id, #post.id %>
<div id="dropin"></div>
<input type="submit" value="Pay">
<% end %>
This will mean that the post_id you put into the link_to method argument on the post will be stored in the form and be submitted to the create action, and you can access it in the same way:
class OrdersController < ApplicationController
def new
#post = Post.find(post_params[:post_id])
end
def create
post = Post.find(post_params[:post_id])
render text: post.price # to demonstrate
end
private
def post_params
params.permit(:post_id)
end
end
I have put together a quick demo application to show the working code.

Controller not receiving object id

I am trying to pass the category.id to the shop/index controller using
<% Category.all.each do |category| %>
<div><%= link_to category.name.titleize, shop_index_path :category_id => category.id %></div>
<hr>
<% end %>
Controller:
class ShopController < ApplicationController
def index
#products = Category.find(params[:category_id]).products
end
end
Where a category has many products. But Whenever I click the link I get
Couldn't find Category without an ID
Can anyone see why this is?
EDIT: This is the route I use
get 'shop/index'
try add params:
shop_index_path(params: { category_id: category.id })
You need refine you routes for passing params:
get 'shop/index/:category_id', to: 'shop#index'
And now you can pass category_id to path helper like:
shop_index_path(category.id)
It should be
<%= link_to category.name.titleize, shop_index_path(category.id) %>
Surely you could just use:
shop_index_path(category.id)
And then load:
Category.find(params[:id]).products

Call a controller method automatically when rendering a partial

I have a partial that needs to have some controller logic run before it can render without issue. Is there some way to associate the partial with some controller logic that is run whenever it is rendered?
For example, this is what my current code looks like:
MyDataController:
class MyDataController < ApplicationController
def view
#obj = MyData.find(params[:id])
run_logic_for_partial
end
def some_method_i_dont_know_about
#obj = MyData.find(params[:id])
# Doesn't call run_logic_for_partial
end
def run_logic_for_partial
#important_hash = {}
for item in #obj.internal_array
#important_hash[item] = "Important value"
end
end
end
view.html.erb:
Name: <%= #obj.name %>
Date: <%= #obj.date %>
<%= render :partial => "my_partial" %>
some_method_i_dont_know_about.html.erb:
Name: <%= #obj.name %>
User: <%= #obj.user %>
<%# This will fail because #important_hash isn't initialized %>
<%= render :partial => "my_partial" %>
_my_partial.html.erb:
<% for item in #obj.internal_array %>
<%= item.to_s %>: <%= #important_hash[item] %>
<% end %>
How can I make sure that run_logic_for_partial is called whenever _my_partial.html.erb is rendered, even if the method isn't explicitly called from the controller? If I can't, are there any common patterns used in Rails to deal with these kinds of situations?
You should be using a views helper for this sort of logic. If you generated your resource using rails generate, a helper file for your resource should already be in your app/helpers directory. Otherwise, you can create it yourself:
# app/helpers/my_data.rb
module MyDataHelper
def run_logic_for_partial(obj)
important_hash = {}
for item in obj.internal_array
important_hash[item] = "Important value" // you'll need to modify this keying to suit your purposes
end
important_hash
end
end
Then, in your partial, pass the object you want to operate on to your helper:
# _my_partial.html.erb
<% important_hash = run_logic_for_partial(#obj) %>
<% for item in important_hash %>
<%= item.to_s %>: <%= important_hash[item] %>
<% end %>
Or:
# app/helpers/my_data.rb
module MyDataHelper
def run_logic_for_partial(item)
# Do your logic
"Important value"
end
end
# _my_partial.html.erb
<% for item in #obj.internal_array %>
<%= item.to_s %>: <%= run_logic_for_partial(item) %>
<% end %>
EDIT:
As commented Ian Kennedy points out, this logic can also reasonably be abstracted into a convenience method in your model:
# app/models/obj.rb
def important_hash
hash = {}
for item in internal_array
important_hash[item] = "Important value"
end
hash
end
Then, you'd access the important_hash attribute in the following manner in your partial:
# _my_partial.html.erb
<% for item in #obj.important_hash %>
<%= item.to_s %>: <%= item %>
<% end %>
What you're trying to do runs against the grain of how Rails controllers/views are designed to be used. It would be better to structure things a bit differently. Why not put run_logic_for_partial into a helper, and make it take an argument (rather than implicitly working on #obj)?
To see an example of a view "helper", look here: http://guides.rubyonrails.org/getting_started.html#view-helpers

Resources