<h5 style="color:red;">系统学习magento二次开发,推荐小册:<a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank">《Magento中文全栈二次开发
》</a></h5>
<div class="image-container">
<p>
<a style="color:blue;" href="https://www.maxiaoke.com/manual/magento_cn_dev.html" target="_blank">
<img src="https://www.maxiaoke.com/uploads/images/20230218/bb9c82995c24d1105676e02f373755f5.jpg" alt="Magento中文全栈二次开发">
</a>
</p>
</div>
<div class="text-container" style="font-size:14px; color:#888">
<p>本小册面向Magento2以上版本,书代码及示例兼容magento2.0-2.4版本。涵盖了magento前端开发,后端开发,magento2主题,magento2重写,magento2 layout,magento2控制器,magento2 block等相关内容,带领您成为magento开发技术专家。</p>
</div>
<hr><p>有几种方法可以在Magento 2中创建客户。客户可以使用注册表单自行创建帐户,可以通过管理界面创建客户,甚至还有一个内置的Magento 2导入功能,可以从CSV文件中批量导入大量客户,前提是CSV中的客户数据已准备好导入。</p><p>但是,如果我们有成千上万的客户,他们的数据在创建之前仍然需要处理怎么办?执行此操作的最佳方法是以编程方式创建客户。</p><p>在本文中,我们将介绍以编程方式创建客户的主题,为此,我们将创建一个简单的Magento 2模块,</p><p>该模块将具有自定义控制台命令和几个模型,这些模型将用于读取,处理和创建客户。</p><p><span style="color: #6a9955;">### 结构</span></p><p>让我们首先在Magento 2安装的目录中创建一个模块。在此示例中,我将使用 Inchoo 作为模块供应商,并将模块命名为 CustomerCreation,但您可以根据需要命名它们。/app/code</p><p>在我们的模块中,我们将需要以下目录和文件:</p><pre class="brush:bash;toolbar:false">registration.php
/etc/module.xml
/Console/Command/CreateCustomers.php
/etc/di.xml
/Model/Customer.php
/Model/Import/CustomerImport.php</pre><p>registation.php</p><p>为了使我们的客户创建模块正常工作,我们需要在Magento系统中注册我们的模块。将以下代码复制到文件中:registration.php</p><pre class="brush:bash;toolbar:false"><?php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Inchoo_CustomerCreation',
__DIR__
);</pre><p><span style="color: #6a9955;"></span></p><p><span style="color: #6a9955;">### module.xml</span></p><p>我们的模块还需要声明它的名称和存在。将以下代码复制到文件中:/etc/module.xml</p><p><?xml <span style="color: #569cd6;">version</span>=<span style="color: #ce9178;">"1.0"</span>?></p><p><br/></p><pre class="brush:bash;toolbar:false"><config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Inchoo_CustomerCreation" setup_version="1.0.0" />
</config></pre><p><span style="color: #6a9955;">### 其它文件</span></p><p>现在我们已经设置了模块,我们需要一种方法来触发我们的客户创建过程。我们可以做到这一点的一种方法是创建自定义控制台命令。</p><p>让我们从在文件中导入一堆类开始,让我们也定义类并使其扩展导入的 Command 类:/Console/Command/CreateCustomers.php</p><pre class="brush:bash;toolbar:false"><?php
namespace Inchoo\CustomerCreation\Console\Command;
use Exception;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\Console\Cli;
use Magento\Framework\Filesystem;
use Magento\Framework\App\State;
use Magento\Framework\App\Area;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Inchoo\CustomerCreation\Model\Customer;
class CreateCustomers extends Command
{
// everything else goes here
}</pre><p>方法:</p><pre class="brush:bash;toolbar:false">public function __construct(
Filesystem $filesystem,
Customer $customer,
State $state
) {
parent::__construct();
$this->filesystem = $filesystem;
$this->customer = $customer;
$this->state = $state;
}</pre><p>我们还需要通过使用类中的方法为其设置名称来配置控制台命令。此方法继承自 Command 类,也可用于设置命令说明、输入参数和其他选项。configure()</p><pre class="brush:bash;toolbar:false">public function configure(): void
{
$this->setName('create:customers');
}</pre><p>现在让我们定义调用命令时将触发的方法。此方法也是从 Command 类继承而来的,我们将在其中编写我们的逻辑。execute()bin/magento create:customers</p><p>在该方法中,我们首先必须将区号设置为 .如果我们不这样做,客户创建过程稍后将产生错误。execute()global</p><p>之后,我们必须获取包含所有客户数据的 CSV 文件的绝对路径。在此示例中,CSV 文件已命名并位于目录中(相对于根目录,而不是我们的模块)。customers.csv/pub/media/fixtures</p><p>获得绝对路径后,我们必须调用该方法(此方法稍后将在我们的客户模型中定义),并将 CSV 文件的绝对路径作为参数传递给它。如果有任何错误,我们需要捕获它们并在CLI中显示错误消息:install()</p><pre class="brush:bash;toolbar:false">public function execute(InputInterface $input, OutputInterface $output): ?int
{
try {
$this->state->setAreaCode(Area::AREA_GLOBAL);
$mediaDir = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA);
$fixture = $mediaDir->getAbsolutePath() . 'fixtures/customers.csv';
$this->customer->install($fixture, $output);
return Cli::RETURN_SUCCESS;
} catch (Exception $e) {
$msg = $e->getMessage();
$output->writeln("<error>$msg</error>", OutputInterface::OUTPUT_NORMAL);
return Cli::RETURN_FAILURE;
}
}</pre><p>为了使我们的自定义命令正常工作,我们还必须使用依赖注入来配置命令名称。</p><p>将以下代码添加到文件中:/etc/di.xml</p><pre class="brush:bash;toolbar:false"><?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Framework\Console\CommandList">
<arguments>
<argument name="commands" xsi:type="array">
<item name="CreateCustomers" xsi:type="object">Inchoo\CustomerCreation\Console\Command\CreateCustomers</item>
</argument>
</arguments>
</type>
</config></pre><p>是时候创建我们的客户模型了。这是我们将读取CSV数据,对其进行处理,将其存储在数组中并将其发送以进行保存的地方。</p><p>在文件中,导入以下类并定义 Customer 类:/Model/Customer.php</p><pre class="brush:bash;toolbar:false"><?php
namespace Inchoo\CustomerCreation\Model;
use Exception;
use Generator;
use Magento\Framework\Filesystem\Io\File;
use Magento\Store\Model\StoreManagerInterface;
use Inchoo\CustomerCreation\Model\Import\CustomerImport;
use Symfony\Component\Console\Output\OutputInterface;
class Customer
{
// everything else goes here
}</pre><p>我们还必须在此类中创建一个构造函数并注入一些依赖项:</p><pre class="brush:bash;toolbar:false">private $file;
private $storeManagerInterface;
private $customerImport;
private $output;
public function __construct(
File $file,
StoreManagerInterface $storeManagerInterface,
CustomerImport $customerImport
) {
$this->file = $file;
$this->storeManagerInterface = $storeManagerInterface;
$this->customerImport = $customerImport;
}</pre><p>让我们在 Customer 类中定义方法。这是我们之前在自定义控制台命令中调用的方法。install()</p><p>我们首先检索商店和网站 ID。之后,我们检索 CSV 标头,然后遍历每个 CSV 行,读取这些行中包含的数据并将它们传递给我们尚未定义的方法。createCustomer()</p><pre class="brush:bash;toolbar:false">public function install(string $fixture, OutputInterface $output): void
{
$this->output = $output;
// get store and website ID
$store = $this->storeManagerInterface->getStore();
$websiteId = (int) $this->storeManagerInterface->getWebsite()->getId();
$storeId = (int) $store->getId();
// read the csv header
$header = $this->readCsvHeader($fixture)->current();
// read the csv file and skip the first (header) row
$row = $this->readCsvRows($fixture, $header);
$row->next();
// while the generator is open, read current row data, create a customer and resume the generator
while ($row->valid()) {
$data = $row->current();
$this->createCustomer($data, $websiteId, $storeId);
$row->next();
}
}</pre><p>要读取 CSV 标题和行,我们使用生成器。生成器允许我们编写代码,这些代码可以迭代一组数据,而无需在内存中构建数组。如果我们有一个大的CSV文件,这可以帮助我们不超过内存限制。该方法将读取行数据并将其映射到从该方法检索的标头。readCsvRows()readCsvHeader()</p><p>创建以下方法:</p><pre class="brush:bash;toolbar:false">private function readCsvRows(string $file, array $header): ?Generator
{
$handle = fopen($file, 'rb');
while (!feof($handle)) {
$data = [];
$rowData = fgetcsv($handle);
if ($rowData) {
foreach ($rowData as $key => $value) {
$data[$header[$key]] = $value;
}
yield $data;
}
}
fclose($handle);
}
private function readCsvHeader(string $file): ?Generator
{
$handle = fopen($file, 'rb');
while (!feof($handle)) {
yield fgetcsv($handle);
}
fclose($handle);
}</pre><p>我们在此类中的最后一个方法是方法。createCustomer()</p><p>传递给该方法的参数是一个关联数组,其中包含来自 CSV 文件的键值对,每个键是标题列表(例如 email_address)的不同字段名称,每个值都是关联的记录(例如 john.doe@email.com)。$data</p><p>在我们的方法中,我们需要定义数组。这也将是一个关联数组,我们需要在其中将数组中的值与该方法需要保存客户的键配对。如果发现任何错误,其错误消息将打印在我们的 CLI 中。$customerData$dataimportCustomerData()</p><p>下面的示例假设CSV文件具有Magento创建客户所需的确切数据,但是,在大多数情况下,情况并非如此。也许客户出生日期的格式不正确,或者我们可能没有客户组 ID,只有客户组名称。在这种情况下,在填充数组之前,我们需要确保处理数组中的数据,使其包含正确的值。$customerData$data</p><pre class="brush:bash;toolbar:false">private function createCustomer(array $data, int $websiteId, int $storeId): void
{
try {
// collect the customer data
$customerData = [
'email' => $data['email_address'],
'_website' => 'base',
'_store' => 'default',
'confirmation' => null,
'dob' => null,
'firstname' => $data['firstname'],
'gender' => null,
'group_id' => $data['customer_group_id'],
'lastname' => $data['last_name'],
'middlename' => null,
'password_hash' => $data['password_hash'],
'prefix' => null,
'store_id' => $storeId,
'website_id' => $websiteId,
'password' => null,
'disable_auto_group_change' => 0,
'some_custom_attribute' => 'some_custom_attribute_value'
];
// save the customer data
$this->customerImport->importCustomerData($customerData);
} catch (Exception $e) {
$this->output->writeln(
'<error>'. $e->getMessage() .'</error>',
OutputInterface::OUTPUT_NORMAL
);
}
}</pre><p><span style="color: #6a9955;">### 实体</span></p><p>对于最后一步,我们需要创建客户导入模型。在文件中,让我们从Magento CustomerImportExport模块导入客户类并定义我们的CustomerImport类。使其扩展导入的客户类。/Module/Import/CustomerImport.php</p><p>我们的类将包含用于准备和保存客户数据的方法。importCustomerData()</p><pre class="brush:bash;toolbar:false"><?php
namespace Inchoo\CustomerCreation\Model\Import;
use Magento\CustomerImportExport\Model\Import\Customer;
class CustomerImport extends Customer
{
// everything else goes here
}</pre><p>现在我们的类已经定义,我们需要创建处理客户创建过程最后步骤的方法。在此方法中,我们将创建或更新客户实体,并保存为该客户提供的任何属性数据。然后,该方法将返回客户实体 ID。此方法实际上是对我们正在扩展的 Customer 类中找到的函数的轻微修改。importCustomerData()</p><pre class="brush:bash;toolbar:false">public function importCustomerData(array $rowData)
{
$this->prepareCustomerData($rowData);
$entitiesToCreate = [];
$entitiesToUpdate = [];
$entitiesToDelete = [];
$attributesToSave = [];
$processedData = $this->_prepareDataForUpdate($rowData);
$entitiesToCreate = array_merge($entitiesToCreate, $processedData[self::ENTITIES_TO_CREATE_KEY]);
$entitiesToUpdate = array_merge($entitiesToUpdate, $processedData[self::ENTITIES_TO_UPDATE_KEY]);
foreach ($processedData[self::ATTRIBUTES_TO_SAVE_KEY] as $tableName => $customerAttributes) {
if (!isset($attributesToSave[$tableName])) {
$attributesToSave[$tableName] = [];
}
$attributesToSave[$tableName] = array_diff_key(
$attributesToSave[$tableName],
$customerAttributes
) + $customerAttributes;
}
$this->updateItemsCounterStats($entitiesToCreate, $entitiesToUpdate, $entitiesToDelete);
/**
* Save prepared data
*/
if ($entitiesToCreate || $entitiesToUpdate) {
$this->_saveCustomerEntities($entitiesToCreate, $entitiesToUpdate);
}
if ($attributesToSave) {
$this->_saveCustomerAttributes($attributesToSave);
}
return $entitiesToCreate[0]['entity_id'] ?? $entitiesToUpdate[0]['entity_id'] ?? null;
}</pre><p>为了完成工作,我们需要调用 and 命令来让我们的模块和自定义命令正常工作。bin/magento setup:upgradebin/magento setup:di:compile</p><p>使用 运行客户创建脚本后,还需要确保重新索引客户网格索引器。我们可以通过调用命令来做到这一点。bin/magento create:customersbin/magento indexer:reindex customer_grid</p><p><span style="color: #6a9955;">### 小结</span></p><p>就是这样,我们现在应该有一个功能齐全的模块,使我们能够在几分钟内以编程方式创建数千个客户。如果您有任何问题或问题,请在下面发表评论。</p>