article.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. package admin
  2. import (
  3. "strconv"
  4. "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/helpers"
  5. "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/interfaces"
  6. "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/mapper"
  7. "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/models"
  8. "github.com/go-playground/validator/v10"
  9. "github.com/gofiber/fiber/v2"
  10. )
  11. const errArticleExists = "Статья с данным URL уже существует"
  12. func ArticleHandler(sp interfaces.ServiceProvider) fiber.Handler {
  13. return func(ctx *fiber.Ctx) error {
  14. articles, err := sp.GetArticleRepository().GetAll(ctx.Context())
  15. if err != nil {
  16. return err
  17. }
  18. return ctx.Render("admin/article", fiber.Map{
  19. "articles": mapper.ConvertArticleModelsToDTO(articles),
  20. "section": "article",
  21. }, "admin/_layout")
  22. }
  23. }
  24. func AddArticleHandler(sp interfaces.ServiceProvider) fiber.Handler {
  25. return func(ctx *fiber.Ctx) error {
  26. var validate = validator.New()
  27. validateErrors := make(map[string]string)
  28. trans, err := helpers.GetDefaultTranslator(validate)
  29. if err != nil {
  30. return err
  31. }
  32. form := models.ArticleForm{
  33. ActiveTags: make(map[int]bool),
  34. }
  35. tags, err := sp.GetTagRepository().GetAll(ctx.Context())
  36. if err != nil {
  37. return err
  38. }
  39. tagsDTO := mapper.ConvertTagModelsToDTO(tags)
  40. if ctx.Method() == fiber.MethodPost {
  41. if err = ctx.BodyParser(&form); err != nil {
  42. return err
  43. }
  44. if err = validate.Struct(form); err != nil {
  45. validateErrors = helpers.FormatValidateErrors(err, trans)
  46. }
  47. if res, _ := sp.GetArticleRepository().GetByURL(ctx.Context(), form.URL); res != nil {
  48. validateErrors["ArticleForm.URL"] = errArticleExists
  49. }
  50. tagIDs := make([]int, 0, len(form.Tags))
  51. for i := range form.Tags {
  52. tagID, tagErr := strconv.Atoi(form.Tags[i])
  53. if tagErr != nil {
  54. return tagErr
  55. }
  56. tagIDs = append(tagIDs, tagID)
  57. }
  58. for i := range tagIDs {
  59. form.ActiveTags[tagIDs[i]] = true
  60. }
  61. if len(validateErrors) == 0 {
  62. articleID, articleErr := sp.GetArticleRepository().Add(ctx.Context(), mapper.ConvertArticleFormToModel(form))
  63. if articleErr != nil {
  64. return articleErr
  65. }
  66. if len(form.Tags) > 0 {
  67. if err = sp.GetArticleTagRepository().Add(ctx.Context(), articleID, tagIDs); err != nil {
  68. return err
  69. }
  70. }
  71. sp.GetCacheService().FlushAll()
  72. if err = ctx.Redirect("/admin"); err != nil {
  73. return err
  74. }
  75. }
  76. }
  77. return ctx.Render("admin/article_edit", fiber.Map{
  78. "form": form,
  79. "errors": validateErrors,
  80. "tags": tagsDTO,
  81. "section": "article",
  82. "title": "Добавление статьи",
  83. "show_apply": false,
  84. }, "admin/_layout")
  85. }
  86. }
  87. func EditArticleHandler(sp interfaces.ServiceProvider) fiber.Handler {
  88. return func(ctx *fiber.Ctx) error {
  89. var validate = validator.New()
  90. validateErrors := make(map[string]string)
  91. trans, err := helpers.GetDefaultTranslator(validate)
  92. if err != nil {
  93. return err
  94. }
  95. ID, err := strconv.Atoi(ctx.Params("id"))
  96. if err != nil {
  97. return err
  98. }
  99. article, err := sp.GetArticleRepository().GetByID(ctx.Context(), ID)
  100. if err != nil {
  101. return err
  102. }
  103. if article == nil {
  104. return fiber.ErrNotFound
  105. }
  106. articleTags, err := sp.GetTagRepository().GetByArticleID(ctx.Context(), ID)
  107. if err != nil {
  108. return err
  109. }
  110. tags, err := sp.GetTagRepository().GetAll(ctx.Context())
  111. if err != nil {
  112. return err
  113. }
  114. tagsDTO := mapper.ConvertTagModelsToDTO(tags)
  115. var form *models.ArticleForm
  116. if ctx.Method() == fiber.MethodGet {
  117. form = mapper.ConvertArticleModelToForm(*article, articleTags)
  118. } else if ctx.Method() == fiber.MethodPost {
  119. form = &models.ArticleForm{
  120. ID: ID,
  121. ActiveTags: make(map[int]bool),
  122. }
  123. if err = ctx.BodyParser(form); err != nil {
  124. return err
  125. }
  126. if err = validate.Struct(*form); err != nil {
  127. validateErrors = helpers.FormatValidateErrors(err, trans)
  128. }
  129. if res, _ := sp.GetArticleRepository().GetByURL(ctx.Context(), form.URL); res != nil {
  130. if res.ID != ID {
  131. validateErrors["ArticleForm.URL"] = errArticleExists
  132. }
  133. }
  134. tagIDs := make([]int, 0, len(form.Tags))
  135. for i := range form.Tags {
  136. tagID, tagErr := strconv.Atoi(form.Tags[i])
  137. if tagErr != nil {
  138. return tagErr
  139. }
  140. tagIDs = append(tagIDs, tagID)
  141. }
  142. for i := range tagIDs {
  143. form.ActiveTags[tagIDs[i]] = true
  144. }
  145. if len(validateErrors) == 0 {
  146. err = sp.GetArticleRepository().Update(ctx.Context(), mapper.ConvertArticleFormToModel(*form))
  147. if err != nil {
  148. return err
  149. }
  150. var tagsToAdd, tagsToDelete []int
  151. oldTagsMap := make(map[int]struct{}, len(articleTags))
  152. for i := range articleTags {
  153. oldTagsMap[articleTags[i].ID] = struct{}{}
  154. if _, ok := form.ActiveTags[articleTags[i].ID]; !ok {
  155. tagsToDelete = append(tagsToDelete, articleTags[i].ID)
  156. }
  157. }
  158. for i := range tagIDs {
  159. if _, ok := oldTagsMap[tagIDs[i]]; !ok {
  160. tagsToAdd = append(tagsToAdd, tagIDs[i])
  161. }
  162. }
  163. if len(tagsToAdd) > 0 {
  164. if err = sp.GetArticleTagRepository().Add(ctx.Context(), ID, tagsToAdd); err != nil {
  165. return err
  166. }
  167. }
  168. if len(tagsToDelete) > 0 {
  169. if err = sp.GetArticleTagRepository().Delete(ctx.Context(), ID, tagsToDelete); err != nil {
  170. return err
  171. }
  172. }
  173. sp.GetCacheService().FlushAll()
  174. if ctx.FormValue("action", "save") == "save" {
  175. if err = ctx.Redirect("/admin"); err != nil {
  176. return err
  177. }
  178. }
  179. }
  180. }
  181. return ctx.Render("admin/article_edit", fiber.Map{
  182. "form": form,
  183. "errors": validateErrors,
  184. "tags": tagsDTO,
  185. "show_apply": true,
  186. "section": "article",
  187. "title": "Редактирование статьи",
  188. }, "admin/_layout")
  189. }
  190. }
  191. func DeleteArticleHandler(sp interfaces.ServiceProvider) fiber.Handler {
  192. return func(ctx *fiber.Ctx) error {
  193. ID, err := strconv.Atoi(ctx.Params("id"))
  194. if err != nil {
  195. return err
  196. }
  197. article, err := sp.GetArticleRepository().GetByID(ctx.Context(), ID)
  198. if err != nil {
  199. return err
  200. }
  201. if ctx.Method() == fiber.MethodPost {
  202. err = sp.GetArticleTagRepository().DeleteByArticleID(ctx.Context(), ID)
  203. if err != nil {
  204. return err
  205. }
  206. err = sp.GetArticleRepository().Delete(ctx.Context(), ID)
  207. if err != nil {
  208. return err
  209. }
  210. sp.GetCacheService().FlushAll()
  211. if err = ctx.Redirect("/admin"); err != nil {
  212. return err
  213. }
  214. }
  215. return ctx.Render("admin/article_delete", fiber.Map{
  216. "article": article.Title,
  217. "section": "article",
  218. }, "admin/_layout")
  219. }
  220. }