Создание клиентской корзины для виртуальных покупок.

Хранение данных типа «cookie» для сайтов электронных покупок – это относительно новая функция, представленная в спецификации «Web Storage», созданной W3C консорциумом. Данная возможность поддерживается в Internet Explorer 8, Firefox, Chrome, Safari и Opera Desktop (для получения более подробного списка, пожалуйста, проанализируйте данные, представленные в статье «Могу ли я это использовать»). В этой серии статей мы подробно рассмотрим практическую реализацию сессии сохранения данных и попытаемся создать полноценную корзину, используемую на веб-сайте электронной коммерции с помощью объекта sessionStorage и jQuery.

Хочу отметить, что в этих статьях я не собираюсь предлагать какие-то новые техники, которые призваны заменить существующие серверные методы. Я хочу просто показать возможности сессии сохранения данных.

Сессия сохранения данных: быстрое напоминание.

Мы используем сессии для хранения данных и перенос этих данных через несколько страниц. Как правило, пользователь должен выбрать продукт, а мы должны сохранить имя продукта наряду с выбранным количеством и ценой.

Затем пользователь должен будет заполнить форму со своей личной информацией, а мы будем сохранять ее в текущей сессии до конца процесса совершения покупки. Этот процесс, как правило, проходит на странице оформления заказа, а затем все необходимые данные поступают на выбранную платежную систему (например, PayPal).

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

JavaScript сессии работают немного по-другому. Как правило, сессия завершается, когда пользователь закрывает свой браузер (но имейте в виду, что понятие «закрытие браузера» не совсем корректно применять для мобильных устройств). Когда сессия завершается, все данные, хранящиеся в памяти веб-браузера, удаляются. Вам не нужно явно инициализировать сессию, поскольку в JavaScript сессия принимает форму глобального sessionStorage объекта и всегда присутствует. Мы с вами должны записать данные в текущую сессию.

Данные сессии приходят в виде пар ключевых значений. Каждое ключевое значение может содержать только строковые типы данных. Для записи данных, мы можем использовать sessionStorage.setItem( name, value ) метод:

sessionStorage.setItem( "total", 120 );

В данном случае имя ключа total равно 120 и обозначено как строковая величина, хотя при вызове .setItem() метода мы используем целочисленное значение. Это значение будет доступно до тех пор, пока не будет завершена сессия. Если мы используем sessionStorage.removeItem( "total" ) для удаления имени ключа или вызываем sessionStorage.clear() для полного удаления всех ключей и значений из сессии сохранения данных.

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

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

var total = sessionStorage.getItem( "total" );
console.log( total ); // '120', a string

Мы также можем обновить его, записав новое значение с помощью sessionStorage.setItem():

var total = parseInt( sessionStorage.getItem( "total" ) );
var quantity = 2;
var updatedTotal = total * quantity;
sessionStorage.setItem( "total", updatedTotal ); // '240', a string

Сейчас, ключ называется total и после нашего последнего обновления имеет значение 240. Почему мы вызываем именно событие parseInt()? Это достаточно простой способ преобразования численной строки в правильный формат, что является гарантией правильности выполнения расчетов. Помните, что все значения в хранилище являются строковыми величинами, а в наших расчетах должны участвовать только числа.

Но подождите! А что же делать с объектами? Объекты также могут храниться в памяти, но для этого нужно сначала преобразовать их в строковые значения JSON (с JSON.stringify()), а затем обратно в объекты JavaScript (с JSON.parse()):

var cart = {
	item: "Product 1",
	price: 35.50,
	qty: 2
};
var jsonStr = JSON.stringify( cart );
sessionStorage.setItem( "cart", jsonStr );
// now the cart is {"item":"Product 1","price":35.50,"qty":2}
var cartValue = sessionStorage.getItem( "cart" );
var cartObj = JSON.parse( cartValue );
// original object

Для обновления объекта необходимо сначала внести все необходимые корректировки, а затем повторить представленную выше процедуру.

Вопросы безопасности.

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

В технической документации по обеспечению безопасности веб-сайтов (PDF), разработанной командой US Computer Emergency Readiness Team, четко сказано:

«Каждая общественная организация, корпорация, компания или государственное учреждение для обеспечения внешних связей использует веб-сайты. Это один из механизмов предоставления пользователям общей информации о себе, возможность объявить о событии, продать товар или услугу. Таким образом, публичные веб-сайты зачастую являются наиболее популярными объектами атак вредоносной активности».

Даже если рабочий сеанс уже завершен закрытием самого браузера, все равно могут иметь место вредоносные атаки. Особенно это актуально в тех случаях, когда браузер был целенаправленно скомпрометирован (утечка или разглашение конфиденциальной информации либо получение её неавторизованными субъектами) вредоносной активностью. Кроме того, скомпрометированные веб-сайты очень часто используются для распространения вредоносных программ, в основном предназначенных для браузеров.

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

Пример типового проекта: винный магазин.

Наш типовой пример представляет собой интернет-магазин по продаже вина. Это обычный веб-сайт, занимающийся электронной коммерцией. Единственная его сложность заключается в том, как организован механизм подсчета стоимости доставки товаров.

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

Наш магазин будет опираться на платежную систему PayPal, поэтому мы должны создать бизнес-аккаунт в PayPal Sandbox и протестировать наш код.

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

Перед перенаправлением на PayPal, пользователь увидит страницу сводки со своими личными данными, их корзину, общую стоимость всех продуктов, находящихся в корзине, плюс расходы, связанные с доставкой.

После завершения процедуры покупки, пользователь должен быть обратно перенаправлен на веб-сайт. Это единственный шаг всего процесса, который мы не можем выполнить только лишь с использованием JavaScript. PayPal будет отправлять различные данные с помощью HTTP запроса, который должен быть обработан с использованием серверного языка (например, PHP). Если вам нужна дополнительная информация, которая позволит начать работу с этим видом обработки, то вы можете воспользоваться обучающим руководством.

HTML структура.

Наш проект состоит из следующих разделов:

  • index.html. Этот документ содержит список, из которого пользователи могут выбрать и добавить товар в корзину, указав количество для каждого продукта.
  • cart.html. Это страница, где пользователи могут обновить или очистить свою корзину. Кроме того, они могут вернуться на главную страницу, чтобы совершить другие покупки, либо перейти на страницу оформления заказа.
  • checkout.html. На этой странице пользователи заполняют форму с личной информацией — в частности, номер счета и адрес доставки товара.
  • order.html. Эта страница содержит краткую сводку по оформлению заказа плюс PayPal форму. После того как пользователь отправляет форму, он перенаправляется на страницу платежной системы PayPal.

В следующих разделах мы рассмотрим разметку для этого проекта.

Index.html

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

<div class="product-description" data-name="Wine #1" data-price="5">
	<h3 class="product-name">Wine #1</h3>
		<p class="product-price">&euro; 5</p>
		<form class="add-to-cart" action="cart.html" method="post">
			<div>
				<label for="qty-1">Quantity</label>
				<input type="text" name="qty-1" id="qty-1" class="qty" value="1" />
			</div>
			<p><input type="submit" value="Add to cart" class="btn" /></p>
		</form>
</div>

Используемые в этом случае для хранения названий продуктов и значений цен атрибуты данных, могут быть доступны через jQuery с использованием .data() и $.data() методов.

Cart.html

Наша страница корзины состоит из трех основных компонентов: таблица с информацией о продукте, элемент, который отображает промежуточную сумму, а также список из доступных в корзине действий.

<form id="shopping-cart" action="cart.html" method="post">
	<table class="shopping-cart">
		<thead>
			<tr>
				<th scope="col">Item</th>
				<th scope="col">Qty</th>
				<th scope="col">Price</th>
			</tr>
		</thead>
		<tbody></tbody>
	</table>
	<p id="sub-total">
		<strong>Sub Total</strong>: <span id="stotal"></span>
	</p>
	<ul id="shopping-cart-actions">
		<li>
			<input type="submit" name="update" id="update-cart" class="btn" value="Update Cart" />
		</li>
		<li>
			<input type="submit" name="delete" id="empty-cart" class="btn" value="Empty Cart" />
		</li>
		<li>
			<a href="index.html" class="btn">Continue Shopping</a>
		</li>
		<li>
			<a href="checkout.html" class="btn">Go To Checkout</a>
		</li>
	</ul>
</form>

Таблица, содержащаяся на этой странице пуста, но мы можем заполнить ее данными с использованием JavaScript. Элемент, который отображает промежуточную сумму, одновременно работает и в качестве указателя заполнения для JavaScript. Первые два действия, «Обновить корзину» и «Очистить корзину», будут обрабатываться с помощью JavaScript, в то время как последние два действия представляют собой лишь простые ссылки на страницу списка продуктов и страницу оформления заказа соответственно.

Checkout.html

Данная страница состоит из четырех компонентов:

  • Таблица, которая отображает заказанные номера (та же таблица уже была показана ранее в разделе корзины), плюс окончательная цена и стоимость доставки;
  • форма, в которой пользователь должен заполнить их платежные данные;
  • форма для отправки информации;
  • флажки, которые позволяет пользователю указать детали платежных реквизитов, также как и детали доставки.
<table id="checkout-cart" class="shopping-cart">
	<thead>
		<tr>
			<th scope="col">Item</th>
			<th scope="col">Qty</th>
			<th scope="col">Price</th>
		</tr>
	</thead>
	<tbody>

	</tbody>
</table>

<div id="pricing">
	<p id="shipping">
		<strong>Shipping</strong>: <span id="sshipping"></span>
	</p>

	<p id="sub-total">
		<strong>Total</strong>: <span id="stotal"></span>
	</p>
</div>

<form action="order.html" method="post" id="checkout-order-form">
	<h2>Your Details</h2>
		<fieldset id="fieldset-billing">
			<legend>Billing</legend>
				<!-- Name, Email, City, Address, ZIP Code, Country (select box) -->

<div>
	<label for="name">Name</label>
	<input type="text" name="name" id="name" data-type="string" data-message="This field may not be empty" />
</div>

<div>
	<label for="email">Email</label>
	<input type="text" name="email" id="email" data-type="expression" data-message="Not a valid email address" />
</div>

<div>
	<label for="city">City</label>
	<input type="text" name="city" id="city" data-type="string" data-message="This field may not be empty" />
</div>

<div>
	<label for="address">Address</label>
		<input type="text" name="address" id="address" data-type="string" data-message="This field may not be empty" />
</div>

<div>
	<label for="zip">ZIP Code</label>
	<input type="text" name="zip" id="zip" data-type="string" data-message="This field may not be empty" />
</div>

<div>
	<label for="country">Country</label>
		<select name="country" id="country" data-type="string" data-message="This field may not be empty">
			<option value="">Select</option>
			<option value="US">USA</option>
			<option value="IT">Italy</option>
		</select>
</div>
</fieldset>

<div id="shipping-same">Same as Billing <input type="checkbox" id="same-as-billing" value=""/></div>

<fieldset id="fieldset-shipping">
<legend>Shipping</legend>
	<!-- Same fields as billing -->
</fieldset>

<p><input type="submit" id="submit-order" value="Submit" class="btn" /></p>

</form>

Атрибуты данных в этом случае используются для проверки. Атрибут data-type определяет тип данных, которые мы должны проверить, а data-message содержит текст сообщения об ошибке, которое будет показано в случае отказа от работы.

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

Order.html

Это последняя страница и она содержит информацию о платежных реквизитах, об особенностях доставки и PayPal форму.

<h1>Your Order</h1>

<table id="checkout-cart" class="shopping-cart">
	<thead>
		<tr>
			<th scope="col">Item</th>
			<th scope="col">Qty</th>
			<th scope="col">Price</th>
		</tr>
	</thead>
	<tbody>
	</tbody>
</table>

<div id="pricing">
	<p id="shipping">
		<strong>Shipping</strong>: <span id="sshipping"></span>
	</p>

	<p id="sub-total">
		<strong>Total</strong>: <span id="stotal"></span>
	</p>
</div>

<div id="user-details">
	<h2>Your Data</h2>
		<div id="user-details-content"></div>
</div>

<form id="paypal-form" action="" method="post">
	<input type="hidden" name="cmd" value="_cart" />
	<input type="hidden" name="upload" value="1" />
	<input type="hidden" name="business" value="" />

	<input type="hidden" name="currency_code" value="" />
	<input type="submit" id="paypal-btn" class="btn" value="Pay with PayPal" />
</form>

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

JavaScript код.

CSS шаблон этого проекта не будет оказывать фактического влияния на цели, которые мы хотим достичь. Даже если мы полностью отключим CSS, проект все равно будет продолжать функционировать благодаря надежной взаимосвязи между структурой HTML и поведением JavaScript.

Из-за сложности целей, которые нам необходимо реализовать, мы будем использовать объектно-ориентированный подход. Наш объект будет основан на простой конструкционной модели и будет использовать как закрытые (ключевое слово в объектно-ориентированных языках программирования, указывающее, что тот или иной объект видим только в рамках модуля или класса, в котором он декларирован), так и открытые (ключевое слово в объектно-ориентированных языках программирования, указывающее, что класс, объект или метод видим для всех классов, объектов или модулей, внешних по отношению к модулю, в котором он объявлен) методы.

Структура объекта.

Наш объект будет иметь очень простую структуру. Функция конструктора одновременно инициализирует элемент верхнего уровня, который упаковывает всю DOM структуру и вызывает метод инициализации.

(function( $ ) {
	$.Shop = function( element ) {
		this.$element = $( element ); // top-level element
		this.init();
	};

	$.Shop.prototype = {
		init: function() {
			// initializes properties and methods
		}
	};

	$(function() {
		var shop = new $.Shop( "#site" ); // object's instance
	});

})( jQuery );

Экземпляр объекта создается, когда будет готов DOM. Надежность работы мы можем проверить следующим образом:

$(function() {
	var shop = new $.Shop( "#site" );
	console.log( shop.$element );
});

Результатом будет следующее:

x.fn.x.init[1]
	0: div#site
	context: document
	length: 1
	selector: "#site"

Теперь, когда мы знаем, что наш экземпляр объекта будет создан правильно, мы можем определить его свойства.

Свойства объекта.

Свойства нашего объекта будут распределяться на две категории: во-первых, свойства для обработки вычислений, формы и проверка, а во-вторых, ссылки на HTML элементы.

$.Shop.prototype = {
	init: function() {
		// Properties

			this.cartPrefix = "winery-"; // prefix string to be prepended to the cart's name in session storage
			this.cartName = this.cartPrefix + "cart"; // cart's name in session storage
			this.shippingRates = this.cartPrefix + "shipping-rates"; // shipping rates key in session storage
			this.total = this.cartPrefix + "total"; // total key in the session storage
			this.storage = sessionStorage; // shortcut to sessionStorage object

			this.$formAddToCart = this.$element.find( "form.add-to-cart" ); // forms for adding items to the cart
			this.$formCart = this.$element.find( "#shopping-cart" ); // Shopping cart form
			this.$checkoutCart = this.$element.find( "#checkout-cart" ); // checkout form cart
			this.$checkoutOrderForm = this.$element.find( "#checkout-order-form" ); // checkout user details form
			this.$shipping = this.$element.find( "#sshipping" ); // element that displays the shipping rates
			this.$subTotal = this.$element.find( "#stotal" ); // element that displays the subtotal charges
			this.$shoppingCartActions = this.$element.find( "#shopping-cart-actions" ); // cart actions links
			this.$updateCartBtn = this.$shoppingCartActions.find( "#update-cart" ); // update cart button
			this.$emptyCartBtn = this.$shoppingCartActions.find( "#empty-cart" ); // empty cart button
			this.$userDetails = this.$element.find( "#user-details-content" ); // element that displays the user's information
			this.$paypalForm = this.$element.find( "#paypal-form" ); // PayPal form

			this.currency = "&euro;"; // HTML entity of the currency to be displayed in layout
			this.currencyString = "€"; // currency symbol as text string
			this.paypalCurrency = "EUR"; // PayPal's currency code
			this.paypalBusinessEmail = "yourbusiness@email.com"; // your PayPal Business account email address
			this.paypalURL = "https://www.sandbox.paypal.com/cgi-bin/webscr"; // URL of the PayPal form

			// object containing patterns for form validation
			this.requiredFields = {
				expression: {
					value: /^([\w-\.]+)@((?:[\w]+\.)+)([a-z]){2,4}$/
				},

				str: {
					value: ""
				}

			};

			// public methods invocation
	}
};

Давайте последовательно рассмотрим все эти свойства.

Сохранение и другие свойства:

  • cartPrefix Данный префикс, который будет добавлен к имени ключа корзины, используется в сессии сохранения данных.
  • cartName Имя ключа корзины в сессии сохранения данных (объединяет cartPrefix строку со строкой cart).
  • shippingRates Ключ доставки в сессии сохранения данных.
  • total Основной ключ в сессии сохранения данных.
  • storage Ярлык для sessionStorage объекта.
  • Currency Логический HTML объект, который используется для отображения в шаблоне текущего вида валюты.
  • currencyString Текущая денежная единица, которая используется в тексте.
  • paypalCurrency Код денежной единицы PayPal.
  • paypalBusinessEmail Адрес электронной почты вашего PayPal Business аккаунта.
  • paypalURL URL-адрес PayPal формы (по умолчанию используется URL-адрес из PayPal Sandbox).
  • requiredFields Объект, содержащий шаблоны и правила для формы проверки данных.

Ссылки на элементы:

  • $formAddToCart Формы для добавления продуктов в корзину.
  • $formCart Форма корзины.
  • $checkoutCart Форма проверки корзины.
  • $checkoutOrderForm Форма проверки, где пользователи осуществляют ввод личной информации.
  • $shipping Элемент, который содержит и отображает информацию о доставке.
  • $subTotal Элемент, который содержит и отображает информацию касательно общих расходов.
  • $shoppingCartActions Элементы, которые отображают действия, доступные в корзине.
  • $updateCartBtn Кнопка обновления корзины.
  • $emptyCartBtn Кнопка позволяющая очистить корзину.
  • $userDetails Элемент, который содержит и отображает информацию, введенную пользователем.
  • $paypalForm форма PayPal.

Все перечисленные элементы имеют префикс $, который означает, что все объекты относятся к jQuery. Однако эти элементы доступны не на всех страницах. Чтобы проверить существует ли элемент jQuery, просто протестируйте его length свойство:

if( $element.length ) {
	// the element exists
}

Другой подход, который не используется в нашем проекте, заключается в добавлении определенного идентификатора или класса в body элемент и условного выполнения действия:

var $body = $( "body" ),
	page = $body.attr( "id" );

	switch( page ) {
		case "product-list":
			// actions for handling products
			break;
		case "shopping-cart":
			// actions for handling the shopping cart
			break;
		case "checkout":
			// actions for handling the checkout's page
			break;
		default:
			break;
	}

Объектные методы.

Основные функции нашего кода находятся в объектных методах, который, в свою очередь, можно разделить на закрытые и открытые. Закрытые методы работают, так сказать, в фоновом режиме и помогают открытым методам выполнять свои задачи. Эти методы имеют в своем составе префикс подчеркивания и никогда не используются отдельно.

Открытые методы функционируют непосредственно на странице элементов и данных, и они используются без префикса. Мы уже видели в действии метод init(), который просто инициализирует свойства и другие открытые методы в функции конструктора объекта. Особенности других методов будут объяснены ниже.

Закрытые методы (помощники).

Первый частный метод, _emptyCart(), просто впадает текущего хранения сессии в браузере:

$.Shop.prototype = {
	// empties session storage

	_emptyCart: function() {
		this.storage.clear();
	}
};

Для форматирования числа в соответствии с заданным количеством знаков после запятой, мы реализуем _formatNumber() метод:

/* Format a number by decimal places
 * @param num Number the number to be formatted
 * @param places Number the decimal places
 * @returns n Number the formatted number
*/

_formatNumber: function( num, places ) {
	var n = num.toFixed( places );
	return n;
}

Этот метод использует JavaScript метод toFixed () объекта Number. Его роль в нашем проекте заключается в правильном форматировании цен.

Поскольку не все цены на страницах содержатся в атрибутах, нам нужен специализированный метод, который бы позволил извлечь числовую часть строки из текстовых узлов. Этот метод будет называться _extractPrice():

/* Extract the numeric portion from a string
 * @param element Object the jQuery element that contains the relevant string
 * @returns price String the numeric string
 */

_extractPrice: function( element ) {
	var self = this;
	var text = element.text();
	var price = text.replace( self.currencyString, "" ).replace( " ", "" );
	return price;
}

В представленном выше фрагменте кода self – это ссылка на объект $.Shop, и мы будем использовать ее каждый раз, когда нужно будет получить доступ к свойству или методу нашего объекта, не беспокоясь об области видимости.

Вы можете сделать этот метод «пуленепробиваемым» (так говорят о программе или системе, которые способны обрабатывать любые исключительные ситуации), добавив процедуру, которая удаляет все конечные пробелы:

var text = $.trim( element.text() );

Имейте в виду, что jQuery $.trim() метод удаляет все новые фрагменты, пространства (в том числе неразрывные пробелы) и вложения из начала и конца строки. Если пробелы буду …

Если вы хотите прочитать полностью статью, посетите сайт наших спонсоров

Comments are closed.