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 }