coresh/module-customer-attribute 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

coresh/module-customer-attribute

最新稳定版本:1.1.2

Composer 安装命令:

composer require coresh/module-customer-attribute

包简介

Magento 2 module that adds immutable UUID support for customers and exposes it through GraphQL.

README 文档

README

Overview

The Coresh_CustomerAttribute module adds a stable customer uuid attribute to Magento 2 / Adobe Commerce.

The module automatically assigns UUIDs to existing and new customers, enforces UUID uniqueness, displays UUIDs in the Admin customer grid, prevents manual UUID changes, and exposes UUID through GraphQL only for authenticated customers.

Compatibility

  • Magento Open Source 2.4.7+
  • Adobe Commerce 2.4.7+
  • Magento 2.4.8 / 2.4.9 compatible
  • PHP 8.2+
  • Composer-installable Magento 2 module

Installation

1. Install the module

Using Composer:

composer require coresh/module-customer-attribute

If installing manually, place the module in:

app/code/Coresh/CustomerAttribute

2. Enable the module

bin/magento module:enable Coresh_CustomerAttribute

3. Run Magento setup

bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:flush
bin/magento indexer:reset customer_grid
bin/magento indexer:reindex customer_grid

4. Verify module status

bin/magento module:status Coresh_CustomerAttribute

Expected result:

Coresh_CustomerAttribute

Database Verification

Verify the UUID column on customer_entity

SHOW COLUMNS FROM customer_entity LIKE 'uuid';

Expected result:

uuid | varchar(36)

Verify UUID values for customers

SELECT entity_id, email, uuid
FROM customer_entity
ORDER BY entity_id DESC
LIMIT 10;

Expected result: each customer should have a UUID value.

Verify UUID uniqueness

SELECT uuid, COUNT(*) AS total
FROM customer_entity
WHERE uuid IS NOT NULL AND uuid <> ''
GROUP BY uuid
HAVING total > 1;

Expected result:

Empty set

Verify UUID in the customer grid index

SHOW COLUMNS FROM customer_grid_flat LIKE 'uuid';

Expected result:

uuid | varchar(255)

Then verify values:

SELECT entity_id, email, uuid
FROM customer_grid_flat
ORDER BY entity_id DESC
LIMIT 10;

Admin Verification

Open Magento Admin:

Customers -> All Customers

Verify:

- The UUID column is visible in the customer grid.
- UUID values are displayed.
- UUID is not editable in the customer edit form.
- Customer grid reindex completes successfully.

If the UUID column is not visible immediately, run:

bin/magento cache:flush
bin/magento indexer:reset customer_grid
bin/magento indexer:reindex customer_grid

GraphQL API Access

The module extends the existing GraphQL Customer object with the uuid field.

UUID is available only through authenticated customer GraphQL requests.

GraphQL query

query {
  customer {
    email
    uuid
  }
}

GraphQL Testing with curl

Set test variables:

BASE_URL="https://example.com"
EMAIL="customer@example.com"
PASSWORD="Pa5Sw0rd123!"

1. Verify guest access is rejected

curl -s -X POST "$BASE_URL/graphql" \
  -H "Content-Type: application/json" \
  --data-binary '{
    "query": "query { customer { email uuid } }"
  }' | jq

Expected result: the request should be rejected with an authorization error.

The response must not expose uuid.

2. Generate customer token

TOKEN=$(curl -s -X POST "$BASE_URL/graphql" \
  -H "Content-Type: application/json" \
  --data-binary "{
    \"query\": \"mutation { generateCustomerToken(email: \\\"$EMAIL\\\", password: \\\"$PASSWORD\\\") { token } }\"
  }" | jq -r '.data.generateCustomerToken.token')

echo "$TOKEN"

Expected result: a customer bearer token is returned.

3. Verify authenticated UUID access

curl -s -X POST "$BASE_URL/graphql" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  --data-binary '{
    "query": "query { customer { email uuid } }"
  }' | jq

Expected result:

{
  "data": {
    "customer": {
      "email": "customer@example.com",
      "uuid": "550e8400-e29b-41d4-a716-446655440000"
    }
  }
}

4. Verify UUID format

curl -s -X POST "$BASE_URL/graphql" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  --data-binary '{
    "query": "query { customer { uuid } }"
  }' | jq -r '.data.customer.uuid'

Expected format:

xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Example:

550e8400-e29b-41d4-a716-446655440000

Testing Procedures

1. PHP syntax check

find vendor/coresh/module-customer-attribute -name "*.php" -print0 \
  | xargs -0 -n1 php -l

Expected result:

No syntax errors detected

2. Composer validation

composer validate vendor/coresh/module-customer-attribute/composer.json --strict

3. Magento dependency injection compilation

bin/magento setup:di:compile

Expected result: compilation completes without errors.

4. Magento setup and index verification

bin/magento setup:upgrade
bin/magento cache:flush
bin/magento indexer:reset customer_grid
bin/magento indexer:reindex customer_grid

Expected result: setup and customer grid reindex complete successfully.

5. Unit tests

vendor/bin/phpunit vendor/coresh/module-customer-attribute/Test/Unit --display-all-issues

Expected result:

OK (4 tests, 13 assertions)

6. Magento Coding Standard

If Magento Coding Standard is installed:

vendor/bin/phpcs vendor/coresh/module-customer-attribute --standard=Magento2

If the Magento test ruleset is available:

vendor/bin/phpcs vendor/coresh/module-customer-attribute \
  --standard=dev/tests/static/testsuite/Magento/Test/Php/_files/phpcs/ruleset.xml

7. Integration tests

Integration tests require a configured Magento integration testing environment.

Check that the integration test configuration exists:

ls -la dev/tests/integration/etc/install-config-mysql.php

Run integration tests:

vendor/bin/phpunit \
  -c dev/tests/integration/phpunit.xml.dist \
  vendor/coresh/module-customer-attribute/Test/Integration

Functional Acceptance Checklist

[OK] Module is installed and enabled.
[OK] setup:upgrade completes successfully.
[OK] setup:di:compile completes successfully.
[OK] customer_entity.uuid exists.
[OK] customer_entity.uuid has a unique index.
[OK] Existing customers have UUID values.
[OK] New customers receive UUID values automatically.
[OK] Existing UUIDs are not overwritten.
[OK] Duplicate UUIDs do not exist.
[OK] customer_grid_flat.uuid exists after customer_grid reindex.
[OK] UUID is visible in the Admin customer grid.
[OK] UUID is not editable in the Admin customer form.
[OK] Guest GraphQL request does not expose UUID.
[OK] Authenticated GraphQL customer query returns UUID.
[OK] Unit tests pass.
[OK] PHP syntax check passes.
[OK] Magento Coding Standard is checked.

Rollback Notes

To disable the module:

bin/magento module:disable Coresh_CustomerAttribute
bin/magento setup:upgrade
bin/magento cache:flush

The module adds persistent database schema changes, including the customer_entity.uuid column. Removing database columns should be handled carefully and only after confirming that no external integrations depend on UUID values.

Recommended safe rollback approach:

1. Disable the module.
2. Keep existing UUID data unless permanent removal is required.
3. Take a database backup before removing schema changes.
4. Remove schema only through a controlled deployment or migration process.

Architecture

It automatically:

  • assigns unique UUIDs to existing customers;
  • assigns UUIDs to new customers;
  • prevents manual UUID changes;
  • displays UUIDs in the Admin customer grid;
  • exposes UUIDs through GraphQL only for authenticated customers.

Its main purpose is to give each customer a stable, unique, secure external identifier without exposing the internal customer_id.

Installation

During bin/magento setup:upgrade, the module creates the customer UUID attribute metadata, updates the customer grid configuration, and assigns UUIDs to existing customer records.

Module Overview

The architecture uses both Data Patches and a Plugin because they solve two separate Magento lifecycle problems.

A Data Patch handles installation and upgrade-time work. It is the right place to create the customer attribute metadata, configure the attribute for the Admin customer grid, and backfill UUIDs for existing customer records during bin/magento setup:upgrade.

A Plugin handles runtime behavior. It is responsible for assigning a UUID when a new customer is saved and protecting an existing UUID from being changed through Admin, REST API, GraphQL, imports, or custom code.

These two mechanisms do not replace each other.

Patch  = installation / upgrade-time setup
Plugin = runtime customer save protection

Why Data Patches Are Used

The assessment requires UUIDs to be assigned to existing customers when the module is installed. That is installation-time data work, so a Data Patch is the correct Magento mechanism.

db_schema.xml can create the physical database column and unique index:

customer_entity.uuid
UNIQUE INDEX(uuid)

However, db_schema.xml cannot perform business/data operations such as:

- creating customer attribute metadata;
- enabling customer grid visibility;
- assigning UUIDs to existing customers;
- preserving existing UUIDs;
- processing existing customers in batches.

That is why the module uses:

db_schema.xml = database structure
Data Patch    = installation-time data and metadata setup

Using old InstallData, UpgradeData, InstallSchema, or UpgradeSchema scripts would be less suitable for a Magento 2.4.7+ implementation. Declarative schema and patches are the modern, production-ready approach.

Why a Plugin Is Used

The Plugin is used because UUID assignment and UUID immutability must be enforced every time a customer is saved.

Customer records can be created or updated through multiple entry points:

- Admin customer form;
- storefront registration;
- REST API;
- GraphQL-related flows;
- imports;
- custom modules;
- programmatic CustomerRepository saves.

The Plugin enforces the rule at the save layer:

If the customer is new:
    generate a UUID

If the customer already exists and already has a UUID:
    preserve the existing UUID

If the customer exists but the UUID is missing:
    generate a UUID

This is stronger than relying only on the Admin UI.

Hiding or disabling the UUID field in Admin is not enough because the value could still be changed through direct POST requests, API calls, imports, or custom code. The save layer must protect the UUID.

Can the Module Be Implemented Without Data Patches?

Technically, yes, but it would be weaker for this assessment.

One alternative is a CLI command:

bin/magento customer:uuid:backfill

That can be useful for very large production databases because it gives more control and can show progress.

However, by itself, a CLI command does not satisfy the requirement that existing customers receive UUIDs during module installation. Someone may forget to run it, and customers may temporarily have empty UUID values.

A stronger enterprise version could include both:

Data Patch  = automatic installation-time backfill
CLI command = optional controlled re-run/backfill tool

A cron-based or lazy backfill would be even weaker because UUIDs would not be available immediately after installation.

Can the Module Be Implemented Without a Plugin?

Yes, but another reliable runtime mechanism would still be required.

Possible alternatives include:

- observer on customer_save_before;
- customer attribute backend model;
- custom service contract used by all customer creation flows.

An observer can work, but it is less explicit than a CustomerRepository plugin and can become hidden business logic if not designed carefully.

A backend model can also work, but in this architecture the UUID is stored as a static column on customer_entity, not as a normal EAV varchar value. Immutability and collision retry handling are clearer in a dedicated service plus plugin.

A DI preference would be a poor choice because it is more invasive, creates a higher conflict risk with other modules, and is harder to maintain during Magento upgrades.

A database trigger would also be a poor Magento solution because it hides business logic outside the application layer, is harder to test, and is less suitable for Adobe Commerce Cloud-style deployments.

Practical Conclusion

For this assessment, the best implementation is:

Use Data Patch: yes
Use Plugin: yes

The Data Patch prepares the system during installation:

- creates customer attribute metadata;
- configures grid visibility;
- backfills existing customers;
- keeps backfill idempotent.

The Plugin protects runtime behavior:

- assigns UUIDs to new customers;
- preserves existing UUIDs;
- prevents Admin/API/GraphQL/custom code from overwriting UUIDs.

This separation is important:

installation lifecycle ≠ runtime lifecycle

The most production-ready architecture is therefore:

Declarative schema
+ Data Patches
+ Customer save Plugin
+ dedicated UUID services
+ GraphQL resolver
+ Admin grid configuration

This design satisfies the assessment requirements while keeping the module upgrade-safe, testable, and aligned with Magento extension architecture.

Check metadata:

SELECT ea.attribute_id,
              ea.attribute_code,
              ea.backend_type,
              ea.is_unique,
              cea.is_visible,
              cea.is_used_in_grid,
              cea.is_visible_in_grid,
              cea.is_filterable_in_grid,
              cea.is_searchable_in_grid
       FROM eav_attribute ea
       JOIN customer_eav_attribute cea
         ON cea.attribute_id = ea.attribute_id
       WHERE ea.entity_type_id = (
           SELECT entity_type_id
           FROM eav_entity_type
           WHERE entity_type_code = 'customer'
       )
       AND ea.attribute_code = 'uuid';
+--------------+----------------+--------------+-----------+------------+-----------------+--------------------+-----------------------+-----------------------+
| attribute_id | attribute_code | backend_type | is_unique | is_visible | is_used_in_grid | is_visible_in_grid | is_filterable_in_grid | is_searchable_in_grid |
+--------------+----------------+--------------+-----------+------------+-----------------+--------------------+-----------------------+-----------------------+
|          831 | uuid           | static       |         1 |          1 |               1 |                  1 |                     1 |                     1 |
+--------------+----------------+--------------+-----------+------------+-----------------+--------------------+-----------------------+-----------------------+
1 row in set (0.000 sec)

Check source of truth:

SELECT entity_id, email, uuid
     FROM customer_entity
     ORDER BY entity_id DESC
     LIMIT 10;
+-----------+---------------------------+--------------------------------------+
| entity_id | email                     | uuid                                 |
+-----------+---------------------------+--------------------------------------+
|       491 | ************************* | 3ce1458a-c109-4e64-b4ea-f3881fd2332e |
|       490 | ************************* | 34a12bfb-a61f-410c-9d59-9be725cd739f |
|       487 | ************************* | e30a6707-1fa5-4519-8ae3-e98dbe9555af |
+-----------+---------------------------+--------------------------------------+

Check Grid:

 SELECT entity_id, email, uuid
       FROM customer_grid_flat
       ORDER BY entity_id DESC
       LIMIT 10;
+-----------+---------------------------+--------------------------------------+
| entity_id | email                     | uuid                                 |
+-----------+---------------------------+--------------------------------------+
|       491 | ************************* | 3ce1458a-c109-4e64-b4ea-f3881fd2332e |
|       490 | ************************* | 34a12bfb-a61f-410c-9d59-9be725cd739f |
|       487 | ************************* | e30a6707-1fa5-4519-8ae3-e98dbe9555af |
+-----------+---------------------------+--------------------------------------+

GraphQL query Guest request:

BASE_URL="https://example.com"
EMAIL="test@example.com"
PASSWORD="************************"

curl -s -X POST "$BASE_URL/graphql" \
  -H "Content-Type: application/json" \
  --data-binary '{
    "query": "query { customer { email uuid } }"
  }' | jq

GraphQL query Guest request result:

{
  "errors": [
    {
      "message": "The current customer isn't authorized.",
      "locations": [
        {
          "line": 1,
          "column": 9
        }
      ],
      "path": [
        "customer"
      ],
      "extensions": {
        "category": "graphql-authorization"
      }
    }
  ],
  "data": {
    "customer": null
  }
}

Get Customer Token:

TOKEN=$(curl -s -X POST "$BASE_URL/graphql" \
  -H "Content-Type: application/json" \
  --data-binary "{
    \"query\": \"mutation { generateCustomerToken(email: \\\"$EMAIL\\\", password: \\\"$PASSWORD\\\") { token } }\"
  }" | jq -r '.data.generateCustomerToken.token')

echo "$TOKEN"

Authenticated request:

curl -s -X POST "$BASE_URL/graphql" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  --data-binary '{
    "query": "query { customer { email uuid } }"
  }' | jq

Authenticated request result:

{
  "data": {
    "customer": {
      "email": "test@example.com",
      "uuid": "e30a6707-1fa5-4519-8ae3-e98dbe9555af"
    }
  }
}

Check UUID format request:

curl -s -X POST "$BASE_URL/graphql" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  --data-binary '{
    "query": "query { customer { uuid } }"
  }' | jq -r '.data.customer.uuid'

Check UUID format request result:

e30a6707-1fa5-4519-8ae3-e98fbe95f5af

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 4
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: proprietary
  • 更新时间: 2026-06-11

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固