. */ declare(strict_types=1); namespace App\Services\InfoProviderSystem\Providers; use App\Services\InfoProviderSystem\DTOs\FileDTO; use App\Services\InfoProviderSystem\DTOs\PartDetailDTO; use App\Services\InfoProviderSystem\DTOs\SearchResultDTO; use Symfony\Component\DomCrawler\Crawler; use Symfony\Contracts\HttpClient\HttpClientInterface; class ReicheltProvider implements InfoProviderInterface { private const SEARCH_ENDPOINT = "https://www.reichelt.com/index.html?ACTION=446&LA=0&nbc=1&q=%s"; public function __construct(private readonly HttpClientInterface $client) { } public function getProviderInfo(): array { return [ 'name' => 'Reichelt', 'description' => 'TODO', 'url' => 'https://www.reichelt.de/', 'disabled_help' => 'TODO' ]; } public function getProviderKey(): string { return 'reichelt'; } public function isActive(): bool { return true; } public function searchByKeyword(string $keyword): array { //Lowercase the keyword and urlencode it $keyword = urlencode($keyword); $response = $this->client->request('GET', sprintf(self::SEARCH_ENDPOINT, $keyword)); $html = $response->getContent(); //Parse the HTML and return the results $dom = new Crawler($html); //Iterate over all div.al_gallery_article elements $results = []; $dom->filter('div.al_gallery_article')->each(function (Crawler $element) use (&$results) { //Extract product id from data-product attribute $artId = json_decode($element->attr('data-product'), true, 2, JSON_THROW_ON_ERROR)['artid']; $productID = $element->filter('meta[itemprop="productID"]')->attr('content'); $name = $element->filter('meta[itemprop="name"]')->attr('content'); $sku = $element->filter('meta[itemprop="sku"]')->attr('content'); //Try to extract a picture URL: $pictureURL = $element->filter("div.al_artlogo img")->attr('src'); $results[] = new SearchResultDTO( provider_key: $this->getProviderKey(), provider_id: $artId, name: $productID, description: $name, category: null, manufacturer: $sku, preview_image_url: $pictureURL, provider_url: $element->filter('a.al_artinfo_link')->attr('href') ); }); return $results; } public function getDetails(string $id): PartDetailDTO { //Check that the ID is a number if (!is_numeric($id)) { throw new \InvalidArgumentException("Invalid ID"); } //Use this endpoint to resolve the artID to a product page $response = $this->client->request('GET', sprintf('https://www.reichelt.com/?ACTION=514&id=74&article=%s&LANGUAGE=EN&CCOUNTRY=DE', $id)); $json = $response->toArray(); //Retrieve the product page from the response $productPage = $this->getBaseURL() . '/shop/product' . $json[0]['article_path']; $response = $this->client->request('GET', $productPage); $html = $response->getContent(); $dom = new Crawler($html); //Extract the product notes $notes = $dom->filter('p[itemprop="description"]')->html(); //Extract datasheets $datasheets = []; $dom->filter('div.articleDatasheet a')->each(function (Crawler $element) use (&$datasheets) { $datasheets[] = new FileDTO($element->attr('href'), $element->filter('span')->text()); }); //Create part object return new PartDetailDTO( provider_key: $this->getProviderKey(), provider_id: $id, name: $json[0]['article_artnr'], description: $json[0]['article_besch'], manufacturer: $json[0]['manufacturer_name'], preview_image_url: $json[0]['article_picture'], provider_url: $productPage, notes: $notes, datasheets: $datasheets ); } private function getBaseURL(): string { //Without the trailing slash return 'https://www.reichelt.com/de/en'; } public function getCapabilities(): array { return [ ProviderCapabilities::BASIC, ProviderCapabilities::PICTURE, ProviderCapabilities::DATASHEET, ProviderCapabilities::PRICE, ]; } }