Eduardorph
Eduardorph

Reputation: 153

How can i make this specific relationship?

The same Teacher can teach in different schools and different subjects,eg:

Dan teaches English and Math in the School A and Physics in the School B

I tried use a has many through between these 3 model, but i don't know how can i add many schools and many subjects in a specific Teacher

Here what i tried

class Teacher < ApplicationRecord
    has_many :school_teacher_subjects
    has_many :schools, through: :school_teacher_subjects
    has_many :subjects, through: :school_teacher_subjects
end

class School < ApplicationRecord
    has_many :school_teacher_subjects
    has_many :teachers, through: :school_teacher_subjects
    has_many :subjects, through: :school_teacher_subjects
end

class Subject < ApplicationRecord   
    has_many :school_teacher_subjects
    has_many :teachers, through: :school_teacher_subjects
    has_many :schools, through: :school_teacher_subjects
end

class SchoolTeacherSubject < ApplicationRecord
  belongs_to :teacher
  belongs_to :school
  belongs_to :subject
end

What i want is that inside the Teacher New/Edit form, i can save one or many schools and one or many subjects in the database at same time in this way:

+----+------------+-----------+------------+
| id | teacher_id | school_id | subject_id |
+----+------------+-----------+------------+
|  1 |          2 |         2 |          4 |
|  2 |          2 |         2 |          1 |
|  3 |          1 |         3 |          2 |
|  4 |          1 |         3 |          6 |
+----+------------+-----------+------------+

But all I can do is:

+----+------------+-----------+------------+
| id | teacher_id | school_id | subject_id |
+----+------------+-----------+------------+
| 72 |          8 |         2 |          2 |
| 74 |          2 |           |          2 |
| 75 |          2 |           |          6 |
| 76 |          1 |         3 |            |
| 77 |          1 |           |          2 |
| 78 |          1 |           |          6 |
+----+------------+-----------+------------+

Here what i'm doing:

my controller and form

def new
    @teacher = Teacher.new
    @schools = School.all.order(name: :asc)
    @subjects = Subject.all.order(name: :asc)
end

def edit
    @teacher = Teacher.find(params[:id])
    @schools = School.all.order(name: :asc)
    @subjects = Subject.all.order(name: :asc)
end

def create
    @teacher = Teacher.new(teacher_params)

    respond_to do |format|
      if @teacher.save
        format.html { redirect_to  admin_teacher_index_path, notice: 'Escola criada com sucesso.' }
      else
        format.html { render :new }
      end
    end
end

def update
    @teacher = Teacher.find(params[:id])

    respond_to do |format|
      if @teacher.update(teacher_params)
        format.html { redirect_to  admin_teacher_index_path, notice: 'Escola editada com sucesso.' }
      else
        format.html { render :edit }
      end
    end
end

private
    def teacher_params
      params.require(:teacher).permit(:full_name, :genre, :status, school_ids: [], subject_ids: [])
    end

FORM.HTML.ERB

<div class="row mb-3">
    <div class="col">
        <%= f.label :school_ids, 'Escolas(s)' %>
        <%= f.collection_select(:school_ids, @schools, :id, :name, {:include_blank => "Selecione uma ou mais"}, {:class => "multiple-select2 custom-select", multiple: true}) %>
    </div>
</div>

<div class="row mb-3">
    <div class="col">
        <%= f.label :subject_ids, 'Disciplina(s)' %>
        <%= f.collection_select(:subject_ids, @subjects, :id, :name, {:include_blank => "Selecione uma ou mais"}, {:class => "multiple-select2 custom-select", multiple: true}) %>
    </div>
</div>

Upvotes: 0

Views: 37

Answers (1)

Pablo
Pablo

Reputation: 3005

One of the problems you have is that a Teacher can teach many subjects at many schools, but in your form you are selecting schools and subjects independently. Schools and Subjects must be selected together. I don't think you can do it with two multiple selects and passing two arrays (school_ids and subject_ids). In fact, a teacher could teach a subject in two schools and this cannot be implemented with your form. You need a more complex form. I would do it in a form where you could dynamically add new lines (subjects and schools related) using cocoon gem.

Models

class Teacher < ApplicationRecord
  has_many :school_teacher_subjects
  has_many :schools, through: :school_teacher_subjects
  has_many :subjects, through: :school_teacher_subjects
  # NEW
  accept_nested_attributes_for :school_teacher_subjects, 
      reject_if: :all_blank, allow_destroy: true
end

class School < ApplicationRecord
  has_many :school_teacher_subjects
  has_many :teachers, through: :school_teacher_subjects
  has_many :subjects, through: :school_teacher_subjects
end

class Subject < ApplicationRecord   
  has_many :school_teacher_subjects
  has_many :teachers, through: :school_teacher_subjects
  has_many :schools, through: :school_teacher_subjects
end

class SchoolTeacherSubject < ApplicationRecord
  belongs_to :teacher
  belongs_to :school
  belongs_to :subject
end

Controller

private
  def teacher_params
    # CHANGED
    params.require(:teacher).permit(:full_name, :genre, :status,
      :school_teacher_subjects_attributes => [ :school_id, :subject_id, :id, :_destroy ])
  end

View (main form):

<Teacher fields (fullname, genre, status, etc)>
<.............................................>

<div class="row mb-3">
  <div class="col">
    <%= f.simple_fields_for :school_teacher_subjects do |sts| %>
      <%= render 'sts_fields', f: sts %>
    <% end %>
    <%= link_to_add_association 'Add new class', f, 
      :school_teacher_subjects,
      :partial => 'sts_fields', 
      :force_non_association_create => true,
      :data => {"association-insertion-method" => "before", "association-insertion-node" => 'this'}
    %>

  </div>
</div>

View (partial form for subject and school) sts_fields.html.erb :

<div class="nested-fields">
  <div class="row">
    <div class="col-xs-2">
      <%= link_to_remove_association 'Remove', f %>
    </div>
    <div class="col-xs-5">
      <%= f.collection_select :subject_id, @subjects, :id, :name %>
    </div>
    <div class="col-xs-5">
      <%= f.collection_select :school_id, @schools, :id, :name %>
    </div>
  </div>
</div>

Upvotes: 1

Related Questions