almost all ready

This commit is contained in:
2026-05-14 01:48:15 +02:00
parent 1b53c1c199
commit d55b0e2914
29 changed files with 5489 additions and 650 deletions
+285
View File
@@ -0,0 +1,285 @@
package handlers
import (
"context"
"fmt"
"net/http"
"strconv"
"strings"
"github.com/labstack/echo/v4"
appmiddleware "git.ma-al.com/goc_marek/ps_shop/internal/http/middleware"
pscart "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/cart"
pscookie "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/cookie"
)
type cartSessionService interface {
RefreshExpiry(ctx context.Context, session *pscookie.SessionContext) error
ResolveCookiePath(ctx context.Context, req *http.Request) (string, error)
}
type CartHandler struct {
carts *pscart.Service
codec pscookie.Codec
sessions cartSessionService
}
func NewCartHandler(carts *pscart.Service, codec pscookie.Codec, sessions cartSessionService) *CartHandler {
return &CartHandler{
carts: carts,
codec: codec,
sessions: sessions,
}
}
func (h *CartHandler) Handle(c echo.Context) error {
if h == nil || h.carts == nil || h.codec == nil || h.sessions == nil {
return echo.NewHTTPError(http.StatusInternalServerError, "cart handler is not initialized")
}
session := appmiddleware.GetSession(c)
action := cartActionFromRequest(c)
input, err := cartMutationInput(c, session)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
var result *pscart.MutationResult
switch action {
case cartActionDelete:
result, err = h.carts.DeleteProduct(c.Request().Context(), input)
case cartActionUpdate:
result, err = h.carts.UpdateProduct(c.Request().Context(), input)
default:
result, err = h.carts.AddProduct(c.Request().Context(), input)
}
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "cart mutation failed: "+err.Error())
}
syncSessionCartID(session, result.CartID)
if err := h.writeSessionCookie(c, session); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "cart cookie update failed: "+err.Error())
}
if wantsJSON(c.Request()) {
return c.JSON(http.StatusOK, result)
}
return c.Redirect(http.StatusSeeOther, cartRedirectTarget(c.Request()))
}
type cartAction string
const (
cartActionAdd cartAction = "add"
cartActionUpdate cartAction = "update"
cartActionDelete cartAction = "delete"
)
func cartActionFromRequest(c echo.Context) cartAction {
switch c.Request().Method {
case http.MethodDelete:
return cartActionDelete
case http.MethodPut, http.MethodPatch:
return cartActionUpdate
}
action := strings.ToLower(strings.TrimSpace(c.FormValue("action")))
switch action {
case string(cartActionDelete):
return cartActionDelete
case string(cartActionUpdate):
return cartActionUpdate
}
switch {
case c.FormValue("delete") != "":
return cartActionDelete
case c.FormValue("update") != "":
return cartActionUpdate
default:
return cartActionAdd
}
}
func cartMutationInput(c echo.Context, session *pscookie.SessionContext) (pscart.MutationInput, error) {
productID, err := formInt64(c, "id_product")
if err != nil || productID == 0 {
return pscart.MutationInput{}, fmt.Errorf("valid id_product is required")
}
quantity := int64(1)
if raw := strings.TrimSpace(c.FormValue("qty")); raw != "" {
quantity, err = strconv.ParseInt(raw, 10, 64)
if err != nil || quantity < 0 {
return pscart.MutationInput{}, fmt.Errorf("valid qty is required")
}
}
productAttributeID, err := firstFormInt64(c, "id_product_attribute", "ipa")
if err != nil {
return pscart.MutationInput{}, fmt.Errorf("valid id_product_attribute is required")
}
customizationID, err := firstFormInt64(c, "id_customization", "customization_id")
if err != nil {
return pscart.MutationInput{}, fmt.Errorf("valid id_customization is required")
}
return pscart.MutationInput{
CartID: int64Value(session.CartID),
ProductID: productID,
ProductAttributeID: productAttributeID,
CustomizationID: customizationID,
Quantity: quantity,
CustomerID: int64Value(session.CustomerID),
GuestID: int64Value(session.GuestID),
LanguageID: int64Value(session.LanguageID),
CurrencyID: int64Value(session.CurrencyID),
ShopID: int64Value(session.ShopID),
}, nil
}
func formInt64(c echo.Context, key string) (int64, error) {
raw := strings.TrimSpace(c.FormValue(key))
if raw == "" {
return 0, nil
}
return strconv.ParseInt(raw, 10, 64)
}
func firstFormInt64(c echo.Context, keys ...string) (int64, error) {
for _, key := range keys {
value, err := formInt64(c, key)
if err != nil {
return 0, err
}
if value != 0 {
return value, nil
}
}
return 0, nil
}
func syncSessionCartID(session *pscookie.SessionContext, cartID int64) {
if session == nil || cartID == 0 {
return
}
if session.Values == nil {
session.Values = map[string]string{}
}
session.CartID = int64Ptr(cartID)
session.Values["id_cart"] = strconv.FormatInt(cartID, 10)
session.OrderedKeys = appendOrderedKeyIfMissing(session.OrderedKeys, "id_cart")
session.OrderedKeys = moveOrderedKeyToEnd(session.OrderedKeys, "checksum")
session.Plaintext = ""
session.RawCookie = ""
}
func appendOrderedKeyIfMissing(keys []string, key string) []string {
for _, existing := range keys {
if existing == key {
return keys
}
}
return append(keys, key)
}
func moveOrderedKeyToEnd(keys []string, key string) []string {
for i, existing := range keys {
if existing == key {
keys = append(keys[:i], keys[i+1:]...)
return append(keys, key)
}
}
return keys
}
func int64Ptr(value int64) *int64 {
if value == 0 {
return nil
}
v := value
return &v
}
func int64Value(value *int64) int64 {
if value == nil {
return 0
}
return *value
}
func (h *CartHandler) writeSessionCookie(c echo.Context, session *pscookie.SessionContext) error {
if err := h.sessions.RefreshExpiry(c.Request().Context(), session); err != nil {
return err
}
cookiePath, err := h.sessions.ResolveCookiePath(c.Request().Context(), c.Request())
if err != nil {
return err
}
encoded, err := h.codec.Encode(session)
if err != nil {
return err
}
session.RawCookie = encoded
writeSessionCookie(c.Request(), c.Response(), session, session.CookieName, encoded, cookiePath)
return nil
}
func writeSessionCookie(req *http.Request, res *echo.Response, session *pscookie.SessionContext, name, value, path string) {
maxAge := 1
if session != nil && session.ExpiresAt != nil {
maxAge = int(session.ExpiresAt.UTC().Unix())
}
if strings.TrimSpace(path) == "" {
path = "/"
}
header := fmt.Sprintf("%s=%s; path=%s; max-age=%d; HttpOnly; SameSite=Lax", name, value, path, maxAge)
if requestCookieSecure(req) {
header += "; Secure"
}
res.Header().Add(echo.HeaderSetCookie, header)
}
func requestCookieSecure(req *http.Request) bool {
if req == nil {
return false
}
if req.TLS != nil {
return true
}
forwarded := req.Header.Get("X-Forwarded-Proto")
if strings.Contains(forwarded, ",") {
forwarded = strings.TrimSpace(strings.Split(forwarded, ",")[0])
}
return strings.EqualFold(forwarded, "https")
}
func wantsJSON(req *http.Request) bool {
if req == nil {
return false
}
return strings.Contains(strings.ToLower(req.Header.Get(echo.HeaderAccept)), "application/json")
}
func cartRedirectTarget(req *http.Request) string {
if req == nil {
return "/cart?action=show"
}
referer := strings.TrimSpace(req.Referer())
if referer != "" {
return referer
}
path := requestLanguagePrefix(req) + "/cart"
if path == "/cart" {
return "/cart?action=show"
}
return path + "?action=show"
}
+131
View File
@@ -0,0 +1,131 @@
package handlers
import (
"context"
"errors"
"net/http"
"github.com/labstack/echo/v4"
"gorm.io/gorm"
appmiddleware "git.ma-al.com/goc_marek/ps_shop/internal/http/middleware"
pscart "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/cart"
pscatalog "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/catalog"
psconfig "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/config"
pscustomer "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/customer"
psroutes "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/routes"
"git.ma-al.com/goc_marek/ps_shop/internal/render"
"git.ma-al.com/goc_marek/ps_shop/internal/viewmodel"
)
type CartPageHandler struct {
catalog *pscatalog.Service
customers *pscustomer.Service
carts *pscart.Service
renderer *render.Engine
config psconfig.Config
products *psroutes.ProductRoute
categories *psroutes.CategoryRoute
}
func NewCartPageHandler(catalog *pscatalog.Service, customers *pscustomer.Service, carts *pscart.Service, renderer *render.Engine, cfg psconfig.Config, products *psroutes.ProductRoute, categories *psroutes.CategoryRoute) *CartPageHandler {
return &CartPageHandler{
catalog: catalog,
customers: customers,
carts: carts,
renderer: renderer,
config: cfg,
products: products,
categories: categories,
}
}
func (h *CartPageHandler) Show(c echo.Context) error {
session := appmiddleware.GetSession(c)
if h == nil || h.catalog == nil || h.carts == nil || h.renderer == nil {
return echo.NewHTTPError(http.StatusInternalServerError, "cart page handler is not initialized")
}
languageID := int64Default(session.LanguageID, 1)
languageID = h.catalog.ResolveLanguageID(c.Request().Context(), c.Request(), languageID)
shopID := int64Default(session.ShopID, 1)
currencyID := int64Default(session.CurrencyID, 1)
var profile *pscustomer.Profile
var err error
if session.CustomerID != nil && h.customers != nil {
profile, err = h.customers.GetByID(c.Request().Context(), *session.CustomerID)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return echo.NewHTTPError(http.StatusInternalServerError, "customer query failed: "+err.Error())
}
}
cartPage := pscart.Page{}
cartSummary := &pscart.Summary{}
if session.CartID != nil {
cartPage, cartSummary, err = h.loadCart(c.Request().Context(), *session.CartID, languageID, shopID, currencyID)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "cart query failed: "+err.Error())
}
assignCartProductLinks(c.Request(), h.products, &cartPage)
assignCartProductImages(requestBaseURL(c.Request()), &cartPage)
}
page := viewmodel.CartPageData{
Cart: cartPage,
Session: session,
Customer: profile,
CartSummary: cartSummary,
ShopBaseURL: h.config.PrestaShopBaseURL,
}
menu, err := loadMenu(c.Request(), h.catalog, h.categories, languageID, shopID)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "menu query failed: "+err.Error())
}
page.Menu = menu
locale, err := loadHeaderLocale(c.Request(), h.catalog, session, languageID)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "locale query failed: "+err.Error())
}
page.Locale = locale
return h.renderer.Cart(c.Response(), c.Request(), page)
}
func (h *CartPageHandler) loadCart(ctx context.Context, cartID, languageID, shopID, currencyID int64) (pscart.Page, *pscart.Summary, error) {
page, err := h.carts.PageByID(ctx, cartID, languageID, shopID, currencyID)
if err != nil {
return pscart.Page{}, nil, err
}
summary := &pscart.Summary{
ID: page.ID,
TotalItems: page.TotalItems,
}
return *page, summary, nil
}
func assignCartProductLinks(req *http.Request, route *psroutes.ProductRoute, page *pscart.Page) {
if page == nil || route == nil {
return
}
langPrefix := requestLanguagePrefix(req)
for i := range page.Items {
product := &page.Items[i]
product.URL = route.BuildPath(psroutes.ProductURLData{
ID: product.ProductID,
Slug: product.Slug,
CategoryPath: product.CategoryPath,
EAN13: product.EAN13,
LanguagePrefix: langPrefix,
})
}
}
func assignCartProductImages(baseURL string, page *pscart.Page) {
if page == nil {
return
}
for i := range page.Items {
page.Items[i].ImageURL = prestashopImageURL(baseURL, page.Items[i].CoverImageID, "home_default")
}
}
+82
View File
@@ -0,0 +1,82 @@
package handlers
import (
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"github.com/labstack/echo/v4"
pscookie "git.ma-al.com/goc_marek/ps_shop/internal/prestashop/cookie"
)
func TestCartActionFromRequestDefaultsToAdd(t *testing.T) {
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/cart", strings.NewReader(url.Values{
"id_product": []string{"42"},
}.Encode()))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationForm)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
if got := cartActionFromRequest(c); got != cartActionAdd {
t.Fatalf("cartActionFromRequest() = %q, want %q", got, cartActionAdd)
}
}
func TestCartActionFromRequestHonorsDeleteFlag(t *testing.T) {
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/cart", strings.NewReader(url.Values{
"delete": []string{"1"},
"id_product": []string{"42"},
}.Encode()))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationForm)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
if got := cartActionFromRequest(c); got != cartActionDelete {
t.Fatalf("cartActionFromRequest() = %q, want %q", got, cartActionDelete)
}
}
func TestSyncSessionCartIDPreservesChecksumOrder(t *testing.T) {
session := &pscookie.SessionContext{
Values: map[string]string{
"id_lang": "5",
"checksum": "123",
"id_guest": "11",
"id_cart": "",
"id_shop": "1",
"id_guest2": "ignored",
},
OrderedKeys: []string{"id_lang", "id_guest", "checksum"},
RawCookie: "raw",
Plaintext: "plaintext",
}
syncSessionCartID(session, 99)
if session.CartID == nil || *session.CartID != 99 {
t.Fatalf("CartID = %v, want 99", session.CartID)
}
if got := session.Values["id_cart"]; got != "99" {
t.Fatalf("Values[id_cart] = %q, want %q", got, "99")
}
wantOrder := []string{"id_lang", "id_guest", "id_cart", "checksum"}
if len(session.OrderedKeys) != len(wantOrder) {
t.Fatalf("OrderedKeys length = %d, want %d", len(session.OrderedKeys), len(wantOrder))
}
for i, want := range wantOrder {
if got := session.OrderedKeys[i]; got != want {
t.Fatalf("OrderedKeys[%d] = %q, want %q", i, got, want)
}
}
if session.RawCookie != "" {
t.Fatalf("RawCookie = %q, want empty", session.RawCookie)
}
if session.Plaintext != "" {
t.Fatalf("Plaintext = %q, want empty", session.Plaintext)
}
}
+90 -3
View File
@@ -3,6 +3,8 @@ package handlers
import (
"errors"
"net/http"
"net/url"
"strconv"
"strings"
"github.com/labstack/echo/v4"
@@ -52,12 +54,16 @@ func (h *CategoryHandler) Show(c echo.Context) error {
languageID := int64Default(session.LanguageID, 1)
languageID = h.catalog.ResolveLanguageID(c.Request().Context(), c.Request(), languageID)
shopID := int64Default(session.ShopID, 1)
currencyID := int64Default(session.CurrencyID, 1)
category, err := h.catalog.GetCategoryPage(c.Request().Context(), pscatalog.CategoryPageRequest{
ID: categoryID(c),
Slug: categorySlug(c),
LanguageID: languageID,
ShopID: shopID,
CurrencyID: currencyID,
Page: categoryPageParam(c.Request()),
PerPage: 30,
})
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
@@ -89,7 +95,9 @@ func (h *CategoryHandler) Show(c echo.Context) error {
CartSummary: cartSummary,
ShopBaseURL: h.config.PrestaShopBaseURL,
}
page.Pagination = categoryPaginationView(c.Request(), category.Pagination)
assignCategoryProductLinks(c.Request(), h.products, &page)
assignCategoryProductImages(requestBaseURL(c.Request()), &page)
menu, err := loadMenu(c.Request(), h.catalog, h.categories, languageID, shopID)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "menu query failed: "+err.Error())
@@ -137,10 +145,27 @@ func assignCategoryProductLinks(req *http.Request, route *psroutes.ProductRoute,
if page == nil {
return
}
assignProductCardsLinks(req, route, page.Category.Products, page.Category.Slug)
}
func assignCategoryProductImages(baseURL string, page *viewmodel.CategoryPageData) {
if page == nil {
return
}
assignProductCardsImages(baseURL, page.Category.Products)
}
func assignProductCardsLinks(req *http.Request, route *psroutes.ProductRoute, products []pscatalog.CategoryProductCard, fallbackCategoryPath string) {
if route == nil {
return
}
langPrefix := requestLanguagePrefix(req)
categoryPath := page.Category.Slug
for i := range page.Category.Products {
product := &page.Category.Products[i]
for i := range products {
product := &products[i]
categoryPath := strings.TrimSpace(product.CategorySlug)
if categoryPath == "" {
categoryPath = fallbackCategoryPath
}
product.URL = route.BuildPath(psroutes.ProductURLData{
ID: product.ID,
Slug: product.Slug,
@@ -150,3 +175,65 @@ func assignCategoryProductLinks(req *http.Request, route *psroutes.ProductRoute,
})
}
}
func assignProductCardsImages(baseURL string, products []pscatalog.CategoryProductCard) {
for i := range products {
products[i].ImageURL = prestashopImageURL(baseURL, products[i].CoverImageID, "home_default")
}
}
func categoryPageParam(req *http.Request) int {
if req == nil || req.URL == nil {
return 1
}
raw := strings.TrimSpace(req.URL.Query().Get("page"))
if raw == "" {
return 1
}
page, err := strconv.Atoi(raw)
if err != nil || page <= 0 {
return 1
}
return page
}
func categoryPageURL(req *http.Request, page int) string {
if req == nil || req.URL == nil || page <= 0 {
return ""
}
query := url.Values{}
for key, values := range req.URL.Query() {
for _, value := range values {
query.Add(key, value)
}
}
if page <= 1 {
query.Del("page")
} else {
query.Set("page", strconv.Itoa(page))
}
path := req.URL.Path
if path == "" {
path = "/"
}
if encoded := query.Encode(); encoded != "" {
return path + "?" + encoded
}
return path
}
func categoryPaginationView(req *http.Request, meta pscatalog.CategoryPagination) viewmodel.CategoryPagination {
view := viewmodel.CategoryPagination{
Page: meta.Page,
PerPage: meta.PerPage,
TotalItems: meta.TotalItems,
TotalPages: meta.TotalPages,
}
if meta.Page > 1 {
view.PrevURL = categoryPageURL(req, meta.Page-1)
}
if meta.TotalPages > 0 && meta.Page < meta.TotalPages {
view.NextURL = categoryPageURL(req, meta.Page+1)
}
return view
}
+30 -1
View File
@@ -14,17 +14,31 @@ import (
const (
testCookieKey = "def000008bf3d70e7012b7493c382d561e193218d0c74ab162fb0ea8029ce20e926531b4bcf0aaec9381152e6c161f198e06918b2d1aad67cc7cf40819a51ee328c63830"
testCookie = "def5020099dce5cd9ecf197adb5532a74e3db2ed9cba3d59b98f365353099b710bd562efa48b6bad1ad0a12b2ee54de0fbfcc6baa0545a8234141b03bfc1fbbbb9061af5011764b9c4dfd9c0ddcad767a453e0cc24d6b4a7c524e6c49aabd66ecc390e1a964b6e81a051b171051c829542facbb36cf64fcfebf069906dcc95476578be3fe59aaae466cf70bd9c877d301d908ec3aa4f55366567f460dfefac1684ce381293e8d4138382a42716d6aaecdcc7"
)
func TestDecodeCookieFromQueryParameter(t *testing.T) {
codec, err := pscookie.NewCodec(pscookie.Config{
CookieName: "PrestaShop-test",
CookieKey: testCookieKey,
CookieIV: "vfRFMV42",
})
if err != nil {
t.Fatalf("NewCodec() error = %v", err)
}
testCookie, err := codec.Encode(&pscookie.SessionContext{
Values: map[string]string{
"date_add": "2026-05-13 18:51:06",
"id_lang": "1",
"id_language": "1",
"detect_language": "1",
"id_currency": "1",
"iso_code_country": "CZ",
},
OrderedKeys: []string{"date_add", "id_lang", "id_language", "detect_language", "iso_code_country", "id_currency", "checksum"},
})
if err != nil {
t.Fatalf("Encode() error = %v", err)
}
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/debug/cookie/decode?value="+testCookie, nil)
@@ -55,10 +69,25 @@ func TestDecodeCookieFromSession(t *testing.T) {
codec, err := pscookie.NewCodec(pscookie.Config{
CookieName: "PrestaShop-test",
CookieKey: testCookieKey,
CookieIV: "vfRFMV42",
})
if err != nil {
t.Fatalf("NewCodec() error = %v", err)
}
testCookie, err := codec.Encode(&pscookie.SessionContext{
Values: map[string]string{
"date_add": "2026-05-13 18:51:06",
"id_lang": "1",
"id_language": "1",
"detect_language": "1",
"id_currency": "1",
"iso_code_country": "CZ",
},
OrderedKeys: []string{"date_add", "id_lang", "id_language", "detect_language", "iso_code_country", "id_currency", "checksum"},
})
if err != nil {
t.Fatalf("Encode() error = %v", err)
}
session, err := codec.Decode(testCookie)
if err != nil {
+63
View File
@@ -0,0 +1,63 @@
package handlers
import (
"database/sql"
"net/http"
"strconv"
"strings"
)
func prestashopImageURL(baseURL string, imageID sql.NullInt64, imageType string) string {
if !imageID.Valid || imageID.Int64 == 0 {
return ""
}
id := strconv.FormatInt(imageID.Int64, 10)
var path strings.Builder
path.Grow(len(baseURL) + len(id)*2 + len(imageType) + 16)
path.WriteString(strings.TrimRight(baseURL, "/"))
path.WriteString("/img/p")
for _, ch := range id {
path.WriteByte('/')
path.WriteRune(ch)
}
path.WriteByte('/')
path.WriteString(id)
if strings.TrimSpace(imageType) != "" {
path.WriteByte('-')
path.WriteString(strings.TrimSpace(imageType))
}
path.WriteString(".webp")
return path.String()
}
func requestBaseURL(req *http.Request) string {
if req == nil {
return ""
}
scheme := "http"
if req.TLS != nil {
scheme = "https"
} else if forwarded := req.Header.Get("X-Forwarded-Proto"); forwarded != "" {
if strings.Contains(forwarded, ",") {
forwarded = strings.TrimSpace(strings.Split(forwarded, ",")[0])
}
if strings.TrimSpace(forwarded) != "" {
scheme = strings.TrimSpace(forwarded)
}
}
host := req.Header.Get("X-Forwarded-Host")
if host == "" {
host = req.Host
}
if strings.Contains(host, ",") {
host = strings.TrimSpace(strings.Split(host, ",")[0])
}
host = strings.TrimSpace(host)
if host == "" {
return ""
}
return scheme + "://" + host
}
+42 -1
View File
@@ -1,6 +1,7 @@
package handlers
import (
"database/sql"
"errors"
"net/http"
"strings"
@@ -27,16 +28,18 @@ type ProductHandler struct {
carts *pscart.Service
renderer *render.Engine
config psconfig.Config
productURL *psroutes.ProductRoute
categories *psroutes.CategoryRoute
}
func NewProductHandler(products *pscatalog.Service, customers *pscustomer.Service, carts *pscart.Service, renderer *render.Engine, cfg psconfig.Config, categories *psroutes.CategoryRoute) *ProductHandler {
func NewProductHandler(products *pscatalog.Service, customers *pscustomer.Service, carts *pscart.Service, renderer *render.Engine, cfg psconfig.Config, productURL *psroutes.ProductRoute, categories *psroutes.CategoryRoute) *ProductHandler {
return &ProductHandler{
products: products,
customers: customers,
carts: carts,
renderer: renderer,
config: cfg,
productURL: productURL,
categories: categories,
}
}
@@ -50,12 +53,14 @@ func (h *ProductHandler) Show(c echo.Context) error {
languageID := int64Default(session.LanguageID, 1)
languageID = h.products.ResolveLanguageID(c.Request().Context(), c.Request(), languageID)
shopID := int64Default(session.ShopID, 1)
currencyID := int64Default(session.CurrencyID, 1)
product, err := h.products.GetProductPage(c.Request().Context(), pscatalog.ProductPageRequest{
ID: productID(c),
Slug: productSlug(c),
LanguageID: languageID,
ShopID: shopID,
CurrencyID: currencyID,
})
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
@@ -63,6 +68,14 @@ func (h *ProductHandler) Show(c echo.Context) error {
}
return err
}
product.ImageURL = prestashopImageURL(requestBaseURL(c.Request()), product.CoverImageID, "large_default")
assignProductGalleryImages(requestBaseURL(c.Request()), product)
assignProductCombinationImages(requestBaseURL(c.Request()), product)
if product.ImageURL == "" && len(product.GalleryImages) > 0 {
product.ImageURL = product.GalleryImages[0].URL
}
assignProductCardsLinks(c.Request(), h.productURL, product.Accessories, "")
assignProductCardsImages(requestBaseURL(c.Request()), product.Accessories)
var profile *pscustomer.Profile
if session.CustomerID != nil && h.customers != nil {
@@ -145,3 +158,31 @@ func productCategoryURL(req *http.Request, route *psroutes.CategoryRoute, produc
LanguagePrefix: requestLanguagePrefix(req),
})
}
func assignProductGalleryImages(baseURL string, product *pscatalog.ProductPageData) {
if product == nil {
return
}
for i := range product.GalleryImages {
id := sqlNullInt64(product.GalleryImages[i].ID)
product.GalleryImages[i].URL = prestashopImageURL(baseURL, id, "large_default")
product.GalleryImages[i].ThumbURL = prestashopImageURL(baseURL, id, "home_default")
}
}
func assignProductCombinationImages(baseURL string, product *pscatalog.ProductPageData) {
if product == nil {
return
}
for i := range product.Combinations {
product.Combinations[i].ImageURL = prestashopImageURL(baseURL, product.Combinations[i].ImageID, "large_default")
product.Combinations[i].ThumbURL = prestashopImageURL(baseURL, product.Combinations[i].ImageID, "home_default")
}
}
func sqlNullInt64(value int64) sql.NullInt64 {
if value == 0 {
return sql.NullInt64{}
}
return sql.NullInt64{Int64: value, Valid: true}
}