VOOZH about

URL: https://deepwiki.com/gp247net/shop/3.1-product-model-and-business-logic

⇱ Product Model and Business Logic | gp247net/shop | DeepWiki


Loading...
Menu

Product Model and Business Logic

Purpose and Scope

This document covers the ShopProduct model (src/Models/ShopProduct.php) which serves as the core business entity for the product catalog system. It defines the data structure, relationships, pricing logic, stock management, and query building capabilities for products in the e-commerce platform.

This page focuses on the model class itself and its business logic. For product administration interfaces, see Admin Interface Patterns. For frontend product display, see Product Frontend Display. For category management, see Category Management.

Sources: src/Models/ShopProduct.php1-939


Model Structure and Database Schema

Class Definition

The ShopProduct model extends Laravel's Model class and uses two traits:

ShopProduct extends Model
├── uses ModelTrait (from GP247\Core)
├── uses UuidTrait (from GP247\Core)
└── table: shop_product

Key Properties:

PropertyTypePurpose
$tablestringDatabase table name with prefix
$guardedarrayMass assignment protection (empty = all fillable)
$connectionstringDatabase connection identifier
$gp247_kindarrayInternal filter for product types
$gp247_tagmixedInternal filter for product tags (physical/download/service)
$gp247_promotionintInternal flag for promotion filtering
$gp247_store_info_idintInternal store context for queries
$gp247_categoryarrayInternal category filter
$gp247_brandarrayInternal brand filter
$gp247_supplierarrayInternal supplier filter
$gp247_range_pricestringInternal price range filter (min__max)

Sources: src/Models/ShopProduct.php17-37

Database Schema

The shop_product table contains the following key fields:

FieldTypeDescription
idstring (UUID)Primary key, auto-generated with 'PRD' prefix
skustringStock keeping unit identifier
aliasstringURL-friendly identifier
imagestringPath to primary product image
brand_idstring/nullForeign key to shop_brand
supplier_idstring/nullForeign key to shop_supplier
pricedecimalBase price
costdecimalCost price
stockintAvailable quantity
soldintTotal quantity sold
minimumintMinimum order quantity
kindintProduct type: 0=single, 1=bundle, 2=group
tagintProduct tag: 0=physical, 1=download, 2=view-only, 3=service
tax_idmixedTax identifier or 'auto'
statusbooleanActive/inactive status
approvebooleanApproval status (for multi-vendor)
date_availabledatetimeProduct availability date
weight, length, width, heightdecimalDimensional data

Sources: src/Commands/ShopSample.php546-573


Product Types (Kind)

The system supports three product types, defined by the kind field:


Single Products

Single products are standalone items with their own pricing and inventory. They represent most typical e-commerce products.

Methods:

Bundle Products

Bundle products are composite items made up of multiple single products. When a bundle is sold, the stock is deducted from each component product.

Implementation:

  • Uses shop_product_build junction table to define components
  • Each component has a quantity multiplier
  • Stock updates cascade to component products

Methods:

Group Products

Group products are containers for product variants (e.g., a t-shirt with multiple size/color options). They don't have their own price or stock; variants are separate single products.

Implementation:

  • Uses shop_product_group junction table to link variants
  • Cannot be sold directly (allowSale() returns false for groups)
  • Used for display and variant selection UI

Methods:

Sources: src/Models/ShopProduct.php27 src/Models/ShopProduct.php580-602 src/Models/ShopProduct.php409-416 src/Commands/ShopSample.php663 src/Commands/ShopSample.php761


Eloquent Relationships


Core Relationships

MethodTypeRelated ModelJunction TablePurpose
brand()belongsToShopBrand-Product manufacturer
supplier()belongsToShopSupplier-Product vendor
categories()belongsToManyShopCategoryShopProductCategoryCategory associations
stores()belongsToManyAdminStoreShopProductStoreMulti-store assignment
descriptions()hasManyShopProductDescription-Localized content
images()hasManyShopProductImage-Product gallery
promotionPrice()hasOneShopProductPromotion-Promotional pricing
attributes()hasManyShopProductAttribute-Configurable options
groups()hasManyShopProductGroup-Product variants (for kind=2)
builds()hasManyShopProductBuild-Bundle components (for kind=1)
downloadPath()hasOneShopProductDownload-Digital download link

Sources: src/Models/ShopProduct.php40-87


Pricing Logic

Price Calculation Flow


Core Pricing Methods

getFinalPrice()

Returns the effective selling price after applying promotions:

1. Call processPromotionPrice()
2. If promotion exists and valid: return promotion price
3. Else: return base price

Implementation: src/Models/ShopProduct.php115-123

processPromotionPrice()

Private method that validates promotional pricing:

Validation Rules:

  • Promotion status must be active (status_promotion = 1)
  • Current date >= date_start (or date_start is null)
  • Current date <= date_end (or date_end is null)

Returns: Promotion price if valid, -1 otherwise

Implementation: src/Models/ShopProduct.php382-394

getFinalPriceTax()

Returns the final price including applicable tax:

final_price_with_tax = getFinalPrice() + (getFinalPrice() * getTaxValue())

Implementation: src/Models/ShopProduct.php128-131

Tax Calculation

The model integrates with ShopTax for tax calculations:

Price Display Methods

Both methods:

  1. Check if price display is enabled via gp247_config('product_price')
  2. Calculate base and final prices
  3. Render appropriate Blade template
  4. Pass kind field to template for type-specific rendering

Sources: src/Models/ShopProduct.php115-184 src/Models/ShopProduct.php382-394 src/Models/ShopProduct.php875-905


Stock Management

Stock Availability Logic


allowSale() Method

Determines if a product can be added to cart based on multiple conditions:

Configuration Dependencies:

  • product_price - Must be enabled to show/sell products
  • product_preorder - Allows pre-orders for unavailable dates
  • product_buy_out_of_stock - Allows purchases when stock = 0
  • product_stock - Enables stock tracking

Business Rules:

  1. Product must be active (status = 1)
  2. Product must not be a group type (groups are variant containers)
  3. Product must be available (date check or preorder enabled)
  4. Product must have stock OR out-of-stock purchases enabled OR stock tracking disabled

Implementation: src/Models/ShopProduct.php358-377

updateStock() Static Method

Updates stock and sold quantities when an order is placed:

For Single Products:

stock = stock - qty_change
sold = sold + qty_change

For Bundle Products:

For each component in builds():
 component.stock -= qty_change * build.quantity
 component.sold += qty_change * build.quantity

Implementation: src/Models/ShopProduct.php399-418

Display Filtering

The query builder automatically filters out-of-stock products when configured:

if (product_display_out_of_stock disabled AND product_stock enabled) {
 WHERE stock > 0
}

Implementation: src/Models/ShopProduct.php800-802

Sources: src/Models/ShopProduct.php358-418 src/Models/ShopProduct.php800-802


Query Builder System

The ShopProduct model implements a fluent query builder pattern for complex product filtering and retrieval.

Query Building Architecture


Fluent Query Methods

These public methods chain together to build complex queries:

MethodPurposeSets Filter
getProductSingle()Filter to single productsgp247_kind = [0]
getProductBuild()Filter to bundle productsgp247_kind = [1]
getProductGroup()Filter to group productsgp247_kind = [2]
getProductToCategory($arr)Filter by category IDsgp247_category
getProductToCategoryStore($id)Filter by vendor categorygp247_category_vendor
getProductToBrand($arr)Filter by brand IDsgp247_brand
getProductToSupplier($arr)Filter by supplier IDsgp247_supplier
getProductPromotion()Filter to promoted productsgp247_promotion = 1
getProductLatest()Sort by created date (desc)Sets limit 10, sort
getProductLastView()Sort by availability dateSets limit 10, sort
getProductBestSell()Sort by sold quantitySets limit 10, sort
getProductFromListID($arr)Filter by product IDsgp247_array_ID
setStore($id)Set store contextgp247_store_info_id
setRangePrice($price)Filter by price rangegp247_range_price

Example Usage Pattern:


Sources: src/Models/ShopProduct.php425-567 src/Models/ShopProduct.php572-698

buildQuery() Method

The core query construction method that applies all accumulated filters:

Query Construction Steps

  1. Subquery for Unique Product IDs

  2. Multi-Store Filtering

    • Joins shop_product_store and admin_store tables
    • Filters by active stores only
    • Applies vendor/store isolation if configured
    • Handles vendor category filtering for MultiVendorPro plugin (src/Models/ShopProduct.php714-741)
  3. Category Filtering

  4. Promotion Filtering

  5. Additional Filters Applied:

    • Product IDs (gp247_array_ID)
    • Status and approval flags
    • Product kind
    • Product tag (physical/download/service)
    • Brand ID
    • Price range
    • Supplier ID
    • Out-of-stock visibility (src/Models/ShopProduct.php766-802)
  6. Main Query Construction

  7. Keyword Search

  8. Eager Loading

  9. Sorting

Implementation: src/Models/ShopProduct.php703-868

Sources: src/Models/ShopProduct.php703-868


Product Detail Retrieval

getDetail() Method

Retrieves a single product with all necessary joins and validations:


Method Signature:

getDetail($key, $type = null, $storeId = null, $checkActive = 1)

Parameters:

ParameterTypeDefaultDescription
$keystringnullLookup value (ID, SKU, or alias)
$typestringnullLookup type: null='id', 'alias', 'sku'
$storeIdstringcurrentStore context for multi-store
$checkActiveint1Whether to filter by status/approval

Features:

  1. Store Validation - Ensures target store exists and is active
  2. Store Isolation - Non-root stores can only view their own products
  3. Multi-Store Join - Joins store tables when multi-store/vendor enabled
  4. Localization - Joins description table by current locale
  5. Flexible Lookup - Supports lookup by ID, alias, or SKU
  6. Conditional Activation Check - Can bypass status checks for admin views
  7. Eager Loading - Preloads images, stores, and promotion relationships

Implementation: src/Models/ShopProduct.php192-258

Sources: src/Models/ShopProduct.php192-258


Localization Support

Products use a separate table (shop_product_description) for localized content:

Localization Methods

MethodReturnsDescription
getText()ShopProductDescriptionGets description for current locale
getName()stringProduct name in current locale
getDescription()stringShort description in current locale
getKeyword()stringSEO keywords in current locale
getContent()stringFull content/details in current locale

Implementation Pattern:

getText() → descriptions()->where('lang', gp247_get_locale())->first()
getName() → getText()->name ?? ''

All localized getters use the getText() method as a base, which queries the descriptions() relationship filtered by current locale.

Sources: src/Models/ShopProduct.php89-110


Multi-Store Integration

Store Association

Products are associated with stores via the shop_product_store junction table:

  • Relationship: stores() - belongsToMany with AdminStore (src/Models/ShopProduct.php58-61)
  • Purpose: Allows catalog sharing across multiple stores
  • Isolation: Query builder automatically filters by store context

Multi-Store Query Logic

The buildQuery() method handles multi-store filtering:

IF (multi-partner OR multi-store installed) THEN
 JOIN shop_product_store
 JOIN admin_store WHERE status = 1
 
 IF (multi-store mode) OR (vendor mode AND not root store) THEN
 WHERE shop_product_store.store_id = current_store
 END IF
END IF

Root Store Behavior:

  • Can view products from all stores
  • No store filtering applied unless explicitly set

Vendor Store Behavior:

  • Can only view own products
  • Automatic filtering by store ID

Implementation: src/Models/ShopProduct.php714-741

Vendor Display

For multi-vendor setups, the model provides vendor display functionality:

Sources: src/Models/ShopProduct.php58-61 src/Models/ShopProduct.php714-741 src/Models/ShopProduct.php912-938 src/Views/front/common/shop_display_store.blade.php1-3


Helper Methods

Image Methods

MethodReturnsDescription
getThumb()stringThumbnail image path
getImage()stringFull-size image path

Both methods use global helper functions (gp247_image_get_path_thumb(), gp247_image_get_path()) to resolve image paths.

Implementation: src/Models/ShopProduct.php298-309

URL Generation

  • getUrl($lang = null) - Generates frontend product detail URL using gp247_route_front() helper
  • Uses product alias for SEO-friendly URLs
  • Supports optional language parameter

Implementation: src/Models/ShopProduct.php315-318

Discount Calculation

  • getPercentDiscount() - Calculates promotion discount percentage:
    discount% = ((base_price - final_price) / base_price) * 100
    

Implementation: src/Models/ShopProduct.php324-327

Attribute Rendering

  • renderAttributeDetails() - Renders product attributes grouped by attribute group
  • Returns rendered Blade view with attributes and groups
  • Template path: common.shop_render_attribute

Implementation: src/Models/ShopProduct.php329-341

Scope: Sort

  • scopeSort($query, $sortBy, $sortOrder) - Query scope for sorting
  • Default sort by ID, ascending

Implementation: src/Models/ShopProduct.php345-349

Sources: src/Models/ShopProduct.php298-349


Lifecycle and Events

Model Boot Method

The static boot() method handles model lifecycle events:


Deleting Event

When a product is deleted, the following cascade operations occur:

  1. Delete related records:

    • Images (images())
    • Descriptions (descriptions())
    • Promotion pricing (promotionPrice())
    • Group associations (groups())
    • Attributes (attributes())
    • Download paths (downloadPath())
    • Build components (builds())
  2. Detach relationships:

    • Categories (categories()->detach())
    • Stores (stores()->detach())
  3. Delete custom fields:

    • Queries admin_custom_field_detail table
    • Removes custom field data where type = 'shop_product'

Implementation: src/Models/ShopProduct.php264-284

Creating Event

When a new product is created:

  1. Check if ID is empty
  2. If empty, generate UUID with 'PRD' prefix using gp247_generate_id()
  3. This ensures all products have consistent ID format

Implementation: src/Models/ShopProduct.php287-291

Sources: src/Models/ShopProduct.php260-293


Summary

The ShopProduct model provides a comprehensive foundation for product management with:

  • Three product types (single, bundle, group) supporting various business models
  • Complex pricing logic with promotion support and tax calculation
  • Flexible stock management with configurable availability rules
  • Powerful query builder with fluent API for filtering and sorting
  • Full internationalization via separate description tables
  • Multi-store architecture with proper isolation and sharing mechanisms
  • Rich relationship mapping to categories, brands, suppliers, and stores
  • Automatic lifecycle management for data integrity

The model serves as the central business entity in the product catalog system, integrating with multiple subsystems including orders, cart, categories, and admin interfaces.

Refresh this wiki

On this page