How to Use Magento 2's Customer Attributes for Personalized Marketing
Welcome — let’s walk through a practical, down-to-earth guide on using Magento 2 customer attributes to make your marketing actually feel personal. I’ll keep things relaxed, like I’m standing beside you at a desk, and I’ll show clear, copy-paste-ready code examples where you need them. We’ll also connect the dots between customer attributes and product attributes (including using Magefine’s Force Product Stock Status) so you can avoid promoting out-of-stock items and trigger targeted campaigns without heavy custom development.
Why customer attributes matter for personalized marketing
Customer attributes are the small pieces of customer data you can attach to each account in Magento — things like loyalty tier, preferred language, local store, or a custom flag like vip_status. When used correctly, these attributes let you:
- Segment audiences precisely (VIPs, high-spenders, region-based campaigns).
- Personalize messaging in transactional and marketing emails using variables.
- Combine with product attributes (stock status, category, special flags) to show only relevant promotions.
- Trigger catalog rules, group pricing or CMS content variations with a low-code approach.
Think of customer attributes as the keys that unlock hyper-relevant offers — but only if they’re created, stored and queried correctly.
Customer attributes vs product attributes — what’s the difference and how they work together
Short version:
- Customer attributes: data tied to the customer entity (EAV model). Examples: customer group, loyalty level, preferred city, newsletter preference.
- Product attributes: data tied to each product (EAV model). Examples: color, size, price, stock status, or custom flags like
force_stock_statusprovided by modules such as Magefine’s Force Product Stock Status.
They’re different EAV entities but they collaborate well. Example use case: show a limited-availability, in-stock-only product feed to VIP customers. That requires combining a customer-level flag (e.g., vip_status) with a product-level flag (e.g., is_in_stock or a module-managed status like force_stock_status).
Plan before you build: data model and naming conventions
Before adding attributes, decide:
- Attribute codes: keep them short, lowercase, underscore_separated (e.g.,
vip_status,preferred_store). - Backend type:
varchar,int,text, etc. Choose appropriately to avoid conversions later. - Used in forms and APIs: Do you need the attribute in adminhtml forms, customer account edit, or only in admin grid & API?
- Privacy and GDPR: keep attributes minimal and documented — if it’s PII, make sure you have consent and deletion procedures.
Step-by-step: Add a customer attribute (Magento 2, modern approach)
Below is a typical Patch Data example to add a simple attribute called vip_status to customers. It’s compatible with Magento 2.3+ where Patch classes are used instead of InstallData.
// app/code/YourCompany/Marketing/Setup/Patch/Data/AddCustomerVipStatus.php
namespace YourCompany\Marketing\Setup\Patch\Data;
use Magento\Customer\Setup\CustomerSetupFactory;
use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
use Magento\Framework\Setup\Patch\PatchInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
class AddCustomerVipStatus implements PatchInterface
{
private $moduleDataSetup;
private $customerSetupFactory;
public function __construct(
ModuleDataSetupInterface $moduleDataSetup,
CustomerSetupFactory $customerSetupFactory
) {
$this->moduleDataSetup = $moduleDataSetup;
$this->customerSetupFactory = $customerSetupFactory;
}
public function apply()
{
$this->moduleDataSetup->getConnection()->startSetup();
/** @var \Magento\Customer\Setup\CustomerSetup $customerSetup */
$customerSetup = $this->customerSetupFactory->create(['setup' => $this->moduleDataSetup]);
$customerEntity = $customerSetup->getEavConfig()->getEntityType('customer');
$attributeSetId = $customerEntity->getDefaultAttributeSetId();
$customerSetup->addAttribute(
\Magento\Customer\Model\Customer::ENTITY,
'vip_status',
[
'type' => 'int',
'label' => 'VIP Status',
'input' => 'select',
'source' => \Magento\Eav\Model\Entity\Attribute\Source\Boolean::class,
'required' => false,
'visible' => true,
'user_defined' => true,
'position' => 100,
'system' => 0,
]
);
$attribute = $customerSetup->getEavConfig()->getAttribute('customer', 'vip_status');
$attribute->setData('attribute_set_id', $attributeSetId);
$attribute->setData('attribute_group_id', $customerSetup->getDefaultAttributeGroupId($customerEntity->getEntityTypeId(), $attributeSetId));
$attribute->setData('used_in_forms', ['adminhtml_customer']);
$attribute->save();
$this->moduleDataSetup->getConnection()->endSetup();
}
public static function getDependencies() { return []; }
public function getAliases() { return []; }
}
Notes:
- We used a boolean source for simplicity (0/1). For multi-level loyalty you’d use a custom source model or a static option array.
used_in_formscontrols where it’s visible — you can addcustomer_account_createandcustomer_account_editif you want customers to edit it.- Run bin/magento setup:upgrade && bin/magento cache:flush and reindex as needed.
Expose attributes to APIs and email templates
Once the attribute exists, you can reference it in transactional email templates and in custom code. For an email template variable, use Magento’s variable extraction like this:
{{var customer.getCustomAttribute('vip_status') ? customer.getCustomAttribute('vip_status').getValue() : '0'}}
That template snippet pulls the raw value. If you’re using a select source with labels, consider mapping the integer value to a label inside your template or prepare the label server-side and pass it as a template variable.
Segmenting customers by attribute — admin & low-code options
If you’re on Adobe Commerce (paid edition), Customer Segments is a built-in admin feature that’s perfect. For Magento Open Source you can:
- Use customer groups. Assign customers to groups based on attributes via an import or a small patch script.
- Use a lightweight segmentation extension that supports customer attributes (choose a trusted vendor).
- Run ad-hoc exports / queries from the database when you need a list for an email blast.
Low-code approach idea (no heavy dev): create a tiny admin-only script or use a saved report to tag customers into groups based on vip_status or purchase history. Once in groups, you can apply standard cart rules, catalog rules and email templates to those groups.
Case study: Email campaigns personalized by group, purchase history and location — and synced with product stock
Objective: Email VIP customers in a region about a set of recommended products — but exclude items that are out of stock. Steps:
- Ensure a customer attribute exists (e.g.,
vip_status), and customers are tagged. - Ensure product stock availability is available and normalized. If you use Magefine’s Force Product Stock Status, that module exposes/configures a reliable attribute (we’ll call it
force_stock_statusin examples) to control stock display independently from catalog inventory, which is useful for large catalogs or multi-source setups. - Build a cron job that pulls VIP customers in the target region, then builds a product list filtered by
force_stock_status= in_stock (or byis_in_stock/ MSI sources depending on your setup). - Compose the email template using customer attributes (first name, city, VIP level) and the filtered product list, then send via TransportBuilder.
Example: simple cron that prepares a recipient list and product items (skeleton)
// app/code/YourCompany/Marketing/Cron/SendVipRecommendations.php
namespace YourCompany\Marketing\Cron;
use Magento\Customer\Model\ResourceModel\CustomerCollectionFactory;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory;
use Magento\Framework\Mail\Template\TransportBuilder;
use Magento\Store\Model\StoreManagerInterface;
class SendVipRecommendations
{
private $customerCollectionFactory;
private $productCollectionFactory;
private $transportBuilder;
private $storeManager;
public function __construct(
CustomerCollectionFactory $customerCollectionFactory,
ProductCollectionFactory $productCollectionFactory,
TransportBuilder $transportBuilder,
StoreManagerInterface $storeManager
) {
$this->customerCollectionFactory = $customerCollectionFactory;
$this->productCollectionFactory = $productCollectionFactory;
$this->transportBuilder = $transportBuilder;
$this->storeManager = $storeManager;
}
public function execute()
{
$storeId = $this->storeManager->getStore()->getId();
// 1) Get VIP customers in specific region
$customers = $this->customerCollectionFactory->create()
->addAttributeToSelect(['email', 'firstname', 'lastname', 'city', 'vip_status'])
->addAttributeToFilter('vip_status', 1)
->addAttributeToFilter('city', ['eq' => 'Paris']);
if (!count($customers)) { return; }
// 2) Get products that are in stock using Magefine's force stock attribute
$products = $this->productCollectionFactory->create()
->addAttributeToSelect(['name', 'price', 'sku', 'url_key'])
// adjust attribute code to the actual one your Magefine module uses
->addAttributeToFilter('force_stock_status', ['eq' => 1])
->setPageSize(20);
$productData = [];
foreach ($products as $p) {
$productData[] = [
'name' => $p->getName(),
'sku' => $p->getSku(),
'url' => $p->getProductUrl(),
];
}
// 3) Build and send one email per customer (or batch as you prefer)
foreach ($customers as $customer) {
$vars = [
'customer' => $customer,
'recommended_products' => $productData,
];
$transport = $this->transportBuilder
->setTemplateIdentifier('vip_recommendations_template')
->setTemplateOptions([
'area' => \Magento\Framework\App\Area::AREA_FRONTEND,
'store' => $storeId
])
->setTemplateVars($vars)
->setFrom('general')
->addTo($customer->getEmail(), $customer->getName())
->getTransport();
$transport->sendMessage();
}
}
}
Important notes:
- Replace
force_stock_statuswith the exact product attribute code created or managed by the Magefine module you installed. Check your product attribute list in admin to confirm. - For big lists, send emails in batches, and use asynchronous sending or an external ESP to avoid timeouts.
Using product attributes (like stock status) to tailor shopping experiences
Product attributes are how you filter and display products. When a module like Magefine’s Force Product Stock Status is used, you get a controlled attribute to override default stock display. That helps when you want to:
- Show 'Available for VIPs' badges only on items that are in-stock
- Recommend items conditionally based on stock or other product flags
- Switch price or availability messaging for different customer groups
Example: display a badge based on product attribute
Suppose the product attribute code is vip_only (boolean) and force_stock_status indicates whether the product should be treated as in-stock for display. You can add a small template override or a widget template that checks these attributes and outputs a badge.
<?php
/** in a product list template (simplified) */
$vipOnly = $product->getData('vip_only');
$forceStock = $product->getData('force_stock_status');
if ($vipOnly && $forceStock) : ?>
<span class="badge badge--vip">VIP In-Stock</span>
<?php endif; ?>
This logic can be applied in product listing templates, product detail templates or widgets — and layered into conditions so only certain customer groups see the badge.
Low-code automation patterns using attributes
You don’t always need full custom development. Here are productive low-code patterns:
- Customer groups + catalog rules: Move customers into groups based on attributes using a scheduled script or manual tagging. Use catalog pricing rules to offer group-specific discounts.
- CMS blocks and widgets with layout updates: Use layout XML conditions (customer group handles) to show different CMS blocks for VIPs.
- Admin-promotions: Use cart price rules with conditions that inspect product attributes (like a custom flag) so promo applies only to products with
force_stock_status= in stock. - ESPs and integrations: Export attribute-backed segments to your ESP and use the ESP to send smart campaigns (recommended for scale).
Example: Show a CMS block only to VIP customers (layout update snippet)
Create a widget or block and add this layout update to show it only for logged-in VIP users (example only — ensure it fits your theme and cache policies):
<referenceContainer name="content">
<block class="Magento\Cms\Block\Block" name="vip_promo_block" before="-" template="cms/block.phtml">
<arguments>
<argument name="block_id" xsi:type="string">vip_only_block</argument>
</arguments>
<action method="setData" ifconfig="">
<argument name="customer_group_condition" xsi:type="string">vip_status == 1</argument>
</action>
</block>
</referenceContainer>
Note: Magento’s layout system doesn’t evaluate arbitrary PHP expressions out-of-the-box. Use a small block plugin or widget that checks the current customer’s attribute and returns the block content conditionally. That keeps the approach low-code: you only add a tiny block class rather than a whole feature.
Advanced automation: trigger catalog rules, group prices and dynamic content without heavy dev
Here’s how you can orchestrate a powerful no-heavy-dev setup:
- Create and maintain a clear set of customer attributes (e.g.,
vip_status,preferred_region,price_tier). - Use a scheduled script (small patch) to evaluate purchase history and set those attributes automatically — for instance, move a customer to
vip_status= 1 after 3 purchases over $300 in 6 months. - Use customer groups to apply pricing tiers (customer group pricing) or catalog rules for those groups. Catalog rules can use product attributes (e.g., a
vip_discountableflag orforce_stock_status). That avoids bespoke checkout logic. - Use CMS + widget blocks with minimal block logic to swap banners or recommended blocks for VIPs. You can cache these blocks but ensure hole-punching (ESI) if they’re fully personalized.
Example: Catalog price rule that applies only to in-stock products for VIP group
In Admin > Marketing > Catalog Price Rules set conditions like:
- Customer Group is VIP
- Product attribute force_stock_status is In Stock
Then set the action to apply X% discount. This is a zero-code approach once customer groups are correctly populated.
Pitfalls to avoid and best practices
- Data hygiene: Clean attribute values regularly. If you use serialized or JSON values, consider normalizing into separate boolean/integer fields for fast filtering.
- Indexation and performance: EAV attribute filtering can be slow if used naively on large collections. Add necessary indexes, and cache results. Use flat catalog/product if it fits your setup and profiling.
- Reindex smartly: Attribute changes may require reindexing. Schedule off-peak reindexing where possible.
- Limit customers loaded at once: Use pagination/batches and avoid loading full collections into memory.
- Privacy: Only collect attributes you need. Implement deletion and export flows for GDPR.
How Magefine’s Force Product Stock Status helps at scale
Real-world stores with large catalogs and multi-source inventory can find stock status tricky: real inventory has delays, partial allocations, and business rules. Magefine’s Force Product Stock Status module is useful because it:
- Provides a manageable attribute to override or force the stock display of a product without touching core inventory tables.
- Makes it easy to batch-update stock display across categories or via CSV, which is great for promotions and VIP-only availability.
- Reduces the need to hack inventory tables for marketing-driven presentations.
Because it’s central to the approach described here, rely on that attribute for filtering promotional product lists and for catalog rule conditions where you want marketing control over what’s shown as available.
Performance checklist when using attributes heavily
- Choose proper attribute backend types (ints for flags, varchar for short codes).
- Add attributes to product flat tables or use indexers for faster filtering (if your setup uses flat tables).
- Avoid N+1 queries when building product lists for emails—use collection joins and select minimal fields.
- Use cron jobs or queue systems for long-running tasks (sending emails, batch updates).
- Monitor slow queries and use EXPLAIN to optimize joins involving EAV tables.
Testing and rollout strategy
Roll this out gradually:
- Staging: add attributes and test small campaigns. Verify email variables, product filtering, and CMS blocks.
- QA: check indexing, reindex operations, cache invalidation and performance impact of attribute filters at scale.
- Pilot: run a small VIP campaign (1–5% of users) to validate deliverability and product selection logic — ensure no OOS products slip through.
- Scale: move to full campaigns and publish admin-run processes for non-dev users (e.g., instructions for marketing ops to toggle attributes or trigger exports).
Checklist: What to have in place
- Well-named, typed customer attributes and a small policy document describing them.
- Mapping between customer attributes and groups for admin use.
- A reliable product attribute for stock display (e.g., Force Product Stock Status from Magefine) and documentation on its codes.
- Cron jobs or exported segments for safe, batched email sends.
- Monitoring & logs for any automated attribute updates and for email sending.
Concrete example: End-to-end flow
- Marketing decides VIPs get early access to sales when items are in stock.
- Dev adds
vip_statuscustomer attribute and a scheduled script that flags customers based on purchase history. - Admin ensures
force_stock_statusis set for applicable products via Magefine UI or a CSV import process. - Marketing creates a catalog price rule for VIP customer group, with condition
force_stock_status = in_stock. - Marketing triggers a scheduled email campaign that queries VIPs (by attribute or group) and lists only products where
force_stock_status = 1. - Campaign runs, customers see accurate product availability, and conversions/returns are reduced because OOS products were excluded.
Small reusable utilities you'll want
- A small admin controller to bulk assign or remove a customer attribute when needed.
- A CLI command to re-evaluate attributes across customers (e.g., recalc VIP status daily).
- A lightweight service class that returns an in-stock product collection using the Magefine attribute to centralize logic.
Wrapping up — practical tips
Use attributes strategically, not excessively. Attributes are powerful for segmentation and personalization, but every new attribute increases maintenance and potential performance cost. Follow a pragmatic approach:
- Start small with a handful of reliable attributes (VIP flag, preferred region, price tier).
- Use Magefine’s Force Product Stock Status to avoid marketing mistakes around stock display — it centralizes stock display decisions for your promotions.
- Prefer catalog rules + customer groups for low-code promotions and reduce the need for bespoke logic in checkout or product lists.
- Batch operations, reindex smartly, and use caching to scale.
Resources and next steps
If you want to implement this now, here’s a practical roadmap:
- Create and test customer attributes on staging using the patch example above.
- Confirm the product attribute code that Magefine's module creates for stock control and test filtering products by that attribute in a staging environment.
- Build a simple cron job to export a VIP product list and send test emails to a QA box.
- Train marketing to use customer groups, catalog rules, and saved exports for low-code campaigns.
If you want, I can also:
- Provide a ready-to-install sample module that adds
vip_statusand a CLI sync script to populate it from order history. - Draft the exact email template that merges customer attributes and a product loop for your ESP or Magento transactional email.
- Help verify the exact attribute code used by the Magefine Force Product Stock Status extension installed on your store.
Want me to prepare any of those? Tell me which one and include the Magento version you run, and I’ll craft the files or templates ready to drop into your repo.
That’s it — using customer attributes with carefully managed product attributes like those from Magefine’s Force Product Stock Status gives you a practical path to personalized, stock-aware campaigns without turning your store into endless custom code. Keep things small, test often, and automate where it counts.



