- Move toggle-count click handler next to filter-group-toggle handler - Use this.$dropdown.on() for proper event delegation within dropdown context - Ensures self reference is correctly captured for showFilterGroupPreviewPopover Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
PrestaShop Condition Traits
A collection of reusable PHP traits for PrestaShop admin controllers that provide powerful UI widgets for targeting and scheduling.
Available Traits
| Trait | Purpose |
|---|---|
| TargetConditions | Multi-criteria targeting (countries, currencies, carriers, groups, languages) |
| ScheduleConditions | Time-based scheduling (datetime range, weekly schedule, holiday exclusions) |
Installation
Via Composer
composer require myprestarocks/prestashop-target-conditions
Manual Installation
Copy the package to your module's vendor directory:
modules/your_module/vendor/myprestarocks/prestashop-target-conditions/
TargetConditions Trait
A powerful multi-criteria targeting widget. Allows users to define conditions based on countries, currencies, carriers, customer groups, and languages.
Basic Usage
1. Include the Trait in Your Admin Controller
<?php
use MyPrestaRocks\TargetConditions\TargetConditions;
class AdminYourModuleController extends ModuleAdminController
{
use TargetConditions;
// Your controller code...
}
2. Register Assets in setMedia()
public function setMedia($isNewTheme = false)
{
parent::setMedia($isNewTheme);
// Register the target conditions assets
$this->registerTargetConditionsAssets();
}
3. Add the Widget to Your Form
In your form rendering method (e.g., renderForm()), add the widget:
public function renderForm()
{
// Get existing restrictions from database (JSON string or array)
$restrictions = $this->object->restrictions ?? '[]';
$this->fields_form = [
'legend' => [
'title' => $this->l('Configuration'),
],
'input' => [
[
'type' => 'free',
'label' => $this->l('Target Conditions'),
'name' => 'restrictions_widget',
'desc' => $this->l('Define where this item should be available'),
],
],
// ... other fields
];
// Render the widget HTML
$this->fields_value['restrictions_widget'] = $this->renderTargetConditionsHtml(
'restrictions', // Field name for form submission
$restrictions, // Current value (JSON or array)
[
// Configuration options (see below)
]
);
return parent::renderForm();
}
4. Process the Submitted Data
public function postProcess()
{
if (Tools::isSubmit('submitYourForm')) {
$restrictions = Tools::getValue('restrictions');
// The value is already JSON-encoded from the widget
// Store it in your database
$this->object->restrictions = $restrictions;
$this->object->save();
}
return parent::postProcess();
}
Configuration Options
The renderTargetConditionsHtml() method accepts a configuration array as the third parameter:
$this->renderTargetConditionsHtml('field_name', $value, [
// Available condition groups to show
// Default: all groups enabled
'groups' => [
'countries' => true, // Country selection
'currencies' => true, // Currency selection
'carriers' => true, // Carrier/shipping method selection
'groups' => true, // Customer group selection
'languages' => true, // Language selection
],
// Empty selection behavior
// true = empty means "all included" (available everywhere)
// false = empty means "none selected" (not available anywhere)
'empty_means_all' => true,
// Show "All X" toggle badges
// When set, displays toggle buttons showing selection state
'show_all_toggle' => [
'empty' => $this->l('Available Everywhere'), // Label when nothing selected
'selected' => $this->l('Restricted'), // Label when items selected
],
// Show group modifiers (Limit/Sort controls)
// true = show limit dropdown and sort controls per group
// false = hide modifiers (cleaner UI for targeting contexts)
'show_modifiers' => true,
// Pre-filter available options based on external restrictions
// Useful when parent entity has its own restrictions
'restrictions' => [
'countries' => ['FR', 'DE', 'PL'], // Only show these countries
'currencies' => ['EUR', 'PLN'], // Only show these currencies
// ... etc
],
// Custom labels for condition groups
'labels' => [
'countries' => $this->l('Countries'),
'currencies' => $this->l('Currencies'),
'carriers' => $this->l('Carriers'),
'groups' => $this->l('Customer Groups'),
'languages' => $this->l('Languages'),
],
]);
Use Case Examples
Payment Method Targeting
Restrict payment methods to specific countries/currencies:
$this->renderTargetConditionsHtml('restrictions', $paymentOption->restrictions, [
'groups' => [
'countries' => true,
'currencies' => true,
'carriers' => false, // Not relevant for payments
'groups' => true,
'languages' => false,
],
'empty_means_all' => true,
'show_modifiers' => false, // No need for limit/sort on targeting
'show_all_toggle' => [
'empty' => $this->l('Available Everywhere'),
'selected' => $this->l('Restricted'),
],
]);
Product Selection Widget
Select products with quantity limits:
$this->renderTargetConditionsHtml('selected_products', $rule->products, [
'groups' => [
'products' => true,
],
'empty_means_all' => false, // Must explicitly select products
'show_modifiers' => true, // Allow limiting quantity per selection
]);
Shipping Rules
Define carrier availability by zone:
$this->renderTargetConditionsHtml('availability', $carrier->zones, [
'groups' => [
'countries' => true,
'groups' => true, // Different rates for customer groups
],
'empty_means_all' => true,
'show_all_toggle' => [
'empty' => $this->l('Ships Everywhere'),
'selected' => $this->l('Limited Zones'),
],
]);
Data Format
The widget stores data as a JSON object with the following structure:
{
"countries": {
"items": ["FR", "DE", "PL"],
"limit": 0,
"sort": "default"
},
"currencies": {
"items": ["EUR", "PLN"],
"limit": 0,
"sort": "default"
},
"carriers": {
"items": [],
"limit": 0,
"sort": "default"
},
"groups": {
"items": [1, 2, 3],
"limit": 0,
"sort": "default"
},
"languages": {
"items": [],
"limit": 0,
"sort": "default"
}
}
Field Descriptions
- items: Array of selected IDs or ISO codes
- limit: Maximum items to use (0 = no limit)
- sort: Sort order for items ("default", "asc", "desc", "random")
Selection Logic
When empty_means_all is true (default):
- Empty selection = item available for ALL values in that group
- Selection made = item restricted to ONLY selected values
When empty_means_all is false:
- Empty selection = item NOT available (nothing selected)
- Selection made = item available for selected values only
Restriction Inheritance
When using the restrictions config option, the widget will only display options that match the parent restrictions. This is useful for hierarchical configurations where child items cannot exceed parent permissions.
Example: A payment method restricted to France and Germany cannot have a sub-option available in Poland.
ScheduleConditions Trait
A time-based scheduling widget with visual timeline controls. Allows users to define when something should be active using datetime ranges, weekly schedules, and public holiday exclusions.
Features
- Datetime Range: Specific start/end period with datetime pickers
- Weekly Schedule: Per-day time ranges with visual timeline sliders
- Public Holiday Exclusions: Optional integration with holiday providers
Basic Usage
1. Include the Trait
<?php
use MyPrestaRocks\TargetConditions\ScheduleConditions;
class AdminYourModuleController extends ModuleAdminController
{
use ScheduleConditions;
// Your controller code...
}
2. Initialize Assets in setMedia()
public function setMedia($isNewTheme = false)
{
parent::setMedia($isNewTheme);
// Initialize schedule conditions (loads CSS + JS)
$this->initScheduleConditions();
}
3. Render the Widget
public function renderForm()
{
// Get saved schedule data (JSON string from database)
$scheduleJson = $this->object->schedule ?? '{}';
$scheduleData = json_decode($scheduleJson, true) ?: [];
$this->fields_form = [
'legend' => ['title' => $this->l('Configuration')],
'input' => [
[
'type' => 'free',
'label' => $this->l('Schedule'),
'name' => 'schedule_widget',
],
],
];
// Render schedule widget
$this->fields_value['schedule_widget'] = $this->renderScheduleConditionsHtml(
[
'id' => 'my-schedule',
'name_prefix' => 'schedule_',
'title' => $this->l('Availability Schedule'),
'subtitle' => $this->l('Define when this should be active'),
'show_datetime_range' => true,
'show_weekly_schedule' => true,
'show_holiday_exclusions' => true,
],
$scheduleData
);
return parent::renderForm();
}
4. Process Form Submission
public function postProcess()
{
if (Tools::isSubmit('submitYourForm')) {
// Parse schedule data from form
$scheduleData = $this->parseScheduleConditionsFromPost('schedule_');
// Serialize to JSON for storage
$this->object->schedule = $this->serializeScheduleConditions($scheduleData);
$this->object->save();
}
return parent::postProcess();
}
5. Evaluate Schedule at Runtime
// Check if current time matches the schedule
$scheduleData = $this->parseScheduleConditions($object->schedule);
if ($this->evaluateScheduleConditions($scheduleData)) {
// Schedule allows - item is active
} else {
// Schedule blocks - item is inactive
}
// Or check against a specific timestamp
$futureTimestamp = strtotime('+1 day');
$willBeActive = $this->evaluateScheduleConditions($scheduleData, $futureTimestamp);
Configuration Options
$this->renderScheduleConditionsHtml([
// Unique ID for the widget
'id' => 'schedule-conditions',
// Form field name prefix
'name_prefix' => 'schedule_',
// Header text
'title' => 'Schedule',
'subtitle' => 'Define when this should be active',
// Which sections to show
'show_datetime_range' => true, // Start/end datetime pickers
'show_weekly_schedule' => true, // Per-day time range sliders
'show_holiday_exclusions' => true, // Holiday exclusion controls
], $savedData);
Data Format
The schedule data is stored as JSON:
{
"enabled": true,
"datetime_start": "2024-12-24 08:00:00",
"datetime_end": "2024-12-24 18:00:00",
"weekly_schedule": {
"0": {"enabled": false, "start": 0, "end": 1440},
"1": {"enabled": true, "start": 540, "end": 1020},
"2": {"enabled": true, "start": 540, "end": 1020},
"3": {"enabled": true, "start": 540, "end": 1020},
"4": {"enabled": true, "start": 540, "end": 1020},
"5": {"enabled": true, "start": 540, "end": 1020},
"6": {"enabled": false, "start": 0, "end": 1440}
},
"exclude_holidays": true,
"holiday_countries": [1, 14, 17]
}
Field Descriptions
- enabled: Whether scheduling is active (false = always active)
- datetime_start/end: ISO datetime strings for absolute date range
- weekly_schedule: Per-day configuration (0=Sunday, 1=Monday, etc.)
- enabled: Whether this day is active
- start/end: Minutes from midnight (0-1440, e.g., 540 = 09:00)
- exclude_holidays: Whether to exclude public holidays
- holiday_countries: Country IDs to check for holidays
Public Holiday Integration
To enable holiday exclusions, implement the HolidayProviderInterface:
<?php
use MyPrestaRocks\TargetConditions\HolidayProviderInterface;
class MyHolidayProvider implements HolidayProviderInterface
{
/**
* Check if a date is a holiday for any of the given countries
*/
public static function isHoliday($date, array $countryIds)
{
// Your implementation
return Db::getInstance()->getValue('
SELECT 1 FROM ps_holidays
WHERE holiday_date = "' . pSQL($date) . '"
AND id_country IN (' . implode(',', array_map('intval', $countryIds)) . ')
') ? true : false;
}
/**
* Get list of country IDs that have holidays configured
*/
public static function getCountriesWithHolidays()
{
return array_column(
Db::getInstance()->executeS('SELECT DISTINCT id_country FROM ps_holidays'),
'id_country'
);
}
/**
* Get holidays within a date range
*/
public static function getHolidaysInRange(array $countryIds, $startDate, $endDate)
{
return Db::getInstance()->executeS('
SELECT holiday_date, display_name, country_iso
FROM ps_holidays h
JOIN ps_country c ON c.id_country = h.id_country
WHERE h.id_country IN (' . implode(',', array_map('intval', $countryIds)) . ')
AND holiday_date BETWEEN "' . pSQL($startDate) . '" AND "' . pSQL($endDate) . '"
');
}
}
Then override the provider method in your controller:
protected function getScheduleHolidayProvider()
{
return 'MyHolidayProvider';
}
Helper Methods
// Get human-readable description of schedule
$description = $this->getScheduleDescription($scheduleData);
// Returns: "Dec 24 08:00 to Dec 24 18:00 • Weekdays only • Excl. holidays (3 countries)"
// Format minutes to time string
$time = $this->formatMinutesToTime(540); // Returns "09:00"
// Parse time string to minutes
$minutes = $this->parseTimeToMinutes("09:00"); // Returns 540
Assets
Both traits share a common CSS file with trait-specific styling:
| File | Purpose |
|---|---|
assets/css/admin/condition-traits.css |
Shared styling for both traits |
assets/js/admin/target-conditions.js |
TargetConditions widget functionality |
assets/js/admin/schedule-conditions.js |
ScheduleConditions widget functionality |
CSS Classes Reference
TargetConditions
.condition-trait { } /* Main wrapper */
.condition-group { } /* Individual group container */
.condition-group-header { } /* Group header with label */
.condition-group-items { } /* Items container */
.condition-item { } /* Individual selectable item */
.condition-item.selected { } /* Selected state */
.group-modifiers { } /* Limit/sort controls container */
.condition-trait-toggle { } /* All/restricted toggle badge */
ScheduleConditions
.schedule-conditions { } /* Main wrapper */
.schedule-section { } /* Section container */
.schedule-datetime-range { } /* Datetime range section */
.schedule-weekly { } /* Weekly schedule section */
.schedule-holidays { } /* Holiday exclusions section */
.day-timeline { } /* Individual day row */
.timeline-track { } /* Time range slider track */
.timeline-range { } /* Active time range bar */
.timeline-handle { } /* Draggable handles */
Requirements
- PrestaShop 1.7.x or 8.x
- PHP 7.1+
License
MIT License