Programmatisch klanten aanmaken in Magento 2.3.x • Proudnerds


Er zijn verschillende manieren om klanten aan te maken in Magento 2. Een klant kan zelf een account aanmaken met behulp van het aanmeldingsformulier, een klant kan worden aangemaakt via de beheerdersinterface en er is zelfs een ingebouwde Magento 2 importfunctie die kan een groot aantal klanten massaal uit een CSV-bestand importeren, op voorwaarde dat de klantgegevens in het CSV gereed zijn voor import.

Maar wat als we duizenden klanten hebben wiens gegevens nog moeten worden verwerkt voordat ze kunnen worden aangemaakt? De beste manier om dit te doen zou zijn om maak de klanten programmatisch aan.

In dit artikel gaan we het onderwerp van het maken van klanten programmatisch behandelen, en voor dit doel gaan we een eenvoudige Magento 2-module maken die een aangepast console-commando zal hebben en een aantal modellen die zullen worden gebruikt om klanten te lezen, te verwerken en aan te maken.

Het lichaam

Laten we beginnen door eerst een module te maken in de /app/code directory van onze Magento 2-installatie. In dit voorbeeld gebruik ik Inhoo als de moduleverkoper en ik ga de module een naam geven Klantcreatie, maar u kunt ze een naam geven zoals u wilt.

Binnen onze module hebben we de volgende mappen en bestanden nodig:

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

registratie.php

Om onze module voor het maken van klanten te laten werken, moeten we onze module registreren in het Magento-systeem. Kopieer de volgende code naar de registration.php het dossier:

<?php
 
MagentoFrameworkComponentComponentRegistrar::register(
MagentoFrameworkComponentComponentRegistrar::MODULE,
'Inchoo_CustomerCreation',
__DIR__
);

module.xml

Onze module moet ook zijn naam en bestaan ​​​​aangeven. Kopieer de volgende code naar de /etc/module.xml het dossier:

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

Het hart

Nu we onze module hebben ingesteld, hebben we een manier nodig om ons proces voor het maken van klanten te activeren. Een manier waarop we dit kunnen doen, is door een aangepaste consoleopdracht te maken.

Laten we beginnen met het importeren van een aantal klassen in de /Console/Command/CreateCustomers.php bestand, laten we ook de klasse definiëren en de geïmporteerde uitbreiden Opdracht klas:

<?php
 
namespace ProudnerdsCustomerCreationConsoleCommand;
 
use Exception;
use MagentoFrameworkAppFilesystemDirectoryList;
use MagentoFrameworkConsoleCli;
use MagentoFrameworkFilesystem;
use MagentoFrameworkAppState;
use MagentoFrameworkAppArea;
use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
use ProudnerdsCustomerCreationModelCustomer;
 
class CreateCustomers extends Command
{
  // everything else goes here
}

U merkt misschien dat onze Klant model is nog niet gedefinieerd. Maak je er nu geen zorgen over, we gaan het later definiëren.

Vervolgens moeten we een maken constructeur binnen onze klas en injecteer er enkele afhankelijkheden mee:

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

We moeten ook onze consoleopdracht configureren door er een naam voor in te stellen met behulp van de configure() methode in onze klas. Deze methode is overgenomen van de klasse Command en kan ook worden gebruikt om de opdrachtbeschrijving, invoerargumenten en andere opties in te stellen.

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

Laten we nu de . definiëren execute() methode die wordt geactiveerd wanneer we de . aanroepen bin/magento create:customers opdracht. Deze methode is ook geërfd van de klasse Command en daarin gaan we onze logica schrijven.

In de execute() methode, moeten we eerst het netnummer instellen op: global. Als we dit niet doen, zal het proces voor het maken van de klant later een fout opleveren.

Daarna moeten we het absolute pad van ons CSV-bestand krijgen dat alle klantgegevens bevat. In dit voorbeeld heet het CSV-bestand customers.csv en bevindt zich in de /pub/media/fixtures directory (ten opzichte van de root directory, niet onze module).

Zodra we het absolute pad hebben, moeten we de . noemen install() methode (deze methode wordt later in onze Klant model) en geef het het absolute pad van het CSV-bestand door als argument. Als er fouten zijn, moeten we ze opvangen en de foutmeldingen in onze CLI weergeven:

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

Om onze aangepaste opdracht te laten werken, moeten we ook de opdrachtnaam configureren met behulp van afhankelijkheidsinjectie.

Voeg de volgende code toe aan de /etc/di.xml het dossier:

<?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="MagentoFrameworkConsoleCommandList">
       <arguments>
           <argument name="commands" xsi:type="array">
               <item name="CreateCustomers" xsi:type="object">ProudnerdsCustomerCreationConsoleCommandCreateCustomers</item>
           </argument>
       </arguments>
   </type>
</config>

De ziel

Het is tijd om onze . te maken Klant model. Dit is waar we de CSV-gegevens gaan lezen, verwerken, opslaan in een array en verzenden om te worden opgeslagen.

In de /Model/Customer.php bestand, importeer de volgende klassen en definieer de Klant klas:

<?php
 
namespace ProudnerdsCustomerCreationModel;
 
use Exception;
use Generator;
use MagentoFrameworkFilesystemIoFile;
use MagentoStoreModelStoreManagerInterface;
use ProudnerdsCustomerCreationModelImportCustomerImport;
use SymfonyComponentConsoleOutputOutputInterface;
 
class Customer
{
    // everything else goes here
}

We moeten in deze klasse ook een constructor maken en enkele afhankelijkheden injecteren:

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

Laten we de . definiëren install() methode in onze Klant klas. Dit is de methode die we eerder in onze aangepaste consoleopdracht hebben genoemd.

We beginnen met het ophalen van de winkel- en website-ID’s. Daarna halen we de CSV-koptekst en dan door elk herhalen CSV-rij, de gegevens in die rijen lezen en doorgeven aan de createCustomer() methode die we nog moeten definiëren.

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

Om de CSV-header en -rijen te lezen, gebruiken we a generator. Met een generator kunnen we code schrijven die een reeks gegevens kan herhalen zonder een array in het geheugen te bouwen. Als we een groot CSV-bestand hebben, kan dit ons helpen de geheugenlimiet niet te overschrijden. De readCsvRows() methode leest de rijgegevens en wijst deze toe aan de koppen die zijn opgehaald uit de readCsvHeader() methode.

Maak de volgende methoden:

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

Onze laatste methode in deze klasse is de createCustomer() methode.

De $data argument dat aan de methode wordt doorgegeven, is een associatieve array die sleutel-waardeparen uit ons CSV-bestand bevat, elke sleutel is een andere veldnaam uit de koplijst (bijv. email_address) en elke waarde is het bijbehorende record (bijv. john.doe@email. com).

Binnen onze methode moeten we de . definiëren $customerData reeks. Dit zal ook een associatieve array zijn waarin we de waarden van onze . moeten koppelen $data array naar toetsen die de importCustomerData() methode gaat nodig hebben om een ​​klant te redden. Als er fouten worden ontdekt, worden hun foutmeldingen afgedrukt in onze CLI.

In het onderstaande voorbeeld wordt ervan uitgegaan dat het CSV-bestand de exacte gegevens bevat die Magento nodig heeft om een ​​klant aan te maken, maar in de meeste situaties is dit niet het geval. Misschien heeft de geboortedatum van de klant niet het juiste formaat of hebben we misschien niet de klantgroep-ID, alleen de naam van de klantgroep. In dat geval, voordat we de . vullen $customerData array, moeten we ervoor zorgen dat de gegevens van de $data array zodat deze de juiste waarden bevat.

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

de entiteit

Voor onze laatste stap moeten we onze Klantimport model. In de /Module/Import/CustomerImport.php bestand, laten we de . importeren Klant klasse uit de Magento KlantImportExport module en definieer onze Klantimport klas. Laat het de geïmporteerde klantklasse uitbreiden.

Onze klas gaat de bevatten importCustomerData() methode die we gebruiken om onze klantgegevens voor te bereiden en op te slaan.

<?php
 
namespace ProudnerdsCustomerCreationModelImport;
 
use MagentoCustomerImportExportModelImportCustomer;
 
class CustomerImport extends Customer
{
  // everything else goes here
}

Nu onze klasse is gedefinieerd, moeten we de . maken importCustomerData() methode die de laatste stappen van het klantcreatieproces afhandelt. Bij deze methode zullen we een klantenentiteit maken of bijwerken en alle verstrekte kenmerkgegevens voor die klant opslaan. De methode retourneert vervolgens de ID van de klantentiteit. Deze methode is eigenlijk een kleine wijziging van een functie in de klasse Customer die we uitbreiden.

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

Om het af te maken, moeten we een beroep doen op de bin/magento setup:upgrade en bin/magento setup:di:compile commando’s om onze module en aangepaste opdracht te laten werken.

Zodra we ons script voor het maken van klanten hebben uitgevoerd met: bin/magento create:customers, moeten we er ook voor zorgen dat de Customer Grid-indexer opnieuw wordt geïndexeerd. We kunnen dat doen door een beroep te doen op de bin/magento indexer:reindex customer_grid opdracht.

Het einde

En dat is alles, we zouden nu een volledig functionerende module moeten hebben waarmee we in enkele minuten programmatisch duizenden klanten kunnen creëren. Als je vragen of problemen hebt, laat dan hieronder een reactie achter.

badges

Let’s connect

We hebben altijd zin in nieuwe en uitdagende projecten. We gaan graag met je in gesprek!