mirror of
https://github.com/kataras/iris.git
synced 2025-12-19 10:57:05 +00:00
Enhance the MVC "using-method-result" example. Prev: update to version 8.5.0
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
This commit is contained in:
206
_examples/mvc/using-method-result/services/movie_service.go
Normal file
206
_examples/mvc/using-method-result/services/movie_service.go
Normal file
@@ -0,0 +1,206 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user