-1

In my communication table I have some columns:

id | UserID | CommunicationMode | CommunicationDetail | Private
1  | 1      | Phone             | 123456789           | 1
2  | 1      | Email             | abc@abc.com         | 1

And I want to update column value using where clause using loop like below:

create
    @user_communication=Communication.where(:UserID => current_user.id)
    if !@user_communication.blank?
      @user_communication.each do |c|
        if params[:ChkBx_Phone].to_i == 1
          c.where("CommunicationMode == 'Phone'").update_attribute( :Private, "1")
        elsif params[:ChkBx_Phone].to_i == 0
          c.where("CommunicationMode == 'Phone'").update_attribute( :Private, "0")
        end
        if params[:ChkBx_Email].to_i == 1
          c.where("CommuicationMode == 'Email'").update_attribute( :Private, "1")
        elsif params[:ChkBx_Email].to_i == 0
          c.where("CommunicationMode == 'Email'").update_attribute( :Private, "0")
        end
      end  
    end
end

I want to check above that if Phone checkbox is checked then it updates Private column with value 1 else 0 where CommunicationMode is Phone and for email I want to check that if Email checkbox is checked then it updates Private column with value 1 else 0 where CommunicationMode is Email

And below is Phone and Email checkboxes:

    <table>
    <% @user_communication.each do |c| %>
           <tr>
               <td>
                  <% if c.CommunicationMode == "Phone" and c.Private.to_s == "1" %>
                            <input type="checkbox" name="ChkBx_Phone"
                        id="ChkBx_Phone" value="1" checked = "checked">
                            <%= label(:lb_Phone, "Phone") %>
                  <% elsif c.CommunicationMode == "Phone" and c.Private.to_s == "0" %>
                                <%= check_box_tag 'ChkBx_Phone' %>
                                <%= label(:lb_Phone, "Phone") %>
                  <% end %>
               </td>
           </tr>
           <tr>
               <td>
                   <% if c.CommunicationMode == "Email" and c.Private.to_s == "1" %>
                        <input type="checkbox" name="ChkBx_Email"
                        id="ChkBx_Email" value="1" checked = "checked">
                        <%= label(:lb_Email, "Email") %>
                   <% elsif c.CommunicationMode == "Email" and c.Private.to_s == "0" %>
                        <%= check_box_tag 'ChkBx_Email' %>
                        <%= label(:lb_Email, "Email") %>                    
                   <% end %>
              </td>
           </tr>
    <% end %>
</table>

But I am getting an error below:

undefined method `where' for #<Communication:0x4bc5490>

But when I am using below code:

create
    @user_communication=Communication.where(:UserID => current_user.id)
    if !@user_communication.blank?
      @user_communication.each do |c|
        if params[:ChkBx_Phone].to_i == 1
          puts "Hassan2"
          c.update_attribute( :Private, "1")
        elsif params[:ChkBx_Phone].to_i == 0
          puts "Ali2"
          c.update_attribute( :Private, "0")
        end
      end  
    end
end

Its working fine but it update both Private column value of Phone and Email and I have checked only Phone checkbox. Kindly suggest me, waiting for your reply. Thanks.

2 Answers2

0

In your code

 @user_communication.each do |c|    
 # Your logic
 end

you looping @user_communication then c variable will contain Communication object not Active record relation,then you are doing c.where(),c contain Communication object. But .where Active record relation.so it is throwing error.

Jenorish
  • 1,694
  • 14
  • 19
0

As Kingston said, when you iterate through the @user_communication collection, the c variable in the block is in fact a concrete Communication object, not an ActiveRecord::Relation object, which is what contains the .where query methods.

Additionally, there are a couple of other problems I have noticed. Some, I cannot directly help you solve because I don't know how your system works, but I will point them out for you and try to suggest an alternative, and hopefully you can figure out the best way to fix them in the context of your system's requirement.

In regards to the create method, there are two approaches that will have the same end result, but one of them I would consider the "naïve approach". I'm going to show the naïve approach only to give you a direct alternative/answer to the first create method you posted, and so you can see the difference and figure out the best solution:

Naïve Approach:

def create
  @user_communication=Communication.where(:UserID => current_user.id)

  # Transaction so we don't execute X number of individual update statements
  Communication.transaction do 

    @user_communication.each do |c|

      if c.CommunicationMode == "Phone"
        # Note: Its not necessary to use the ternary operator if you're not 
        # comfortable with it, however it can save you a few lines of code.
        c.update(Private: (params[:ChkBx_Phone].to_i == 1 ? "1" : "0") )
      elsif c.CommunicationMode == "Email"
        c.update(Private: (params[:ChkBx_Email].to_i == 1 ? "1" : "0") )
      end

    end # end communication loop

  end # end transaction
end

This will iterate through the the Communication objects for that user id, and update each one based on its CommunicationMode value, setting it to a 1 or a 0, depending on the value of the two checkboxes. What you will see in your log are several individual UPDATE statements for each Communication object you have in the collection. Normally, the database will execute the UPDATE statement immediately, since the updates are wrapped in their own transactions, and consequently, this becomes rather costly over time if you have a large number of records. So to avoid that problem, as you can see I've wrapped the entire operation in a transaction, and consequently, the updates are only committed at the end; this is much more efficient (you can do some experiments to verify this yourself; you should be able to see a noticeable time difference with and without the transaction wrapper).

And now, the "potentially" better approach:

The "Potentially" better / less naïve approach

... other method code
Communication.where(UserID: current_user.id, CommunicationMode: "Phone").update_all(Private: (params[:ChkBx_Phone].to_i == 1 ? "1" : "0") )

Communication.where(UserID: current_user.id, CommunicationMode: "Email").update_all(Private: (params[:ChkBx_Email].to_i == 1 ? "1" : "0") )

This will execute only two UPDATE statements (which you can also wrap in a transaction if you like). This should execute even quicker than the above approach, and saves you even more lines of code.

Note that both this and the naïve approach can be moved to a method in the Communication model, to keep heavy model-related operations out of the controller.

Another issue of note

In your view code, you appear to be iteration over a collection @user_communication, within which you're creating a checkbox for each object:

<input type="checkbox" name="ChkBx_Email" id="ChkBx_Email" value="1" checked = "checked">

Consequently, if you have five objects in that collection, you'll see a list of five checkboxes. Because of the way you've named this input, only one of the values is ever being sent (I believe it is typically the "last" input in the DOM). In other words, if you click "on" the first checkbox, but leave the last one "off", the value of the checkbox will be "off". I have a strong feeling this is probably not how you want your system to behave. Perhaps you will want to place those checkboxes outside of the loop (above), since they don't appear to have anything to do with the Communication objects themselves.

Additionally, an unchecked checkbox is never sent to the server. The line params[:ChkBx_Phone].to_i == 1 will crash if you unchecked that checkbox because params[:ChkBx_Phone] is nil, and you'd be invoking to_i on a nil object. This answer shows you an alternative for this solution. In your case, you will want to make sure of the Rails helper tags, such as check_box_tag, and hidden_field_tag, like so:

<%=hidden_field_tag 'ChkBx_Phone', '0'%>
<%=check_box_tag 'ChkBx_Phone', '1', true %>

As a result, if the checkbox is unchecked, because of the existence of the identically named hidden field input, the server will always receive a value for this parameter.

Community
  • 1
  • 1
Paul Richter
  • 10,908
  • 10
  • 52
  • 85