Using after_create to update a cart's order_id - ruby-on-rails

First, I have a cart stored in a session. The carts contain line items(products). The checkout button leads to a Order.new form with the cart_id passed to the new order(cart has_one order, order belongs_to cart).
Hopefully so far so good.
The cart params contains an order_id, that initially is nil, until the user hits the checkout button. When the order is created, the order_id in the cart is still nil. How can I use an after_create on the order model to update the cart to now have a valid order_id?
I have a current_cart method in my application controller, so I have access to current_cart in all my controllers, but I can't access it in the model -> as it is stored in a session and I've heard I don't want to be accessing sessions in the model anyway.
How one one recommend doing this?
I'll show some code in case my description wasn't clear.
orders_controller.rb
def create
#cart = current_cart
#order = #cart.build_order(order_params)
if #order.save
redirect_to #order, notice: "The order has been successfully created. Below are your shipping options."
else
render action: "new"
end
end
rails console
>> #order.cart.order_id
=> nil
Does it even really matter? Maybe I'm being paranoid, but I think it would be good practice to have access to the order_id from the cart as well as the cart_id from the order.
Thanks all,
Pat

You do not need to store the order_id in both directions, in fact you are probably creating more problems than you are hoping to solve with this design. For example, you will have to keep the value up to date in both directions, either when the order's cart changes or the other way around.
Rails is smart enough by itself to figure out which record is associated with which, even without an order_id on the Cart model:
cart = Cart.last
cart.order
#=> #<Order ...>
order = Order.last
order.cart
#=> #<Cart ...>
I also want to point out that you probably do not even need to persist Cart to the database, since it only makes sense in the context of a single browser session. You could instead design it as a plain old Ruby object (just a class, not inheriting from ActiveRecord::Base) and further simplify your design that way.

Related

Rails 4 accepts nested attributes id can't be blank

I am trying to update two models with one form. Everything seems to checkout, expect except both models will not save (not valid) because the required customer_order_id. How do I save the customer_order first, so that it will generate an id and then populate the payment?
def create
#customer_order = CustomerOrder.new(customer_order_params)
if current_user.id.present?
#customer_order.user_id = current_user.id
#customer_order.payments.first.user_id = current_user.id
end
respond_to do |format|
if #customer_order.save
format.html { redirect_to #customer_order, notice: 'Customer order was successfully created.' }
When I check if the model(s) are valid? here is the error.message
{:"payments.customer_order_id"=>["can't be blank"], :payments=>["is invalid"]}
It was my understanding that the customer_order would create the order first, then create the Payment. Thanks in advance for your help.
Validations run before saving to the database. It will be impossible to validate the presence of customer_order_id in this case since the customer order is not yet saved.
Consider validating the presence of customer_order instead. This allows the validation to pass when a customer order is present whether it is saved or not.
Caveat
When building records through associations and nested attributes ActiveRecord will make assignments based on inverse associations. In your case the inverse associations being used are payments (on CustomerOrder) and customer_order (on Payment). ActiveRecord will use those to set payment.customer_order correctly when a payment is built through nested attributes. If ActiveRecord in unable to determine the inverse associations (due to naming, joins in the associations, etc.) then you may need to explicily add the :inverse_of option to each association.
In your after create hook create the first payment ( in the customer_order model)
#Customer order model
after_create :create_first_payment
def create_first_payment
self.payments.create(user_id: user_id)
end

rails revert a activerecord update?

Let's say I have a use case where a user can submit an order. This order contains some information of how many items in my inventory. However, a user can also cancel this order after the order is created. In that case, the inventory should be added back.
I'm wondering how shall I implement this in rails? I'm not sure the correct google key words to search for it
Any pointers would be appreciated!
I think it would be better to implement the logic explicitly. So have an action that deletes the Order and also sets the inventory back to what it should be.
I thinks its ok to go straight way to code
inventory has_many items
order has_many items
items belong_to order
items belong_to inventory
when order created deducts items_amount in inventory
when order cancel(remove, destroy) add items_amount in inventory
Elaborating on #user2280271's answer I'd build a service object to take care of the logic, and then just call it from the controller, here's a great post on service objects: http://brewhouse.io/blog/2014/04/30/gourmet-service-objects.html
So maybe you'll end up with something like this:
class OrdersController
def cancel
#order = Order.find(params[:id])
if CancelOrder.call(#order)
redirect_to order_path(#order), notice: 'Your order has been canceled'
else
redirect_to order_path(#order), alert: "There's been an error while canceling your order"
end
end
end

Rails save copy of original when model is edited

I have a simple scaffold:
rails g scaffold Order name:string notes:text
I would like for the user to be able to edit their order but keep copies of the past versions for reference. I considered adding a separate "Revision" model that would hold the text instead, but I would also like the form to stay populated with the most recent information for easy editing.
sorry for not posting more code, I'm not sure what would be helpful
I think that the better idea is create two models: Order and OrderHistory
Order will have a relation 1 to much to OrderHistory.
In update action of your OrderController you have to create a OrderHistory with the previous data and with foreign key pointing to Order. Also you can use default rails fields like create_date for getting the last revised version and mantain a good history of them.
Is a bad practise use Model Order for do that as I said.
class OrderController < ApplicationController::Base
before_action :set_order #implement this function
def update
orderHistory = OrderHistory.create(#order.params) #this code can fail I don't remmember how exactly do that now
respond_to do |format|
if #order.update(order_params)
orderHistory.save
else
#response in fail case
end
end
end
end

Rails forms - Should I build `accepts_nested_attributes_for` associations in the Controller, Model, or View?

The Question
I have a parent that accepts_nested_attributes_for a child. So, when I have a form for the parent, I need to build the child so I can display form fields for it as well. What I want to know is: where should I build the child? In the Model, View, or Controller?
Why I Am Asking This
You may be shaking your head and thinking I'm a madman for asking a question like this, but here's the line of thinking that got me here.
I have a Customer model that accepts_nested_attributes_for a billing_address, like so:
class Customer
belongs_to :billing_address, class_name: 'Address'
accepts_nested_attributes_for :billing_address
end
When I present a form for a new Customer to the user, I want to make sure there is a blank billing_address, so that the user actually sees fields for the billing_address. So I have something like this in my controller:
def new
#customer = Customer.new
#customer.build_billing_address
end
However, if the user doesn't fill out any of the billing_address fields, but tries to submit an invalid form, they will be presented with a form that no longer has fields for the billing_address, unless I put something like this in the create action of my controller:
def create
#customer = Customer.new(params[:customer])
#customer.build_billing_address if #customer.billing_address.nil?
end
There is another issue, which is that if a user tries to edit a Customer, but that Customer doesn't have an associated billing_address already, they won't see fields for the billing_address. So I have to add somethign like this to the controller:
def edit
#customer = Customer.find(params[:id])
#customer.build_billing_address if #customer.billing_address.nil?
end
And something similar needs to happen in the controller's update method.
Anyway, this is highly repetitive, so I thought about doing something in the model. My initial thinking was to add a callback to the model's after_initialize event, like so:
class CustomerModel
after_initialize :build_billing_address, if: 'billing_address.nil?'
end
But my spidey sense started tingling. Who's to say I won't instantiate a Customer in some other part of my code in the future and have this wreak havoc in some unexpected ways.
So my current thinking is that the best place to do this is in the form view itself, since what I'm trying to accomplish is to have a blank billing_address for the form and the form itself is the only place in the code where I know for sure that I'm about to show a form for the billing_address.
But, you know, I'm just some guy on the Internet. Where should I build_billing_address?
Though this advice by Xavier Shay is from 2011, he suggests putting it in the view, "since this is a view problem (do we display fields or not?)":
app/helpers/form_helper.rb:
module FormHelper
def setup_user(user)
user.address ||= Address.new
user
end
end
app/views/users/_form.html.erb:
<%= form_for setup_user(#user) do |f| %>
Note that I had to change the helper method to the following:
def setup_user(user)
user.addresses.build if user.addresses.empty?
user
end
The controller remains completely unchanged.
If you know your model should always have a billing address, you can override the getter for this attribute in your model class as described in the docs:
def billing_address
super || build_billing_address
end
Optionally pass in any attributes to build_billing_address as required by your particular needs.
You would use build if you want to build up something and save it later. I would say, use it in nested routes.
def create
#address = #customer.billing_addresses.build(params[:billing_address])
if #address.save
redirect_to #customer.billing_addresses
else
render "create"
end
end
Something like that. I also use the build when I'm in the console.
You have to remember the principles of MVC, which is to create DRY(don't repeat yourself) code, which is efficiently distributed between the various moving parts of the app
accepts_nested_attributes_for Is Great For Keeping Things DRY
accepts_nested_attributes_for is a model function which allows you to pass data through an association to another model. The reason why it exists is to give you the ability to populate another model's data based on a single form, and is excellent for extending functionality without too much extra code
The problem you're citing is that if you want to use the code in other areas of the app, you'll end up having all sorts of problems
My rebuttal to that is in order to create as efficient an application as possible, you want to write as little code as possible - letting Rails handle everything. The accepts_nested_attributes_for function does allow you to do this, but obviously has a cost, in that you have to accommodate it every time you want to use it
My recommendation is to use what you feel is the most efficient code you can, but also keep to conventions; as this will ensure speed & efficiency
You should handle all these scenarios in controller, since it is not a responsibility of model.
Just in terms of keeping things DRY, you can write a method,
def build_customer(customer)
customer.build_billing_address if customer.billing_address.nil?
#add more code if needed
end
And inside controller you can call this method wherever it is needed. e.g.
def create
#customer = Customer.new(params[:customer])
if #customer.save
redirect_to #customer.billing_addresses
else
build_customer(#customer)
render "new"
end
end

Is there a more elegant way to do this in Rails (has_one association)

Hey, I'm very new to Ruby and Rails. I was wondering if I was doing this the right way.
#user = User.new(params[:user])
#student.user = #user
#student.save
if #user.save
...rest of the code
In my app, a Student has one User, and each User belongs to a Student (think of a User as an Account). The association itself isn't my question though; is the block of code above the best way to save a new User that's associated to a Student?
I'm surprised nobody mentioned the right way.
#user = #student.build_user(params[:user])
if #user.save
# ... rest of the code ...
end
This way #user will be built already associated with #student.
If the correspondence between user and student is 1:1 may be this relationship is redundant.
(maybe I'm missing something).
A cooler approach would be perform all the operations you need to the user object and then the last one would be:
#student.user = #user
#student.save
If the Student has_one User, then you should put the foreign key on the User. As in, the User will have the student_id column in the database. A user_id column on the Student isn't necessary.
In which case you can just do this:
#user = User.new(params[:user])
#user.student = #student
if #user.save
...rest of the code
And you won't even need to modify #student.
I think you have #-overload here. Only use # if you want to make the variable an instance variable of the class, rather than a local variable that only exists inside your function. Anyway, here's one possible way to do it:
#student.user = User.create(params[:user])
#student.save
if #student.user.new_record?
# didn't get saved...
end
ActiveRecord::Base#create creates a new object, tries to save it to the database, and then returns the object, and is a useful shortcut for the new-save pattern. It always returns the object, though, so you need to ask whether or not it was successfully saved, hence new_record?.
The question that comes up for me is whether the relationship between Student and User is a has-a or an is-a relationship. It has to do with object modeling of the domain problem.
I suspect it may be is-a, in which case you don't want has_one and belongs_to--known as "composition"--but instead Single-Table Inheritance (STI)--known as "inheritance".
The questions to ask are:
1. Is a Student also a User, i.e. does a Student have the same attributes and methods as a User, plus more methods or restrictions? Since in this case the Student IS also a user, the question of whether a Student can have 0, 1 or more Users does not apply.
2. Does the Student "have" a User in the sense that it could also have 0 Users, or perhaps more than 1?

Resources