Skip to content

Products by Market

ProductByMarket represents how a ProductTemplate is sold in a specific market. It connects the base product to a market, adds localized content, and configures flight search parameters.

  • Assign products to markets (ES, DE, FR)
  • Store market-specific locale (es_ES, ca_ES for Spain)
  • Configure departure airports and flight routes
  • Define search date ranges for flight caching
  • Hold translated content via ProductByMarketTranslation

Table: products_by_market

FieldTypePurpose
product_template_idFKReference to ProductTemplate
market_idFKReference to Market
localevarcharProduct locale (e.g., es_ES)
skuvarcharMarket SKU: ES-138-10-ES1
statusvarchardraft, active, inactive
trip_duration_daysintOverride template duration
search_start_datedateFlight search period start
search_end_datedateFlight search period end
excluded_datesjsonbDates to skip in searches

Unique Constraint: product_template_id + market_id + locale

Source: backend/app/Models/ProductByMarket.php

<MARKET>-<TEMPLATE_ID>-<DAYS>-<LANG><VERSION>

Examples:

  • ES-138-10-ES1 - Spain, template 138, 10 days, Spanish, version 1
  • ES-138-10-CA1 - Spain, template 138, 10 days, Catalan, version 1
  • DE-138-10-DE1 - Germany, template 138, 10 days, German, version 1

Source: backend/app/Models/ProductByMarket.php:217-242

Translated content is stored in ProductByMarketTranslation:

FieldPurpose
title, subtitleLocalized product name
short_description, long_descriptionMarketing copy
itineraryTranslated day titles and details
url_slugSEO-friendly URL path
meta_title, meta_descriptionSEO metadata

AI Translation: Use “Translate from Product” button in admin to auto-translate all fields.

Source: backend/app/Services/ProductByMarketTranslationService.php

Each ProductByMarket can have multiple departure airports, each with its own route configuration.

ProductByMarket
├── search_start_date, search_end_date (shared)
├── excluded_dates (shared)
└── ProductByMarketFlightConfig (per departure airport)
├── airport_id (MAD, BCN, etc.)
├── type (multi_city or separate)
├── is_active
└── ProductByMarketFlightLeg[] (route segments)
├── origin_airport_id
├── destination_airport_id
├── day_offset
└── flight_type (international, domestic, arnk)
TypeDescriptionAPI Calls
multi_citySingle booking for all legs1 per date
separateIndividual bookings per legN per date
TypeDescription
internationalCross-border flight (requires API search)
domesticSame-country flight (requires API search)
arnkGround transport (no flight search needed)

Source: backend/app/Models/ProductByMarketFlightConfig.php, backend/app/Models/ProductByMarketFlightLeg.php

Flight searches are configured with:

  • search_start_date - First departure date to search
  • search_end_date - Last departure date to search
  • excluded_dates - Specific dates to skip (holidays, blackouts)
$product->shouldSearchDate($date); // Combined check
$product->isDateInSearchRange($date); // Range check only
$product->isDateExcluded($date); // Exclusion check only
$product->getSearchableDaysCount(); // Total searchable days

Source: backend/app/Models/ProductByMarket.php:159-207

// Per flight config
$config->getApiCallsPerDate(); // 1 for multi_city, N for separate
$config->getTotalEstimatedApiCalls(); // Days * calls per date
// Per product by market (all configs)
$product->getTotalEstimatedApiCalls(); // Sum across all configs
  • One ProductByMarket per template + market + locale combination
  • Templates cannot be edited once they have market products (locked)
  • Flight configs are generated based on itinerary when product is created
  • Search dates are shared across all departure airports
  • ARNK legs do not trigger flight searches