――It seems that you can write a form for a model that is not tied to ActiveRecord in a monolithic Rails application by using Form Object. ――I knew it for a long time, but I had never actually implemented it by myself. ――This time, I got an opportunity to implement it by myself, so I will keep a record. ――Since it is announced and the implementation is blurred to the extent that there is no problem, there may be typographical errors in the process. Please note.
--I want to add a page for admins that sends push notifications to the app.
--First, I implemented it as follows without using Form object.
config.rb
namespace :admin do
resources :push_notifications, only: [:new, :create]
end
app/controllers/admin/push_notifications_controller.rb
class Admin::PushNotificationsController < Admin::BaseController
def new
@demo_form = Admin::PushNotificationDemoForm.new
end
def create
@demo_form = Admin::PushNotificationDemoForm.new(
# user_If the id is an empty string or a string that cannot be converted to an int, an error will be thrown, but in form f.number_It is good because the field is specified
user_id: Integer(demo_form_params['user_id']),
title: demo_form_params['title'],
body: demo_form_params['body'],
url: demo_form_params['url'],
)
if @demo_form.save
flash[:notice] = "user#{@demo_form.user_id}of#{Rails.env}アプリへofプッシュ通知が送信されました。"
redirect_to new_admin_push_notification_path
else
flash.now[:alert] = @demo_form.errors.full_messages.join(" / ")
return render :new
end
end
private
def demo_form_params
params.require(:admin_push_notification_demo_form).permit(:user_id, :title, :body, :url)
end
end
app/views/admin/new.html.haml
%h1= "Test sending push notifications"
= form_with(url: admin_push_notifications_path, local: true) do |f|
.div
= f.label :user_id, 'User ID (Mandatory)'
= f.number_field :user_id, autofocus: true, required: true
.div
= f.label :title, 'Title: (Mandatory)'
= f.text_area :title, autofocus: true, required: true
.div
= f.label :body, 'Body'
= f.text_area :body, autofocus: true
.div
= f.label :url, 'URL'
= f.text_field :url, autofocus: true
.div
= f.submit("Send")
――As you can see, the repeated validations performed in the Controller before the desired process `` are impressive in a bad sense.
config.rb
namespace :admin do
resources :push_notifications, only: [:new, :create]
end
app/controllers/admin/push_notifications_controller.rb
class Admin::PushNotificationsController < Admin::BaseController
def new
@demo_form = Admin::PushNotificationDemoForm.new
render 'new'
end
def create
@demo_form = Admin::PushNotificationDemoForm.new(demo_form_params)
if @demo_form.save
flash[:notice] = "user#{@demo_form.user_id}A push notification has been sent to."
redirect_to new_admin_push_notification_path
else
flash.now[:alert] = @demo_form.errors.full_messages.join(" / ")
return render :new
end
end
private
def demo_form_params
params.require(:admin_push_notification_demo_form).permit(:user_id, :title, :body, :url)
end
end
app/forms/push_notification_demo_form.rb
class Admin::PushNotificationDemoForm
include ActiveModel::Model
validates :user_id, presence: true
validates :title, presence: true
validate :is_valid_user_id_and_has_individual_job
def save
return false if invalid?
VisitNotificationService.new.notify(
user_job_id: @job.id,
user_id: @user_id,
push_notification_content: VisitNotificationService.build_push_notification(
title: @title,
body: @body,
url: @url,
)
)
return true
end
private
def is_valid_user_id_and_has_individual_job
user = User.find_by(id: @user_id)
unless user
errors.add(:user_id, 'Is invalid.')
return false
end
@job = user.individual_job
unless @job
errors.add(:base, "Specified User(#{@user_id})There is no Individual Job associated with.")
return false
end
true
end
end
app/views/admin/new.html.haml
%h1= "Test sending push notifications"
= form_for(@demo_form, url: admin_push_notifications_path, local: true) do |f|
.div
= f.label :user_id, 'User ID (Mandatory)'
= f.number_field :user_id, autofocus: true, required: true
.div
= f.label :title, 'Title: (Mandatory)'
= f.text_area :title, autofocus: true, required: true
.div
= f.label :body, 'Body'
= f.text_area :body, autofocus: true
.div
= f.label :url, 'URL'
= f.text_field :url, autofocus: true
.div
= f.submit("Send")
--As you can see, the validation (essentially business logic) is pushed into the form object so it can be implemented very nicely.
--In my implementation this time, the model specified by form_with and the url are different. --It's also convenient to be able to store error messages in errors, which are instance variables of the form object (like active record).
Recommended Posts