magendoo / module-customer-segment
Magento 2 dynamic customer segmentation module — rule-based segments with admin UI, REST API, CLI, and Cart Price Rule integration.
Magendoo CustomerSegment for Magento 2
A comprehensive Customer Segmentation module for Magento 2 Community Edition that enables merchants to create dynamic customer segments based on various criteria including customer attributes, order history, shopping cart data, and behavior patterns.
Screenshots
Segment Grid — manage all your customer segments at a glance
[image: Customer Segments Grid]
Segment Editor — visual rule builder with conditions and customer matching
[image: Customer Segment Edit]
Documentation
| Document | Description |
|---|---|
| User Guide | End-user documentation for managing segments |
| Developer Documentation | Technical documentation for developers |
| API Documentation | REST API reference and examples |
| Testing Guide | Unit testing patterns and best practices |
| Changelog | Version history and changes |
Features
Customer Segmentation
- Dynamic Segments: Automatically assign customers based on rules
- Manual Segments: Static customer assignments
- Real-time Updates: Refresh segments on customer events
- Scheduled Updates: Cron-based segment refresh
Condition Types
Customer Attributes
- Email, First Name, Last Name
- Date of Birth, Gender
- Tax/VAT Number
- Website, Store View, Customer Group
- Account Creation Date
Order History
- Total Orders Count
- Total Revenue / Average Order Value
- First/Last Order Date
- Total Items Purchased
- Used Coupon Codes
- Payment/Shipping Methods
- Shipping Countries
- Order Status
Shopping Cart
- Cart Subtotal
- Cart Items Count
- Contains Products (by SKU)
- Has Active Cart
- Days Since Cart Activity
Product Interactions
- Purchased Products (SKU)
- Purchased from Categories
- Wishlist Items Count
Viewed-categories (product-view) segmentation is on the roadmap and not yet available.
Admin Features
- Grid view of all segments with customer counts
- Create/Edit segments with visual rule builder
- Preview matching customers before saving
- "Matched Customers" tab on edit page shows assigned customers
- Refresh button on edit page
- Mass actions (Delete, Refresh)
- Export segment customers (CSV/XML)
API & Integrations
- REST API for segment management
- CLI commands for segment operations
- Integration with Cart Price Rules
- Segment indexer with mview support
Installation
Via Composer (Recommended)
This package is not on Packagist yet, so register the repository first:
composer config repositories.magendoo-customer-segment vcs https://github.com/florinel-chis/magendoo-m2-customersegment
composer require magendoo/module-customer-segment
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento setup:static-content:deploy -f
bin/magento cache:flush
Manual Installation
- Extract files to
app/code/Magendoo/CustomerSegment/ - Run the following commands:
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento setup:static-content:deploy -f
bin/magento cache:flush
Configuration
- Segment grid (create/edit/refresh segments): Customers → Customer Segments
- Module settings (enable/disable, default refresh mode, cron schedule):
Stores → Configuration → Customers → Customer Segments
Usage
Creating a Segment
- Navigate to Customers → Customer Segments
- Click "Add New Segment"
- Fill in the general information:
- Name (required)
- Description (optional)
- Status (Active/Inactive)
- Refresh Mode (Manual/Cron/Real-time)
- Configure conditions in the Conditions tab
- Save the segment
- Click "Refresh Segment Data" to populate customers
Segment Refresh Modes
| Mode | Description |
|---|---|
| Manual | Admin must click refresh button to update |
| Cron | Updated automatically on cron schedule (default: */5 * * * *, every 5 minutes) |
| Real-time | Updated on customer events (register, save, login, order, quote merge) |
CLI Commands
# Refresh specific segment(s)
bin/magento magendoo:customer-segment:refresh 1
bin/magento magendoo:customer-segment:refresh 1 2 3
# Refresh all active segments
bin/magento magendoo:customer-segment:refresh --all
# Export segment customers
bin/magento magendoo:customer-segment:refresh 1 --export --format=csv
Using Segments in Cart Price Rules
- Go to Marketing → Cart Price Rules
- Create or edit a rule
- In the Conditions section, add condition:
- Customer Segment → is → [Select your segment]
- Save the rule
API Reference
REST API Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /V1/customer-segments |
List all segments |
| GET | /V1/customer-segments/:segmentId |
Get segment by ID |
| POST | /V1/customer-segments |
Create new segment |
| PUT | /V1/customer-segments/:segmentId |
Update segment (partial updates merge onto the stored record) |
| DELETE | /V1/customer-segments/:segmentId |
Delete segment |
| POST | /V1/customer-segments/:segmentId/refresh |
Refresh a single segment; returns the assigned count |
| POST | /V1/customer-segments/refresh-all |
Refresh all active segments |
| GET | /V1/customers/:customerId/segments |
Get the customer's segments |
| GET | /V1/customers/:customerId/segment-ids |
Get the customer's segment IDs |
| GET | /V1/customers/:customerId/segments/:segmentId/check |
Check if a customer is in a segment |
| GET | /V1/customer-segments/:segmentId/customers?format=csv|xml |
Export a segment's customers; format is required and returns a CSV or XML string |
Example: Create Segment via API
curl -X POST "https://your-store.com/rest/V1/customer-segments" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"segment": {
"name": "VIP Customers",
"description": "Customers with 10+ orders",
"is_active": true,
"refresh_mode": "cron",
"conditions_serialized": "{...}"
}
}'
Database Structure
Tables
| Table | Description |
|---|---|
magendoo_customer_segment |
Stores segment definitions |
magendoo_customer_segment_customer |
Customer-segment relationships |
magendoo_customer_segment_log |
Segment activity log (save/refresh actions) |
Events
The module dispatches the following events:
| Event | Payload keys | Description |
|---|---|---|
magendoo_customersegment_segment_save_before |
segment |
Before a segment model is saved |
magendoo_customersegment_segment_save_after |
segment |
After a segment model is saved |
magendoo_customersegment_refresh_before |
segment, segment_id |
Before a segment refresh |
magendoo_customersegment_refresh_after |
segment, segment_id, assigned_customers, assigned_count |
After a segment refresh |
magendoo_customersegment_customer_assigned |
customer_id, segment_id |
Customer assigned to a segment |
magendoo_customersegment_customer_removed |
customer_id, segment_id |
Customer removed from a segment |
magendoo_customersegment_conditions |
additional |
Register custom condition types |
Extension Points
Adding Custom Conditions
Create a plugin to add custom conditions:
class AddCustomConditionPlugin
{
public function afterGetNewChildSelectOptions($subject, $result)
{
$result[] = [
'label' => __('My Custom Condition'),
'value' => 'Vendor\Module\Model\Condition\MyCondition'
];
return $result;
}
}
Troubleshooting
Segments not refreshing
- Check if the segment is Active
- Verify cron is running:
bin/magento cron:run - Check logs at
var/log/system.log
Customers not matching
- Verify condition logic
- Check that customer data exists
- Test with CLI:
bin/magento magendoo:customer-segment:refresh --all
Performance issues
- Prefer conditions that resolve set-based (a single query) over ones that fall back to per-customer validation
- Use Manual or Cron refresh mode for large segments rather than Real-time
- Schedule cron refresh during low-traffic hours
Testing
Running Tests
# Run all module tests
vendor/bin/phpunit --filter Magendoo app/code/Magendoo/CustomerSegment/Test/Unit
# Run specific test class
vendor/bin/phpunit --filter SegmentManagementTest app/code/Magendoo/CustomerSegment/Test/Unit/Model/SegmentManagementTest.php
# Run with coverage
vendor/bin/phpunit --filter Magendoo --coverage-html coverage app/code/Magendoo/CustomerSegment/Test/Unit
Test Coverage
The unit suite covers SegmentManagement and every condition type (Combine, Customer, Order,
Cart, Product) plus set-based matching. Tests target PHPUnit 12.
Security Tested
- ✅ CSV export neutralizes leading formula characters (
=,+,-,@, tab, CR) to prevent spreadsheet formula injection - ✅ Condition type allowlist prevents arbitrary class instantiation
See TESTING.md for detailed testing documentation.
Support
For support and questions:
- Email: [email protected]
License
This module is licensed under the MIT License. See LICENSE for details.
Credits
Developed by Magendoo (https://magendoo.com)
Version: 2.0.0
Compatibility: Magento 2.4.x
PHP Version: 8.1+
Changelog
All notable changes to the Magendoo CustomerSegment module will be documented in this file.
The format is based on Keep a Changelog.
[2.0.0] - 2026-07-20
A correctness-focused release that fixes the segmentation engine along its main axis. Several fixes
change stored data and behaviour, so this is a breaking release.
Upgrade step (required): run
bin/magento setup:upgrade. It applies two patches:
a schema patch that removes stale duplicate indexes/foreign keys left by older installs, and a
data patch that rewrites any segment whose conditions were stored in the legacy numeric-key shape
into the canonicalconditionstree. Re-run refresh on affected segments afterwards.
Fixed
- CRITICAL: Admin-created segments ignored their conditions and matched every customer. Condition
children were serialized under numeric position keys with noconditionskey, so the rule tree
loaded as an empty combine. Conditions now serialize under an orderedconditionslist that
round-trips through Magento's rule engine. Existing broken rows are migrated bysetup:upgrade. - CRITICAL: Whole condition families matched nobody or inverted their meaning — order
payment/status/coupon/country, product purchased-negation and purchased-categories, cart subtotal
>/<, and cart/negation operators. These now evaluate correctly, with negation applied at the
customer level and explicit zero-row (no orders / empty cart) handling. - CRITICAL: Partial REST updates (
PUT) overwrote stored scalars with defaulted DTO values,
resettingcustomer_count,is_active,refresh_mode, and wipingconditions_serialized.
Updates now merge onto the loaded model. - MAJOR: Membership matching was O(customers x conditions), issuing one query per customer per
leaf. Conditions that can express themselves as a single query now resolve set-based. - MAJOR:
refresh <id> --exportalways crashed understrict_types; the CLI now casts the id
and handles export correctly. - MAJOR: CSV export crashed on PHP 8.4 (
fputcsvdeprecation). Fixed, and export now neutralizes
leading formula characters to prevent spreadsheet formula injection. - MAJOR: MView customer subscription refreshed the wrong segment (customer ids were treated as
segment ids). The changelog/indexer wiring is now segment-scoped and no longer self-perpetuates. - MEDIUM: Staleness checks compared stored UTC timestamps against session-timezone
NOW();
comparisons are now UTC-consistent (UTC_TIMESTAMP()/ UTC-aware parsing). - MEDIUM: Segment refresh is now atomic (remove-all + mass-assign wrapped in one transaction).
Added
Helper\Data::isEnabled()now actually gates the module: realtime observers, the cron dispatcher,
and the CLI no-op whencustomersegment/general/enabledis off.SegmentManagementInterface::updateCustomerMembership(int $customerId)— re-evaluates a single
customer against active realtime segments (no full rescan). Realtime observers call only this.- Activity log is now live:
magendoo_customer_segment_logreceives real rows on save and refresh
(Model\ResourceModel\Log::log()). Setup\Patch\Schema\DropLegacyDuplicateIndexesandSetup\Patch\Data\MigrateConditionsSerialized.
Changed
- Behavior: a segment with no conditions now matches no customers. Previously an empty (or
admin-saved-but-broken) condition tree silently matched every customer. Define at least one
condition to select customers. - License is MIT across the module (
composer.json, headers,LICENSE). db_schema_whitelist.jsonregenerated to match the explicit referenceIds indb_schema.xml
(fixes duplicate indexes and foreign keys on clean installs).- System config
enabledis now a global (default-scope-only) operational toggle. - Test suite migrated to PHPUnit 12 attributes.
Removed
- Dead
Model\SqlBuilderscaffold (replaced by real set-based matching). - Non-functional customer-grid segment column stub (see Roadmap).
viewed_categoriesproduct condition — it was a placeholder that always matched nobody (see Roadmap).
[1.1.0] - 2026-04-03
Added
- Product Interactions condition type (viewed categories, purchased products, purchased categories, wishlist items count) with
betweenoperator - SqlBuilder for batch customer validation (N+1 performance fix)
- Segment indexer with mview support (
etc/indexer.xml,etc/mview.xml) - System configuration admin panel (
etc/adminhtml/system.xml,etc/config.xml) for enable/disable, default refresh mode, cron schedule - RefreshButton on segment edit page
- Matched Customers tab on segment edit page with pagination
- MatchedCustomersDataProvider for matched customers grid
- CustomerSegmentRelation model and ResourceModel for segment-customer relationships
- Product::class added to condition type allowlists (SegmentManagement + NewConditionHtml)
- Cart Price Rule integration now loads segment options from repository (was stub)
- Functional test suite (Playwright) — API, CLI, Admin UI, Integration tests
- Unit tests for Product condition and SqlBuilder
Fixed
- CRITICAL: Segment model extended AbstractModel instead of AbstractExtensibleModel — getExtensionAttributes() crashed all REST API serialization
- CRITICAL: SegmentSearchResultsInterface used short class name in @return docblock — Magento Web API reflection failed with "Class SegmentInterface does not exist" on getList endpoint
- CRITICAL: Edit controller stored full Segment model object in DataPersistor (session) — non-serializable dependencies (FormFactory/ObjectManager/Closures) caused "Serialization of 'Closure' is not allowed" fatal error on every admin page load
- Conditions blocks now load segment from DB via request param instead of DataPersistor
- Removed DataPersistorInterface dependency from Conditions blocks
Changed
- Segment model constructor now accepts ExtensionAttributesFactory and AttributeValueFactory (required by AbstractExtensibleModel)
- Version bumped to 1.1.0
[1.0.1] - 2026-04-01
Added
- Comprehensive Unit Test Suite - 106 tests with 198 assertions
- SegmentManagement tests (31 tests) - CRUD, refresh, export, validation
- Condition tests (75 tests) - Customer, Order, Cart, Combine conditions
- Security-critical tests for CSV injection prevention
- Condition type allowlist verification
- Error handling and edge case coverage
- Testing documentation:
- TESTING.md - Testing patterns and best practices
Fixed
- Security: CSV export now uses fputcsv() to prevent formula injection
- Security: Condition instantiation uses allowlist to prevent arbitrary class loading
- Table prefix support in database queries
- Type mismatches in API methods
- Deprecated class replacements (Zend_Db_Expr, Registry)
Technical
- Reduced test code duplication by 30% through refactoring
- Established testing patterns for future development
- All production code issues resolved
[1.0.0] - 2026-04-01
Added
- Initial release of Magendoo CustomerSegment module
- Customer segmentation with dynamic rules engine
- Three condition types: Customer Attributes, Order History, Shopping Cart
- Admin grid for segment management with filtering and mass actions
- Create, edit, delete, and refresh segments
- Three refresh modes: Manual, Cron, Real-time
- REST API for all segment operations
- CLI command for segment refresh
- Integration with Cart Price Rules
- Event system for extensibility
- Database schema with foreign key constraints
- Multi-condition support with AND/OR logic
- Customer count caching
- Export segment customers (CSV/XML)
- Full ACL support for permissions
- Observer-based real-time updates
- Comprehensive documentation
Features
- Dynamic Segments: Automatically assign customers based on rules
- Visual Rule Builder: Admin UI for building complex conditions
- Batch Processing: Efficient customer matching in batches of 1000
- Scheduled Updates: Cron-based segment refresh (default: daily at 2 AM)
- Event-Driven: Real-time updates on customer events
- API Access: Full REST API coverage
- Extensible: Plugin and event support for custom conditions
Technical
- Magento 2.4.x compatibility
- PHP 8.1+ support
- Service Contracts pattern
- Dependency Injection throughout
- Unit and integration test support
- Playwright functional tests
Roadmap (not yet implemented)
- Customer grid segment column / filtering
- Viewed-categories (product-view) condition
- GraphQL API support
- Segment-based email templates and CMS content
- Import/Export of segment definitions
Requires 9
| Package | Constraint |
|---|---|
| magento/framework | >=103.0.0 |
| magento/module-backend | >=102.0.0 |
| magento/module-catalog | >=104.0.0 |
| magento/module-customer | >=103.0.0 |
| magento/module-quote | >=101.2.0 |
| magento/module-rule | >=100.4.0 |
| magento/module-sales | >=103.0.0 |
| magento/module-ui | >=101.2.0 |
| php | >=8.1 |
Requires-dev 2
| Package | Constraint |
|---|---|
| phpstan/phpstan | ^1.10 || ^2.0 |
| phpunit/phpunit | ^10.5 |
Compatibility
Each Magento release line is installed on its supported PHP versions, then the module is built (DI compilation + static-content deploy) and its unit and integration suites are run. The matrix shows the lines and PHP versions the module is confirmed to install and run on. Code-quality results further down (phpstan, phpcs, …) are reported separately and never affect compatibility.
Code Quality
Advisory checks against the module's source. Static analysis runs once across the whole module; PHPStan re-runs per Magento + PHP version because resolvable symbols differ between releases. These NEVER affect the Compatibility badge. A phpcs finding can't make a module incompatible.
Static analysis
Coding standards (phpcs), mess detection (phpmd), copy-pasted code (cpd), PHP cross-version compatibility, composer.json validity. Each runs once for the whole module.
| Tool | Status | Findings | Summary |
|---|---|---|---|
| PHPCS | Warning | 55 | 55 warnings (ruleset: Magento2), 20 auto-fixable with phpcbf |
| PHPMD | Warning | 129 | 129 rule violations (UnusedPrivateField:129) |
| Cpd | Warning | 1 | 1 duplicated chunk spanning 54 total lines (min-lines=5, min-tokens=70) |
| Composer validate | Info | 9 | valid; 9 advisory notes (composer validate --strict) |
PHPStan
Type-checks the module's PHP against a real Magento install at the configured gate level. Re-runs per Magento and PHP version because resolvable symbols differ between releases.
Tests
Unit and integration suites, run for each applicable Magento and PHP version. A test failure speaks to the module's behaviour, not its compatibility with a Magento line, so it is reported here separately and never reddens the compatibility matrix.
Unit tests
Integration tests
| Magento | PHP 8.2 | PHP 8.3 | PHP 8.4 | PHP 8.5 |
|---|---|---|---|---|
| 2.4.7 | N/A | N/A | ||
| 2.4.8 | N/A | N/A | ||
| 2.4.9 | N/A | N/A |
Security
Security checks run directly against the module: an audit of its declared dependencies for known vulnerabilities (composer audit) and a scan of its source for malware and web-shell signatures. Each runs once. A malware detection fails the version outright.
More from magendoo
View vendorEU Base Price (Grundpreis) display for Magento 2. Shows price per reference unit (kg, litre, m) on product pages, category listings, search results and cart. Compliant with EU Price Indication Directive 98/6/EC and German PAngV.
Shipping Restrictions module
Product labels for Magento 2 - first-class label entities with stable codes, manual assignment via product attribute, rule-computed assignments materialized by an indexer, PLP/PDP badges and GraphQL exposure
EU Omnibus Directive (2019/2161) price tracker for Magento 2. Records price changes with timestamps, computes the lowest prior price in the disclosure window, and displays it on PDP and PLP, with async queue processing for bulk operations.
Turn an existing module into recurring revenue.
If you already maintain a Magento 2 module on GitHub or GitLab, listing it on Packagento takes about five minutes. We mirror your tags, handle distribution signing, and route paid licenses through Stripe Connect, so you can keep shipping the way you already do.