Антон Скобляков
blog-post-1

PHP: Интернет магазин для лендинга

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

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

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

Создадим файл init.php со следующим содержимым:

<?php
session_start();
define('ORDER_EMAIL', 'cюда_будут_падать@заказы.ru'); 

$db = array(
   'id1' => array(
       'title' => 'Товар 1',
       'description' => 'Описание товара 1',
       'image' => 'http://landing-editor.ru/img/mysql.png',
       'price' => 999.99,
    ),
    'id2' => array(
       'title' => 'Товар 1',
       'description' => 'Описание товара 1',
       'image' => 'http://landing-editor.ru/img/mysql.png',
       'price' => 999.99,
    ),
    //и т.д. 
);

Что делает код выше и зачем мы вынесли его в отдельный файл? Переменные и логику данного файла мы будет использовать как при генерации страницы нашего лендинга, так и в скрипте, который будет обрабатывать наши ajax запросы. Поэтому логичней вынести все это в отдельный файл. Из кода видно, что в нем мы определяем почту, куда будем слать заказы, стартуем сессию (в ней мы будем хранить корзину), а так же описываем каталог наших товаров.

Рассуждаем дальше. Обычно интернет магазины имеют «мини корзину» — информационный блок, который показывает сколько товаров в корзине, возможно их общую стоимость, возможно какие то дополнительные мелочи. Данный блок отображается на landing-е (возможно в нескольких местах), а так же обновляется каждый раз после изменения состояния корзины. Поэтому логично вынести генерацию кода данного блока в функцию и разместить ее так же в файле init.php. Корзину в сессии обзовем как индекс cart .

<?php
session_start();
define('ORDER_EMAIL', 'cюда_будут_падать@заказы.ru'); 

$items = array(
   'id1' => array(
       'title' => 'Товар 1',
       'description' => 'Описание товара 1',
       'image' => 'http://landing-editor.ru/img/mysql.png',
       'price' => 999.99,
    ),
    'id2' => array(
       'title' => 'Товар 1',
       'description' => 'Описание товара 1',
       'image' => 'http://landing-editor.ru/img/mysql.png',
       'price' => 999.99,
    ),
    //и т.д. 
);

function getCartMiniHtml() {
  global $items;
  $count = $sum = 0;
  if(isset($_SESSION['cart'])) foreach($_SESSION['cart'] as $id => $countItem) {
    if(isset($items[$id])) {
      if($countItem > 0) {
        $sum += $countItem * $items[$id]['price'];
        $count += $countItem;
      }
    }
  }
  return '<a class="basket-btn-mini"><span>'.$count.'</span> товар'.
    ($count == 1 ? '' : ($count > 1 && $count < 5 ? 'а' : 'ов')).
    ' <span class="basket-price">'.$sum.' р.</span></a>';
}

Далее нам необходим механизм добавления и удаления товаров из корзины, формирования полной корзины и отправки заказа. Для этого создадим файл ajax.php и реализуем в нем необходимый функционал:

<?php
include __DIR__.'/init.php';

$response = '';

$email = isset($_REQUEST['email']) ? trim(strip_tags($_REQUEST['email'])) : ''; 
$phone = isset($_REQUEST['phonenumber']) ? trim(strip_tags($_REQUEST['phonenumber'])) : ''; 
$headers = "From: MYSITE ROBOT \r\n";
$headers .= "Reply-To: ". strip_tags($email) . "\r\n";
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: text/html; charset=utf-8\r\n";

if(!empty($_REQUEST['a'])) switch($_REQUEST['a']) { //флаг нашего действия

  case 'order': //оформление заказа
    $comment = trim(strip_tags($_REQUEST['comment']));
    if(!preg_match('/^[a-zA-Z0-9_\-]+@[a-zA-Z0-9\-]{2,}\.[a-zA-Z]{2,8}$/si', $email)) {
      $response = array('status' => 1, 'data' => 'email');
      break;
    }
    if(!preg_match('/^\+7\s\(\d{3}\)\s\d{3}\-\d{2}\-\d{2}$/si', $phone)) {
      $response = array('status' => 1, 'data' => 'phonenumber');
      break;
    }
    if(!isset($_SESSION['cart']) || count($_SESSION['cart']) == 0) {
      $response = array('status' => 2, 'data' => 'Нет товаров в корзине.');
      break;
    }
    
    $msg = '<b>Email:</b> '.$email.'<br />
      <b>Телефон:</b> '.$phone.'<br />
      <b>Комментарий</b><br /><i>'.$comment.'</i><br /><br />';
    $total = 0;
    foreach($_SESSION['cart'] as $id => $countItem) { 
      $sum = $items[$id]['price'] * $countItem;
      $total += $sum;
      $msg .= '<div>Артикул: '.$id.', <i>'.$items[$id]['title'].'</i> x '.$countItem.'шт. = '.$sum.' руб.</div>';
    }
    $msg .= '<br /><b>Итого: '.$total.' руб.</b>';
    $msg .= '<br/><br/><small>Дата отправки '.date('d.m.Y H:i:s').'</small>';
    
    if(mail(ORDER_EMAIL, 'Новый заказ', $msg, $headers)) {
      $response = array('status' => 0);
    } else {
      $response = array('status' => 2, 'data' => 'Возникла ошибка при создании заказа. Попробуйте еще раз.');
    }
    break;

  case 'cart': //попап оформления заказа
    foreach($_SESSION['cart'] as $id => $countItem) { if(isset($items[$id])) {
      $response .= '<div class="item" data-id="'.$id.'">
            <img class="good-img" src="'.$items[$id]['image'].'" alt="#">'.$items[$id]['title'].'
            <input class="counter" type="number" min="1" value="'.$countItem.'">
            x <b class="price">'.$items[$id]['price'].'</b>р. = <u class="total">'.($items[$id]['price'] * $countItem).'<u>р.
            <a class="rm-item" data-id="'.$id.'">удалить</a>';
    } }
    break;

  case 'remove': //удаляем товар из корзины
    if(!empty($_REQUEST['id']) && isset($items[$_REQUEST['id']]) && isset($_SESSION['cart'][$_REQUEST['id']])) {
      unset($_SESSION['cart'][$_REQUEST['id']]);   
    }
    $response = getCartMiniHtml();
    break;
  
  case 'info': //получаем информацию о товаре
    if(!empty($_REQUEST['id']) && isset($items[$_REQUEST['id']])) {
      $response = json_encode($items[$_REQUEST['id']]);
    }
    break;
  
  case 'add': //добавляем товар в корзину
    if(!empty($_REQUEST['id']) && isset($items[$_REQUEST['id']])) {
      $id = $_REQUEST['id'];
      $count = !empty($_REQUEST['count']) && is_numeric($_REQUEST['count']) && $_REQUEST['count'] >= 0 ? $_REQUEST['count'] : 1;
      if(!isset($_SESSION['cart'][$id])) {
        $_SESSION['cart'][$id] = 0;
      } 
      if(!empty($_REQUEST['set'])) { //если есть флаг SET, то переписываем количество товара в корзине
        $_SESSION['cart'][$id] = $count;
      } else { //иначе товар уже был в корзине, нужно сложить количество в корзине с добавляемым числом товаров
        $_SESSION['cart'][$id] += $count;
      }
    }
    $response = getCartMiniHtml();
    break;

}

echo is_array($response) ? json_encode($response) : $response;

Данный скрипт возвращает данные в формате JSON или в виде HTML кода. При формате ответа JSON запрос считается успешно отработанным, если поле status в ответе содержит значение 0.
По сути, наш бекенд готов. Единственное, что нам остается, это вывести товары нашего каталога обычным циклом на нужной странице лендинга.

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

var ajaxUrl = '/ajax.php'; //ссылка на файл ajax.php, созданный выше

function addItemToCart(id, count) {
    count = count || 1;
    $.ajax({ url: ajaxUrl, data: { a: 'add', id: id, count: count }, success: function(i) {
      $('#basket-mini').html(i); //обновляем блок с "мини" корзиной 
    }}); 
}

function removeItemFromCart(id) {
    $.ajax({ url: ajaxUrl, data: { a: 'remove', id: id }, success: function(i) {
      $('#basket-mini').html(i); //обновляем блок с "мини" корзиной 
    }}); 
}

$('body').on('click', '.rm-item', function (e) { //удаление товара из полной корзины
    $(this).closest('.item').slideUp(); 
    removeItemFromCart($(this).attr('data-id'));    
});

function initCountChange(parent) { //Функция для навешивания события на блок изменения количества товара в корзине
  parent = parent || 'body';
  $(parent).find('.counter').change(function() {
    if($(this).closest('.item').length > 0) {
      var $row = $(this).closest('.item');
      $row.find('.total').text( this.value * $row.find('.price').text() );
      $.ajax({ url: ajaxUrl, data: { set: true, a: 'add', id: $row.attr('data-id'), count: this.value }, success: function(i) {
         $('#basket-mini').html(i); //обновляем блок с "мини" корзиной 
      }}); 
    }
  });
}

$('body').on('click', '.basket-btn-mini', function (e) { //клик на "мини" корзине открывает форму заказа
    e.preventDefault();
    $.ajax({ url: ajaxUrl, data: { a: 'cart' }, success: function(i) {
      if(i == '') return;
      $('#checkout .basket').html(i); //обновляем контент блока с товарами полной корзины
      $('#checkout').show(); //показываем корзину
      initCountChange('#checkout'); 
    }});
});

$("#form_order").submit(function() { //отправка формы
    $.ajax({ url: ajaxUrl, data: $(this).serialize() + '&a=order', dataType: 'json', success: function(j) {
      if(j.status == 0) {
        alert('Спасибо за заказ!');
        $.ajax({ url: ajaxUrl, data: { a: 'removeAll' }, success: function(i) {
          $('#basket-mini').html(i);
        }}); 
      } else {
        if(j.status == 2) {
          alert(j.data);
        } else {
          $('#form_order [name="' + j.data + '"]').addClass('error');  //если назвать поля формы, так же как и текст ошибки, то автоматически к данному полю добавиться класс error
        }
      }
    }}); 
    return false;
  });

Стоит заметить, что селекторы скриптов могут быть отличны и будут зависеть от HTML кода Вашего сайте. Так же приведенный выше пример подразумевает наличие jquery на Вашем сайте.

Описанный выше пример, лишь пример. Не нужно бездумно копировать его на свой сайт и говорить что «ничего не работает». Я лишь хотел показать общий подход и логику, как можно быстро и просто реализовать корзину на своем лендинг пейдже.

Спасибо за внимание