article.go 6.5 KB

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