Lookup by slug and id - ruby-on-rails

I currently have the following in my products controller show action:
#product = Spree::Product.active.where(slug: params[:id]).first!
This only works when slug is passed, I want to be able to pass in id or slug and for it to work:
kinda like:
Spree::Product.active.where(slug: params[:id]).first! || Spree::Product.active.where(id: params[:id]).first!
Right now when I type products/foo-bar it works, not when I type products/1
Is there a way to do this? Thank you in advance.

.first!
Raises error when record not found.
Try
Spree::Product.active.find_by(slug: params[:id]) || Spree::Product.active.find_by(id: params[:id])
It'll try second query if first does not return value

The simplest way would be to search for both in a where like so:
Spree::Product.active.where("slug = :parameter OR id = :parameter", parameter: params[:id]).first!
This will find the Spree::Product where either the slug or id is equal to the params[:id].

Related

Problem with selecting elements with the same params

What i do wrong? I want to return every products which pass condition:
def show
products = Product.select{|x| x[:category_id] == params[:id]}
render json: products
end
When i write
def show
products = Product.select{|x| x[:category_id] == 1}
render json: products
end
it works why the first example doesn't work?
I am pretty sure that there is mismatch in data type.
1=='1' #will be always false
1==1 #will be true
'1'=='1' #will be true as well
And also check for nil value from params[:id]
Please make sure to change as follows
def show
products = Product.select{|x| x.category_id == params[:id].to_i}
render json: products
end
OR
The best solution as suggested by #Eyeslandic is to use .where as it will not check for mismatch in data type. And also you don't have to take care of nil value from params[:id].
You should really be using a where to stop sql from loading all your products.
#products = Product.where('category_is = ?', params[:id])
The being said, if you are sticking to rails restful conventions, the fact you have a param called :id that is the category_id suggests you are on the category controller. So maybe consider changing your logic to:
#category = Category.includes(:products).find(params[:id])
you can then access products via
#category.products
or if your not interested in the category too much maybe
#products = Category.includes(:products).find(params[:id])&.products

LIKE statement but skips current post

I'm trying to use the LIKE condition to find similar posts.
Here is my code:
#post = Post.find(params[:id])
#post_gsub = Post.find(params[:id]).name.gsub(/something|something else|something else again/, '')
#related_posts = Post.where("name LIKE '%#{#post_gsub}%'")
But when I pull this into the Rails view, there is currently always one match that shows up, which is the current blog post the user is on. How do I skip that current blog post so that "#related_posts" is only showing unique recommendations and not the post the user is currently on?
You can try chaining the where.not method Considering you're using Rails 3, you can use part of raw SQL:
Post.where('name LIKE ? AND id != ?', "%#{#post_gsub}%", #post.id)
That'll make your query to add a != operator to get every post other than the one with id equal to #post.id.
For rails 3 try
Post.where('name LIKE ? AND id != ?', "%#{#post_gsub}%", #post.id)

Ruby on Rails beginner question : equality

I'm starting to know ROR and I was doing a kind of blog with articles, etc...
I did this code :
def show
id = params[:id]
list = Article.all
is_valid = false
list.all.each do |article|
if article.id == id
#is_valid = true
break
end
end
As you can see, this code just wants to check if the article ID exists or not. So I'm testing equality between id and article.id (which's a model linked to the appropriated table in the database) BUT when I try to use or display #is_valid boolean I saw that article.id == id is FALSE every time, even if article.id = 2 and id = 2. I tried to think about everything that can make this occuring, but I admit I still misunderstand this.
Then I ask you if you know why this is occuring. Of course, an equality like 2 == 2 will change #is_valid to true.
Thank you for your help !
Maybe its because params[:id] it's a string and article.id it's an Integer
(byebug) params
{"controller"=>"admin/my_controller", "action"=>"edit", "id"=>"1"}
And yes it is... "id" is a string "1", so you may try this:
def show
id = params[:id].to_i
list = Article.all
is_valid = false
list.all.each do |article|
if article.id == id
#is_valid = true
break
end
end
end
And maybe could work.
This is the answer to your question,
But if you want to learn a little more about Activerecord you can do this
Article.exists?(params[:id])
and that will do what you are trying to do just with a query against db.
and if you want to get just a simple article
record = Article.find_by(id: params[:id]) #return nil when not exist
if record # if nil will threat like false on ruby
#my code when exist
else
#my code when not exist
end
will work (you also can use find but find will throw an exception ActiveRecord::RecordNotFound when not exists so you have to catch that exception.
Activerecord has many ways to check this you dont need to do it by hand.
def show
#article = Article.find(params[:id])
end
This will create a database query which returns a single row. .find raises a ActiveRecord::NotFound exception if the record is not found. Rails catches this error and shows a 404 page. Article.find_by(id: params[:id]) is the "safe" alternative that does not raise.
Your code is problematic since list = Article.all will load all the records out of the database which is slow and will exhaust the memory on the server if you have enough articles. Its the least effective way possible to solve the task.
If you want to just test for existence use .exists? or .any?. This creates a COUNT query instead of selecting the rows.
Article.where(title: 'Hello World').exists?

Rails OR condition inside where with same input

I have the following active record query:
User.where(["id = ? OR token = ?", params[:id], params[:id]]).first
Here, params[:id] = 9MrEflgr
PROBLEM
As per logic, params[:id] can be numeric id or alphanumeric token.
I want to get something like User.find_by_id_or_token(params[:id]) in where clause.
Here, since the params[:id] starts with '9', so active record gives me user with id 9 instead of checking for token field. How to avoid this?
As the comment mentioned, you need to check if the params is an integer. This SO question has good suggestions on how to do that (where you can implement is_integer? below).
if params[:id].is_integer?
User.where(["id = ? OR token = ?", params[:id], params[:id]]).first
else
User.where(["token = ?", params[:id]]).first
end

Rails 3 - controller Conditional?

I have a controller that is:
def create
.
.
#project = Project.find(params[:project]
#Log = Logs.create(params[:action]).merge(:project_id => #project.id))
...
end
The issue hereis that sometimes when DEF CREATE, I'll have a project and I want to record that. Other times I won't and that's perfectly fine, I still want to create the #Log
What's the right way in Rails to handle this. I'll want to make sure:
The first line #project doesn't error.
Also that the #log doesn't error but inserts '' or NIL whatever is rails standard.
Thank you
Try this:
def create
#project = (project_id = params[:project_id]).blank? ? nil :
Project.find(project_id)
#Log = Logs.create(params[:action].merge(#project.nil? ? {} :
{:project_id => #project.id}))
end
If the input has a project_id, then above solution will throw an error if a project with the given id is not found. If you don't want this behavior use find_by_id instead of find.
Create a protected method near the bottom of your controller like so:
protected
def project_id
# return the cached value if we've already figured it out
return #project_id if defined?(#project_id)
# get the project by id in a failsafe way
project = params[:project_id] ? Project.find_by_id(params[:project_id]) : nil
# return nil if the project is nil, so we don't try to call "id" on it
return #project_id = nil if project.nil?
# cache and return the project id
#project_id = project.id
end
Notice I changed the parameter to :project_id instead of just project. This fits the rails convention better. Now in your create action, and all other actions, you can call it safely:
#Log = Logs.create(params[:action]).merge(:project_id => project_id))
I hope this helps!

Resources