mirror of
https://github.com/kataras/iris.git
synced 2025-01-24 19:21:03 +01:00
82b5a1d4ed
Prev Commit: 49ee8f2d75
[formerly 6a3579f2500fc715d7dc606478960946dcade61d]
Changelog: https://github.com/kataras/iris/blob/master/HISTORY.md#mo-09-october-2017--v850
This example is updated with the current commit: https://github.com/kataras/iris/tree/master/_examples/mvc/using-output-result
Former-commit-id: 29486ef014b3667fa1c7c66e11c8e95c76a37e57
207 lines
5.3 KiB
Go
207 lines
5.3 KiB
Go
// file: services/movie_service.go
|
|
|
|
package services
|
|
|
|
import (
|
|
"errors"
|
|
"sync"
|
|
|
|
"github.com/kataras/iris/_examples/mvc/using-method-result/models"
|
|
)
|
|
|
|
// MovieService handles CRUID operations of a movie entity/model.
|
|
// It's here to decouple the data source from the higher level compoments.
|
|
// As a result a different service for a specific datasource (or repository)
|
|
// can be used from the main application without any additional changes.
|
|
type MovieService interface {
|
|
GetSingle(query func(models.Movie) bool) (movie models.Movie, found bool)
|
|
GetByID(id int64) (models.Movie, bool)
|
|
|
|
InsertOrUpdate(movie models.Movie) (models.Movie, error)
|
|
DeleteByID(id int64) bool
|
|
|
|
GetMany(query func(models.Movie) bool, limit int) (result []models.Movie)
|
|
GetAll() []models.Movie
|
|
}
|
|
|
|
// NewMovieServiceFromMemory returns a new memory-based movie service.
|
|
func NewMovieServiceFromMemory(source map[int64]models.Movie) MovieService {
|
|
return &MovieMemoryService{
|
|
source: source,
|
|
}
|
|
}
|
|
|
|
// A Movie Service can have different data sources:
|
|
// func NewMovieServiceFromDB(db datasource.MySQL) {
|
|
// return &MovieDatabaseService{
|
|
// db: db,
|
|
// }
|
|
// }
|
|
|
|
// Another pattern is to initialize the database connection
|
|
// or any source here based on a "string" name or an "enum".
|
|
// func NewMovieService(source string) MovieService {
|
|
// if source == "memory" {
|
|
// return NewMovieServiceFromMemory(datasource.Movies)
|
|
// }
|
|
// if source == "database" {
|
|
// db = datasource.NewDB("....")
|
|
// return NewMovieServiceFromDB(db)
|
|
// }
|
|
// [...]
|
|
// return nil
|
|
// }
|
|
|
|
// MovieMemoryService is a "MovieService"
|
|
// which manages the movies using the memory data source (map).
|
|
type MovieMemoryService struct {
|
|
source map[int64]models.Movie
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
// GetSingle receives a query function
|
|
// which is fired for every single movie model inside
|
|
// our imaginary data source.
|
|
// When that function returns true then it stops the iteration.
|
|
//
|
|
// It returns the query's return last known boolean value
|
|
// and the last known movie model
|
|
// to help callers to reduce the LOC.
|
|
//
|
|
// It's actually a simple but very clever prototype function
|
|
// I'm using everywhere since I firstly think of it,
|
|
// hope you'll find it very useful as well.
|
|
func (s *MovieMemoryService) GetSingle(query func(models.Movie) bool) (movie models.Movie, found bool) {
|
|
s.mu.RLock()
|
|
for _, movie = range s.source {
|
|
found = query(movie)
|
|
if found {
|
|
break
|
|
}
|
|
}
|
|
s.mu.RUnlock()
|
|
|
|
// set an empty models.Movie if not found at all.
|
|
if !found {
|
|
movie = models.Movie{}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// GetByID returns a movie based on its id.
|
|
// Returns true if found, otherwise false, the bool should be always checked
|
|
// because the models.Movie may be filled with the latest element
|
|
// but not the correct one, although it can be used for debugging.
|
|
func (s *MovieMemoryService) GetByID(id int64) (models.Movie, bool) {
|
|
return s.GetSingle(func(m models.Movie) bool {
|
|
return m.ID == id
|
|
})
|
|
}
|
|
|
|
// InsertOrUpdate adds or updates a movie to the (memory) storage.
|
|
//
|
|
// Returns the new movie and an error if any.
|
|
func (s *MovieMemoryService) InsertOrUpdate(movie models.Movie) (models.Movie, error) {
|
|
id := movie.ID
|
|
|
|
if id == 0 { // Create new action
|
|
var lastID int64
|
|
// find the biggest ID in order to not have duplications
|
|
// in productions apps you can use a third-party
|
|
// library to generate a UUID as string.
|
|
s.mu.RLock()
|
|
for _, item := range s.source {
|
|
if item.ID > lastID {
|
|
lastID = item.ID
|
|
}
|
|
}
|
|
s.mu.RUnlock()
|
|
|
|
id = lastID + 1
|
|
movie.ID = id
|
|
|
|
// map-specific thing
|
|
s.mu.Lock()
|
|
s.source[id] = movie
|
|
s.mu.Unlock()
|
|
|
|
return movie, nil
|
|
}
|
|
|
|
// Update action based on the movie.ID,
|
|
// here we will allow updating the poster and genre if not empty.
|
|
// Alternatively we could do pure replace instead:
|
|
// s.source[id] = movie
|
|
// and comment the code below;
|
|
current, exists := s.GetByID(id)
|
|
if !exists { // ID is not a real one, return an error.
|
|
return models.Movie{}, errors.New("failed to update a nonexistent movie")
|
|
}
|
|
|
|
// or comment these and s.source[id] = m for pure replace
|
|
if movie.Poster != "" {
|
|
current.Poster = movie.Poster
|
|
}
|
|
|
|
if movie.Genre != "" {
|
|
current.Genre = movie.Genre
|
|
}
|
|
|
|
// map-specific thing
|
|
s.mu.Lock()
|
|
s.source[id] = current
|
|
s.mu.Unlock()
|
|
|
|
return movie, nil
|
|
}
|
|
|
|
// DeleteByID deletes a movie by its id.
|
|
//
|
|
// Returns true if deleted otherwise false.
|
|
func (s *MovieMemoryService) DeleteByID(id int64) bool {
|
|
if _, exists := s.GetByID(id); !exists {
|
|
// we could do _, exists := s.source[id] instead
|
|
// but we don't because you should learn
|
|
// how you can use that service's functions
|
|
// with any other source, i.e database.
|
|
return false
|
|
}
|
|
|
|
// map-specific thing
|
|
s.mu.Lock()
|
|
delete(s.source, id)
|
|
s.mu.Unlock()
|
|
|
|
return true
|
|
}
|
|
|
|
// GetMany same as GetSingle but returns one or more models.Movie as a slice.
|
|
// If limit <=0 then it returns everything.
|
|
func (s *MovieMemoryService) GetMany(query func(models.Movie) bool, limit int) (result []models.Movie) {
|
|
loops := 0
|
|
|
|
s.mu.RLock()
|
|
for _, movie := range s.source {
|
|
loops++
|
|
|
|
passed := query(movie)
|
|
if passed {
|
|
result = append(result, movie)
|
|
}
|
|
// we have to return at least one movie if "passed" was true.
|
|
if limit >= loops {
|
|
break
|
|
}
|
|
}
|
|
s.mu.RUnlock()
|
|
|
|
return
|
|
}
|
|
|
|
// GetAll returns all movies.
|
|
func (s *MovieMemoryService) GetAll() []models.Movie {
|
|
movies := s.GetMany(func(m models.Movie) bool { return true }, -1)
|
|
return movies
|
|
}
|