feat: get movies, url parametes parsing and validating, sql exeme querying
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"time"
|
||||
@@ -14,6 +15,69 @@ type MovieModel struct {
|
||||
DB *sql.DB
|
||||
}
|
||||
|
||||
// Create a new GetAll() method which returns a slice of movies. Although we're not
|
||||
// using them right now, we've set this up to accept the various filter parameters as
|
||||
// arguments.
|
||||
func (m MovieModel) GetAll(title string, genres []string, filters Filters) ([]*Movie, error) {
|
||||
// Use full-text search for the title filter.
|
||||
query := `
|
||||
SELECT id, created_at, title, year, runtime, genres, version
|
||||
FROM movies
|
||||
WHERE (to_tsvector('simple', title) @@ plainto_tsquery('simple', $1) OR $1 = '')
|
||||
AND (genres @> $2 OR $2 = '{}')
|
||||
ORDER BY id`
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Use QueryContext() to execute the query. This returns a sql.Rows resultset
|
||||
// containing the result.
|
||||
rows, err := m.DB.QueryContext(ctx, query, title, pq.Array(genres))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Importantly, defer a call to rows.Close() to ensure that the resultset is closed
|
||||
// before GetAll() returns.
|
||||
defer rows.Close()
|
||||
|
||||
// Initialize an empty slice to hold the movie data.
|
||||
movies := []*Movie{}
|
||||
|
||||
// Use rows.Next to iterate through the rows in the resultset.
|
||||
for rows.Next() {
|
||||
// Initialize an empty Movie struct to hold the data for an individual movie.
|
||||
var movie Movie
|
||||
|
||||
// Scan the values from the row into the Movie struct. Again, note that we're
|
||||
// using the pq.Array() adapter on the genres field here.
|
||||
err := rows.Scan(
|
||||
&movie.ID,
|
||||
&movie.CreatedAt,
|
||||
&movie.Title,
|
||||
&movie.Year,
|
||||
&movie.Runtime,
|
||||
pq.Array(&movie.Genres),
|
||||
&movie.Version,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add the Movie struct to the slice.
|
||||
movies = append(movies, &movie)
|
||||
}
|
||||
|
||||
// When the rows.Next() loop has finished, call rows.Err() to retrieve any error
|
||||
// that was encountered during the iteration.
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If everything went OK, then return the slice of movies.
|
||||
return movies, nil
|
||||
}
|
||||
|
||||
// Add a placeholder method for inserting a new record in the movies table.
|
||||
func (m MovieModel) Insert(movie *Movie) error {
|
||||
// Define the SQL query for inserting a new record in the movies table and returning
|
||||
@@ -28,10 +92,14 @@ func (m MovieModel) Insert(movie *Movie) error {
|
||||
// make it nice and clear *what values are being used where* in the query.
|
||||
args := []interface{}{movie.Title, movie.Year, movie.Runtime, pq.Array(movie.Genres)}
|
||||
|
||||
// Create a context with a 3-second timeout.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Use the QueryRow() method to execute the SQL query on our connection pool,
|
||||
// passing in the args slice as a variadic parameter and scanning the system-
|
||||
// generated id, created_at and version values into the movie struct.
|
||||
return m.DB.QueryRow(query, args...).Scan(&movie.ID, &movie.CreatedAt, &movie.Version)
|
||||
return m.DB.QueryRowContext(ctx, query, args...).Scan(&movie.ID, &movie.CreatedAt, &movie.Version)
|
||||
}
|
||||
|
||||
func (m MovieModel) Get(id int64) (*Movie, error) {
|
||||
@@ -52,11 +120,20 @@ func (m MovieModel) Get(id int64) (*Movie, error) {
|
||||
// Declare a Movie struct to hold the data returned by the query.
|
||||
var movie Movie
|
||||
|
||||
// Use the context.WithTimeout() function to create a context.Context which carries a
|
||||
// 3-second timeout deadline. Note that we're using the empty context.Background()
|
||||
// as the 'parent' context.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
|
||||
// Importantly, use defer to make sure that we cancel the context before the Get()
|
||||
// method returns.
|
||||
defer cancel()
|
||||
|
||||
// Execute the query using the QueryRow() method, passing in the provided id value
|
||||
// as a placeholder parameter, and scan the response data into the fields of the
|
||||
// Movie struct. Importantly, notice that we need to convert the scan target for the
|
||||
// genres column using the pq.Array() adapter function again.
|
||||
err := m.DB.QueryRow(query, id).Scan(
|
||||
err := m.DB.QueryRowContext(ctx, query, id).Scan(
|
||||
&movie.ID,
|
||||
&movie.CreatedAt,
|
||||
&movie.Title,
|
||||
@@ -98,10 +175,13 @@ func (m MovieModel) Update(movie *Movie) error {
|
||||
movie.ID,
|
||||
movie.Version,
|
||||
}
|
||||
// Create a context with a 3-second timeout.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Use the QueryRow() method to execute the query, passing in the args slice as a
|
||||
// variadic parameter and scanning the new version value into the movie struct.
|
||||
err := m.DB.QueryRow(query, args...).Scan(&movie.Version)
|
||||
err := m.DB.QueryRowContext(ctx, query, args...).Scan(&movie.Version)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, sql.ErrNoRows):
|
||||
@@ -124,11 +204,14 @@ func (m MovieModel) Delete(id int64) error {
|
||||
query := `
|
||||
DELETE FROM movies
|
||||
WHERE id = $1`
|
||||
// Create a context with a 3-second timeout.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Execute the SQL query using the Exec() method, passing in the id variable as
|
||||
// the value for the placeholder parameter. The Exec() method returns a sql.Result
|
||||
// object.
|
||||
result, err := m.DB.Exec(query, id)
|
||||
result, err := m.DB.ExecContext(ctx, query, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user