Rails search form, two models - ruby-on-rails

I am trying to implement a search field in Rails 4 to find data behind two models. I have two controllers: buildings and rooms, and two models: building and room. A building, naturally, has many rooms and a room belongs to a building, which I have stated in the respective models.
Basically, a user could type either a building or a room name into the search form and it would return a response with details about the building or room. Of course, a building needs to have an address, and a room needs to know in which building it is with the address as well. So I'd need to display different details according to the searched for instance. Both have the same String attribute name, so this could make things easier.
I have no luck in finding a relevant example on how to implement such a search form. This is the working basic search I have at the moment in views\buildings\index.html.erb, which can only search buildings:
<%= form_tag({controller: "buildings", action: "show"}, method: "get", class: "nifty_form") do %>
<%= label_tag(:name, "Search for a building (later also rooms):") %>
<%= text_field_tag(:name) %>
<%= submit_tag("Search") %>
<% end %>
This is the show method in controllers\buildings_controller.rb:
def show
#building = Building.where('lower(name) = ?', params[:name].downcase).first
end
And this is the route it refers to:
get 'buildings/:id' => 'buildings#show'
Any and all help is appreciated!

I would recommend you to add the search code in the index method of buildings controller, there are many things you can do but here is what i will recommend:
def index
#
if params.has_key?(:name)
#buildings = Building.joins(:rooms).
where([
'lower(buildings.name) like ? or lower(rooms.name) like ?',
"%#{params[:name].downcase}%",
"%#{params[:name].downcase}%"
])
else
# your normal code goes here.
end
end
Any other information you need such address ( is that a different model ) can he included in there.
Hope this helps,
PD: if necessary you can render a different view when a search is present inside your if block
render action: 'my_custom_view', status: :ok

Due to time restrictions, I decided to merge these models into one common model, Space. It holds all the information that a building and a room needs. Thanks anyways!

Related

Multiple domains, one rails application

I'm creating a rails 5 application that I would like to be able to be accessed by different URLS. For example, the following two URLS would be powered by the same app;
example.com.au
example.co.nz
Let's say on the homepage of each I wanted to display a list of links to that countries main cities. Each main city will also have it's own page;
example.com.au/sydney
example.co.nz/auckland
They would share the same database.
This seems like it would be a common problem but I've been unable to find a straightforward answer.
Any help is much appreciated.
I'd "register" the URLs in a hash somewhere in your app:
{
"example.com.au" => "AU",
"example.co.nz" => "NZ",
}
Then you can add a method in your controller that looks something like this, to get the country:
def current_country
domain_name = extract_domain_name(request.original_url)
Settings.domain_country_map[domain_name]
end
If you're using a database to save the countries that have_many cities, you'd do a find in that method. And, for the home page, you can show current_country.cities.popular.limit(5) or something like that.
I also assume you wouldn't want example.co.nz/sydney to work. In that case, make sure that you scope it to the country: when the /city_name url is hit, you do a current_country.cities.find_by(slug: params[:city_slug]).
To make the route /city_slug work, you'd need something like this in your routes file:
get '/:city_slug', to: 'cities#show'
There are probably better ways of doing it, but if you call request.url, rails will return the current address. For example we could make the following in our application controller:
expose(:sydney?)
expose(:auckland?)
def sydney?
request.url.include?('sydney')
end
def auckland?
request.url.include?('auckland')
end
Then in your view:
<% if sydney? %>
<%= Do some stuff %>
<% elsif auckland? %>
<%= Do some other stuff %>
<% else %>
<%= Do stuff for no city %>
<% end %>
Remember to expose both methods in your controller or you can't access them from your view.

Restricting a model to only view their own items in the has_many

So I'm trying to think about how to route my site and I need a little help. I have a business who can .build (as in business creates) buildings (sorry for the repetition haha) in a has_many. Each property has many something else.
I would like it so even though there will be more than one building, each business should only be able to view their own buildings, so if someone tries to alter a url, it would redirect home.
I have
resources :buildings
so as it is set up, anyone could just type in
host/buildings/whatever
I would like to redirect with an error if the building ID does not belong to the current_business (devise) it will redirect to their home page. each building has a business_id
Would I have to break the RESTful for this?
Thank you!
Assuming you have user_id in builduing resource:
buildings_controller.rb
def index
#buildings = current_user.buildings
end
def show
#building = current_user.buildings.find(params[:id])
end
buildings/index.html.erb
<% #buildings.each do |building| %>
<%= building.whatever_atribute %>
<% end %>
buildings/show.html.erb
<%= #building.whatever_atribute %>
With the above code when user will go to /buildings he will see only his buildings, and if he'll go to buildings/3 he will see this building if he owns it, in other case he will see a not found error that you can customize it with a redirect or display a styled page.

Using one search form to search multiple models

I am working a simple rails app and i would like to know how possible it is to use one search form to search inside multiple models. like i have a story model and a book model. this search form should be able to search the both models with a single parameter.
<%= for_tag :url => search_path %>
<%= text_field_tag :q %>
<% end %>
How can i make this search from work for multipple models
Whatever search you need to do, is done inside an action in a controller. You could basically create a controller, say search_controller and have an action say, item
def item
if params[:q]
#found_stories = Story.find_all_by_...(params[:q])
#found_books= Book.find_all_by_...(params[:q])
end
end
Then you could use the objects #found_stories and #found_books in your view to show the search results.
This is just an example of how you could do to fulfill your requirement.
Thanks.

Rails Search Form

I'm creating an application that tracks users and achievements (think, xbox live, etc.) These tables are linked via a join table. I would like to have a search form on my index that lets users type in a users name and a new page is loaded with a list of all achievements that user has earned. I'm not entirely sure how to set up this search form, on the index, to actually search the user table and return the results on a new page. Any help would be greatly appreciated. If you require more information then I'll be happy to provide it.
Here's a bit of skeleton code to get you started based off what I think you need from what you have said. I hope this is useful.
For the search bit you could do something like this in your index view:
<%= form_for User.new, :url => "search" do |f| %>
<%= f.label :name %>
<%- f.text_field :name %>
<%- end %>
In your controller:
def search
q = params[:user][:name]
#users = User.find(:all, :conditions => ["name LIKE %?%",q])
end
and in your search view:
<%-#users.each do |user| %>
Name: <%=user.name %>
<%- user.achievements.each do |achievement| %>
<%= achievement.name %>
<%- end %>
<%- end %>
You would, of course, need to ensure the users and achievement models are correctly linked:
class User << ActiveRecord::Base
has_many :achievements
end
There are plenty of tutorials and things about this e.g.:
http://blog.devinterface.com/2010/05/how-to-model-a-custom-search-form-in-rails/
Look the thing is every basic explanation in Rails3 starting with the Initial Tutorial provided by them explains you how to setup a new Controller/Model. The example was only one of thousands explaining the same problem.
It is a very broad range of different things you can do to achieve this. Basically you have to put some code in the controller:
which handles the search (including the activerecord stuff or whichever technique you use to access your model)
which sets some variables necessary for the search form
Setup two routes etc... Its to broad and completely covered even by the basic official rails3 tutorial.
Here is an application based on searchlogic is very useful and you can search by whatever you want
https://github.com/railscasts/176-searchlogic
You may want to check out the Ransack gem. https://github.com/activerecord-hackery/ransack

What is the elegant solution for unrelated views in MVC web frameworks?

I've had a problem with the following issue in Rails and ASP.Net MVC. Often there are multiple widgets of functionality on a page, yet one controller action is supposed to render the page. Let me illustrate:
Let's say I have a normal e-commerce site, and the menu is made of categories, while the page is to display an group of products.
For the products, let's say I have an action on a controller that looks something like:
def product_list
#products = Products.find_by_category(:name => 'lawnmowers')
end
And I have a layout with something like
<div id="menu"><%= render :partial => 'menu' %></div>
<div id="content"><%= yield %></div>
The products have a view...
<%= render :partial => 'product', :collection => #products %>
(note I've ommited the product view as irrelevant)
And the menu has a partial...
<% Category.each {|c| %>
<%= render :partial => 'menu_node', :locals => { :category => c } %>
<% } %>
The line I have a problem with is the "Category.each.do" in the view. I'm fetching data in the view, as opposed to using variables that were set and bound in the controller. And it could easily be a more complex method call that produces the menu.
The solutions I've considered are:
-A view model base class that knows how to get various pieces of data. But you could end up with one of these for each conceptual "section" of the site.
-a local variable that populates at the top of each method (violates DRY)
-the same thing, but in a before_filter call
None of these seem very elegant to me. I can't help but look at this problem and think that a MVP presenter per view (not screen) is a more elegant solution.
ASP.Net MVC has render action (different from rails render :action), which does address this, but I'm not sure what I think of that solution.
Thoughts? Solution suggestions?
Added Note:
The answers provided so far are good suggestions. And they apply to the example I gave, where a menu is likely present in every layout, and is clearly secondary to the product data.
However, what if there is clearly no second class citizen? Portal type sites commonly have multiple unrelated widgets, in which each is important.
For example, What if this page was displaying weather trends, with widgets for temperature, humidity, and precipitation (and each is a different model and view type).
In rails we like to have a concept of thin-controllers, thick-models. So I think you're right to not want to have variables set in the controller.
Also, in order to enable a more-complex method later on, I recommend doing something like:
/app/controllers/application_controller.rb
before_filter :add_menu_nodes
def add_menu_nodes
#menu_nodes = Category.menu_nodes(current_user)
end
/app/views/layouts/application.html.erb
<%= render :partial=>:menu, :locals=>{:categories=>#menu_nodes} %>
/app/models/category.rb
def self.menu_nodes(current_user)
Category.all.order(:name)
end
That way in the future you could update Category.menu_nodes with a more complicated solution, based on the current user, if you need.
Forgive me if I butcher the Ruby (or misunderstand your question), but what's wrong with
class section_helper
def menu( section )
// ...
menuBuiltAbove
end
end
in the view
<%= section_helper.menu( 'section' ) %>
?

Resources