route = $route;
$this->seo = $seo;
$this->content = $content;
$this->productRepository = $productRepository;
$this->articleRepository = $articleRepository;
$this->httpResponder = $httpResponder;
$this->templateRenderer = $templateRenderer;
}
public function handle()
{
$slug = $this->route->values[1] ?? '';
if ($slug === '') {
$this->httpResponder->redirect('/catalog/', 301);
return;
}
$parts = explode('-', $slug);
$articleCode = array_pop($parts);
$brandSlug = implode('-', $parts);
$nParam = isset($_GET['n']) ? (string)$_GET['n'] : '';
$bParam = isset($_GET['b']) ? (int)$_GET['b'] : 0;
$isOem = ($nParam !== '');
$localData = $this->productRepository->findArticle($brandSlug, $articleCode);
$brandInfo = $localData ? $localData['brand'] : null;
$articleInfo = $localData ? $localData['article'] : null;
$brandName = '';
if ($brandInfo && !empty($brandInfo['title'])) {
$brandName = $brandInfo['title'];
}
if (!$brandName) {
$brandName = strtoupper(str_replace('-', ' ', $brandSlug));
}
$originalBrandName = $brandName;
$brandName = \firmSyn::mkFirm(\DB::getInstance(), $brandName) ?: $brandName;
$supplierId = $bParam;
if (!$supplierId) {
$supplierId = $this->productRepository->findSupplierIdByUrl($brandSlug);
}
if (!$supplierId && $brandName) {
$supplierId = $this->productRepository->findSupplierIdByBrandName($brandName);
}
$tovar = $this->buildTovar($articleCode, $brandName, $originalBrandName, $brandSlug, $isOem, $nParam, $supplierId, $brandInfo, $articleInfo);
if (empty($tovar['articleNumber']) && empty($tovar['mfrName'])) {
$this->httpResponder->notFound();
return;
}
$this->addBreadcrumbs($articleInfo);
$builder = new SeoBuilder($this->seo, $this->route);
$builder->applyProductCard($tovar);
$this->seo = $builder->getSeo();
\APP::getInstance()->body_class = 'body_bg';
\APP::getInstance()->show_h1 = false;
$imgForSchema = isset($tovar['img']) ? $tovar['img'] : '/images/no_image.png';
if (substr($imgForSchema, 0, 1) === '/') {
$imgForSchema = 'https://mirzap.ru' . $imgForSchema;
}
$loadAnaloguesAjax = !IsBotDetector::isBot($_SERVER['HTTP_USER_AGENT'] ?? '', $_SERVER['REMOTE_ADDR'] ?? '');
if (!empty($tovar['panoramic_images']) || !empty($tovar['panoramic_iframe_url'])) {
\APP::getInstance()::add_js('/script/libs/image360.js');
}
$html = $this->templateRenderer->render('tovar_card_td.tpl', array(
'tovar' => $tovar,
'path' => array(),
'seo' => $this->seo,
'load_analogues_ajax' => $loadAnaloguesAjax,
'search_results' => array('this_code' => array(), 'analog' => array()),
'search_filters' => array(),
'img' => $imgForSchema,
'url' => '/' . $this->route->values[0] . '/' . $slug . '/',
));
$this->content->html[] = $html;
}
private function buildTovar(
string $articleCode,
string $brandName,
string $originalBrandName,
string $brandSlug,
bool $isOem,
string $nParam,
int $supplierId,
?array $brandInfo,
?array $articleInfo
): array {
$tecdocData = $this->loadTecdocData($isOem, $nParam, $articleCode, $supplierId);
$genericDesc = '';
if ($articleInfo && !empty($articleInfo['title'])) {
$genericDesc = $articleInfo['title'];
} elseif ($tecdocData && !empty($tecdocData['genericArticles'])) {
$ga = is_object($tecdocData['genericArticles'][0])
? $tecdocData['genericArticles'][0]
: (object)$tecdocData['genericArticles'][0];
$genericDesc = isset($ga->genericArticleDescription) ? $ga->genericArticleDescription : '';
}
$localImages = ($articleInfo) ? $this->productRepository->getArticleLocalImages((int)$articleInfo['id']) : array();
$img = $this->resolveImage($isOem, $tecdocData, $articleInfo, $localImages);
$otherImg = $this->resolveOtherImages($isOem, $tecdocData, $articleInfo, $localImages);
$params = $this->resolveParams($isOem, $tecdocData, $articleInfo);
$offers = $this->productRepository->getOffers($articleCode, $brandName);
$displayArticle = \mb_strtoupper(\STANDART::parseCode($articleCode), 'UTF-8');
$panoramicImages = [];
$panoramicIframeUrl = null;
if (\CURRENT_USER::getInstance()->is_test() && $supplierId > 0) {
$artNumber = $isOem ? $nParam : $articleCode;
if ($artNumber !== '') {
$panoramic = $this->articleRepository->getPanoramicData($artNumber, $supplierId, $isOem);
$panoramicImages = $panoramic['images'] ?? [];
$panoramicIframeUrl = $panoramic['iframe_url'] ?? null;
}
}
$tovar = array(
'articleNumber' => $displayArticle,
'articleNumber_original' => $articleCode,
'mfrName' => $brandName,
'mfrName_original' => $originalBrandName,
'genericArticleDescription' => $genericDesc,
'description' => $articleInfo ? ($articleInfo['description'] ?? '') : '',
'title' => trim($genericDesc . ' ' . $brandName . ' ' . $displayArticle),
'img' => $img,
'other_img' => $otherImg,
'images' => array(),
'panoramic' => $panoramicImages, // image360: массив JPG URL
'panoramic_images' => $panoramicImages,
'panoramic_iframe_url' => $panoramicIframeUrl,
'data' => $offers,
'params' => $params,
'brand_url' => $this->productRepository->findBrandCatalogUrl($brandName),
'oemNumbers' => array(),
'replacesArticles' => array(),
'replacedByArticles' => array(),
'price_parsed' => !empty($offers) ? $offers[0]['price'] : ($articleInfo['price'] ?? 0),
'days_filter' => $this->buildDaysFilter($offers),
'applicability' => $this->checkApplicability($isOem, $nParam, $articleCode, $supplierId),
'article_info' => $articleInfo,
'viewed_tovar' => '',
'see_also' => '',
);
if (!empty($brandName)) {
$viewer = new \viewer();
$badge = $viewer->getBrandBadge($brandName);
if ($badge) {
$tovar['brand_badge'] = $badge;
}
}
if ($articleInfo) {
$aid = (int)$articleInfo['id'];
$tovar['viewed_tovar'] = $this->buildViewedHtml($aid);
if (!empty($articleInfo['search_priority']) && $articleInfo['search_priority'] > 0) {
$firmId = (int)($articleInfo['firm'] ?? 0);
$tovar['see_also'] = $this->buildSeeAlsoHtml($aid, $firmId, $brandName);
}
}
return $tovar;
}
private function loadTecdocData(bool $isOem, string $nParam, string $articleCode, int $supplierId): ?array
{
if ($supplierId <= 0) {
return null;
}
$artNumber = $isOem ? $nParam : $articleCode;
if ($artNumber === '') {
return null;
}
$pairs = array(array('articleNumber' => $artNumber, 'dataSupplierId' => $supplierId));
$cards = $this->articleRepository->getPcArticleCardsByPairs($pairs, true, 'all', 0, true, $isOem);
return !empty($cards[0]) ? $cards[0] : null;
}
private function resolveImage(bool $isOem, ?array $tecdocData, ?array $articleInfo, array $localImages): string
{
if (!empty($localImages[0]['img'])) {
return \image_helper::getImageWithWatermark('/images/pict_art/' . $localImages[0]['img']);
}
if (!$isOem && $tecdocData && !empty($tecdocData['images'])) {
$first = $tecdocData['images'][0];
$url = is_object($first) ? $first->url : ($first['url'] ?? '');
if ($url) return (string)$url;
}
if ($articleInfo && !empty($articleInfo['title'])) {
return \image_helper::getNoImage('jpg', $articleInfo['title']);
}
return '/images/no_image.png';
}
private function resolveOtherImages(bool $isOem, ?array $tecdocData, ?array $articleInfo, array $localImages): array
{
$otherImg = array();
for ($i = 1; $i < count($localImages); $i++) {
if (!empty($localImages[$i]['img'])) {
$otherImg[] = \image_helper::getImageWithWatermark('/images/pict_art/' . $localImages[$i]['img']);
}
}
if ($otherImg) return $otherImg;
if (!empty($localImages)) return $otherImg; // есть свои фото — TecDoc не подмешиваем
if (!$isOem && $tecdocData && !empty($tecdocData['images'])) {
foreach (array_slice($tecdocData['images'], 1) as $imgObj) {
$url = is_object($imgObj) ? $imgObj->url : ($imgObj['url'] ?? '');
if ($url) $otherImg[] = (string)$url;
}
}
return $otherImg;
}
private function resolveParams(bool $isOem, ?array $tecdocData, ?array $articleInfo): array
{
if ($articleInfo) {
$params = $this->productRepository->getArticleParams((int)$articleInfo['id']);
if ($params) return $params;
}
if (!$isOem && $tecdocData && !empty($tecdocData['linkages'])) {
$params = array();
$seen = array();
foreach ($tecdocData['linkages'] as $link) {
$linkObj = is_object($link) ? $link : (object)$link;
if (empty($linkObj->linkageCriteria)) continue;
foreach ($linkObj->linkageCriteria as $crit) {
$critObj = is_object($crit) ? $crit : (object)$crit;
if (empty($critObj->criteriaDescription)) continue;
$desc = (string)$critObj->criteriaDescription;
if (isset($seen[$desc])) continue;
$seen[$desc] = true;
$params[] = array(
'criteriaDescription' => $desc,
'formattedValue' => isset($critObj->formattedValue) ? $critObj->formattedValue : (isset($critObj->rawValue) ? $critObj->rawValue : ''),
);
}
}
if ($params) return $params;
}
return array();
}
private function checkApplicability(bool $isOem, string $nParam, string $articleCode, int $supplierId): int
{
$car = \CURRENT_USER::getInstance()->getCar();
$modId = !empty($car['modification_id']) ? (int)$car['modification_id'] : 0;
if ($modId <= 0 || $supplierId <= 0) {
return -1;
}
$artForCheck = $isOem ? $nParam : $articleCode;
if ($artForCheck === '') {
return -1;
}
return $this->articleRepository->checkApplicability($modId, $artForCheck, $supplierId, $isOem) ? 1 : 0;
}
private function buildDaysFilter(array $offers): array
{
$days = array(
array('v' => 2, 't' => 'До 2х дней', 'show' => false),
array('v' => 4, 't' => 'До 4х дней', 'show' => false),
array('v' => 6, 't' => 'До 6-ти дней', 'show' => false),
array('v' => 8, 't' => 'До 8-ми дней', 'show' => false),
array('v' => 14, 't' => 'До 2х недель', 'show' => false),
array('v' => 30, 't' => 'Не больше месяца', 'show' => false),
array('v' => 60, 't' => 'Не больше 2х месяцев', 'show' => false),
);
foreach ($offers as $offer) {
$daysDeliver = isset($offer['days_deliver']) ? $offer['days_deliver'] : 0;
for ($t = 0; $t < count($days); $t++) {
if ($days[$t]['v'] >= $daysDeliver) {
$days[$t]['show'] = true;
break;
}
}
}
$result = array();
foreach ($days as $day) {
if ($day['show']) $result[] = $day;
}
return $result;
}
private function addBreadcrumbs(?array $articleInfo): void
{
if (!$articleInfo) return;
$categoryPath = $this->productRepository->getCategoryPath((int)$articleInfo['id']);
foreach ($categoryPath as $node) {
if (!empty($node['assemblyGroupName']) && !empty($node['url'])) {
$this->route->add_path(array(
'title' => $node['assemblyGroupName'],
'URL' => '/catalog/' . $node['url'] . '/',
));
}
}
}
private function buildViewedHtml(int $articleId): string
{
$maxElements = 12;
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
if (stripos($userAgent, 'Googlebot') !== false || stripos($userAgent, 'YandexBot') !== false || stripos($userAgent, 'Yandex') !== false) {
return '';
}
$user = \CURRENT_USER::getInstance();
$id = [];
if ($user->id && $articleId) {
$id = $this->productRepository->trackViewedArticle($articleId, $user->id);
} else {
if (!isset($_SESSION['viewed'])) {
$_SESSION['viewed'] = [];
}
foreach ($_SESSION['viewed'] as $s) {
$s = trim($s);
if ($s != $articleId && $s !== '') {
$id[] = $s;
}
}
$_SESSION['viewed'] = $id;
array_unshift($_SESSION['viewed'], $articleId);
$_SESSION['viewed'] = array_slice($_SESSION['viewed'], 0, $maxElements);
}
if (empty($id)) {
return '';
}
$idFiltered = array_filter(array_unique($id), function ($v) { return !empty($v) && is_numeric($v); });
if (empty($idFiltered)) {
return '';
}
$mirzap = new \mirzap_catalog();
$mirzap->setFilter(['IN=id' => implode(',', $idFiltered)]);
$mirzap->setTemplate('mirzap_catalog_slider.tpl')->setTitle('Недавно просмотренные товары');
return $mirzap->get();
}
private function buildSeeAlsoHtml(int $articleId, int $firmId, string $brandTitle): string
{
$ids = $this->productRepository->getRelatedArticleIds($articleId, $firmId);
if (empty($ids)) {
return '';
}
$idFiltered = array_filter(array_unique($ids), function ($v) { return !empty($v) && is_numeric($v); });
if (empty($idFiltered)) {
return '';
}
$mirzap = new \mirzap_catalog();
$mirzap->setFilter(['IN=id' => implode(',', $idFiltered)]);
$mirzap->setTemplate('mirzap_catalog_slider.tpl')->setTitle('Популярные товары ' . $brandTitle);
return $mirzap->get();
}
}