Интерактивная корзина на основе AJAX, PHP и JQuery

home

«Какая корзина? И почему вдруг корзина?» – возможно удивитесь вы, прочитав заголовок. Так вот, корзина самая обычная – та, которую все мы используем, покупая что-нибудь в интернет-магазине. А опубликовать статью о ее создании, я решила по одной, единственной причине – не смогла устоять перед замечательным и красивым решением.

Не верите? можете посмотреть результат. А если вам интересно как это делается, читайте далее.

Вступление

В этой статье, мы собираемся создать корзину, работающую на основе технологии Ajax. Все товары будут записываться в базу данных MySQL, данные будут обрабатываться с помощью PHP.

JQuery, будет запускать Ajax на странице, кроме этого, плагин simpletip, добавит всему процессу интерактивности.

Итак, давайте начнем, скачайте демо-файлы, и начинайте чтение.

Шаг 1 – База данных MySQL

Если вы хотите получить рабочую демо-версию, вам понадобится выполнить следующий SQL-запрос в панели управления базой данных (то есть в phpMyAdmin). Этот запрос создаст таблицу, и внесет несколько продуктов. Этот код запроса, также доступен в файле table.sql, в демо-файлах.

table.sql

CREATE TABLE IF NOT EXISTS `internet_shop` (
  `id` int(6) NOT NULL auto_increment,
  `img` varchar(32) collate utf8_unicode_ci NOT NULL default '',
  `name` varchar(64) collate utf8_unicode_ci NOT NULL default '',
  `description` text collate utf8_unicode_ci NOT NULL,
  `price` double NOT NULL default '0',
  PRIMARY KEY  (`id`),
  UNIQUE KEY `img` (`img`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=7 ;

INSERT INTO `internet_shop` VALUES(1, 'iPod.png', 'iPod', 'The original and popular iPod.', 200);
INSERT INTO `internet_shop` VALUES(2, 'iMac.png', 'iMac', 'The iMac computer.', 1200);
INSERT INTO `internet_shop` VALUES(3, 'iPhone.png', 'iPhone', 'This is the new iPhone.', 400);
INSERT INTO `internet_shop` VALUES(4, 'iPod-Shuffle.png', 'iPod Shuffle', 'The new iPod shuffle.', 49);
INSERT INTO `internet_shop` VALUES(5, 'iPod-Nano.png', 'iPod Nano', 'The new iPod Nano.', 99);
INSERT INTO `internet_shop` VALUES(6, 'Apple-TV.png', 'Apple TV', 'The new Apple TV. Buy it now!', 300);

После этого, вам нужно заполнить данные вашей учетной записи MySQL, в файле connect.php.

Шаг 2 – XHTML

Сначала мы создадим основную разметку.

demo.php

<div id="main-container"> <!-- основной контент -->
<div class="tutorialzine">	<!-- заголовок -->

<h1>Корзина</h1>
<h3>Лучшие товары, по лучшим ценам</h3>
</div>

<div class="container">  <!-- первая секция - товары -->

<span class="top-label">
    <span class="label-txt">Товары</span>  <!-- название секции -->
</span>

<div class="content-area">

<div class="content drag-desired">

// php-код, который генерирует товары

<div class="clear"></div>   <!-- чистка потока -->
</div>
</div>

<div class="bottom-container-border">	<!-- нижняя часть секции -->
</div>

</div>	<!-- окончание секции товаров -->

<div class="container">	<!-- вторая секция- корзина-->

<span class="top-label">
   <span class="label-txt">Корзина</span>	<!-- название секции -->
</span>

<div class="content-area">
  <div class="content drop-here">
         <div id="cart-icon">
               <img src="img/Shoppingcart_128x128.png" alt="shopping cart" class="pngfix" width="128" height="128" />	<!-- использование класса pngfix-->

                <img src="img/ajax_load_2.gif" alt="loading.." id="ajax-loader" width="16" height="16" />	<!-- прелоадер - спрятан по умолчанию, и отображается когда работает AJAX -->
    </div>

<form name="checkoutForm" method="post" action="order.php">	<!-- форма -->
    <div id="item-list">	<!-- в этот блок мы вставим все товары корзины -->

     </div>
</form>	<!-- конец формы -->

<div class="clear"></div>	<!-- чистка потока -->

<div id="total"></div>	<!-- здесь расположена общая сумма -->

<div class="clear"></div>	<!-- чистка потока -->

<a href="" onclick="document.forms.checkoutForm.submit(); return false;" class="button">
	Оформить
</a>	<!-- кнопка отправки заказа, спрятана по умолчанию. обратите внимание на атрибут onclick -->

</div>
</div>

<div class="bottom-container-border">	<!-- нижняя часть секции -->
</div>

</div><!-- конец основного контейнера -->

Как вы могли заметить, из вышеприведенного кода, мы расположили наш контент в двух основных секциях, полностью идентичных с точки зрения XHTML-разметки. В первой колонке мы отображаем все наши продукты, вторая колонка выступает в качестве корзины.

Ниже, вы можете увидеть детальное представление структуры нашей секции товаров.

scheme

Товары, генерируются с помощью нашего PHP-кода, как можно увидеть в строке 18. Мы разберем это подробнее, через несколько минут. Теперь, давайте взглянем, как мы обработаем XHTML-разметку, для получения финального дизайна.

Шаг 3 – CSS

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

demo.css

body,h1,h2,h3,p,td,quote,small,form,input,ul,li,ol,label{
/* сброс первоначальных стилей, для совместимости браузеров */
	margin:0px;
	padding:0px;
	font-family:Arial, Helvetica, sans-serif;
}

body{
	color:#555555;
	font-size:13px;
	background-color:#282828;
}

.clear{	/*  clear-fix хак для чистки потока от флоатов */
	clear:both;
}

#main-container{	/* это основной контейнер, содержащий две секции */
	width:700px;
	margin:20px auto;
}

.container{	/* основной контейнер для секций контента - товаров и корзины */
	margin-bottom:40px;
}

.top-label{	/* внешний span, включающий в себя название секции*/
	background: url(img/label_bg.png) no-repeat;	/* отображение левой части label_bg.png - широкого изображения с закругленными краями */
	display:inline-block;
	margin-left:20px;
	position:relative;
	margin-bottom:-15px;	/* название секции прилегает к верхнему краю секции товаров*/
}

.label-txt{	/* внутренний span - обведен красной рамкой, на рисунке выше*/
	background: url(img/label_bg.png) no-repeat top right;	/* отображение правой части изображения label_bg.png */
	display:inline-block;
	font-size:10px;
	height:36px;
	margin-left:10px;	/* слева оставлено пустое пространство, чтобы отображить фон внешнего span'а */
	padding:12px 15px 0 5px;
	text-transform:uppercase;
}

.content-area{	/* Верхняя часть изображения с закругленными краями, смотрите на рисунке выше */
	background:url(img/container_top.png) no-repeat #fcfcfc;
	padding:15px 20px 0 20px;
}

.content{	/* общий отступ для обеих секций */
	padding:10px;
}

.drag-desired{	/* индивидуально назначенные свойства */
	background:url(img/drag_desired_label.png) no-repeat top right;
	padding:20px;
}

.drop-here{	/* не предназначено для других секций */
	background:url(img/drop_here_label.png) no-repeat top right;
}

.bottom-container-border{	/* нижняя часть закругленной картинки, которая завершает секцию */
	background:url(img/container_bottom.png) no-repeat;
	height:14px;
}

.product{	/* стили для товаров */
	border:2px solid #F5F5F5;
	float:left;
	margin:15px;
	padding:10px;
}

.product img{
	cursor:move;
}

p.descr{
	padding:5px 0;
}

small{
	display:block;
	margin-top:4px;
}

.tooltip{	/* тултипы, которые создаются с помощью плагина simpletip */
	position: absolute;
	top: 0;
	left: 0;
	z-index: 3;
	display: none;

	background-color:#666666;
	border:1px solid #666666;
	color:#fcfcfc;

	padding:10px;

	-moz-border-radius:12px;	/* закругленные углы */
	-khtml-border-radius: 12px;
	-webkit-border-radius: 12px;
	border-radius:12px;
}

Обратите внимание на класс tooltip. Он создается автоматически, плагином simpletip, но не имеет никаких стилей, по умолчанию. Вот почему, мы назначаем ему стиль здесь. Я использовал свойство border-radius, которое еще не поддерживается всеми браузерами, но не принесет сильного ущерба, тем, кто его не поддерживает.

Теперь, давайте взглянем на CSS-стили, для секции корзины.

#cart-icon{	/* div, который содержит иконку корзины */
	width:128px;
	float:left;
	position:relative;	/* устанавливаем относительное позиционирование, так, чтобы ajax-загрузчик позиционировался по отношению к div*/
}

#ajax-loader{
	position:absolute;	/* абсолютное позиционирование располагает элемент на странице, относительно его родительского элемента, которому назначено относительное позиционирование */
	top:0px;
	left:0px;
	visibility:hidden;
}

#item-list{	/* содержимое корзины будет расположено в этом блоке */
	float:left;
	width:490px;
	margin-left:20px;
	padding-top:15px;
}

a.remove,a.remove:visited{	/* Удаление ссылки */
	color:red;
	font-size:10px;
	text-transform:uppercase;
}

#total{	/* блок, с общей суммой */
	clear:both;
	float:right;
	font-size:10px;
	font-weight:bold;
	padding:10px 12px;
	text-transform:uppercase;
}

#item-list table{	/* каждый товар в корзине, позиционируется внутри блока item-list*/
	background-color:#F7F7F7;
	border:1px solid #EFEFEF;
	margin-top:5px;
	padding:4px;
}

a.button,a.button:visited{	/* Кнопка оформления заказа */
	display:none;

	height:29px;
	width:136px;

	padding-top:15px;
	margin:0 auto;
	overflow:hidden;

	color:white;
	font-size:12px;
	font-weight:bold;
	text-align:center;
	text-transform:uppercase;

	background:url(img/button.png) no-repeat center top;	/* отображаем только верхнюю часть фонового изображения */
}

a.button:hover{
	background-position:bottom;	/* при наведении, мы показываем нижнюю часть фоногового изображения */
	text-decoration:none;
}

/* Несколько менее интересных стилей */

a, a:visited {
	color:#00BBFF;
	text-decoration:none;
	outline:none;
}

a:hover{
	text-decoration:underline;
}

h1{
	font-size:28px;
	font-weight:bold;
	font-family:"Trebuchet MS",Arial, Helvetica, sans-serif;
}

h2{
	font-weight:normal;
	font-size:20px;

	color:#666666;
	text-indent:30px;
	margin:20px 0;
}

.tutorialzine h1{
	color:white;
	margin-bottom:10px;
	font-size:48px;
}

.tutorialzine h3{
	color:#F5F5F5;
	font-size:10px;
	font-weight:bold;
	margin-bottom:30px;
	text-transform:uppercase;
}

.tutorial-info{
	color:white;
	text-align:center;
	padding:10px;
	margin-top:-20px;
}

Любой разработчик скажет нам, что здесь мы кое-что упустили. Как вы, наверное, догадались – специальные процедуры лечения для IE6.

Лично я, планирую в скором времени прекратить поддержку IE6 во всех своих проектах – если бы не IE6, вышеприведенный код, был бы на четверть короче, и мне не пришлось бы тратить столько времени на его отладку.

Но, в любом случае, вот, что нам нужно, чтобы IE6 понимал, то, что мы от него хотим:

demo.php

<!--[if lt IE 7]>
<style type="text/css">
	.pngfix { behavior: url(pngfix/iepngfix.htc);}	/* this is a special htc file that fixes the IE6 transparency issues */
	.tooltip{width:200px;};	/* provide a default width for the tooltips */
</style>
<![endif]-->

Отлично. Теперь, давайте взглянем на окончательный вариант PHP.

cart

Шаг 4 – PHP

Мы используем PHP несколькими способами и в разных местах. Для начала, давайте посмотрим, как формируется список товаров на главной странице.

demo.php

<?php
	$result = mysql_query("SELECT * FROM internet_shop");	// выбираем все товары
		while($row=mysql_fetch_assoc($result))
		{
			echo '<div class="product"><img src="img/products/'.$row['img'].'" alt="'.htmlspecialchars($row['name']).'" width="128" height="128" class="pngfix" /></div>';

?>

Далее, в файле tips.php, мы снова используем PHP, для того чтобы передать в качестве параметра имя файла картинки, проверяя какой товар ассоциируется с этим изображением, и выводим подсказку в виде HTML. Это позднее, будет использоваться плагином simpletip.

ajax/tips.php

<?php
define('INCLUDE_CHECK',1);
require "../connect.php";

if(!$_POST['img']) die("Нет таких товаров!");

$img=mysql_real_escape_string(end(explode('/',$_POST['img'])));

$row=mysql_fetch_assoc(mysql_query("SELECT * FROM internet_shop WHERE img='".$img."'"));

if(!$row) die("Нет таких товаров!");

echo '<strong>'.$row['name'].'</strong>
<p class="descr">'.$row['description'].'</p>
<strong>price: $'.$row['price'].'</strong>
<small>Для того, чтобы купить, просто перетащите его в коризну</small>';

 ;?>

Кроме этого, мы используем PHP для получения данных, необходимых для добавления товаров в корзину. Разница в том, что в этот раз мы получаем данные в качестве JSON (javascript-объекта).

ajax/addtocart.php

<?php
define('INCLUDE_CHECK',1);
require "../connect.php";

if(!$_POST['img']) die("Нет таких товаров!");

$img=mysql_real_escape_string(end(explode('/',$_POST['img'])));
$row=mysql_fetch_assoc(mysql_query("SELECT * FROM internet_shop WHERE img='".$img."'"));

echo '{status:1,id:'.$row['id'].',price:'.$row['price'].',txt:\'

<table width="100%" id="table_'.$row['id'].'">
<tr>
<td width="60%">'.$row['name'].'</td>
<td width="10%">$'.$row['price'].'</td>
<td width="15%"><select name="'.$row['id'].'_cnt" id="'.$row['id'].'_cnt" onchange="change('.$row['id'].');">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option></slect>

</td>
<td width="15%"><a href="#" onclick="remove('.$row['id'].');return false;" class="remove">удалить</a></td>
</tr>
</table>\'}';

?>

Полученный объект имеет свойства status, id, price и txt. Все они используются AJAX-функциями, как вы скоро убедитесь.

Обратите внимание на то, что я заканчиваю каждую строку обратным слешем. Это сделано потому, что JavaScript не поддерживает множественные строчки.

И последний файл, в котором мы используем PHP – это order.php, который используется для обработки заказа. Сейчас, он просто выводит ваш заказ. Мы можете изменить его, включив форму обратной связи, функции платежных систем, или что-нибудь еще, что позволит превратить этот пример в функциональный онлайн-магазин.

order.php

<?php

define('INCLUDE_CHECK',1);
require "connect.php";

if(!$_POST)	// если данные не были отправлены в форму
{
	if($_SERVER['HTTP_REFERER'])	// редирект
	header('Location : '.$_SERVER['HTTP_REFERER']);
	exit;	// и выход
}

?>

<!-- XHTML код.. -->

<?php
$cnt = array();
$products = array();
$total = 0;
foreach($_POST as $key=>$value)
{
	$key=(int)str_replace('_cnt','',$key);
	$products[]=$key;	// запись ID товара в массив
	$cnt[$key]=$value;	// создание пары ключ-значение, где для каждого ID товара есть соответствующее значение количества товаров
 }

$result = mysql_query("SELECT * FROM internet_shop WHERE id IN(".join($products,',').")");	// выбор всех товаров с помощью функции IN()

if(!mysql_num_rows($result))	// товары не найдены
{
	echo '<h1>При оформлении заказа возникла ошибка!</h1>';
}
else
{
	echo '<h1>Ваш заказ:</h1>';
	while($row=mysql_fetch_assoc($result))
	{
		echo '<h2>'.$cnt[$row['id']].' x '.$row['name'].'</h2>';
		$total+=$cnt[$row['id']]*$row['price'];
	}

	echo '<h1>Всего: $'.$total.'</h1>';
}

?>

На этом, часть PHP закончена. Все, что нам осталось сделать, это добавить немного магии jQuery.

checkout

Шаг 5 – JQuery

Поскольку мы будем использовать jQuery в полном объеме, нам понадобится подключить библиотеку jQuery UI, так же, как и основную.

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js"></script>
<script type="text/javascript" src="simpletip/jquery.simpletip-1.3.1.pack.js"></script> <!-- плагин к jQuery simpletip -->
<script type="text/javascript" src="script.js"></script> <!-- наш script.js -->

Теперь, мы можем продолжить с нашим скриптом.

script.js

var purchased=new Array();	//массив, содержащий все товары, которые мы приобрели
var totalprice=0;	//цена

$(document).ready(function(){

$('.product').simpletip({	//используем плагин simpletip

	offset:[40,0],
	content:'<img style="margin:10px;" src="img/ajax_load.gif" alt="loading" />',	// контент по умолчанию
	onShow: function(){

		var param = this.getParent().find('img').attr('src');
		// fix для IE6
		if($.browser.msie && $.browser.version=='6.0')
		{
			param = this.getParent().find('img').attr('style').match(/src="([^"]+)"/);
			param = param[1];
		}

		// после того как отображена подсказка, загружаем файл tips.php и передаем название изображения в качестве параметра
		this.load('ajax/tips.php',{img:param});
	} 

});

$(".product img").draggable({	// разрешаем перетаскивание картинок товаров

containment: 'document',
opacity: 0.6,
revert: 'invalid',
helper: 'clone',
zIndex: 100

});

$("div.content.drop-here").droppable({	// разрешаем выгружать товары в корзину

		drop:
			function(e, ui)
			{
				var param = $(ui.draggable).attr('src');
				// IE6 fix
				if($.browser.msie && $.browser.version=='6.0')
				{
					param = $(ui.draggable).attr('style').match(/src="([^"]+)"/);
					param = param[1];
				}

				addlist(param);	// специальная функция addlist - смотрите ниже
			}

});

});

Основная идея в том, что мы используем атрибут изображения src, в качестве уникального ключа, который передается PHP. Каждый товар в базе данных имеет поле c именем файла, что позволяет нам найти нужный товар по его картинке.

Этот блок кода выполняется после того как страница полностью загрузилась, так, что мы уверены, что все элементы на странице инициализированы.

Ниже представлена вторая часть кода script.js.

function addlist(param)
{
	// функция addlist добавляет товар в корзину

	$.ajax({	// посылаем ajax-request в addtocart.php
	type: "POST",
	url: "ajax/addtocart.php",
	data: 'img='+encodeURIComponent(param),	// картинка товара в качестве параметра
	dataType: 'json',	// ждем json
	beforeSend: function(x){$('#ajax-loader').css('visibility','visible');},	// отображаем прелоадер
	success: function(msg){

		$('#ajax-loader').css('visibility','hidden');	// прячем прелоадер
		if(parseInt(msg.status)!=1)
		{
			return false;	// если обнаружена ошибка, возвращаем false
		}
		else
		{
			var check=false;
			var cnt = false;

			for(var i=0; i<purchased.length;i++)
			{
				if(purchased[i].id==msg.id)	// ищем, не покупали ли мы этот товар ранее
				{
					check=true;
					cnt=purchased[i].cnt;

					break;
				}
			}

			if(!cnt)	// если еще не покупали, или удалили из покупок, то вставляем в корзину
				$('#item-list').append(msg.txt);

			if(!check)	// если еще не купили, вставляем в массив покупок
			{
				purchased.push({id:msg.id,cnt:1,price:msg.price});
			}

			else	// иначе, если купили
			{
				if(cnt>=3) return false;	// больше 3 товаров

				purchased[i].cnt++;
				$('#'+msg.id+'_cnt').val(purchased[i].cnt);	// обновляем select box
			}

			totalprice+=msg.price;	// пересчитываем стоимость recalculate the price
			update_total();	// обновляем блок общей стоимости
		}

		$('.tooltip').hide();	// прячем подсказку (иногда она остается после перетаскивания)

	}
	});
}

function findpos(id)	// полезная функция, помогающая найти поизицию товара в массиве, возвращаяя ее
{
	for(var i=0; i<purchased.length;i++)
	{
		if(purchased[i].id==id)
			return i;
	}

	return false;
}

function remove(id)	// удаляем товары из корзины
{
	var i=findpos(id);	// находим их позицию в массиве

	totalprice-=purchased[i].price*purchased[i].cnt;	// пересчитываем стоимость
	purchased[i].cnt = 0;	// сбрасываем счетчик

	$('#table_'+id).remove();	// удаляем их из таблицы
	update_total();	// обновляем счетчик общей стоимости на странице
}

function change(id)	// вызывается когда мы изменям количество товаров в селекте
{
	var i=findpos(id);

	totalprice+=(parseInt($('#'+id+'_cnt').val())-purchased[i].cnt)*purchased[i].price;

	purchased[i].cnt=parseInt($('#'+id+'_cnt').val());
	update_total();
}

function update_total()	// функция, которая обновляет блок с общей стоимостью на странице
{
	if(totalprice)
	{
		$('#total').html('всего: $'+totalprice); // Если мы купили что-нибудь, отобразить блок с общей стоимостью и кнопку оформления
		$('a.button').css('display','block');
	}
	else	// прячем их
	{
		$('#total').html('');
		$('a.button').hide();
	}
}

В нескольких местах этого кода, мы используем id для указания товара. ID – это уникальный идентификатор, который создается базой данных MySQL один раз, когда мы вносим новую позицию.

Он передается AJAX-запросом, и нам необходимо перевести его в index-позицию нашего массива товаров для дальнейшего использования, все это осуществляет функция findpos().

Теперь, наша интерактивная корзина готова.

Скачать архив с примером.

Перевод статьи «An AJAX Based Shopping Cart with PHP, CSS & jQuery«, автор Martin

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

Напишите свой комментарий