PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

Komponenty Symfony bez frameworka – Console, Validator, HttpClient

by Henryk Tews / wtorek, 08 grudnia 2020 / Opublikowano w PHP, Symfony

Symfony to nie tylko framework – to zestaw niezależnych komponentów PHP, które możesz używać osobno w dowolnym projekcie. Magento 2 samo z nich korzysta (Console, Filesystem, Serializer). Pokazuję kilka komponentów, które realnie przydają się w codziennej pracy z PHP i Magento: Console do pisania komend CLI, Validator do walidacji danych i HttpClient do komunikacji z zewnętrznymi API.

Symfony Console – komendy CLI jak w Magento

Jeśli piszesz skrypty migracyjne, importery lub narzędzia deweloperskie – Symfony Console to standard. Magento 2 buduje na nim własny system komend bin/magento:

composer require symfony/console
<?php

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Helper\ProgressBar;

class ImportProductsCommand extends Command
{
    protected function configure(): void
    {
        $this
            ->setName('import:products')
            ->setDescription('Importuje produkty z pliku CSV')
            ->addArgument('file', InputArgument::REQUIRED, 'Ścieżka do pliku CSV')
            ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Tylko walidacja, bez zapisu')
            ->addOption('batch', 'b', InputOption::VALUE_OPTIONAL, 'Rozmiar batcha', 100);
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $file    = $input->getArgument('file');
        $dryRun  = $input->getOption('dry-run');
        $batch   = (int) $input->getOption('batch');

        if (!file_exists($file)) {
            $output->writeln("Plik nie istnieje: {$file}");
            return Command::FAILURE;
        }

        $rows = array_slice(file($file), 1); // pomiń nagłówek
        $total = count($rows);

        $output->writeln("Importuję {$total} produktów (batch: {$batch})");

        if ($dryRun) {
            $output->writeln('Tryb dry-run – bez zapisu do bazy');
        }

        $progressBar = new ProgressBar($output, $total);
        $progressBar->start();

        foreach (array_chunk($rows, $batch) as $chunk) {
            foreach ($chunk as $row) {
                // logika importu
                $progressBar->advance();
            }

            if (!$dryRun) {
                // zapis batcha do bazy
            }
        }

        $progressBar->finish();
        $output->writeln('');
        $output->writeln('Import zakończony.');

        return Command::SUCCESS;
    }
}

// Bootstrap aplikacji CLI
$app = new \Symfony\Component\Console\Application('Import Tool', '1.0.0');
$app->add(new ImportProductsCommand());
$app->run();

Symfony Validator – walidacja danych wejściowych

Zamiast pisać własne walidatory od zera, Symfony Validator oferuje gotowe constraints i możliwość tworzenia własnych:

composer require symfony/validator
<?php

use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Validation;

// Walidacja prostej tablicy danych
$validator = Validation::createValidator();

$productData = [
    'sku'   => '',           // błąd - wymagane
    'price' => -10.0,        // błąd - musi być >= 0
    'email' => 'nie-email',  // błąd - niepoprawny format
    'name'  => 'Produkt testowy',
];

$constraints = new Assert\Collection([
    'sku'   => [new Assert\NotBlank(), new Assert\Length(['min' => 3, 'max' => 64])],
    'price' => [new Assert\NotNull(), new Assert\GreaterThanOrEqual(0)],
    'email' => [new Assert\Email()],
    'name'  => [new Assert\NotBlank(), new Assert\Length(['max' => 255])],
]);

$violations = $validator->validate($productData, $constraints);

if (count($violations) > 0) {
    foreach ($violations as $violation) {
        echo $violation->getPropertyPath() . ': ' . $violation->getMessage() . PHP_EOL;
    }
    // [sku]: This value should not be blank.
    // [price]: This value should be greater than or equal to 0.
    // [email]: This value is not a valid email address.
}

Walidacja obiektów przez adnotacje lub atrybuty PHP 8.0:

<?php

use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Validation;

class ProductImportDto
{
    public function __construct(
        #[Assert\NotBlank]
        #[Assert\Length(min: 3, max: 64)]
        public readonly string $sku,

        #[Assert\NotBlank]
        #[Assert\Length(max: 255)]
        public readonly string $name,

        #[Assert\GreaterThanOrEqual(0)]
        public readonly float $price,

        #[Assert\Email]
        #[Assert\NotBlank]
        public readonly string $contactEmail
    ) {}
}

$validator = Validation::createValidatorBuilder()
    ->enableAttributeMapping()
    ->getValidator();

$dto = new ProductImportDto(
    sku: 'MG-001',
    name: 'Testowy produkt',
    price: 29.99,
    contactEmail: 'bledny-email'
);

$violations = $validator->validate($dto);
// [contactEmail]: This value is not a valid email address.

Symfony HttpClient – komunikacja z zewnętrznymi API

Symfony HttpClient to nowoczesna alternatywa dla Guzzle – lżejsza, z wbudowanym wsparciem dla asynchronicznych requestów:

composer require symfony/http-client
<?php

use Symfony\Component\HttpClient\HttpClient;

$client = HttpClient::create([
    'base_uri' => 'https://api.example.com',
    'headers'  => ['Authorization' => 'Bearer ' . $token],
    'timeout'  => 10,
]);

// Synchroniczny request
$response = $client->request('GET', '/v1/products', [
    'query' => ['page' => 1, 'limit' => 100],
]);

if ($response->getStatusCode() !== 200) {
    throw new \RuntimeException('API error: ' . $response->getStatusCode());
}

$products = $response->toArray(); // automatyczny JSON decode

// Kilka równoległych requestów - Symfony wykonuje je współbieżnie
$responses = [];
foreach ($productIds as $id) {
    $responses[$id] = $client->request('GET', "/v1/products/{$id}");
}

// Dane pobierane są dopiero tu - współbieżnie
foreach ($responses as $id => $response) {
    $data = $response->toArray();
    echo $data['name'] . PHP_EOL;
}

Kiedy komponenty Symfony zamiast pisania od zera?

Główna zasada: nie reinventuj koła. Jeśli piszesz skrypt CLI i zaczynasz tworzyć własny parser argumentów – użyj Console. Jeśli walidacja danych przychodzących z CSV lub API rośnie w złożone if-else – użyj Validator. Symfony komponenty są battle-tested, dobrze udokumentowane i mają pełne pokrycie testami.

W kontekście Magento 2: pisząc moduł z rozbudowaną komendą CLI, możesz oprzeć się na Magento\Framework\Console\Command (która dziedziczy z Symfony Command) i korzystać z pełnego API Symfony Console – progress bary, tabele, pytania interaktywne.

Podsumowanie

Symfony jako zestaw komponentów to jeden z najlepszych zasobów ekosystemu PHP. Nie musisz używać pełnego frameworka żeby korzystać z Console, Validator czy HttpClient. W projektach Magento 2 komponenty Symfony pojawiają się naturalnie przy pisaniu narzędzi CLI, walidacji danych importowanych z zewnętrznych systemów i komunikacji z API – to dobre miejsca żeby zacząć.

About Henryk Tews

Co możesz przeczytać następne

PHP 8.5 RC1 – pipe operator w realnym kodzie, pułapki, readonly dziedziczenie potwierdzone
PHP 8.0 – premiera i pierwsze wrażenia z nowych funkcji
Typowanie w PHP 7.x, strict_types, PHPStan, przygotowanie na PHP 7.4
  • Publikacje
  • O autorze
  • Kontakt

© 2026 Created by

GÓRA
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Zawsze aktywne
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Zarządzaj opcjami
  • Zarządzaj serwisami
  • Zarządzaj {vendor_count} dostawcami
  • Przeczytaj więcej o tych celach
Zobacz preferencje
  • {title}
  • {title}
  • {title}