I am making an app that imitates Instagram. As shown below, there is a post linked to the user, and it has a structure that has multiple images.
It was nice to have created a new post form using form object with this structure,
▼ Implemented in this article The story of saving an image with carrierwave in a nested form using a form object.
From there, I had a lot of trouble creating the update form, so I'll put together the code I wrote.
The execution environment is as follows.
Rails 5.2.3Ruby 2.6.0Also, I use carrier wave to post images.
I will introduce the code I actually wrote and add an explanation to it.
controller
The instance created by form object is passed to the view in the form of @post_form.
controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :require_login, only: %i(new edit)
  before_action :set_post, only: %i(edit update)
  def new
    @post_form = PostForm.new(current_user)
  end
  def create
    @post_form = PostForm.new(current_user, post_params, post: Post.new)
    if @post_form.save!
      redirect_to root_path
      flash[:success] = "Posted"
    else
      flash.now[:danger] = "Posting failed"
      render :new
    end
  end
  def edit
    @post_form = PostForm.new(current_user, post: @post)
  end
  def update
    @post_form = PostForm.new(current_user, post_params, post: @post)
    if @post_form.save!
      redirect_to root_path
      flash[:success] = "I edited the post"
    else
      flash.now[:danger] = "Post edit failed"
      render :edit
    end
  end
  private
  def post_params
    params.require(:post).permit(:body, photoes: [])
  end
  def set_post
    @post = Post.find(params[:id])
  end
end
view
The view receives an instance in the form model: @post_form. (Actually, the same partial was used for new and edit, so @ post_form was made a local variable called form.)
Ruby:views/posts/_post_form.html.slim
= form_with model: @post_form, local: true do |f|
  .form-group
    = f.label :photoes, t('activerecord.models.images.photo'), class: 'bmd-label-floating"'
    = f.file_field :photoes, multiple: true, class: 'form-control mb-1'
  .form-group
    = f.label :body, t('activerecord.models.posts.body'), class: 'bmd-label-floating"'
    = f.text_field :body, class: 'form-control'
  = f.submit 'register', class: 'btn btn-raised btn-success'
form object
forms/post_form.rb
class PostForm
  include ActiveModel::Model
  attr_accessor :body, :photoes, :user
  validates :body, presence: true
  def initialize(user, params = {}, post: '')
    @post ||= Post.new
    @post.assign_attributes({user: user, body: params[:body]})
    super(params)
  end
  def to_model #I will explain
    @post
  end
  def save!
    return false if invalid?
    if photoes
      photoes.each do |photo|
        @post.images.build(photo: photo).save!
      end
    end
    @post.save! ? true : false
  end
end
The point is that to_model makes the form object behave like a model. What this means is that the form object is a" just class "different from the ActiveRecord model, so the routing path that Rails guesses by default on new / edit forms is applied well. not.
So by making to_model think of form object as if it were a model, Rails' default routing can be used with form object as well.
The implementation around here was very helpful to this article.
Rails: Separate validation from model using Form Object and # to_model (translation)
However, even with this implementation, for the update action, I tried to access POST'posts /: id' and did not follow the routing well.
On the other hand, I was able to find the following two solutions on the net.
-Overwrite the URL of form.
-Change the action to apply using delegate.
In my case, the former was too verbose in code and the latter couldn't work well, so I had no choice but to rewrite routes.rb as follows.
config/routes.rb
post '/posts/:id', to: 'posts#update'
I can't deny the feeling of a monkey patch, but this time I'm happy with this ... I would like to refactor it when my ability improves a little more.
In my work implementation, the update method of the form object didn't take the time to implement it and gave up, but now I have a clue for the implementation: relaxed:
I want to improve my knowledge so that I can share it within the company: sparkles:
Recommended Posts