Form object

Witam.
Zaczynam przygodę z form object w rails.
Mam Branch, Branch has_one BranchAddress.
BranchAddress tworzony jest automatycznie po stworzeniu Branch.

Kontroller Branch:

class BranchesController < ApplicationController

  def new
    @branch_form = BranchForm.new
  end

  def edit
    @branch = Branch.find(params[:id])
    @branch_form = BranchForm.new(branch: @branch)
  end

  def update
    @branch = Branch.find(params[:id])
    @branch_form = BranchForm.new(branch: @branch, attributes: branch_params)
    if @branch_form.update
      render root_path
    else
      render :edit
    end
  end

  def create
    @branch_form = BranchForm.new(user: current_user, attributes: branch_params)
    if @branch_form.save
      redirect_to root_path
    else
      render :new
    end
  end
  private
    def branch_params
      params.require(:branch).permit( :branch_name,
                                      :branch_description,
                                      :branch_url,
                                      :branch_publish,
                                      :branch_main,
                                      :branch_date_published,
                                      :address_country,
                                      :address_street,
                                      :address_zip_code,
                                      :address_city,
                                      :address_real_estate,
                                      :address_apartment,
                                      :address_lat,
                                      :address_lng )
    end
end

BranchForm:

class BranchForm < BaseForm

  attr_reader :branch
  attr_reader :address
  #
  ## branch address
  #
  attribute :branch_name, String
  attribute :branch_description, String
  attribute :branch_url, String
  attribute :branch_publish, Boolean, default: false
  attribute :branch_main, Boolean
  attribute :branch_date_published, Date

  # branch address
  attribute :address_country, String
  attribute :address_street, String
  attribute :address_zip_code, String
  attribute :address_city, String
  attribute :address_real_estate, String
  attribute :address_apartment, String
  attribute :address_lat, String
  attribute :address_lng, String
  #
  ## branch validator
  #
  validates :branch_name,
    presence: true,
    length: { maximum: 255 }

  validates :branch_description,
    presence: true,
    length: { maximum: 65535 }

  validates :branch_url, length: { maximum: 255 }
  #
  ## branch address validator
  #
  validates :address_country, length: { maximum: 255 }
  validates :address_street, length: { maximum: 255 }
  validates :address_zip_code, length: { maximum: 255 }
  validates :address_city, length: { maximum: 255 }
  validates :address_real_estate, length: { maximum: 255 }
  validates :address_apartment, length: { maximum: 255 }
  validates :address_lat, length: { maximum: 255 }
  validates :address_lng, length: { maximum: 255 }

  def initialize(user: nil, branch: nil, attributes: {})
    if !branch.nil?
      @branch = branch
      @branch_address = @branch.branch_address
      super(branch_attributes.merge(attributes))
      super(branch_address_attributes.merge(attributes))
    end
    if !user.nil?
      @user = user
    end
    super(attributes)
  end

  def save
    if valid?
      create_branch
      update_branch_address
      true
    else
      false
    end
  end

  def update
    if valid?
      update_branch
      update_branch_address
      true
    else
      false
    end
  end

  def update_branch
    @branch.update_attributes(branch_permitted_parameters)
  end

  def create_branch
    @branch = @user.branches.create(branch_permitted_parameters)
  end
  def update_branch_address
    @branch.branch_address.update_attributes(branch_address_permitted_parameters)
  end

  def branch_permitted_parameters
    {
      name: branch_name,
      description: branch_description,
      url: branch_url,
      publish: branch_publish,
      main: branch_main,
      date_published: branch_date_published,
    }
  end

  def branch_address_permitted_parameters
    {
      country: address_country,
      street: address_street,
      zip_code: address_zip_code,
      city: address_city,
      real_estate: address_real_estate,
      apartment: address_apartment,
      lat: address_lat,
      lng: address_lng,
    }
  end

  def branch_attributes
    {
      branch_name: branch.name,
      branch_description: branch.description,
      branch_url: branch.url,
      branch_publish: branch.publish,
      branch_main: branch.main,
      branch_date_published: branch.date_published
    }
  end

  def branch_address_attributes
    {
      address_country: @branch_address.country,
      address_street: @branch_address.street,
      address_zip_code: @branch_address.zip_code,
      address_city: @branch_address.city,
      address_real_estate: @branch_address.real_estate,
      address_apartment: @branch_address.apartment,
      address_lat: @branch_address.lat,
      address_lng: @branch_address.lng,
    }
  end
end

Widok simple_Form:

= simple_form_for(@branch_form,  as: :branch, url: generate_url, method: generate_method) do |f|
          = f.input :branch_name
          = f.input :branch_description
          = f.input :branch_url
          = f.input :branch_main, as: :boolean, wrapper: :icheck_form_left_text
          = f.input :branch_publish, as: :boolean, wrapper: :icheck_form_left_text
          = f.input :branch_date_published, wrapper: :datepicker, icon: "fa fa-calendar", :input_html => {minlength: 8, required: true, data: { :mask => "9999-99-99"}}
          = f.input :address_lat
          = f.input :address_lng
          = f.input :address_country
          = f.input :address_city
          = f.input :address_zip_code
          .form-group
            = f.input :address_street, wrapper: :address, maxlength: 255
            = f.input :address_real_estate, wrapper: :address_nr, input_html: {:min => 1}
            #slash_adress
              = f.input :address_apartment,wrapper: :address_nr, input_html: {:min => 1}
          .hr-line-dashed
          .form-group
            .col-sm-4.col-sm-offset-2
              = f.submit "Save", class: "btn btn-primary"

url i method do simple_form generuje w helperze.

Moje pytanie brzmi czy jest sens używać w tym przypadku form object?
Może ma ktoś pomysł jak lepiej to rozwiązać za pomocą form object?
Pytanie na stackoverflow:
https://stackoverflow.com/questions/42714405/use-form-object-for-the-new-create-and-edit-update

Wasciwie to chyba nie potrzebujesz tutaj form objectu. Z tego co widze powielasz metody zawarte w modelu Branch. Mozesz dodac adres do brancha za pomoca nested_attributes w jednym formularzu.

Dzięki :slight_smile: Czyli w w prostym CUD jest sens stosować form object? A w bardziej złożonych przypadkach trzymać się Rails Way?

Tak, całą “sztuką” jest zdefiniować kiedy kończy się prosty CRUD, a zaczyna moment w którym FO jest przydatny.

Jeśli mogę coś zrobić za pomoca gotowych narzędzi w Rails, to staram się tego trzymać. Stosowałbym form object jeśli jednorazowo musiałbym przedstawić dane w formularzu np z kilku modeli do dropdownów albo modyfikować jakoś dane przed zapisem. W tym przypadku wydaje mi się, że nie jest to konieczne.

Kiedyś spotkałem się z opinią, że FO warto jest używać ponieważ uszczupla nam model itd.

W tym przypadku, większość rzeczy z modelu powieliłeś w FO więc masz 2x wiecej kodu niż potrzeba. Moglbyś użyc dziedziczenia FO z modelu Branch lub użyć delegacji w FO do modelu Branch, wtedy wykorzystywałbyś już istniejące metody dla modelu ( walidacja, save itp) i nie powielał kodu. Jednak, naprawde nie widze sensu uzywania FO w kodzie który wkleiłeś, poniewaz nie masz zdefiniowanych metod ktore nie bylby juz dostepne w modelu.

Dzięki za opinię. Na siłę chciałem napisać to w FO :slight_smile: