Add User to project using his email - ruby-on-rails

I have projects which can have one projectOwner and multiple projectMembers. After creating my project, I want to be able to add users to this project.
Right now my problem is that I have to enter the user_id and project_id when creating a new projectMember. The form is expecting a user_id and project_id so how can I change that so that it takes an email and then finds the user_id related to it.
View:
<%= form_for([#project, #project.project_members.build]) do |f| %>
<p>
<%= f.label :project_id %><br>
<%= f.text_field :project_id %>
</p>
<p>
<%= f.label :user_id %><br>
<%= f.text_field :user_id %>
</p>
<p>
<%= f.submit :Add, class: 'btn btn-info' %>
</p>
<% end %>
Controller:
def create
#project_member = ProjectMember.new(member_params)
#project_member.save
end
private
def member_params
params.require(:project_member).permit(:user_id, :project_id)
end
Model relation:
class User < ApplicationRecord
has_many :roles
has_many :projects, through: :roles
end
class Role < ApplicationRecord
belongs_to :user
belongs_to :project
end
class ProjectOwner < Role
end
class ProjectMember < Role
end
class Project < ApplicationRecord
has_many :project_members
has_many :members, class_name: "User", through: :project_members
has_one :project_owner
has_many :owners, class_name: "User", through: :project_owner
end
Right now my routes look like this but I feel like this isnt the right way of doing it:
devise_for :users
resources :users do
resources :projects
end
resources :projects do
resources :project_members
end

The form is expecting a user_id and project_id so how can I change
that so that it takes an email and then finds the user_id related to
it.
You can have an input for the email in the form and allow the controller to do the rest
<%= form_for([#project, #project.project_members.build]) do |f| %>
<p>
<%= f.label :project_id %><br>
<%= f.text_field :project_id %>
</p>
<p>
<%= label_tag :email %><br>
<%= email_field_tag :email %>
</p>
<p>
<%= f.submit :Add, class: 'btn btn-info' %>
</p>
<% end %>
In the controller, find the user with the email
def create
#project_member = ProjectMember.new(member_params)
#user = User.find_by(email: params[:email])
#project_member.user_id = #user.id
#project_member.save
end
Also, you don't need to provide project_id explicitly in the form. The #project in the form will give you the project_id. You just need to let the controller do the mapping
<%= form_for([#project, #project.project_members.build]) do |f| %>
<p>
<%= label_tag :email %><br>
<%= email_field_tag :email %>
</p>
<p>
<%= f.submit :Add, class: 'btn btn-info' %>
</p>
<% end %>
In the controller,
def create
#project = Project.find(params[:project_id])
#project_member = #project.project_members.new(member_params)
#user = User.find_by(email: params[:email])
#project_member.user_id = #user.id
#project_member.save
end

In your view, replace user_id with user_email field:
<p>
<%= f.label :user_email %><br>
<%= f.text_field :user_email %>
</p>
In controller, get the email
def create
user = User.find_by_email(params[:project_member][:user_email]) # find the user by email
#project_member = ProjectMember.new(:project_id => params[:project_member][:project_id]) # create new project member with project id
#project_member.users << user # assign user to newly created project member
#project_member.save
end
private
def member_params
params.require(:project_member).permit(:user_email, :project_id)
end
Hope it helps!
Note: Please make sure if the User we find by email exists!

One way you achieve this is modifying your member_params as
def member_params
member_params = params.require(:project_member).permit(:email, :project_id)
if (user_id = User.find_by_email(member_params[:email]).try(:id)).present?
member_params.merge!(user_id: user_id)
member_params.permit!
else
# display error
end
end
Or let your params be as it is
def member_params
params.require(:project_member).permit(:email, :project_id)
end
in your controller,
def create
#project_member = ProjectMember.build_by(member_params)
if #project_member.save
# success
else
# failure
end
end
and model,
def self.build_by(params)
project_member = self.new(params)
project_member.user_id = User.find_by_email(params[:email]).try(:id)
end
I think, second option is more cleaner and logic moves to model..

Related

Rails has many through fields_for

I have a form I would like to create that populates the corresponding model form for, and a fields_for that populates a has many through table.
The plan.rb model:
class Plan < ApplicationRecord
has_many :plan_materials
has_many :materials, through: :plan_materials
accepts_nested_attributes_for :materials
end
The materials.rb model:
class Material < ApplicationRecord
has_many :plan_materials
has_many :plans, through: :plan_materials
end
And the PlanMaterial model:
class PlanMaterial < ApplicationRecord
belongs_to :plan
belongs_to :material
end
This is what I have in the plan form:
<%= form_for #plan do |form| %>
<div class="form-group">
<%= form.label :name %>
<%= form.text_field :name, class: 'form-control' %>
</div>
<div class="form-group">
<%= form.label :description %>
<%= form.text_field :description, class: 'form-control' %>
</div>
<%= form.fields_for :materials, plan.materials.build do |material_fields| %>
<%= material_fields.text_field :title %>
<% end %>
<%= form.submit %>
<% end %>
I have created fields_for forms before but never trying to input the information for the form being created, and the id of the material they are selecting, in a new table from that same form.
I am creating this through a plan.materials.build which I realize could possibly be the wrong way to go about doing this because I don't think it would be building that in the plan_materials table.
class PlansController < ApplicationController
def index
#plans = Plan.all
end
def new
#plan = Plan.new
#plan.materials.build
end
def create
#plan = Plan.new(plan_params)
respond_to do |format|
if #plan.save
format.html { redirect_to plan_path, notice: 'Plan has been created' }
else
format.html { render :new, notice: 'There was an error saving your plan' }
end
end
end
private
def plan_params
params.require(:plan).permit(:name, :description, :grade_id, :subject_id, :unit_id, materials_attributes: [:title])
end
end
So, just to recap, I would like to create a plan, and be able to add materials to that plan. I need to have that plan created in the plans table, along with the id of the plan that was created and the id of the material that was added in that form. How do I go about doing this?

Rails nested form fields_for doubled in edit

I'm trying to create a form where users can see a list of their friends and add or remove friends from lists (sort of like facebook's groups). For the create list view, I can get the form working properly, but for the edit view I'm having trouble with check_box syntax and fields_for.
routes.rb
resources :friendships
resources :friend_lists
class User < ActiveRecord::Base
has_many :friendships, dependent: :destroy
has_many :friends, through: :friendships
has_many :friend_lists
has_many :flist_memberships, class_name: 'FlistMembership', foreign_key: 'member_id', dependent: :destroy
...
end
class FriendList < ActiveRecord::Base
belongs_to :user
has_many :flist_memberships
has_many :members, through: :flist_memberships
accepts_nested_attributes_for :members, allow_destroy: true
accepts_nested_attributes_for :flist_memberships, allow_destroy: true
end
class FlistMembership < ActiveRecord::Base
belongs_to :friend_list
belongs_to :member, class_name: 'User'
end
class FriendListsController < ApplicationController
def new
#friend_list = current_user.friend_lists.build
#friends = current_user.friends.paginate(page: params[:page])
#friend_list.flist_memberships.build
end
def create
#friend_list = current_user.friend_lists.build(friend_list_params)
respond_to do |format|
format.html {
if #friend_list.save
flash[:success] = "Friends list created!"
redirect_to friendships_path
else
render 'friend_lists/new'
end
}
end
end
def edit
#friend_list = FriendList.find(params[:id])
#friends = current_user.friends
end
def update
#friend_list = FriendList.find(params[:id])
if #friend_list.update_attributes(friend_list_params)
flash[:success] = "Friend list updated!"
redirect_to friend_list_path(#friend_list)
else
render 'edit'
end
end
private
def friend_list_params
params.require(:friend_list).permit(:name, flist_memberships_attributes: [:id, :member_id, :friend_list_id])
end
end
new.html.erb
<%= form_for #friend_list do |f| %>
<%= f.label :name, "Name for this friends list:" %>
<%= f.text_field :name %>
<% #friends.each do |friend| %>
<%= f.fields_for :flist_memberships do |m| %>
<%= m.check_box :member_id, {}, friend.id %>
<%= m.label :member_id, friend.name %>
<% end %>
<% end %>
<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>
edit.html.erb
<%= form_for #friend_list do |f| %>
<%= f.label :name, "Name for this friends list:" %>
<%= f.text_field :name %>
<%= f.fields_for :flist_memberships do |m| %>
<%= m.collection_check_boxes(:member_id, #friends.all, :id, :name) %>
<% end %>
<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>
new.html.erb renders and saves the selected items correctly, but I can't figure out how to write the edit form so that it doesn't iterate through the collection once for each item in the collection (both with collection_check_boxes and the form as written in new.html.erb). Trying
<%= f.fields_for :flist_memberships do |m| %>
<%= m.check_box :id %>
<%= m.label :id, :name %>
<% end %>
in the edit form just passes the string "id" as params rather than pulling the id of each member in the collection, and I'm not sure how to pull the individual item's name/id without iterating over the collection, which goes back to the multiple renders problem. I'm using cocoon elsewhere in the project, but for this I'd rather present the user a list of all options and allow them to check a box for each one, rather than having to manually add each item on the list. If there is a way to do it with cocoon, I'd be happy to hear it.

rails has_one through form

administrator.rb:
class Administrator < ActiveRecord::Base
has_one :administrator_role, dependent: :destroy
has_one :role, through: :administrator_role
end
role.rb:
class Role < ActiveRecord::Base
has_many :administrator_roles
has_many :administrators, through: :administrator_roles
end
administrator_role.rb:
class AdministratorRole < ActiveRecord::Base
belongs_to :administrator
belongs_to :role
end
in view for "new" action administrator_controller:
<%= form_for #administrator do |f| %>
<%= render 'shared/errors', object: #administrator %>
<div class="form-group">
<%= f.label :role_id, "Роль:" %>
<%= f.collection_select(:role_id, #roles, :id, :name) %>
</div>
...
<%= f.submit 'Save', class: 'btn btn-primary btn-lg' %>
<% end %>
administrator_controller.rb:
class AdministratorsController < ApplicationController
def new
#administrator = Administrator.new
#roles = Role.all
end
def create
#administrator = Administrator.new(administrators_params)
if #administrator.save
flash[:success] = "Account registered!"
redirect_to root_path
else
render :new
end
end
...
private
def administrators_params
params.require(:administrator).permit(:login, :password, :password_confirmation, :role_id)
end
end
when you open the page get the error:
undefined method `role_id' for #<Administrator:0x007f6ffc859b48>
Did you mean? role
How to fix it? if I put in place role_id a role, when you create administrator will get the error:
ActiveRecord::AssociationTypeMismatch (Role(#69964494936160) expected, got String(#12025960)):
You have to rewrite the form as below:
<%= form_for #administrator do |f| %>
<%= render 'shared/errors', object: #administrator %>
<div class="form-group">
<%= f.fields_for :role do |role_form| %
<%= role_form.label :role_id, "Роль:" %>
<%= role_form.select(:id, #roles.map { |role| [role.name, role.id] }) %>
<% end %>
</div>
...
<%= f.submit 'Save', class: 'btn btn-primary btn-lg' %>
<% end %>
You also need to add 1 line which enables the nested form logic as:
class Administrator < ActiveRecord::Base
has_one :administrator_role, dependent: :destroy
has_one :role, through: :administrator_role
accepts_nested_attributes_for :role
end
And also change the controller like:
class AdministratorsController < ApplicationController
#....
private
def administrators_params
params.require(:administrator).permit(
:login, :password,
:password_confirmation,
role_attributes: [ :id ]
)
end
end
When you are using has_one association, you get the below method, but not association_id=, and that is what error is saying.
association(force_reload = false)
association=(associate)
build_association(attributes = {})
create_association(attributes = {})
create_association!(attributes = {})

how to add details in two database table in one submit

I have 3 tables: coaches, categories and also a join table categories_coaches, on submit I want to store category_id and coach_id in join table categories_coaches and name, email, university, batch, phone in coach table. how to do so?
now details are storing in coach table but not storing in join table
please help me to solve this problem.
coach.rb
class Coach < ActiveRecord::Base
has_and_belongs_to_many :categories
end
category.rb
class Category < ActiveRecord::Base
belongs_to :coach
end
registrationcontroller.erb
class Coaches::RegistrationsController < Devise::RegistrationsController
def new
#individual=#individual ||= Coach.new
super
end
def create
build_resource sign_up_params
#individual=#individual ||= Coach.new
super
end
private
def sign_up_params
params.require(:coach).permit(:name, :email, :university, :batch, :linkedin_url, :code, :phone,category_ids: []
)
end
end
view page
<%= simple_form_for(#individual, as: :coach, url: registration_path(:coach)) do |f| %>
<%= f.input :name, required: true, %>
<%= f.input :university %>
<%= f.input :batch %>
<%= f.input :email%>
<%= f.input :phone%>
<div class="category-scroll">
<% Category.all.each do |c| %>
<% if c.parent_id != nil %>
<div class="category-left">
<%= check_box_tag "category_ids[]", c.id, false, :id => "category_ids_#{c.id}" %>
<%= c.name %>
</div>
<% else %>
<b><%= c.name %></b>
<% end %>
<% end %>
</div>
<div class="form-group">
<%= f.button :submit, "SUBMIT", class: "apply-continue_form" %
<% end %>
What you've mentioned sounds like a has_and_belongs_to_many relationship to me.
I'll detail what you should do, and the underlying mechanics of the association:
#app/models/coach.rb
class Coach < ActiveRecord::Base
has_and_belongs_to_many :categories
end
#app/models/category.rb
class Category < ActiveRecord::Base
has_and_belongs_to_many :coaches
end
This, as opposed to the has_many :through relationship, does most of the legwork for you. You were correct in setting up your join_table as you did:
The importance of getting this right is that each time you CRUD either your Coach or Category objects, you'll have access to their associated data through the :categories and :coaches methods respectively.
Thus, you'll be able to populate the data like this:
#config/routes.rb
resources :coaches #-> url.com/coaches/new
#app/controllers/coaches_controller.rb
class CoachesController < ApplicationController
def index
#coaches = Coach.all
end
def new
#coach = Coach.new
end
def create
#coach = Coach.new coach_params
end
private
def coach_params
params.require(:coach).permit(:name, :email, :university, :batch, :phone, :categories)
end
end
This will then allow you to make the following view:
#app/views/coaches/new.html.erb
<%= form_for #coach do |f| %>
<%= f.text_field :name %>
<%= f.email_field :email %>
<%= f.text_field :university %>
<%= f.text_field :batch %>
<%= f.text_field :phone %>
<%= f.collection_select :categories, Category.all, :id, :name %>
<%= f.submit %>
<% end %>

nested forms : update child foreign key

I have a nested association:
class User < ActiveRecord::Base
has_many :hostels
accepts_nested_attributes_for :hostels
end
class Hostel < ActiveRecord::Base
belongs_to :user
end
The form :
<%= form_for #user do |f| %>
<%= f.label :email %><br>
<%= f.text_field :email %>
<% f.object.hostels << #hostel -%>
<%= f.fields_for :hostels do |ff| %>
<%= ff.hidden_field :id %>
<% end -%>
<%= f.submit %>
<% end -%>
the controller
def create
#user = User.new(user_params)
raise #user.hostels.inspect
end
private
def user_params
params.require(:user).permit(:email, hostels_attributes: [:id])
end
I would like to relink existing records of hostels to new users by updating hostel foreign key. This way, it definitly don't work.
Tried update_only: true parameter to nested too.
Any ideas about the subject or am I totally wrong about trying to do the operation like that ?
you can use a multiple select for hotels in you form, then in the controller you must require hostel_ids.
Models:
class User < ActiveRecord::Base
has_many :hostels
end
class Hostel < ActiveRecord::Base
belongs_to :user
end
Form: where :name is what you see in your multiple select
<%= form_for #user do |f| %>
<%= f.label :email %><br>
<%= f.text_field :email %>
<%= f.label "Hostels" %><br>
<%= select_tag :hotel_ids, options_for_select(Hostel.all.map{|h| [h.name, h.id]}), { :multiple => true } %>
<%= f.submit %>
<% end %>
Controller:
def create
#user = User.new(user_params)
end
private
def user_params
params.require(:user).permit(:email, hostel_ids)
end
I did not test the code but must work well.

Resources