0

tldr: How do I find and update or create a record through a nested association?

I have a Registrations model that belongs to a Lead model. Leads are created via nested attributes when the user submits the registration form. This works fine, but now I need to update the Registration so that it either creates a new lead, OR finds and updates the lead if a lead already exists with that email.

registration.rb:

class Registration < ApplicationRecord
  belongs_to :showing
  belongs_to :lead

  accepts_nested_attributes_for :lead
  validates_associated :lead

end

lead.rb

class Lead < ApplicationRecord
  belongs_to :account
  has_many :registrations, dependent: :destroy
  has_many :showings, through: :registrations
  validates :name, :email, :phone, presence: true

end

registrations_controller.rb

class RegistrationsController < ApplicationController
  before_action :set_account

  def index
    redirect_to new_showing_registration_path
  end

  def new
    @registration = @showing.registrations.build
    @lead = Lead.new
  end

  def create
    @showing = Showing.find(params[:showing_id])
    @registration = @showing.registrations.build(registration_params)
    if @registration.save
      redirect_to new_showing_registration_path, notice: 'Thank you for registering.'
    else
      render :new, status: :unprocessable_entity
    end
  end

  def show
    redirect_to new_showing_registration_path
  end

  def edit
    @showing = Showing.find(params[:showing_id])
    @registration = Registration.find(params[:id])
    @account_id = @showing.listing.account.id.to_i
  end

  def update
    @registration = Registration.find(params[:id])
    if @registration.update(registration_params)
      redirect_to new_showing_registration_path, notice: 'Thank you for registering.'
    else
      render :edit, status: :unprocessable_entity
    end
  end

  private
    def registration_params
      params.require(:registration).permit(lead_attributes: [:account_id, :id, :name, :email, :phone, :agent, :mortgage, :source])
    end

    def set_account
      @showing = Showing.find(params[:showing_id])
      @account_id = @showing.listing.account.id.to_i
    end
end
Seth
  • 1
  • 1

2 Answers2

0

maybe this one will help

https://stackoverflow.com/a/3580267/11544219

or this

https://dev.to/katkelly/why-yes-i-ll-accept-those-nested-attributes-18f7


class Registration < ApplicationRecord
  belongs_to :showing
  belongs_to :lead

  accepts_nested_attributes_for :lead
  validates_associated :lead

  def autosave_associated_records_for_lead
   existed_lead = Lead.find_by(registration: self)
   if existed_lead.present?
     existed_lead.update!(lead.attributes)
   else
     lead.save!
   end
  end

end
WALVISK
  • 591
  • 4
  • 16
  • Thanks! This was close, but I starting running into issues setting the lead_id on the parent when overriding the autosave. Not totally sure why. So, I decided to go with a callback instead using your similar logic. I posted my solution in case you're interested. Thanks again! – Seth Jan 28 '21 at 18:53
  • oh that callback works as well. sure dude – WALVISK Jan 29 '21 at 03:05
0

For others who are working on similar features, here's how I solved it (below). Ultimately, I chose to go with a callback and not override the autosave method. When overriding the autosave method, I was running into problems setting the nested object id on the parent object in the join table. So, I went with the following callback process:

  1. check if a lead exists by that email
  2. if one exists, update the existing lead with the new lead attributes
  3. set the new lead to the updated existing lead
  4. if lead doesn't exist, pass the new lead back to the controller for creation
class Registration < ApplicationRecord
  belongs_to :showing
  belongs_to :lead
  accepts_nested_attributes_for :lead, :reject_if => :all_blank, allow_destroy: true
  before_validation :update_or_create_lead

  private

  def update_or_create_lead
    existing = Lead.find_by(email: self.lead.email)
    if existing
      existing.update(
        name: lead.name,
        phone: lead.phone,
        agent: lead.agent,
        mortgage: lead.mortgage,
        source: lead.source
    )
      self.lead = existing
    else
      self.lead
    end
  end

end
Seth
  • 1
  • 1