Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions docs/03_Development/02_Localization/03_States/02_Setup_Command.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Setup States Command

CoreShop provides a CLI command to create states/regions for countries after the initial installation.

By default, CoreShop only creates states for Austria (AT) during installation. Use this command to add states for additional countries.

## Usage

```bash
# Setup states for a single country
php bin/console coreshop:setup:states DE

# Setup states for multiple countries
php bin/console coreshop:setup:states DE,US,FR

# Setup states and activate the country if not already active
php bin/console coreshop:setup:states DE --activate-country

# Verbose output to see individual states being created
php bin/console coreshop:setup:states DE -v
```

## Arguments

| Argument | Description |
|----------|-------------|
| countries | Comma-separated list of country ISO codes (e.g., DE,US,FR) |

## Options

| Option | Description |
|--------|-------------|
| --activate-country | Also activate the country if not already active |

## Prerequisites

Before running this command, ensure that:

1. CoreShop is installed (`coreshop:install` has been run)
2. The country fixtures have been loaded (countries exist in the database)

## How It Works

The command:

1. Looks up each specified country in the database by ISO code
2. Loads the country's divisions (states/regions) from the Rinvex country data library
3. Creates states for each division that doesn't already exist
4. Optionally activates the country if `--activate-country` is specified

## Example Output

```
CoreShop States Setup
=====================

Setting up states for countries: DE

Processing country: DE
-----------------------

[OK] Countries processed: 1
States created: 16
States skipped (already exist): 0
Countries activated: 0
```
182 changes: 182 additions & 0 deletions src/CoreShop/Bundle/CoreBundle/Command/SetupStatesCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
<?php

declare(strict_types=1);

/*
* CoreShop
*
* This source file is available under two different licenses:
* - GNU General Public License version 3 (GPLv3)
* - CoreShop Commercial License (CCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) CoreShop GmbH (https://www.coreshop.com)
* @license https://www.coreshop.com/license GPLv3 and CCL
*
*/

namespace CoreShop\Bundle\CoreBundle\Command;

use CoreShop\Component\Address\Model\StateInterface;
use CoreShop\Component\Address\Repository\CountryRepositoryInterface;
use CoreShop\Component\Resource\Factory\FactoryInterface;
use CoreShop\Component\Resource\Repository\RepositoryInterface;
use Doctrine\ORM\EntityManagerInterface;
use Pimcore\Tool;
use Rinvex\Country\CountryLoader;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

final class SetupStatesCommand extends Command
{
public function __construct(
private CountryRepositoryInterface $countryRepository,
private RepositoryInterface $stateRepository,
private FactoryInterface $stateFactory,
private EntityManagerInterface $entityManager,
) {
parent::__construct();
}

protected function configure(): void
{
$this
->setName('coreshop:setup:states')
->setDescription('Create states/regions for specified countries.')
->addArgument(
'countries',
InputArgument::REQUIRED,
'Comma-separated list of country ISO codes (e.g., DE,US,FR)',
)
->addOption(
'activate-country',
null,
InputOption::VALUE_NONE,
'Also activate the country if not already active',
)
->setHelp(
<<<EOT
The <info>%command.name%</info> command creates states/regions for specified countries.

Examples:
<info>php bin/console %command.name% DE</info>
<info>php bin/console %command.name% DE,US,FR</info>
<info>php bin/console %command.name% DE --activate-country</info>
EOT
)
;
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);

$countriesArg = $input->getArgument('countries');
$activateCountry = $input->getOption('activate-country');
$countryCodes = array_map('strtoupper', array_map('trim', explode(',', $countriesArg)));

$languages = Tool::getValidLanguages();

$io->title('CoreShop States Setup');
$io->writeln(sprintf('Setting up states for countries: <info>%s</info>', implode(', ', $countryCodes)));

$createdStates = 0;
$skippedStates = 0;
$countriesProcessed = 0;
$activatedCountries = 0;

foreach ($countryCodes as $countryCode) {
$io->section(sprintf('Processing country: %s', $countryCode));

// Check if country exists in database
$country = $this->countryRepository->findByCode($countryCode);
if ($country === null) {
$io->warning(sprintf('Country with code "%s" not found in database. Run fixtures first or install CoreShop.', $countryCode));

continue;
}

// Load country data from Rinvex
try {
$rinvexCountry = CountryLoader::country($countryCode);
} catch (\Exception $e) {
$io->warning(sprintf('Country data not found for code "%s" in Rinvex data: %s', $countryCode, $e->getMessage()));

continue;
}

// Activate country if requested
if ($activateCountry && !$country->getActive()) {
$country->setActive(true);
$this->entityManager->persist($country);
$activatedCountries++;
$io->writeln(sprintf(' <comment>Activated country: %s</comment>', $countryCode));
}

// Get divisions (states/regions)
$divisions = $rinvexCountry->getDivisions();

if (!is_array($divisions) || empty($divisions)) {
$io->writeln(sprintf(' <comment>No divisions/states found for %s</comment>', $countryCode));
$countriesProcessed++;

continue;
}

foreach ($divisions as $isoCode => $division) {
if (!$division['name']) {
continue;
}

// Check if state already exists
$existingState = $this->stateRepository->findOneBy([
'isoCode' => $isoCode,
'country' => $country,
]);

if ($existingState !== null) {
$skippedStates++;
$io->writeln(sprintf(' <comment>Skipping existing state: %s (%s)</comment>', $division['name'], $isoCode), OutputInterface::VERBOSITY_VERBOSE);

continue;
}

/**
* @var StateInterface $state
*/
$state = $this->stateFactory->createNew();

foreach ($languages as $lang) {
$state->setName($division['name'], $lang);
}

$state->setIsoCode($isoCode);
$state->setCountry($country);
$state->setActive(true);

$this->entityManager->persist($state);
$createdStates++;

$io->writeln(sprintf(' <info>Created state: %s (%s)</info>', $division['name'], $isoCode), OutputInterface::VERBOSITY_VERBOSE);
}

$countriesProcessed++;
}

$this->entityManager->flush();

$io->success([
sprintf('Countries processed: %d', $countriesProcessed),
sprintf('States created: %d', $createdStates),
sprintf('States skipped (already exist): %d', $skippedStates),
sprintf('Countries activated: %d', $activatedCountries),
]);

return Command::SUCCESS;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,12 @@ services:
tags:
- { name: console.command, command: coreshop:migration:generate }

CoreShop\Bundle\CoreBundle\Command\SetupStatesCommand:
arguments:
- '@coreshop.repository.country'
- '@coreshop.repository.state'
- '@coreshop.factory.state'
- '@doctrine.orm.entity_manager'
tags:
- { name: console.command, command: coreshop:setup:states }

Loading