Forked from prestashop-target-conditions Renamed all references from target-conditions to entity-selector
575 lines
16 KiB
Markdown
Executable File
575 lines
16 KiB
Markdown
Executable File
# 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
|
|
|
|
```bash
|
|
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
|
|
<?php
|
|
|
|
use MyPrestaRocks\TargetConditions\TargetConditions;
|
|
|
|
class AdminYourModuleController extends ModuleAdminController
|
|
{
|
|
use TargetConditions;
|
|
|
|
// Your controller code...
|
|
}
|
|
```
|
|
|
|
### 2. Register Assets in setMedia()
|
|
|
|
```php
|
|
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:
|
|
|
|
```php
|
|
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
|
|
|
|
```php
|
|
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:
|
|
|
|
```php
|
|
$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:
|
|
|
|
```php
|
|
$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:
|
|
|
|
```php
|
|
$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:
|
|
|
|
```php
|
|
$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:
|
|
|
|
```json
|
|
{
|
|
"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
|
|
<?php
|
|
|
|
use MyPrestaRocks\TargetConditions\ScheduleConditions;
|
|
|
|
class AdminYourModuleController extends ModuleAdminController
|
|
{
|
|
use ScheduleConditions;
|
|
|
|
// Your controller code...
|
|
}
|
|
```
|
|
|
|
### 2. Initialize Assets in setMedia()
|
|
|
|
```php
|
|
public function setMedia($isNewTheme = false)
|
|
{
|
|
parent::setMedia($isNewTheme);
|
|
|
|
// Initialize schedule conditions (loads CSS + JS)
|
|
$this->initScheduleConditions();
|
|
}
|
|
```
|
|
|
|
### 3. Render the Widget
|
|
|
|
```php
|
|
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
|
|
|
|
```php
|
|
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
|
|
|
|
```php
|
|
// 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
|
|
|
|
```php
|
|
$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:
|
|
|
|
```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
|
|
<?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:
|
|
|
|
```php
|
|
protected function getScheduleHolidayProvider()
|
|
{
|
|
return 'MyHolidayProvider';
|
|
}
|
|
```
|
|
|
|
## Helper Methods
|
|
|
|
```php
|
|
// 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
|
|
|
|
```css
|
|
.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
|
|
|
|
```css
|
|
.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
|