How to Build Simple Go Lang and CockroachDB Web App with MVC pattern Part 4
Introduction
This is part four in the tutorial explaining how to build a simple Golang and CockroachDB web App with the MVC pattern. This part of the tutorial will discuss how the models work and explain how to create CockroachDB record using Golang. update CockroachDB record using Golang as well as retrieve data in CockroachDB using Golang. The next section, part five in this series, will discuss how to update and delete CockroachDB using Golang.
Prerequisites
- A through understanding of parts one through three of this tutorial.
The Project Directory Structure
New files have been added to the project directory that were used for demonstrations purposes in the previous parts of this tutorial. A quick review of the code follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /webGo /config /db.go /tpl.go /controllers /handlers.go /models /models.go /templates /index.gohtml /insert.gohtml /show.gohtml /new.gohtml /update.gohtml main.go |
An example of the index page of the web app is shown here:
How to View a Single Record
As shown in the following image, click the “View” link on the right side of the table to view a specific record.
Here is an explanation of what is happening in the background:
The “View” link HTML tags [View](https://intern.textbroker.com/a/teamorder-write-submit.php)
is the link “/restaurants/show” and it passes an id
with a corresponding value of {{.Id}}
as the parameter.
The following script shows the “/restaurants/show” string has an equivalent handler in the func main()
:
1 | http.HandleFunc("/restaurants/show", controllers.Show) |
The http.HandleFunc()
takes a string for its first argument and a function in the second argument. In this case the second argument calls the controller.Show
and passing the value of the id (?id={{.Id}}
) via URL parameter.
This is how the program handles the URL that eventually calls to the controllers
, in this case the controller is Show. The codes within this function include:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | func Show(w http.ResponseWriter, r *http.Request) { // (1) if r.Method != "GET" { http.Error(w, http.StatusText(405), http.StatusMethodNotAllowed) return } // (2) resto, err := models.OneRestaurant(r) switch { case err == sql.ErrNoRows: http.NotFound(w, r) return case err != nil: http.Error(w, http.StatusText(500), http.StatusInternalServerError) return } // (3) config.TPL.ExecuteTemplate(w, "show.gohtml", resto) } |
r.Method
will determine if the request is not “GET” and will then return an error 405.- Calling for the model
models.OneRestaurant(r)
to perform a query to locate a specific record matching the value ofr
that is the value ofid
. Then “switch-case” is used to check the conditions of the result. An error will be returned if no record is found. An error 500 will be returned if there is a server-related error.
- After successfully obtaining a set of records, call the template using the
config.TPL.ExecuteTemplate()
passing in thehttp.ResponseWriter
(w). Now the name of the templateshow.gohtml
, theresto
(data), will be used by the template.
The following is the structure of the show.gohtml command:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | {{template "header"}} [« Back to the List](https://intern.textbroker.com/a/teamorder-write-submit.php) ## Restaurant's Name: {{.Name}} Phone Number: {{.Phone}} Email Address: {{.Email}} Stars: {{.Stars}} Category: {{.Category}} {{template "footer"}} |
As mentioned earlier, the controllers.show
calls the models.OneRestaurant
and the results from the models.OneRestaurant
will be stored in the variable resto
.
Here are the codes within this model function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | package models import ( "database/sql" "errors" "net/http" "strconv" "webGo/config" ) type Restaurant struct { Id int Name string Phone string Email string Stars int Category string } func OneRestaurant(r *http.Request) (Restaurant, error) { resto := Restaurant{} id := r.FormValue("id") if id == "" { return resto, errors.New("400. Bad Request") } row := config.DB.QueryRow("SELECT * FROM tblrestaurants WHERE id = $1", id) err := row.Scan(&resto.Id, &resto.Name, &resto.Phone, &resto.Email, &resto.Stars, &resto.Category) if err != nil { return resto, err } return resto, nil } |
This function merely obtains a specific record in the tblrestaurants
and returns the result “resto”. This result will be used by the controllers.Show in the code config.TPL.ExecuteTemplate(w, "show.gohtml", resto)
that displays the results via the template show.gohtml
.
How to Create a New Record
With an understanding of how the Model-View-Controller pattern works in the webApp, proceed with the other features of our webApp.
Create a new record by clicking “Create New Restaurant” just above the table. As shown below, this link is composed of the following tags [Create New Restaurant](https://intern.textbroker.com/a/teamorder-write-submit.php)
and the link restaurants/create
will call the controllers.Create
in the func main().
1 2 3 4 5 | func Create(w http.ResponseWriter, r *http.Request) { Count := models.CreateId() config.TPL.ExecuteTemplate(w, "new.gohtml", Count+1) } |
As shown below, the above code will create an ID for the new record and call the template new.gohtml
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | {{template "header"}} [« Back to the List](https://intern.textbroker.com/a/teamorder-write-submit.php) ## Create Restaurant -form action=" /restaurants/create/process"="/restaurants/create/process"" method=" post"="post""- -label-Restaurant's ID :-/label- -input type=" text"="text"" class=" form-control"="form-control"" name=" resto_id"="resto_id"" maxlength=" 4"="4"" size=" readonly="readonly" value="{{.}}"- -label-Restaurant's Name :-/label- -input class=" form-control"="form-control"" type=" text"="text"" name=" resto_name"="resto_name""- -label-Phone :-/label- -input class=" form-control"="form-control"" type=" text"="text"" name=" phone"="phone""- -label-Email Address :-/label- -input class=" form-control"="form-control"" type=" text"="text"" name=" email"="email""- -label-Stars :-/label- -input class=" form-control"="form-control"" type=" text"="text"" name=" stars"="stars""- -label-Category :-/label- -input class=" form-control"="form-control"" type=" text"="text"" name=" category"="category""- -input type=" submit"="submit"" value=" class=" btn="btn" btn-primary"="btn-primary"" create="Create" restaurants"="Restaurants""- -/form- {{template "footer"}} |
As shown in the following script, this template submits a form value to the link “/restaurants/create/process” that calls to a controller function from the func main() http.HandleFunc("/restaurants/create/process", controllers.CreateProcess)
.
1 2 3 4 5 6 7 8 9 10 11 12 13 | func CreateProcess(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, http.StatusText(405), http.StatusMethodNotAllowed) return } _, err := models.CreateRestaurant(r) if err != nil { http.Error(w, http.StatusText(406), http.StatusNotAcceptable) return } http.Redirect(w, r, "/restaurants", http.StatusSeeOther) } |
The following controller’s code calls to one of the model’s function models.CreateRestaurant(r)
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | func CreateRestaurant(r *http.Request) (Restaurant, error) { // get form values resto := Restaurant{} i := r.FormValue("resto_id") resto.Name = r.FormValue("resto_name") resto.Phone = r.FormValue("phone") resto.Email = r.FormValue("email") s := r.FormValue("stars") resto.Category = r.FormValue("category") // validate form values if i == "" || resto.Name == "" || resto.Phone == "" || resto.Email == "" || s == "" || resto.Category == "" { return resto, errors.New("400. Bad Request. All fields must be complete!") } // convert form values id64, err := strconv.ParseInt(i, 10, 32) stars64, err := strconv.ParseInt(s, 10, 32) if err != nil { return resto, errors.New("406. Not Acceptable. Stars must be a number") } resto.Id = int(id64) resto.Stars = int(stars64) // insert values _, err = config.DB.Exec("INSERT INTO tblrestaurants (id,name,phone,email,stars,category) VALUES ($1,$2,$3,$4,$5,$6)", resto.Id, resto.Name, resto.Phone, resto.Email, resto.Stars, resto.Category) if err != nil { return resto, errors.New("500. Internal Server Error." + err.Error()) } return resto, nil } |
Now, clicking on the “Create Restaurants” will create the record with the details as specified in the form.
The results should resemble the following:
Note the newly added record, at the bottom of the list, with an ID of “7.”
Conclusion
This was part four in the tutorial series explaining how to build a simple Golang and CockroachDB web App via MVC pattern. This tutorial covered how the models works and showed how to create CockroachDB record using Golang. update CockroachDB record using Golang as well as retrieve data in CockroachDB using Golang. Remember that the func main()
command only calls the needed controller against the clients request. Part five in this series will discuss how to update and delete CockroachDB using Golang.
Pilot the ObjectRocket Platform Free!
Try Fully-Managed CockroachDB, Elasticsearch, MongoDB, PostgreSQL (Beta) or Redis.
Get Started