126 lines
3.6 KiB
Go
126 lines
3.6 KiB
Go
package repository
|
||
|
||
import (
|
||
"context"
|
||
"regexp"
|
||
"strings"
|
||
|
||
"github.com/jackc/pgx/v5"
|
||
"github.com/jackc/pgx/v5/pgxpool"
|
||
|
||
"shop/internal/models"
|
||
)
|
||
|
||
type CategoryRepository struct {
|
||
pool *pgxpool.Pool
|
||
}
|
||
|
||
func NewCategoryRepository(pool *pgxpool.Pool) *CategoryRepository {
|
||
return &CategoryRepository{pool: pool}
|
||
}
|
||
|
||
var slugRe = regexp.MustCompile(`[^a-z0-9]+`)
|
||
|
||
func Slugify(name string) string {
|
||
translit := map[rune]string{
|
||
'а': "a", 'б': "b", 'в': "v", 'г': "g", 'д': "d", 'е': "e", 'ё': "e",
|
||
'ж': "zh", 'з': "z", 'и': "i", 'й': "y", 'к': "k", 'л': "l", 'м': "m",
|
||
'н': "n", 'о': "o", 'п': "p", 'р': "r", 'с': "s", 'т': "t", 'у': "u",
|
||
'ф': "f", 'х': "h", 'ц': "ts", 'ч': "ch", 'ш': "sh", 'щ': "sch",
|
||
'ъ': "", 'ы': "y", 'ь': "", 'э': "e", 'ю': "yu", 'я': "ya",
|
||
}
|
||
var b strings.Builder
|
||
for _, r := range strings.ToLower(name) {
|
||
if t, ok := translit[r]; ok {
|
||
b.WriteString(t)
|
||
} else if (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') {
|
||
b.WriteRune(r)
|
||
} else if r == ' ' || r == '-' {
|
||
b.WriteRune('-')
|
||
}
|
||
}
|
||
s := slugRe.ReplaceAllString(b.String(), "-")
|
||
return strings.Trim(s, "-")
|
||
}
|
||
|
||
func (r *CategoryRepository) All(ctx context.Context) ([]models.Category, error) {
|
||
rows, err := r.pool.Query(ctx, `
|
||
SELECT c.id, c.name, c.slug, c.description, c.sort_order,
|
||
COUNT(p.id) FILTER (WHERE p.is_active = true) AS cnt
|
||
FROM categories c
|
||
LEFT JOIN products p ON p.category_id = c.id
|
||
GROUP BY c.id
|
||
ORDER BY c.sort_order, c.name
|
||
`)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
|
||
var list []models.Category
|
||
for rows.Next() {
|
||
var c models.Category
|
||
if err := rows.Scan(&c.ID, &c.Name, &c.Slug, &c.Description, &c.SortOrder, &c.Count); err != nil {
|
||
return nil, err
|
||
}
|
||
list = append(list, c)
|
||
}
|
||
return list, rows.Err()
|
||
}
|
||
|
||
func (r *CategoryRepository) GetBySlug(ctx context.Context, slug string) (models.Category, error) {
|
||
var c models.Category
|
||
err := r.pool.QueryRow(ctx, `
|
||
SELECT id, name, slug, description, sort_order FROM categories WHERE slug = $1
|
||
`, slug).Scan(&c.ID, &c.Name, &c.Slug, &c.Description, &c.SortOrder)
|
||
return c, err
|
||
}
|
||
|
||
func (r *CategoryRepository) GetByID(ctx context.Context, id int) (models.Category, error) {
|
||
var c models.Category
|
||
err := r.pool.QueryRow(ctx, `
|
||
SELECT id, name, slug, description, sort_order FROM categories WHERE id = $1
|
||
`, id).Scan(&c.ID, &c.Name, &c.Slug, &c.Description, &c.SortOrder)
|
||
return c, err
|
||
}
|
||
|
||
func (r *CategoryRepository) Create(ctx context.Context, name, description string) (models.Category, error) {
|
||
slug := Slugify(name)
|
||
var c models.Category
|
||
err := r.pool.QueryRow(ctx, `
|
||
INSERT INTO categories (name, slug, description)
|
||
VALUES ($1, $2, $3)
|
||
ON CONFLICT (slug) DO UPDATE SET name = EXCLUDED.name
|
||
RETURNING id, name, slug, description, sort_order
|
||
`, name, slug, description).Scan(&c.ID, &c.Name, &c.Slug, &c.Description, &c.SortOrder)
|
||
return c, err
|
||
}
|
||
|
||
func (r *CategoryRepository) Update(ctx context.Context, c *models.Category) error {
|
||
tag, err := r.pool.Exec(ctx, `
|
||
UPDATE categories SET name = $1, slug = $2, description = $3, sort_order = $4 WHERE id = $5
|
||
`, c.Name, c.Slug, c.Description, c.SortOrder, c.ID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if tag.RowsAffected() == 0 {
|
||
return pgx.ErrNoRows
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (r *CategoryRepository) Delete(ctx context.Context, id int) error {
|
||
_, err := r.pool.Exec(ctx, `UPDATE products SET category_id = NULL WHERE category_id = $1`, id)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
tag, err := r.pool.Exec(ctx, `DELETE FROM categories WHERE id = $1`, id)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
if tag.RowsAffected() == 0 {
|
||
return pgx.ErrNoRows
|
||
}
|
||
return nil
|
||
}
|