Search code examples
ruby-on-railsruby-on-rails-3ruby-on-rails-4

Rails call custom validation before .new or .create


I make objects in controller's loop.

I need to check pet_name array before loop starts. (because i got undefined method 'each' for nil:NilClass when params[:pet_name].each do |pid|) runs)

But my validation always called after User.new or User.create. I want to change to validate as when i push submit button and check validation, and redirects back when pet_name array is nil. Ho can i change my code?

Controller

  def create
    user_name = params[:user_name]
    
    params[:pet_name].each do |pid|
      @user = User.new
      @user.name = user_name
      @user.pet_name = pid
      render :new unless @user.save
    end
    redirect_to users_path
  end

User.rb

class User < ApplicationRecord
  has_many :pet

  validates :name, presence: true
  validates :pet_name, presence: true
  validate :check_pet
  def check_pet
    if pet_name.nil?
      errors.add(:pet_name, 'at least one pet id')
    end
  end
end

Prams structure

  { name: 'blabla', pet_name: ['blabla', 'blablabla', 'bla'] }

Solution

  • Sorry but that isn't even close to how you approach the problem in Rails.

    If you want a user to have many pets and accept input for the pets when creating users you need to create a working assocation to a Pet model and have the User accept nested attributes:

    class User < ApplicationRecord
      has_many :pets # has_many assocations should always be plural!
      validates :name, presence: true
      validates :pets, presence: true
      accepts_nested_attributes_for :pets
    end
    
    # rails g model pet name user:reference
    class Pet < ApplicationRecord
      belongs_to :user
      validates :name, presence: true
    end
    
    class UsersController < ApplicationController
      def new
        @user = User.new(user_params)
        3.times { @user.pets.new } # seeds the form with blank inputs
      end
    
      def create
        @user = User.new(user_params)
        if @user.save
          redirect_to @user, 
            success: 'User created',
            status: :created
        else
          render :new, 
            status: :unprocessable_entity
        end
      end
    
      private
    
      def user_params
        params.require(:user)
              .permit(:name, pets_attributes: [:name])
      end
    end
    
    <%= form_with(model: @user) do |form| %>
      <div class="field">
        <%= form.label :name %>
        <%= form.text_input :name %>
      </div>
    
      <fieldset>
         <legend>Pets</legend>
         <%= form.fields_for(:pets) do |pet_fields| %>
         <div class="nested-fieldset">
           <div class="field">
             <%= pet_fields.label :name %>
             <%= pet_fields.text_input :name %>
           </div>
         </div>
         <% end %>
      </fieldset>
    
      <div class="actions">
         <%= f.submit %>
      </div>
    <% end %>
    

    This is a pretty advanced topic and you should have your Rails CRUD basics well figured out before you attempt it. You should also consider if you instead want to use a separate controller to create the pets one by one as a nested resource after creating the user.