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
Related
I am trying to implement a retweet (repost) function for a Twitter clone. I initially thought I might need a separate retweet model, but I want to try and implement it using my existing tweet (micropost) model.
I'm having trouble wrapping my head around the schema and what migrations I would need to run. A Micropost would need a Repost column, but would a user need a repost_id? So I would add a Repost column to Micropost, and it would reference User? Or should I actually be adding the column to the User model?
Also if the repost function would work similar to create, would it not?
def repost
#micropost = Micropost.find(params[:id])
#repost = #micropost.repost by current_user
if #repost.save
flash[:success] = "Repost created!"
redirect_to root_url
else
#feed_items = current_user.feed.paginate(page: params[:page])
render 'static_pages/home'
end
end
Is there any way to edit my current Micropost model, or do I need to utilize a specific Repost model?
The way I go about is asking if I need a one to many or a many to many relationship between reposts and users. A user can have (make) many reposts and a repost can be reposted by many users, so that would be a many to many relationship. Meaning you need a joint table between users and micropost - and the optimal name for it in this case is repost.
If you want to think about it the other way round - the way you proposed it: if you would reference the user id in the micropost table in a user_id column, there wouldonly be "enough space" to save one user id... so that would be a one to many association, which doesn't make that much sense for reposts.
For your question about the method, you will need to create a new repost instance once a user clicks on a button (submits a form) to repost. You should have a look at nested resources: https://guides.rubyonrails.org/routing.html#nested-resources
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.
I'm trying to create an ordering system so that when an order is placed, the customer logs in/registers and the order is added to their index. I have two controllers and all their respective models/views etc - called orders and customers. I'm assuming I'd need to add a method so that the customer_id is added to the order when it is placed, and then I could display all orders with that id in that particular customer's index - i.e. they can only see their own orders.
It's probably quite a simple question, apologies! But I'm new to rails and can't figure out how to get started on this one. Please can someone suggest how I would go about doing this?
Edit:
Thanks for everyone's help! How would I get the customer index to display only orders for that customer? Would I store the customer_id in a session when they log in, then place that in the order, then find all orders with that id in the index page? I think I know how to get their id into a session at login, but am unsure how to extract this into the customer index view. Any help would be much appreciated!
IF you create your orders table to have a customer_id, and fill that id when the customer places an order, then the customer view of orders should always be scoped to their orders. You will need to differentiate between a customer user and your admin user (perhaps you?), as in (something like):
class customer
has_many :orders
end
class order
belongs_to :customer
end
class OrdersController
def index
# determine the current customer (or admin), then
if current_user.isAdmin?
#orders = Order.all
else
#orders = current_user.orders
end
end
end
#railsdog is correct, however, here is the other part of the puzzle (how to get your customer's ID in the order in the first place):
class OrdersController
def create
#order = Order.new(params[:order])
#order.customer_id = current_user.id
# ...
end
#...
end
Note here the approach of setting the customer_id in the controller, not from the form. The reason for this is that even a hidden form field can be changed by the client, so if you did that without checking on the server side, an attacker could place orders for other customers.
Hello i have a rails app that handles sales, right now, what i need is to be able to delete the sale in order to keep accounting clear, but log somewhere else, the details of that record.
I am thinking i may need to create a logger, but have no idea how, or maybe another object who gets created on the destroy of the sale.
Thanks in advance
Just an idea - you could add a column to your current table that would act as a "deleted" flag (I've always called this a logical delete). Then you could add a default scope to filter out "deleted" records and add some named scopes that would include the "deleted" records when you need them.
acts as paranoid is a plugin that will handle this for you, but if there's anything about it you don't like you can roll your own version like Andy suggested, maybe with a deleted_at timestamp. You might try overriding the destroy action on the model - I haven't tried it myself, but something like this should work:
class Sale < ActiveRecord::Base
def destroy
update_attributes(:deleted_at => Time.now)
end
end
Like you said, you could create another object/model that you create a new instance of each time a Sale is deleted. Name it SaleRecord or SaleHistory... something like that. Then you can retrieve these records and do whatever. Then, a nice use case would be to look up sales records for a particular product to calculate statistics on how popular it was...
i ended up creating a delete_sale object, and then on my sale observer i created and populated with the data of the sale just before it was destroyed.
#delsale = DeletedSale.create
#last_deleted_sale = DeletedSale.find(:last)
ActiveRecord::Base.connection.execute "UPDATE deleted_sales SET name = #{#venta.id} WHERE id = #{#last_deleted_sale.id};"
#delsale.update_attributes :cliente => #venta.cliente.name
#delsale.update_attributes :vendedor => #venta.vendedor.name
#delsale.update_attributes :instalador => #venta.instalador.name
#delsale.update_attributes :precio_de_venta => #venta.precio_de_venta
#delsale.update_attributes :precio_de_instalacion => #venta.precio_de_instalacion
if #venta.producto_id?
#delsale.update_attributes :producto_id => #venta.producto_id
end
if #venta.cart_id?
#delsale.update_attributes :cart_id => #venta.cart_id
end
I've got a User model that has many Items. A Rating belongs to a User and an Item.
In the DB, I have set ratings.user_id to be not NULL.
when I am creating an Item, I would like to do this:
def create
current_user.items.create(params[:item]).ratings.create(params[:rating]
redirect_to items_path
end
However, this balks with an SQL error "user_id cannot be nil"
so I rewrote the create method as
def create
current_user.items.create(params[:item]).ratings.create(params[:rating].merge({:user_id => current_user}))
redirect_to items_path
end
which works fine.
However, I had thought that chaining the create methods off the current user's receiver would have populated the rating's user_id. Anyone know why not?
TIA.
I'd recommend you normalize this if possible in the database. Maybe take out the user_id attribute from the ratings table and if you need it in your model get it through a join using a :through method
class Rating
has_many :items
has_one :user, :through=>:items
If you created and saved the Item, then made a Rating from that item, it wouldn't pass the user along to the Rating, right? You'd just refer to it as #rating.item.user, right?
When you think about it like that, you wouldn't expect the Item created via the current_user to pass the user information along to the rating.
Make me wonder if you really need the user has_many ratings relationship.
Because Item has many Ratings and that association does not know about the user id. Given that association chain Item would have a user id because it belongs to a user. And Rating would have an item id because it belongs to an item. But the Item to Rating assocation doesn't know anything about a user unless you tell it.