Agile Web Development with Rails Translator (18)
The only interesting thing here is the code associated with the selection list. We have already assumed that the list of valid payment options is a property of the order model-it will be an array of an array in the model file. The first element of each sub-array is made into a string that is displayed as an option for the selection, and the second value is stored in the database. [If we expect the non-rails application to be able to update the Orders table, we might want to move the payment type list into a separate table and have a foreign key for the orders column reference to the new table. In this environment, rails also provides good support for generating list listings. ] We'd better define this array of options in model ORDER.RB before we forget.
Payment_types = [
["Check", "Check"],
["Credit Card", "CC"],
["Purchase Order", "PO"]
].freeze # Freeze to make this array constant
If there is no current option in model, we want to display some hint text in the input area of the browser. We start by adding a new option to this by the options returned in the model. This new option has a suitable display string and a null value.
After all this has been done, we can open the browser. To add some items to the shopping cart, click on the Checkout link and you will see the new Payment page as shown in Figure 9.1:
It looks good. Of course, if you click on the Checkout button, you will be greeted with the following:
Unknown Action
No Action responded to Save_order
Let's continue to implement the Save_order () action in the store controller.
The method must:
1. Capture a value from the form to assemble a new Order model object.
2. Add items to the order from our shopping cart.
3. Confirm and save order. If it fails, display the appropriate information and let the user correct the problem.
4. Once the order is successfully saved, the catalog page is re-displayed, including the information confirming that the order has been saved.
The method ultimately looks like the following:
Line 1 def save_order
-@cart = Find_cart
-@order = Order.new (Params[:order])
-@order. Line_items << @cart. Items
5 if @order. Save
-@cart. empty!
-Redirect_to_index (' Thank for your order. ')
-Else
-Render (: action = ' checkout ')
Ten End
-End
In line 3rd, we create a new Order object and initialize it with the form data. In this example we want all the form data to be related to the order object, so we chose the: Order Hash table from the parameter (we'll discuss how the form is connected to the model on page 341). The next line adds the items that are already in the shopping cart to order-the session data is still there after the last action is processed. Note that for those different foreign key fields we do not need to do any special processing, such as setting the ORDER_ID behavior in line item to the newly created order lines. Rails does this automatically when we use Has_many () and belongs_to () declarations (which we add separately to order and LineItem model).
Then on line 5th, we tell the order object to save itself (as well as its children, commodity items) to the database. During this time, the order object will complete the validation (but we'll handle it in a moment). If the save is successful, we empty the cart to prepare for the next order and re-display the catalog. Use our Redirect_to_index () method to display a delightful message. Conversely, if the save fails, we re-display the checkout form.
The last thing we do before we call a client, remember when we show her our first product Management page. She asked us to increase the validity confirmation. We might as well do the same for our checkout page. For now, we only check that each field in order is given a value. We know how to do this--add a validates_presence_of () method to the order model:
Validates_presence_of:name: Email, address,:p Ay_type
----------------------------------------------------------
Joe asked ...
Don't you create a copy of orders?
Joe saw our controller in two actions, checkout and Save_order, created the order model object, and he wondered why this did not result in a duplicate order in the database. The answer is simple: The checkout action creates an order object in memory and simply passes the template code to work together. Once the response is sent to the browser, the particular object is thrown away, and finally it is reclaimed by the Ruby garbage collector. It has never been exposed to a database.
The Save_order action also creates an order object, which is assembled from form fields. This object is stored in the database. Therefore, the model object plays two roles: they map data into and out of the database, but they are also a normal object to hold business data. When you typically call the Save () action, they affect the database.
---------------------------------------------------
So, as a first test of this, click the Checkout button without filling out any form fields. We want to see the checkout page re-displayed and give some error information about the empty field. Instead, we just see the checkout page-there's no error message. We forgot to tell rails to write them out. [If you ' re following along @ Home and get the message No action responded to Save_order, it's possible that you added The Save_order () method after the private declaration in the controller. Private methods cannot be called as actions.]
Any errors related to the validation or preservation of the model should be kept together by the model. Here's another helper method, Error_messages_for (), which extracts and formats these errors in view. We just need to add a line at the beginning of the checkout.rhtml file.
<%= error_messages_for ("Order")%>
As with the validation of the Admin page, we need to add the SCAFFOLD.CSS style sheet to our store layout file to get the appropriate format for these errors.
<%= Stylesheet_link_tag "Scaffold", "Depot",: Media = "All"%>
When we're done, submitting an empty checkout page will show us a lot of high-brightness errors, as shown in Figure 9.2.
If we fill in some data, as shown in Figure 9.3, click Checkout, we should go back to the catalogue page, as shown at the bottom of the figure. But does it work. Let's look at the database.
depot> MySQL Depot_development
Welcome to the MySQL Monitor. Commands End With; or G.
Mysql> select * from Orders;
+----+-------------+-----------------------+-------------+----------+
| ID | name | email | Address | Pay_type |
+----+-------------+-----------------------+-------------+----------+
| 3 | Dave Thomas | customer@pragprog.com |
+----+-------------+-----------------------+-------------+----------+
1 row in Set (0.00 sec)
Mysql> select * from Line_items;
+----+------------+----------+----------+------------+
| ID | product_id | order_id | Quantity | Unit_price |
+----+------------+----------+----------+------------+
| 4 | 4 | 3 | 1 | 29.95 |
+----+------------+----------+----------+------------+
1 row in Set (0.00 sec)
Save it. At least, now we can show it to our customers. She likes it very much, except ... Do you think we should add a summary of the contents of a shopping cart on the checkout page? It sounds as if we need a new repeating process.
123 Main St| Check |