github.com/ngocphuongnb/tetua@v0.0.7-alpha/app/entities/entities.go (about)

     1  package entities
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"math"
     8  	"net/url"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/ngocphuongnb/tetua/app/config"
    13  	"github.com/ngocphuongnb/tetua/app/utils"
    14  )
    15  
    16  // Entities are used in all other parts. This will store properties of business objects and associated methods. Example: Article, User
    17  
    18  type Entity interface {
    19  	Comment | File | Permission | Post | Page | Role | Setting | Topic | User
    20  }
    21  
    22  type EntityFilter interface {
    23  	PostFilter | PageFilter | FileFilter | CommentFilter | UserFilter | PermissionFilter | RoleFilter | TopicFilter
    24  }
    25  
    26  type NotFoundError struct {
    27  	Message string
    28  }
    29  
    30  // Error implements the error interface.
    31  func (e *NotFoundError) Error() string {
    32  	return e.Message
    33  }
    34  
    35  // IsNotFound returns a boolean indicating whether the error is a not found error.
    36  func IsNotFound(err error) bool {
    37  	if err == nil {
    38  		return false
    39  	}
    40  	var e *NotFoundError
    41  	return errors.As(err, &e)
    42  }
    43  
    44  type Paginate[E Entity] struct {
    45  	BaseUrl     string `json:"base_url"`
    46  	QueryString string `json:"query_string"`
    47  	Total       int    `json:"total"`
    48  	PageSize    int    `json:"page_size"`
    49  	PageCurrent int    `json:"page_current"`
    50  	Data        []*E   `json:"data"`
    51  }
    52  
    53  type Message struct {
    54  	Type    string `json:"type"`
    55  	Message string `json:"message"`
    56  }
    57  
    58  type Messages []*Message
    59  
    60  type Meta struct {
    61  	Title       string
    62  	Description string
    63  	Query       string
    64  	Type        string
    65  	Image       string
    66  	Canonical   string
    67  	User        *User
    68  	Messages    *Messages
    69  }
    70  
    71  type Map map[string]interface{}
    72  
    73  func (ms *Messages) Append(m *Message) {
    74  	*ms = append(*ms, m)
    75  }
    76  
    77  func (ms *Messages) AppendError(m string) {
    78  	*ms = append(*ms, &Message{
    79  		Type:    "error",
    80  		Message: m,
    81  	})
    82  }
    83  
    84  func (ms *Messages) Length() int {
    85  	return len(*ms)
    86  }
    87  
    88  func (ms *Messages) HasError() bool {
    89  	errorCount := 0
    90  
    91  	for _, m := range *ms {
    92  		if m.Type == "error" {
    93  			errorCount++
    94  		}
    95  	}
    96  
    97  	return errorCount > 0
    98  }
    99  
   100  func (ms *Messages) Get() []*Message {
   101  	return *ms
   102  }
   103  
   104  type GetPostFn func(ctx context.Context, id int) (*Post, error)
   105  type GetPostsFn func(ctx context.Context, limit, offset int) ([]*Post, error)
   106  type PostPaginateFn func(ctx context.Context, filters ...*PostFilter) (*Paginate[Post], error)
   107  
   108  type Filter struct {
   109  	BaseUrl         string   `json:"base_url"`
   110  	Search          string   `form:"search" json:"search"`
   111  	Page            int      `form:"page" json:"page"`
   112  	Limit           int      `form:"limit" json:"limit"`
   113  	Sorts           []*Sort  `form:"orders" json:"orders"`
   114  	IgnoreUrlParams []string `form:"ignore_url_params" json:"ignore_url_params"`
   115  	ExcludeIDs      []int    `form:"exclude_ids" json:"exclude_ids"`
   116  }
   117  
   118  func (p *Filter) FilterBaseUrl() string {
   119  	if p.BaseUrl == "" {
   120  		return utils.Url("")
   121  	}
   122  
   123  	return p.BaseUrl
   124  }
   125  
   126  func (p *Filter) Base() string {
   127  	q := url.Values{}
   128  	if !utils.SliceContains(p.IgnoreUrlParams, "search") && p.Search != "" {
   129  		q.Add("q", p.Search)
   130  	}
   131  
   132  	if queryString := q.Encode(); queryString != "" {
   133  		return p.FilterBaseUrl() + "?" + q.Encode()
   134  	}
   135  
   136  	return p.FilterBaseUrl()
   137  }
   138  
   139  func (f *Filter) GetSearch() string {
   140  	return f.Search
   141  }
   142  
   143  func (f *Filter) GetPage() int {
   144  	return f.Page
   145  }
   146  
   147  func (f *Filter) GetLimit() int {
   148  	return f.Limit
   149  }
   150  
   151  func (f *Filter) GetSorts() []*Sort {
   152  	return f.Sorts
   153  }
   154  
   155  func (f *Filter) GetIgnoreUrlParams() []string {
   156  	return f.IgnoreUrlParams
   157  }
   158  
   159  func (f *Filter) GetExcludeIDs() []int {
   160  	return f.ExcludeIDs
   161  }
   162  
   163  type Sort struct {
   164  	Field string
   165  	Order string
   166  }
   167  
   168  type PaginateLink struct {
   169  	Link  string
   170  	Label string
   171  	Class string
   172  }
   173  
   174  func (pp *Paginate[E]) Links() []*PaginateLink {
   175  	var url string
   176  	var links = []*PaginateLink{}
   177  	totalPages := int(math.Ceil(float64(pp.Total) / float64(pp.PageSize)))
   178  
   179  	for i := 1; i <= totalPages; i++ {
   180  		page := strconv.Itoa(i)
   181  		class := []string{}
   182  
   183  		if i == 1 {
   184  			class = []string{"first"}
   185  		}
   186  
   187  		if i == totalPages {
   188  			class = []string{"last"}
   189  		}
   190  
   191  		if i == pp.PageCurrent {
   192  			class = append(class, "active")
   193  		}
   194  
   195  		if strings.Contains(pp.BaseUrl, "?") {
   196  			url = pp.BaseUrl + "&page=" + page
   197  		} else {
   198  			url = pp.BaseUrl + "?page=" + page
   199  		}
   200  
   201  		links = append(links, &PaginateLink{
   202  			Link:  url,
   203  			Label: page,
   204  			Class: strings.Join(class, " "),
   205  		})
   206  	}
   207  
   208  	return links
   209  }
   210  
   211  func (m *Meta) GetTitle() string {
   212  	title := config.Setting("app_name")
   213  
   214  	if m.Title != "" {
   215  		title = fmt.Sprintf("%s - %s", m.Title, title)
   216  	}
   217  
   218  	return title
   219  }