Оценок: 6
4.356
Закладки (Wishlist) без регистрации opencart 2.x-3.x

Закладки без регистрации opencart 2-3

Обновления:
Показать
02.07.2020 - Статья переписана, много изменений
06.07.2020 - Файл: /catalog/controller/account/wishlist.php, вместо:
$this->session->data['wishlist'] = '';
Нужно:
$this->session->data['wishlist'] = array();
03.08.2020 - Файл: /system/library/cart/customer.php, изменения в получении закладок, а так же добавил перенос закладок при регистрации

Изначально в opencart есть кнопки добавления закладок в модулях, на странице товара, в категориях и др. Нажимаем на кнопку, товар добавляется в закладки, это отображается в header. Закладки могут просмотреть только зарегистрированные пользователи

Что будет сделано (понадобится примерно 20 минут):

  1. Просмотр и удаление закладок без регистрации
  2. Проверка наличия товара в закладках с присвоением отдельного класса
  3. Сохранение закладок при авторизации

Убираем проверку авторизации

Сначала получим работающий список закладок без регистрации нужно будет внести изменения в контроллер - /catalog/controller/account/wishlist.php

В самом начале файла комментируем строки (должно получиться так):

/*if (!$this->customer->isLogged()) {
	$this->session->data['redirect'] = $this->url->link('account/wishlist', '', true);
	$this->response->redirect($this->url->link('account/login', '', true));
}*/

Возможность удаления из закладок

Чуть дальше находим:

// Remove Wishlist
$this->model_account_wishlist->deleteWishlist($this->request->get['remove']);

Здесь добавим проверку - авторизован ли посетитель, если нет возьмем из сессии список закладок, кроме товара из запроса на удаление, почистим закладки и запишем обратно получившийся результат:

if ($this->customer->isLogged()) {
	$this->model_account_wishlist->deleteWishlist($this->request->get['remove']);
} else {
	$prods = $this->session->data['wishlist'];
	$this->session->data['wishlist'] = array();
	foreach ($prods as $prod) if ($prod != $this->request->get['remove']) {
		$this->session->data['wishlist'][] = $prod;
	}
}

Далее необходимо сделать еще одну правку здесь, а также в /catalog/controller/common/header.php

В зависимости от того, нужно ли отображение наличия каждого товара в закладках код будет отличаться. Если необходимо только открыть закладки всем, получение списка закладок можно оставить как есть - у зарегистрированных пользователей за это отвечает модель /catalog/model/account/wishlist.php, для гостей - сессия. Если же мы хотим показать, что товар есть в закладках, то в таком варианте придется делать много лишних запросов к БД (как минимум два - для header и для контроллера товара, или категории). Поэтому лучше будет получение закладок перенести в библиотеки и обойтись одним запросом.

Я напишу код для первого случая, затем для второго, берем один из вариантов.

Вариант 1. Получение товаров из закладок. Без проверки наличия товара в закладках

Открываем контроллер /catalog/controller/account/wishlist.php. Здесь все по аналогии с удалением, работаем с закладками в сессии. Находим:

$results = $this->model_account_wishlist->getWishlist();

Меняем на следующий код:

$results = array();
if ($this->customer->isLogged()) {
	$results = $this->model_account_wishlist->getWishlist();
} else if (!empty($this->session->data['wishlist'])) {
	$prods = $this->session->data['wishlist'];
	foreach ($prods as $prod) {
		$results[]['product_id'] = $prod;
	}
}

В контроллере /catalog/controller/common/header.php Находим:

$data['text_wishlist'] = sprintf($this->language->get('text_wishlist'), (isset($this->session->data['wishlist']) ? count($this->session->data['wishlist']) : 0));

Меняем на:

$data['text_wishlist'] = sprintf($this->language->get('text_wishlist'), (!empty($this->session->data['wishlist']) ? count($this->session->data['wishlist']) : 0));

Вариант 2. Закладки для гостей с проверкой наличия товара в закладках

Изменения в библиотеке customer.php

Здесь первое что сделаем, внесем изменения в класс Customer: /system/library/cart/customer.php, это позволит делать один запрос для получения закладок во всех контроллерах, а заодно посчитаем количество товаров по результату этого запроса, а не отдельным.

Находим:

private $address_id;

Добавляем:

private $w_list;

Находим:

if (isset($this->session->data['customer_id'])) {
	***********
	if ($customer_query->num_rows) {
		***********
	} else {
		$this->logout();
	}
}

Добавим получение закладок для авторизованного пользователя и через "else" для гостя (изменено 03.08.20):

if (isset($this->session->data['customer_id'])) {
	***********
	if ($customer_query->num_rows) {
		***********
		$w_list_q = $this->db->query("SELECT * FROM " . DB_PREFIX . "customer_wishlist WHERE customer_id = '" . (int)$this->customer_id . "'");
		$this->w_list = $w_list_q->rows;
	} else {
		$this->logout();
	}
} else {
	if (!empty($this->session->data['wishlist'])) {
		$this->w_list = array();
		foreach ($this->session->data['wishlist'] as $prod_id) {
			$this->w_list[] = array(
				'product_id' => $prod_id
			);
		}
	} else {
			$this->w_list = array();
	}
}

В результате получим что-то такое (кликабельно):

Функция после изменений

Далее находим:

$this->address_id = '';

Добавляем (это очистит закладки при выходе пользователя из аккаунта):

$this->w_list = array();

Находим:

public function getRewardPoints() {
	******
}

Добавляем после этой функции:

public function getWishlist() {
	return $this->w_list;
}
public function getTotalWishlist() {
	return count($this->w_list);
}

Здесь все. Теперь в любом контроллере можем использовать (кроме функций добавления и удаления закладок):

$this->customer->getWishlist();
$this->customer->getTotalWishlist();

Изменения в header

Открываем контроллер: catalog/controller/common/header.php. Здесь находим:

// Wishlist
if ($this->customer->isLogged()) {
	$this->load->model('account/wishlist');
	$data['text_wishlist'] = sprintf($this->language->get('text_wishlist'), $this->model_account_wishlist->getTotalWishlist());
} else {
	$data['text_wishlist'] = sprintf($this->language->get('text_wishlist'), (isset($this->session->data['wishlist']) ? count($this->session->data['wishlist']) : 0));
}

И меняем на следующее:

// Wishlist
$data['text_wishlist'] = sprintf($this->language->get('text_wishlist'), $this->customer->getTotalWishlist());

Получение товаров из закладок

/catalog/controller/account/wishlist.php

Находим:

$results = $this->model_account_wishlist->getWishlist();

Меняем на следующий код:

$results = array();
$results = $this->customer->getWishlist();

Показать наличие отдельного товара в закладках

На примере страницы товара сделаем проверку наличия товара в закладках и если он там есть - поменяем цвет кнопки.

1. Изменения в контроллере товара: /catalog/controller/product/product.php

Для начала нужно будет получить закладки, затем добавить в массив $data некий класс, если текущий товар там обнаружился. Для этого где то в контроллере, например после:

$data['description'] = html_entity_decode($product_info['description'], ENT_QUOTES, 'UTF-8');

Добавим:

$w_list = array_column($this->customer->getWishlist(), 'product_id');
if (in_array($this->request->get['product_id'], $w_list)) {
	$data['wl_class'] = 'wl-add';
} else {
	$data['wl_class'] = '';
}

Это все изменения в контроллере, если нужно проверить наличие только текущего товара. Еще Можно сделать то же самое для похожих. Для этого перед:

$data['products'][] = array(

Добавляем:

if (in_array($result['product_id'], $w_list)) {
	$wl_class = 'wl-add';
} else {
	$wl_class = '';
}

И теперь в массиве $data['products'][] можно добавить:

'wl_class' => $wl_class,

Далее переходим к шаблону - /catalog/view/theme/default/template/product/product.tpl (product.twig для oc 3.x) и добавляем класс к нужным кнопкам, после чего останется задать этому классу некие свойства в CSS. Для конопки текщего товара это будет выглядеть так (изменения в атрибуте "class"):

<button type="button" data-toggle="tooltip" class="btn btn-default <?php echo $wl_class; ?>" title="<?php echo $button_wishlist; ?>" onclick="wishlist.add('<?php echo $product_id; ?>');"><i class="fa fa-heart"></i></button>

Аналогично для кнопки в похожих товарах:

<button type="button" data-toggle="tooltip" title="<?php echo $button_wishlist; ?>" class="<?php echo $product['wl_class']; ?>" onclick="wishlist.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-heart"></i></button>

Удаление из закладок по кнопке добавления (в товаре)

Т.е. если товара нет в закладках - добавляем, если он там уже есть - удаляем.

Здесь есть несколько вариантов решения, я выбрал самый простой (как мне кажется). Для начала правим /catalog/controller/account/wishlist.php. Находим функцию add:

public function add() {
	*******
}

И заменим ее полностью:

	public function add() {
		$this->load->language('account/wishlist');

		$json = array();

		if (isset($this->request->post['product_id'])) {
			$product_id = $this->request->post['product_id'];
		} else {
			$product_id = 0;
		}
		$w_list = array_column($this->customer->getWishlist(), 'product_id');
		if (!in_array($product_id, $w_list)) {
			//ADD
			$this->load->model('catalog/product');
			$product_info = $this->model_catalog_product->getProduct($product_id);
			if ($product_info) {
				if ($this->customer->isLogged()) {
					// Edit customers cart
					$this->load->model('account/wishlist');
					$this->model_account_wishlist->addWishlist($this->request->post['product_id']);
					$json['success'] = sprintf($this->language->get('text_success'), $this->url->link('product/product', 'product_id=' . (int)$this->request->post['product_id']), $product_info['name'], $this->url->link('account/wishlist'));
					$json['total'] = sprintf($this->language->get('text_wishlist'), $this->model_account_wishlist->getTotalWishlist());
				} else {
					if (!isset($this->session->data['wishlist'])) {
						$this->session->data['wishlist'] = array();
					}
					$this->session->data['wishlist'][] = $this->request->post['product_id'];
					$this->session->data['wishlist'] = array_unique($this->session->data['wishlist']);
					$json['success'] = sprintf($this->language->get('text_success'), $this->url->link('product/product', 'product_id=' . (int)$this->request->post['product_id']), $product_info['name'], $this->url->link('account/wishlist'));
					$json['total'] = sprintf($this->language->get('text_wishlist'), (isset($this->session->data['wishlist']) ? count($this->session->data['wishlist']) : 0));
				}
			}
		} else {
			//REM
			if ($this->customer->isLogged()) {
				$this->load->model('account/wishlist');
				$this->model_account_wishlist->deleteWishlist($product_id);
				$json['total'] = sprintf($this->language->get('text_wishlist'), $this->model_account_wishlist->getTotalWishlist());
			} else {
				$prods = $this->session->data['wishlist'];
				$this->session->data['wishlist'] = array();
				foreach ($prods as $prod) if ($prod != $product_id) {
					$this->session->data['wishlist'][] = $prod;
				}
				$json['total'] = sprintf($this->language->get('text_wishlist'), (!empty($this->session->data['wishlist']) ? count($this->session->data['wishlist']) : 0));
			}
			$json['success'] = sprintf($this->language->get('text_rem'), $this->url->link('account/wishlist'));
		}

		$this->response->addHeader('Content-Type: application/json');
		$this->response->setOutput(json_encode($json));
	}

Теперь нужно открыть перевод (catalog/language/ru-ru/account/wishlist.php) и добавить:

$_['text_rem']  = 'Вы удалили товар из <a href="%s">Закладок</a>!';

При удалении я не стал делать получение названия товара, что бы не создавать лишнюю нагрузку. Аналогичную запись желательно добавить и в соответствующие файлы для других языков.

Вид кнопки в зависимости от наличия товара в закладках

Следующий шаг (да, это еще не все) - изменение кнопки после добавления/удаления товара из закладок. Для этого придется переделать саму кнопку и скрипт за нее отвечающий (или добавить новый).

У самой кнопки удаляем событие onclick и добавляем какой-нибудь класс, например: "add-to-wl". Так же нужно добавить атрибут "data-id" c ID товара. Получим:

<button type="button" data-toggle="tooltip" class="btn btn-default add-to-wl <?php echo $wl_class; ?>" title="<?php echo $button_wishlist; ?>" data-id="<?php echo $product_id; ?>"><i class="fa fa-heart"></i></button>

Теперь JS. В /catalog/view/javascript/common.js, после:

$(document).ready(function() {

Добавляем функцию:

	$('.add-to-wl').click(function() {
		var product_id = $(this).data('id');
		$.ajax({
			url: 'index.php?route=account/wishlist/add',
			type: 'post',
			data: 'product_id=' + product_id,
			dataType: 'json',
			success: function(json) {
				$('.alert-dismissible').remove();
				if (json['redirect']) {
					location = json['redirect'];
				}
				if (json['success']) {
					$('.add-to-wl[data-id=\''+product_id+'\']').toggleClass('wl-add');
					$('#content').parent().before('<div class="alert alert-success alert-dismissible"><i class="fa fa-check-circle"></i> ' + json['success'] + ' <button type="button" class="close" data-dismiss="alert">×</button></div>');
				}
				$('#wishlist-total span').html(json['total']);
				$('#wishlist-total').attr('title', json['total']);
				$('html, body').animate({ scrollTop: 0 }, 'slow');
			},
			error: function(xhr, ajaxOptions, thrownError) {
				alert(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
			}
		});
	});

Сохранение закладок при регистрации пользователя

В опенкарт есть один странный момент, связанный с переносом закладок между гостем и авторизованным пользователем. Если у пользователя уже есть аккаунт, он авторизуется и если он как гость добавлял товары в закладки, они замечательно переносятся. А когда пользователь создает аккаунт, сразу после идет авторизация, но в закладках пусто. Почему?

В контроллере авторизации (/catalog/controller/account/login.php) есть проверка на наличие товаров в закладках и если они есть - перенос. Код там вот такой:

// Wishlist
			if (isset($this->session->data['wishlist']) && is_array($this->session->data['wishlist'])) {
				$this->load->model('account/wishlist');
				foreach ($this->session->data['wishlist'] as $key => $product_id) {
					$this->model_account_wishlist->addWishlist($product_id);
					unset($this->session->data['wishlist'][$key]);
				}
			}

Что касается регистрации, то в соответствующем контроллере про закладки видимо забыли, исправим. для этого внесем небольшое изменение в /catalog/controller/account/register.php

Здесь найдем:

$this->customer->login($this->request->post['email'], $this->request->post['password']);

И после этой строки добавим приведенный выше код из login.php. Вот и все.

Изменение уведомления при добавлении

Если делали пункт "Удаление из закладок по кнопке добавления" - это не нужно

В стандартном уведомлении при добавлении закладок говорится о том, что необходимо выполнить вход или зарегистрироваться для сохранения закладок, но по сути это уже не нужно. Есть два варианта для изменений - можно в языковом файле изменить текст, или в контроллере закладок вывести одинаковый текст, тот что для зарегистрированных пользователей - для всех

Языковой файл можно найти по пути /catalog/language/ru-ru/account/wishlist.php, здесь нужно изменить 'text_login'.

Что касается правок в контроллере (все тот же wishlist.php), здесь нужно найти функцию add() и в ней проверку на регистрацию.

if ($this->customer->isLogged()) {
......
$json['success'] = sprintf($this->language->get('text_success'), $this->url->link('product/product', 'product_id=' . (int)$this->request->post['product_id']), $product_info['name'], $this->url->link('account/wishlist'));
......
} else {
......
$json['success'] = sprintf($this->language->get('text_login'), $this->url->link('account/login', '', true), $this->url->link('account/register', '', true), $this->url->link('product/product', 'product_id=' . (int)$this->request->post['product_id']), $product_info['name'], $this->url->link('account/wishlist'));
......
}

Здесь нужно код для второго 'success' (для гостей) заменить первым (для зарегистрированных).

Оценок: 6

Комментарии (87)

  1. Интересно а тема еще актуальна?))))
    пытаюсь это все вытащить еще и в catalog.php для карточек, но возникает НО))) страница выпадает в 200 ошибку, подскажите пожалуйста

    Показать код
    
    $wishlist_model = $this->model_account_wishlist;
    
    				$w_list = array_column($wishlist_model->getWishlist(), 'product_id');
    
    				$data['products'][] = array(
    					'product_id'  => $result['product_id'],
    					'thumb'       => $image,
    					'name'        => $result['name'],
    					'description' => utf8_substr(strip_tags(html_entity_decode($result['description'], ENT_QUOTES, 'UTF-8')), 0, $this->config->get($this->config->get('config_theme') . '_product_description_length')) . '..',
    					'price'       => $price,
    					'special'     => $special,
    					'tax'         => $tax,
    					'minimum'     => $result['minimum'] > 0 ? $result['minimum'] : 1,
    					'rating'      => $result['rating'],
    					'href'        => $this->url->link('product/product', 'path=' . $this->request->get['path'] . '&product_id=' . $result['product_id'] . $url)
    				);
    
    				// if (in_array($this->request->get['product_id'], $w_list)) {
    				// 	$data['products'][count($data['products'])-1]['wl_class'] = 'active';
    				// } else {
    				// 	$data['products'][count($data['products'])-1]['wl_class'] = '';
    				// }
    
    вот что пытаемся сделать

    Станислав 18 февраля 2024, 14:37 0
    • Посмотрите в статье эту часть — «Еще Можно сделать то же самое для похожих...»
      Все аналогично модулям, категориям.
      До foreach нужно получить товары в закладках (не тянуть их к каждому товару отдельно), а уже внутри проверять наличие для конкретного товара и присваивать класс.

      Показать код
      $w_list = array_column($this->customer->getWishlist(), 'product_id');
      foreach () {
      	if (in_array($result['product_id'], $w_list)) {
      		$wl_class = 'wl-add';
      	} else {
      		$wl_class = '';
      	}
      	$data['products'][] = array(
      		'product_id'  => $result['product_id'],
      		'wl_class' => $wl_class,
      
      И кстати, модель wishlist подключать не нужно для этого

      Владимир 18 февраля 2024, 15:18 0
      • Спасибо большое, заработало, делал ошибку, при выводе.

        Станислав 18 февраля 2024, 16:59 0
    • По какой то причине toggleClass не переключал классы при нажатии на кнопку добавления/удаления.
      Решил так:
      Заменить

      Показать код
      $(this).toggleClass('wl-add');
      на
      Показать код
      
      					if (json['success'] == 'Вы удалили товар из Закладок!'){
      						$(this).removeClass('wl-add');
      					} else {
      						$(this).addClass('wl-add');
      					}
      Не самое элегантное решение, но пойдет.

      Валерий 07 октября 2021, 20:32 0
      • Выяснил, что из-за $.ajax({ перестает корректно работать $(this)
        Гуглю решение, чтобы this родительской функции работал в вложенной функции )

        Валерий 07 октября 2021, 20:43 0
        • Ой… не будет. Внутри json уже другая функция, только передача внутрь через переменную. Исправил

          Владимир 07 октября 2021, 20:54 0
          • Вот, решил всё таки.
            Надо сохранить this в переменную self и потом использовать ее в дочерней функции.

            Показать код
            $('.add-to-wl').click(function(){
            		var product_id = $(this).attr('data');
            		var self = $(this);
            		$.ajax({
            			url: 'index.php?route=account/wishlist/add',
            			type: 'post',
            			data: 'product_id=' + product_id,
            			dataType: 'json',
            			success: function(json) {
            				$('.alert-dismissible').remove();
            				if (json['redirect']) {
            					location = json['redirect'];
            				}
            				if (json['success']) {
            					self.toggleClass('wl-add');
            					$('#content').parent().before('<div class="alert alert-success alert-dismissible"><i class="fa fa-check-circle"></i> ' + json['success'] + ' <button type="button" class="close" data-dismiss="alert">×</button></div>');
            					
            				}
            				$('#wishlist-total span').html(json['total']);
            				$('#wishlist-total').attr('title', json['total']);
            				$('html, body').animate({ scrollTop: 0 }, 'slow');
            			},
            			error: function(xhr, ajaxOptions, thrownError) {
            			alert(thrownError + "\r\n" + xhr.statusText + "\r\n" + xhr.responseText);
            			}
            		});
            	});

            Валерий 07 октября 2021, 22:13 +1
        • Какое счастье, что я нашел эту статью, очень выручил! Обязательно задоначу через пару дней!
          Оформи как ocmod и продавай!

          Валерий 07 октября 2021, 19:46 0
          • Добавил в закладки браузера статью по закладкам opencart ))

            webair 11 августа 2021, 20:01 0
            • Для гостей не работает, для зарегистрированных работает. Opencart Version 3.0.3.6 (rs.1). Почему то в сессии не хранятся добавленные товары, там просто пусто

              zoraideare 01 декабря 2020, 23:04 0
              • А нет, всё нормально. Ребята, если у вас сторонняя тема, то посмотрите в кеше модификаторов storage/modification/catalog/controller/account/wishlist.php. Сторонняя тема или другие модули могут немного изменить ваш вывод.

                zoraideare 02 декабря 2020, 09:32 0
              • Установил чистый ocStore 2.3.0.2 и сделал как описано выше. В итоге — всё так же просит зарегистрироваться. Печально, пойду искать другой вариант((

                Прохожий 24 ноября 2020, 01:52 0
                • Самое начало, /catalog/controller/account/wishlist.php, проверка на залогинен ли пользователь. Если закомментировать — уже на этом этапе ничего просить не будет.
                  ps. не забываем чистить кэш модификаторов.

                  Владимир 24 ноября 2020, 01:55 0
                  • Прошу прощения, ступил. Делаю правки на одном сервере а кеш зачищаю на другом)))
                    Врё работает! Автору огромное спасибо.

                    Прохожий 24 ноября 2020, 03:28 0
                  • Просто отличная статья! Есть ли что-то похожее по сравнению товаров, что бы сделать кнопку добавить/удалить из сравнения, и проверку на то есть ли товар в сравнении

                    Jemik 29 октября 2020, 14:16 0
                    • У меня вопрос на счёт проверки наличия товара в закладках, если он там есть — меняем цвет кнопки.
                      Как это сделать для страницы каталога? Я взял ваш код для блока «Похожие», но он в catalog.php не работает.
                      PS: код для product.php работает прекрасно.

                      Овечка 16 августа 2020, 13:41 0
                      • Я уточню свой вопрос)))
                        У меня товар удаляется и добавляется на странице каталога, но класс к кнопке не добавляется.

                        Овечка 16 августа 2020, 13:55 0
                        • Простите!!! Вопрос отпал. Я ступил!!! Не обновил модификатор, у меня в кэше контроллер catalog.php был

                          Овечка 16 августа 2020, 14:07 0
                        Надежный хостинг VPS серверов
                        • Свои ISO образы
                        • VDS с оплатой раз и навсегда
                        • Аренда VDS на любой срок, с оплатой по дням
                        • Большое разнообразие конфигураций
                        • Дата-центры в ЕС и России
                        + скидка 10%