当前位置: 技术文章>> magento2.3版本中如何以编程的方式向系统中添加用户

文章标题:magento2.3版本中如何以编程的方式向系统中添加用户
  • 文章分类: Magento
  • 25732 阅读
系统学习magento二次开发,推荐小册:《Magento中文全栈二次开发 》

本小册面向Magento2以上版本,书代码及示例兼容magento2.0-2.4版本。涵盖了magento前端开发,后端开发,magento2主题,magento2重写,magento2 layout,magento2控制器,magento2 block等相关内容,带领您成为magento开发技术专家。


有几种方法可以在Magento 2中创建客户。客户可以使用注册表单自行创建帐户,可以通过管理界面创建客户,甚至还有一个内置的Magento 2导入功能,可以从CSV文件中批量导入大量客户,前提是CSV中的客户数据已准备好导入。

但是,如果我们有成千上万的客户,他们的数据在创建之前仍然需要处理怎么办?执行此操作的最佳方法是以编程方式创建客户。

在本文中,我们将介绍以编程方式创建客户的主题,为此,我们将创建一个简单的Magento 2模块,

该模块将具有自定义控制台命令和几个模型,这些模型将用于读取,处理和创建客户。

### 结构

让我们首先在Magento 2安装的目录中创建一个模块。在此示例中,我将使用 Inchoo 作为模块供应商,并将模块命名为 CustomerCreation,但您可以根据需要命名它们。/app/code

在我们的模块中,我们将需要以下目录和文件:

registration.php
/etc/module.xml
/Console/Command/CreateCustomers.php
/etc/di.xml
/Model/Customer.php
/Model/Import/CustomerImport.php

registation.php

为了使我们的客户创建模块正常工作,我们需要在Magento系统中注册我们的模块。将以下代码复制到文件中:registration.php

<?php
 
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Inchoo_CustomerCreation',
__DIR__
);

### module.xml

我们的模块还需要声明它的名称和存在。将以下代码复制到文件中:/etc/module.xml

<?xml version="1.0"?>


<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>

### 其它文件

现在我们已经设置了模块,我们需要一种方法来触发我们的客户创建过程。我们可以做到这一点的一种方法是创建自定义控制台命令。

让我们从在文件中导入一堆类开始,让我们也定义类并使其扩展导入的 Command 类:/Console/Command/CreateCustomers.php

<?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
}

方法:

public function __construct(
    Filesystem $filesystem,
    Customer $customer,
    State $state
) {
parent::__construct();
    $this->filesystem = $filesystem;
    $this->customer = $customer;
    $this->state = $state;
}

我们还需要通过使用类中的方法为其设置名称来配置控制台命令。此方法继承自 Command 类,也可用于设置命令说明、输入参数和其他选项。configure()

public function configure(): void
{
    $this->setName('create:customers');
}

现在让我们定义调用命令时将触发的方法。此方法也是从 Command 类继承而来的,我们将在其中编写我们的逻辑。execute()bin/magento create:customers

在该方法中,我们首先必须将区号设置为 .如果我们不这样做,客户创建过程稍后将产生错误。execute()global

之后,我们必须获取包含所有客户数据的 CSV 文件的绝对路径。在此示例中,CSV 文件已命名并位于目录中(相对于根目录,而不是我们的模块)。customers.csv/pub/media/fixtures

获得绝对路径后,我们必须调用该方法(此方法稍后将在我们的客户模型中定义),并将 CSV 文件的绝对路径作为参数传递给它。如果有任何错误,我们需要捕获它们并在CLI中显示错误消息:install()

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;
  }
}

为了使我们的自定义命令正常工作,我们还必须使用依赖注入来配置命令名称。

将以下代码添加到文件中:/etc/di.xml

<?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>

是时候创建我们的客户模型了。这是我们将读取CSV数据,对其进行处理,将其存储在数组中并将其发送以进行保存的地方。

在文件中,导入以下类并定义 Customer 类:/Model/Customer.php

<?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
}

我们还必须在此类中创建一个构造函数并注入一些依赖项:

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;
}

让我们在 Customer 类中定义方法。这是我们之前在自定义控制台命令中调用的方法。install()

我们首先检索商店和网站 ID。之后,我们检索 CSV 标头,然后遍历每个 CSV 行,读取这些行中包含的数据并将它们传递给我们尚未定义的方法。createCustomer()

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();
    }
}

要读取 CSV 标题和行,我们使用生成器。生成器允许我们编写代码,这些代码可以迭代一组数据,而无需在内存中构建数组。如果我们有一个大的CSV文件,这可以帮助我们不超过内存限制。该方法将读取行数据并将其映射到从该方法检索的标头。readCsvRows()readCsvHeader()

创建以下方法:

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);
}

我们在此类中的最后一个方法是方法。createCustomer()

传递给该方法的参数是一个关联数组,其中包含来自 CSV 文件的键值对,每个键是标题列表(例如 email_address)的不同字段名称,每个值都是关联的记录(例如 john.doe@email.com)。$data

在我们的方法中,我们需要定义数组。这也将是一个关联数组,我们需要在其中将数组中的值与该方法需要保存客户的键配对。如果发现任何错误,其错误消息将打印在我们的 CLI 中。$customerData$dataimportCustomerData()

下面的示例假设CSV文件具有Magento创建客户所需的确切数据,但是,在大多数情况下,情况并非如此。也许客户出生日期的格式不正确,或者我们可能没有客户组 ID,只有客户组名称。在这种情况下,在填充数组之前,我们需要确保处理数组中的数据,使其包含正确的值。$customerData$data

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
      );
  }
}

### 实体

对于最后一步,我们需要创建客户导入模型。在文件中,让我们从Magento CustomerImportExport模块导入客户类并定义我们的CustomerImport类。使其扩展导入的客户类。/Module/Import/CustomerImport.php

我们的类将包含用于准备和保存客户数据的方法。importCustomerData()

<?php
 
namespace Inchoo\CustomerCreation\Model\Import;
 
use Magento\CustomerImportExport\Model\Import\Customer;
 
class CustomerImport extends Customer
{
  // everything else goes here
}

现在我们的类已经定义,我们需要创建处理客户创建过程最后步骤的方法。在此方法中,我们将创建或更新客户实体,并保存为该客户提供的任何属性数据。然后,该方法将返回客户实体 ID。此方法实际上是对我们正在扩展的 Customer 类中找到的函数的轻微修改。importCustomerData()

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;
}

为了完成工作,我们需要调用 and 命令来让我们的模块和自定义命令正常工作。bin/magento setup:upgradebin/magento setup:di:compile

使用 运行客户创建脚本后,还需要确保重新索引客户网格索引器。我们可以通过调用命令来做到这一点。bin/magento create:customersbin/magento indexer:reindex customer_grid

### 小结

就是这样,我们现在应该有一个功能齐全的模块,使我们能够在几分钟内以编程方式创建数千个客户。如果您有任何问题或问题,请在下面发表评论。

推荐文章