1: <?php
2: namespace Opencart\Catalog\Controller\Product;
3: /**
4: * Class Product
5: *
6: * @package Opencart\Catalog\Controller\Product
7: */
8: class Product extends \Opencart\System\Engine\Controller {
9: /**
10: * @return ?\Opencart\System\Engine\Action
11: */
12: public function index(): ?\Opencart\System\Engine\Action {
13: $this->load->language('product/product');
14:
15: if (isset($this->request->get['product_id'])) {
16: $product_id = (int)$this->request->get['product_id'];
17: } else {
18: $product_id = 0;
19: }
20:
21: $this->load->model('catalog/product');
22:
23: $product_info = $this->model_catalog_product->getProduct($product_id);
24:
25: if ($product_info) {
26: $this->document->setTitle($product_info['meta_title']);
27: $this->document->setDescription($product_info['meta_description']);
28: $this->document->setKeywords($product_info['meta_keyword']);
29: $this->document->addLink($this->url->link('product/product', 'language=' . $this->config->get('config_language') . '&product_id=' . $product_id), 'canonical');
30:
31: $data['breadcrumbs'] = [];
32:
33: $data['breadcrumbs'][] = [
34: 'text' => $this->language->get('text_home'),
35: 'href' => $this->url->link('common/home', 'language=' . $this->config->get('config_language'))
36: ];
37:
38: $this->load->model('catalog/category');
39:
40: if (isset($this->request->get['path'])) {
41: $path = '';
42:
43: $parts = explode('_', (string)$this->request->get['path']);
44:
45: $category_id = (int)array_pop($parts);
46:
47: foreach ($parts as $path_id) {
48: if (!$path) {
49: $path = $path_id;
50: } else {
51: $path .= '_' . $path_id;
52: }
53:
54: $category_info = $this->model_catalog_category->getCategory((int)$path_id);
55:
56: if ($category_info) {
57: $data['breadcrumbs'][] = [
58: 'text' => $category_info['name'],
59: 'href' => $this->url->link('product/category', 'language=' . $this->config->get('config_language') . '&path=' . $path)
60: ];
61: }
62: }
63:
64: // Set the last category breadcrumb
65: $category_info = $this->model_catalog_category->getCategory($category_id);
66:
67: if ($category_info) {
68: $url = '';
69:
70: if (isset($this->request->get['sort'])) {
71: $url .= '&sort=' . $this->request->get['sort'];
72: }
73:
74: if (isset($this->request->get['order'])) {
75: $url .= '&order=' . $this->request->get['order'];
76: }
77:
78: if (isset($this->request->get['page'])) {
79: $url .= '&page=' . $this->request->get['page'];
80: }
81:
82: if (isset($this->request->get['limit'])) {
83: $url .= '&limit=' . $this->request->get['limit'];
84: }
85:
86: $data['breadcrumbs'][] = [
87: 'text' => $category_info['name'],
88: 'href' => $this->url->link('product/category', 'language=' . $this->config->get('config_language') . '&path=' . $this->request->get['path'] . $url)
89: ];
90: }
91: }
92:
93: $this->load->model('catalog/manufacturer');
94:
95: if (isset($this->request->get['manufacturer_id'])) {
96: $data['breadcrumbs'][] = [
97: 'text' => $this->language->get('text_brand'),
98: 'href' => $this->url->link('product/manufacturer', 'language=' . $this->config->get('config_language'))
99: ];
100:
101: $url = '';
102:
103: if (isset($this->request->get['sort'])) {
104: $url .= '&sort=' . $this->request->get['sort'];
105: }
106:
107: if (isset($this->request->get['order'])) {
108: $url .= '&order=' . $this->request->get['order'];
109: }
110:
111: if (isset($this->request->get['page'])) {
112: $url .= '&page=' . $this->request->get['page'];
113: }
114:
115: if (isset($this->request->get['limit'])) {
116: $url .= '&limit=' . $this->request->get['limit'];
117: }
118:
119: $manufacturer_info = $this->model_catalog_manufacturer->getManufacturer($this->request->get['manufacturer_id']);
120:
121: if ($manufacturer_info) {
122: $data['breadcrumbs'][] = [
123: 'text' => $manufacturer_info['name'],
124: 'href' => $this->url->link('product/manufacturer.info', 'language=' . $this->config->get('config_language') . '&manufacturer_id=' . $this->request->get['manufacturer_id'] . $url)
125: ];
126: }
127: }
128:
129: if (isset($this->request->get['search']) || isset($this->request->get['tag'])) {
130: $url = '';
131:
132: if (isset($this->request->get['search'])) {
133: $url .= '&search=' . $this->request->get['search'];
134: }
135:
136: if (isset($this->request->get['tag'])) {
137: $url .= '&tag=' . $this->request->get['tag'];
138: }
139:
140: if (isset($this->request->get['description'])) {
141: $url .= '&description=' . $this->request->get['description'];
142: }
143:
144: if (isset($this->request->get['category_id'])) {
145: $url .= '&category_id=' . $this->request->get['category_id'];
146: }
147:
148: if (isset($this->request->get['sub_category'])) {
149: $url .= '&sub_category=' . $this->request->get['sub_category'];
150: }
151:
152: if (isset($this->request->get['sort'])) {
153: $url .= '&sort=' . $this->request->get['sort'];
154: }
155:
156: if (isset($this->request->get['order'])) {
157: $url .= '&order=' . $this->request->get['order'];
158: }
159:
160: if (isset($this->request->get['page'])) {
161: $url .= '&page=' . $this->request->get['page'];
162: }
163:
164: if (isset($this->request->get['limit'])) {
165: $url .= '&limit=' . $this->request->get['limit'];
166: }
167:
168: $data['breadcrumbs'][] = [
169: 'text' => $this->language->get('text_search'),
170: 'href' => $this->url->link('product/search', 'language=' . $this->config->get('config_language') . $url)
171: ];
172: }
173:
174: $url = '';
175:
176: if (isset($this->request->get['path'])) {
177: $url .= '&path=' . $this->request->get['path'];
178: }
179:
180: if (isset($this->request->get['filter'])) {
181: $url .= '&filter=' . $this->request->get['filter'];
182: }
183:
184: if (isset($this->request->get['manufacturer_id'])) {
185: $url .= '&manufacturer_id=' . $this->request->get['manufacturer_id'];
186: }
187:
188: if (isset($this->request->get['search'])) {
189: $url .= '&search=' . $this->request->get['search'];
190: }
191:
192: if (isset($this->request->get['tag'])) {
193: $url .= '&tag=' . $this->request->get['tag'];
194: }
195:
196: if (isset($this->request->get['description'])) {
197: $url .= '&description=' . $this->request->get['description'];
198: }
199:
200: if (isset($this->request->get['category_id'])) {
201: $url .= '&category_id=' . $this->request->get['category_id'];
202: }
203:
204: if (isset($this->request->get['sub_category'])) {
205: $url .= '&sub_category=' . $this->request->get['sub_category'];
206: }
207:
208: if (isset($this->request->get['sort'])) {
209: $url .= '&sort=' . $this->request->get['sort'];
210: }
211:
212: if (isset($this->request->get['order'])) {
213: $url .= '&order=' . $this->request->get['order'];
214: }
215:
216: if (isset($this->request->get['page'])) {
217: $url .= '&page=' . $this->request->get['page'];
218: }
219:
220: if (isset($this->request->get['limit'])) {
221: $url .= '&limit=' . $this->request->get['limit'];
222: }
223:
224: $data['breadcrumbs'][] = [
225: 'text' => $product_info['name'],
226: 'href' => $this->url->link('product/product', 'language=' . $this->config->get('config_language') . $url . '&product_id=' . $product_id)
227: ];
228:
229: $this->document->setTitle($product_info['meta_title']);
230: $this->document->setDescription($product_info['meta_description']);
231: $this->document->setKeywords($product_info['meta_keyword']);
232: $this->document->addLink($this->url->link('product/product', 'language=' . $this->config->get('config_language') . '&product_id=' . $product_id), 'canonical');
233:
234: $this->document->addScript('catalog/view/javascript/jquery/magnific/jquery.magnific-popup.min.js');
235: $this->document->addStyle('catalog/view/javascript/jquery/magnific/magnific-popup.css');
236:
237: $data['heading_title'] = $product_info['name'];
238:
239: $data['text_minimum'] = sprintf($this->language->get('text_minimum'), $product_info['minimum']);
240: $data['text_login'] = sprintf($this->language->get('text_login'), $this->url->link('account/login', 'language=' . $this->config->get('config_language')), $this->url->link('account/register', 'language=' . $this->config->get('config_language')));
241: $data['text_reviews'] = sprintf($this->language->get('text_reviews'), (int)$product_info['reviews']);
242:
243: $data['tab_review'] = sprintf($this->language->get('tab_review'), $product_info['reviews']);
244:
245: $data['error_upload_size'] = sprintf($this->language->get('error_upload_size'), $this->config->get('config_file_max_size'));
246:
247: $data['config_file_max_size'] = ((int)$this->config->get('config_file_max_size') * 1024 * 1024);
248:
249: $this->session->data['upload_token'] = oc_token(32);
250:
251: $data['upload'] = $this->url->link('tool/upload', 'language=' . $this->config->get('config_language') . '&upload_token=' . $this->session->data['upload_token']);
252:
253: $data['product_id'] = $product_id;
254:
255: $manufacturer_info = $this->model_catalog_manufacturer->getManufacturer($product_info['manufacturer_id']);
256:
257: if ($manufacturer_info) {
258: $data['manufacturer'] = $manufacturer_info['name'];
259: } else {
260: $data['manufacturer'] = '';
261: }
262:
263: $data['manufacturers'] = $this->url->link('product/manufacturer.info', 'language=' . $this->config->get('config_language') . '&manufacturer_id=' . $product_info['manufacturer_id']);
264: $data['model'] = $product_info['model'];
265: $data['reward'] = $product_info['reward'];
266: $data['points'] = $product_info['points'];
267: $data['description'] = html_entity_decode($product_info['description'], ENT_QUOTES, 'UTF-8');
268:
269: if ($product_info['quantity'] <= 0) {
270: $this->load->model('localisation/stock_status');
271:
272: $stock_status_info = $this->model_localisation_stock_status->getStockStatus($product_info['stock_status_id']);
273:
274: if ($stock_status_info) {
275: $data['stock'] = $stock_status_info['name'];
276: } else {
277: $data['stock'] = '';
278: }
279: } elseif ($this->config->get('config_stock_display')) {
280: $data['stock'] = $product_info['quantity'];
281: } else {
282: $data['stock'] = $this->language->get('text_instock');
283: }
284:
285: $data['rating'] = (int)$product_info['rating'];
286: $data['review_status'] = (int)$this->config->get('config_review_status');
287: $data['review'] = $this->load->controller('product/review');
288:
289: $data['wishlist_add'] = $this->url->link('account/wishlist.add', 'language=' . $this->config->get('config_language'));
290: $data['compare_add'] = $this->url->link('product/compare.add', 'language=' . $this->config->get('config_language'));
291:
292: $this->load->model('tool/image');
293:
294: if ($product_info['image'] && is_file(DIR_IMAGE . html_entity_decode($product_info['image'], ENT_QUOTES, 'UTF-8'))) {
295: $data['popup'] = $this->model_tool_image->resize($product_info['image'], $this->config->get('config_image_popup_width'), $this->config->get('config_image_popup_height'));
296: $data['thumb'] = $this->model_tool_image->resize($product_info['image'], $this->config->get('config_image_thumb_width'), $this->config->get('config_image_thumb_height'));
297: } else {
298: $data['popup'] = '';
299: $data['thumb'] = '';
300: }
301:
302: $data['images'] = [];
303:
304: $results = $this->model_catalog_product->getImages($product_id);
305:
306: foreach ($results as $result) {
307: if ($result['image'] && is_file(DIR_IMAGE . html_entity_decode($result['image'], ENT_QUOTES, 'UTF-8'))) {
308: $data['images'][] = [
309: 'popup' => $this->model_tool_image->resize($result['image'], $this->config->get('config_image_popup_width'), $this->config->get('config_image_popup_height')),
310: 'thumb' => $this->model_tool_image->resize($result['image'], $this->config->get('config_image_additional_width'), $this->config->get('config_image_additional_height'))
311: ];
312: }
313: }
314:
315: if ($this->customer->isLogged() || !$this->config->get('config_customer_price')) {
316: $data['price'] = $this->currency->format($this->tax->calculate($product_info['price'], $product_info['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']);
317: } else {
318: $data['price'] = false;
319: }
320:
321: if ((float)$product_info['special']) {
322: $data['special'] = $this->currency->format($this->tax->calculate($product_info['special'], $product_info['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']);
323: } else {
324: $data['special'] = false;
325: }
326:
327: if ($this->config->get('config_tax')) {
328: $data['tax'] = $this->currency->format((float)$product_info['special'] ? $product_info['special'] : $product_info['price'], $this->session->data['currency']);
329: } else {
330: $data['tax'] = false;
331: }
332:
333: $discounts = $this->model_catalog_product->getDiscounts($product_id);
334:
335: $data['discounts'] = [];
336:
337: if ($this->customer->isLogged() || !$this->config->get('config_customer_price')) {
338: foreach ($discounts as $discount) {
339: $data['discounts'][] = [
340: 'quantity' => $discount['quantity'],
341: 'price' => $this->currency->format($this->tax->calculate($discount['price'], $product_info['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency'])
342: ];
343: }
344: }
345:
346: $data['options'] = [];
347:
348: // Check if product is variant
349: if ($product_info['master_id']) {
350: $master_id = (int)$product_info['master_id'];
351: } else {
352: $master_id = (int)$this->request->get['product_id'];
353: }
354:
355: $product_options = $this->model_catalog_product->getOptions($master_id);
356:
357: foreach ($product_options as $option) {
358: if ((int)$this->request->get['product_id'] && !isset($product_info['override']['variant'][$option['product_option_id']])) {
359: $product_option_value_data = [];
360:
361: foreach ($option['product_option_value'] as $option_value) {
362: if (!$option_value['subtract'] || ($option_value['quantity'] > 0)) {
363: if ((($this->config->get('config_customer_price') && $this->customer->isLogged()) || !$this->config->get('config_customer_price')) && (float)$option_value['price']) {
364: $price = $this->currency->format($this->tax->calculate($option_value['price'], $product_info['tax_class_id'], $this->config->get('config_tax') ? 'P' : false), $this->session->data['currency']);
365: } else {
366: $price = false;
367: }
368:
369: if ($option_value['image'] && is_file(DIR_IMAGE . html_entity_decode($option_value['image'], ENT_QUOTES, 'UTF-8'))) {
370: $image = $option_value['image'];
371: } else {
372: $image = '';
373: }
374:
375: $product_option_value_data[] = [
376: 'product_option_value_id' => $option_value['product_option_value_id'],
377: 'option_value_id' => $option_value['option_value_id'],
378: 'name' => $option_value['name'],
379: 'image' => $this->model_tool_image->resize($image, 50, 50),
380: 'price' => $price,
381: 'price_prefix' => $option_value['price_prefix']
382: ];
383: }
384: }
385:
386: $data['options'][] = [
387: 'product_option_id' => $option['product_option_id'],
388: 'product_option_value' => $product_option_value_data,
389: 'option_id' => $option['option_id'],
390: 'name' => $option['name'],
391: 'type' => $option['type'],
392: 'value' => $option['value'],
393: 'required' => $option['required']
394: ];
395: }
396: }
397:
398: // Subscriptions
399: $data['subscription_plans'] = [];
400:
401: $results = $this->model_catalog_product->getSubscriptions($product_id);
402:
403: foreach ($results as $result) {
404: $description = '';
405:
406: if ($result['trial_status']) {
407: $trial_price = $this->currency->format($this->tax->calculate($result['trial_price'], $product_info['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']);
408: $trial_cycle = $result['trial_cycle'];
409: $trial_frequency = $this->language->get('text_' . $result['trial_frequency']);
410: $trial_duration = $result['trial_duration'];
411:
412: $description .= sprintf($this->language->get('text_subscription_trial'), $trial_price, $trial_cycle, $trial_frequency, $trial_duration);
413: }
414:
415: $price = $this->currency->format($this->tax->calculate($result['price'], $product_info['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']);
416: $cycle = $result['cycle'];
417: $frequency = $this->language->get('text_' . $result['frequency']);
418: $duration = $result['duration'];
419:
420: if ($duration) {
421: $description .= sprintf($this->language->get('text_subscription_duration'), $price, $cycle, $frequency, $duration);
422: } else {
423: $description .= sprintf($this->language->get('text_subscription_cancel'), $price, $cycle, $frequency);
424: }
425:
426: $data['subscription_plans'][] = [
427: 'subscription_plan_id' => $result['subscription_plan_id'],
428: 'name' => $result['name'],
429: 'description' => $description
430: ];
431: }
432:
433: if ($product_info['minimum']) {
434: $data['minimum'] = $product_info['minimum'];
435: } else {
436: $data['minimum'] = 1;
437: }
438:
439: $data['share'] = $this->url->link('product/product', 'language=' . $this->config->get('config_language') . '&product_id=' . (int)$this->request->get['product_id']);
440:
441: $data['attribute_groups'] = $this->model_catalog_product->getAttributes($product_id);
442:
443: $data['products'] = [];
444:
445: $results = $this->model_catalog_product->getRelated($product_id);
446:
447: foreach ($results as $result) {
448: $description = trim(strip_tags(html_entity_decode($result['description'], ENT_QUOTES, 'UTF-8')));
449:
450: if (oc_strlen($description) > $this->config->get('config_product_description_length')) {
451: $description = oc_substr($description, 0, $this->config->get('config_product_description_length')) . '..';
452: }
453:
454: if ($result['image'] && is_file(DIR_IMAGE . html_entity_decode($result['image'], ENT_QUOTES, 'UTF-8'))) {
455: $image = $result['image'];
456: } else {
457: $image = 'placeholder.png';
458: }
459:
460: if ($this->customer->isLogged() || !$this->config->get('config_customer_price')) {
461: $price = $this->currency->format($this->tax->calculate($result['price'], $result['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']);
462: } else {
463: $price = false;
464: }
465:
466: if ((float)$result['special']) {
467: $special = $this->currency->format($this->tax->calculate($result['special'], $result['tax_class_id'], $this->config->get('config_tax')), $this->session->data['currency']);
468: } else {
469: $special = false;
470: }
471:
472: if ($this->config->get('config_tax')) {
473: $tax = $this->currency->format((float)$result['special'] ? $result['special'] : $result['price'], $this->session->data['currency']);
474: } else {
475: $tax = false;
476: }
477:
478: $product_data = [
479: 'product_id' => $result['product_id'],
480: 'thumb' => $this->model_tool_image->resize($image, $this->config->get('config_image_related_width'), $this->config->get('config_image_related_height')),
481: 'name' => $result['name'],
482: 'description' => $description,
483: 'price' => $price,
484: 'special' => $special,
485: 'tax' => $tax,
486: 'minimum' => $result['minimum'] > 0 ? $result['minimum'] : 1,
487: 'rating' => $result['rating'],
488: 'href' => $this->url->link('product/product', 'language=' . $this->config->get('config_language') . '&product_id=' . $result['product_id'])
489: ];
490:
491: $data['products'][] = $this->load->controller('product/thumb', $product_data);
492: }
493:
494: $data['tags'] = [];
495:
496: if ($product_info['tag']) {
497: $tags = explode(',', $product_info['tag']);
498:
499: foreach ($tags as $tag) {
500: $data['tags'][] = [
501: 'tag' => trim($tag),
502: 'href' => $this->url->link('product/search', 'language=' . $this->config->get('config_language') . '&tag=' . trim($tag))
503: ];
504: }
505: }
506:
507: if ($this->config->get('config_product_report_status')) {
508: $this->model_catalog_product->addReport($this->request->get['product_id'], $this->request->server['REMOTE_ADDR']);
509: }
510:
511: $data['language'] = $this->config->get('config_language');
512:
513: $data['column_left'] = $this->load->controller('common/column_left');
514: $data['column_right'] = $this->load->controller('common/column_right');
515: $data['content_top'] = $this->load->controller('common/content_top');
516: $data['content_bottom'] = $this->load->controller('common/content_bottom');
517: $data['footer'] = $this->load->controller('common/footer');
518: $data['header'] = $this->load->controller('common/header');
519:
520: $this->response->setOutput($this->load->view('product/product', $data));
521: } else {
522: return new \Opencart\System\Engine\Action('error/not_found');
523: }
524:
525: return null;
526: }
527: }
528: