I'm using FriendlyID to make my URLs look nice, so what I got now is:
/items/my-personal-item
But what I would like is:
/user1/my-personal-item
Where user1 is the username of a user owning my-personal-item.
#config/routes.rb
resources :users, path: "" do
resources :items, path: "", only: [:show, :index] #-> url.com/:user_id/
end
FriendlyID has to reside in any model with which you're using, so if you wanted to use it on User as well as Item, you'd have to use the following setup:
#app/models/user.rb
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :username
end
#app/models/item.rb
class Item < ActiveRecord::Base
extend FriendlyId
friendly_id :title
end
This should give you the ability to call: url.com/user-name/my-personal-item
--
If you wanted to back it all up with the appropriate controller actions, you could use the following:
#app/controllers/items_controller.rb
class ItemsController < ApplicationController
def show
#user = User.find params[:user_id]
#item = #user.items.find params[:id]
end
end
As a pro-tip, you'll want to look at the friendly_id initializer to set some defaults for your app:
#config/initializers/friendly_id.rb
config.use :slugged #-> make sure this is valid
config.use :finders #-> make sure this is valid
By setting the above options, you'll just be able to call friendly_id [[attribute]] in your models.
FriendlyID also supports Unique Slugs by Scope.
Because you haven't provided some information about your app I can't give you a exact explanation.
But I've created an explanation based on your url.
If I'm right than you have a Item and a User model. The Item model belongs to the User model and the User model has many Items.
Here is how I would solve this problem:
Models:
# User model
class User < ActiveRecord::Base
extend FriendlyId
has_many :items
friendly_id :name, :use => :slugged
end
# Item model
class Item < ActiveRecord::base
extend FriendlyId
belongs_to :user
friendly_id :name, :use => :scoped, :scope => :user
end
Controller:
class ItemsController < ApplicationController
before_action :set_user, :set_item
def show
# Show your item ...
end
private
def set_user
User.friendly.find(params[:user_id])
end
def set_item
#user.items.friendly.find(params[:item_id])
end
end
Routes:
Rails.application.routes.draw do
get ':user_id/:item_id' => 'items#show'
end
This is a minimal implementation without validation. I hope this helps.
Happy coding :)
Related
I'm still somewhat new to Ruby and am having trouble displaying data on the show page from another class. I have two classes, Company and Job. On the Job show page I would like to display the Company's name, website and description from the Company form fields that created/posted the job when a job applicant views the respective job.
Was receiving an error when tinkering with the Job show controller action. Not entirely sure if the company is not being assigned an id when being created or if there's an issue with the show action login in the controller or a model association error on my end. Any help and explanation to resolve this issue is greatly appreciated.
Screenshot for Error Received on Job Show Page
Models
class Company < ApplicationRecord
has_many :jobs
has_many :job_applications, through: :jobs
class Job < ApplicationRecord
belongs_to :user
belongs_to :company, optional: true
has_many :job_applications, dependent: :destroy
class JobApplication < ApplicationRecord
belongs_to :user
belongs_to :job
Controllers
class CompaniesController < ApplicationController
private
# Use callbacks to share common setup or constraints between actions.
def set_company
#company = Company.find(params[:id])
# #company = self.create_company
end
# Only allow a list of trusted parameters through.
def company_params
params.require(:company).permit(:name, :website, :about, :user_id, :avatar)
end
class JobsController < ApplicationController
# GET /jobs/1 or /jobs/1.json
def show
#company = Company.find(params[:user_id])
# #company = Company.all
# #job = Job.find(params[:id])
end
Routes
resources :companies
resources :jobs
resources :jobs do
resources :job_applications
end
Job Show Page
<%= #company.name %>
<%= #company.website %>
<%= #company.about %>
I believe the problem lies in your show method in the JobsController.
It should look something like this:
class JobsController < ApplicationController
# GET /jobs/1 or /jobs/1.json
def show
#job = Job.find(params[:id])
#company = #job.company
end
This might throw some errors since you have optional: true in your relation. Also, I didn't care of n+1 queries since it's just a record, but this could be improved to be only 1 SQL query to the database.
currently I have the model commodity_group, there are created_by, and updated_by columns in it. It belongs to user, and one user will have commodity_groups. Now, I want to whenever a user created a commodity group, his id will be saved into the created_by, and whenever somebody update a commodity group, his id will be saved into the field update_by.
At the moment, i get this error:
unknown attribute 'user_id' for CommodityGroup.
basically, I don't want to add the column user_id to the commodity_group table since it is the same with the column created_by. Therefore, could somebody guide me here a little bit. Here are my files:
commodity_groups_controller.rb:
class CommodityGroupsController < ApplicationController
before_action :set_commodity_group, only: [:show, :edit, :update, :destroy]
# GET /commodity_groups
def index
#commodity_groups = CommodityGroup.all
end
# GET /commodity_groups/1
def show
end
# GET /commodity_groups/new
def new
#commodity_group = current_user.commodity_groups.build
end
# GET /commodity_groups/1/edit
def edit
end
# POST /commodity_groups
def create
#commodity_group = current_user.commodity_groups.build(commodity_group_params)
if #commodity_group.save
redirect_to commodity_groups_path, notice: init_message(:success, t('message.new_success', page_name: t('page_name.commodity_group')))
else
redirect_to new_commodity_group_path, notice: init_message(:error, t('message.new_error', page_name: t('page_name.commodity_group')))
end
end
# PATCH/PUT /commodity_groups/1
def update
if #commodity_group.update(commodity_group_params)
redirect_to #commodity_group, notice: 'Commodity group was successfully updated.'
else
render :edit
end
end
# DELETE /commodity_groups/1
def destroy
#commodity_group.destroy
redirect_to commodity_groups_url, notice: 'Commodity group was successfully destroyed.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_commodity_group
#commodity_group = CommodityGroup.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def commodity_group_params
params[:commodity_group]
end
end
commodity_group.rb:
class CommodityGroup < ActiveRecord::Base
extend FriendlyId
friendly_id :code, use: :history
belongs_to :user_created,
class_name: 'User',
primary_key: :id,
foreign_key: :created_by
belongs_to :user_updated,
class_name: 'User',
primary_key: :id,
foreign_key: :updated_by
validates_presence_of :code
validates_presence_of :name
validates_presence_of :user
end
user.rb:
class User < ActiveRecord::Base
extend FriendlyId
include Filterable
friendly_id :slug_candidates, use: :history
has_secure_password
acts_as_paranoid
has_many :activities
has_many :pricing_histories
has_many :commodity_groups
end
CommodityGroup is searching for user_id in commodity_groups table.
You need to specify that in the User model.
class User < ActiveRecord::Base
extend FriendlyId
include Filterable
friendly_id :slug_candidates, use: :history
has_secure_password
acts_as_paranoid
has_many :activities
has_many :pricing_histories
has_many :commodity_groups_created, class_name: 'CommodityGroup',
foreign_key: :created_by
has_many :commodity_groups_updated, class_name: 'CommodityGroup',
foreign_key: :updated_by
end
you've got the foreign_key on the CommodityGroup side, you just need to add it to the User side as well and you should be good to go eg:
has_many :commodity_groups, class_name: 'User', foreign_key: :created_by
(you may need to jigger with the details to figure out what else you need to add, but this will basically give you the idea on how to solve it)
I have a Company and a User model, both with a slug via friendly_id. Slugs are ensured to be unique across both models.
I'd like to have URLs:
http://www.example.com/any_company_name
http://www.example.com/any_user_name
e.g. both /apple and /tim
I'm not sure how to achieve this in Rails.
I have tried various permutations of:
routes.rb:
resources :users, path: ''
resources :companies, path: ''
get '*search', to: 'my_controller#redirect'
and
my_controller#redirect:
#company = Company.friendly.find(params[:search])
redirect_to #company if #company
#user = User.friendly.find(params[:search])
redirect_to #user if #user
However I can't get it to work. I can get /apple to redirect to /companies/apple and /tim to redirect to /users/tim (by removing the path: '' option) but this is not what I want to achieve.
Instead redirect with urls, you can redirect with controller#action by using url_for.
For example:
my_controller#redirect:
#company = Company.friendly.find(params[:search])
redirect_to url_for(action: 'show', controller: :companies , status: :success, company_id:#company.id)
#user = User.friendly.find(params[:search])
redirect_to url_for(action: 'show', controller: :users, status: :success,user_id:#user.id)
I had a similar problem and was able to solve it by creating a PublicSlug model with a slug attribute and a polymorphic association to a "Sluggable" class. I also used a Sluggable concern that I included in models I would like to query.
The PublicSlug model
class PublicSlug < ActiveRecord::Base
extend FriendlyId
friendly_id :sluggable_name, use: :slugged
belongs_to :sluggable, polymorphic: true
private
# Generate the slug based on the title of the Sluggable class
def sluggable_name
sluggable.name
end
end
The Sluggable concern
module Sluggable
extend ActiveSupport::Concern
included do
has_one :public_slug, dependent: :destroy, as: :sluggable
delegate :slug, to: :public_slug
end
end
Company and User models.
class User < ActiveRecord::Base
include Sluggable
end
class Company < ActiveRecord::Base
include Sluggable
end
I can now query both models using
Sluggable.friendly.find(slug).sluggable
The redirect could be handled in your controller as follows:
def redirect
#sluggable = Sluggable.friendly.find(params[:search]).sluggable
redirect_to #sluggable
end
Building off of #Essn's answer...
Still use a PublicSlug model:
# app/models/public_slug.rb
class PublicSlug < ActiveRecord::Base
extend FriendlyId
friendly_id :sluggable_name, use: :slugged
belongs_to :sluggable, polymorphic: true
validates :slug, presence: true, uniqueness: { case_sensitive: false }
private
# Generate the slug based on the title of the Sluggable class
def sluggable_name
sluggable.name
end
end
And a Sluggable concern:
# app/models/concerns/sluggable.rb
module Sluggable
extend ActiveSupport::Concern
included do
before_validation :create_public_slug
has_one :public_slug, dependent: :destroy, as: :sluggable
delegate :slug, to: :public_slug
private
def create_public_slug
self.public_slug = PublicSlug.new unless public_slug.present?
end
end
end
Include that concern in all models you want to lookup:
# app/models/user.rb
class User < ActiveRecord::Base
include Sluggable
...
end
Create a migration:
# db/migrate/...create_public_slugs.rb
class CreatePublicSlugs < ActiveRecord::Migration[5.0]
def change
create_table :public_slugs do |t|
t.references :sluggable, polymorphic: true
t.string :slug
end
end
end
Then you can lookup the model via:
# app/controllers/home_controller.rb
class HomeController < ApplicationController
# /:slug
def show
#sluggable = PublicSlug.friendly.find(params[:id]).sluggable
render #sluggable
end
end
I've been following the Railscast tutorial on how to implement friendly_id and for some reason my URL's doesn't change after I update my attributes.
Say I registered a user with :fullname 'John Doe' it creates the slug /john-doe successfully. However if I update my name to 'Test user' the url is still /john-doe
My current setup is this:
users_controller
def show
#user = User.friendly.find(params[:id])
end
model - user.rb
extend FriendlyId
friendly_id :fullname, use: [:slugged, :history]
I've also migrated
class AddSlugsToUsers < ActiveRecord::Migration
def change
add_column :users, :slug, :string
add_index :users, :slug, unique: true
end
end
so that is working. Also installed
rails generate friendly_id
and done:
User.find_each(&:save)
in rails c
What am I doing wrong?
friendly_id
It's a common issue with friendly_id - it defaults to setting the slug only if the slug attribute is blank:
Documentation
As of FriendlyId 5.0, slugs are only generated when the slug field is
nil. If you want a slug to be regenerated,set the slug field to nil:
restaurant.friendly_id # joes-diner
restaurant.name = "The Plaza Diner"
restaurant.save!
restaurant.friendly_id # joes-diner
restaurant.slug = nil
restaurant.save!
restaurant.friendly_id # the-plaza-diner
You can also override the #should_generate_new_friendly_id? method, which lets you control exactly when new friendly ids are set:
class Post < ActiveRecord::Base
extend FriendlyId
friendly_id :title, :use => :slugged
def should_generate_new_friendly_id?
title_changed?
end
end
If you want to extend the default behavior but, adding your own conditions, don't forget to invoke super from your implementation:
class Category < ActiveRecord::Base
extend FriendlyId
friendly_id :name, :use => :slugged
def should_generate_new_friendly_id?
name_changed? || super
end
end
For you, I'd recommend:
#app/models/user.rb
Class User < ActiveRecord::Base
...
def should_generate_new_friendly_id?
name_changed? || super
end
end
I am having a devise authenticated login and a contacts module...the idea is that, users have multiple contacts... the contacts class has name and number as its attribute...but when I try to create a contact, it throws an error stating "unknown attribute: user_id" ... where am I going wrong?? I tried adding user_id in contact model...but still getting the error...help would be much appreciated..
Contact model:
class Contact < ActiveRecord::Base
belongs_to :user
attr_accessible :name, :number, :user_id
end
contact controller:
class ContactsController < ApplicationController
def new
end
def show
#contacts=current_user.contacts
#contacts.save
end
def index
#contact=current_user.email_id
end
def create
# #contact=contacts.new
#contact= current_user.contacts.build( :name=> params[:name] , :number=>params[:number] )
#contact.save
redirect_to contacts_show_path
end
end
You need to add has_many :contacts to your user model as well as add the "user_id" column to your contacts migration file.
class User < ActiveRecord::Base
has_many :contacts
end