Рубрика «PHP»

Каталог продукции: сортировка + постраничная навигация

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

<?php
$dblocation = "localhost";   // Имя сервера
$dbuser     = "root";        // Имя пользователя
$dbpswrd    = "";            // Пароль
$dbname     = "products";    // Имя базы данных

DEFINE('ITEMS_PER_PAGE', 7);

// Соединение с сервером базы данных
$dblink = mysql_connect( $dblocation, $dbuser, $dbpswrd );
mysql_query( 'SET NAMES cp1251' );
// Выбираем базу данных
mysql_select_db( $dbname, $dblink );

// Выбираем из БД общее количество записей
$query = "SELECT COUNT(*) FROM products WHERE 1";
$res = mysql_query( $query );
$total = mysql_result( $res, 0, 0 );
   
// Проверяем передан ли номер текущей страницы (постраничная навигация)
if ( isset($_GET['page']) ) {
  $page = (int)$_GET['page'];
  if ( $page < 1 ) $page = 1;
} else {
  $page = 1;
}

$uri = $_SERVER['PHP_SELF'].'?';
if ( $_SERVER['QUERY_STRING'] != '' ) {
  foreach( $_GET as $key => $value ) {
    if ( $key != 'page' ) $uri = $uri.$key.'='.urlencode($value).'&';
  } 
}
// Сколько всего получится страниц
$cnt_pages = ceil( $total / ITEMS_PER_PAGE );
if ( $page > $cnt_pages ) $page = $cnt_pages;
// Начальная позиция
$start = ( $page - 1 ) * ITEMS_PER_PAGE;

// По умолчанию сортировка по наименованию, по возрастанию
$orderby = 'title';
$sort = 'ASC';
if ( isset( $_GET['orderby'] ) and isset( $_GET['sort'] ) ) {
  if ( in_array( $_GET['orderby'], array( 'code', 'title', 'price' ) ) ) $orderby = $_GET['orderby'];
  if ( in_array( $_GET['sort'], array( 'ASC', 'DESC' ) ) ) $sort = $_GET['sort'];
}

$query = 'SELECT code, title, description, price
          FROM products
          ORDER BY '
.$orderby.' '.$sort.'
          LIMIT '
.$start.', '.ITEMS_PER_PAGE;
$res = mysql_query( $query );

echo '<h1>Каталог продукции</h1>'."\n";
// Выводим "шапку" таблицы
echo '<table border="1" cellpadding="4" cellspacing="0" style="border-collapse: collapse; empty-cells: show;">'."\n";
echo '<tr>';
if ( $sort == 'ASC' ) {
  $tmp = 'DESC';
  $image = 'down.gif';
} else {
  $tmp = 'ASC';
  $image = 'up.gif';
}
if ( $orderby == 'code' )
  echo '<th><a href="'.$_SERVER['PHP_SELF'].'?orderby=code&sort='.$tmp.'&page='.$page.'">Код</a>&nbsp;<img src="'.$image.'" alt="" /></th>';
else
  echo '<th><a href="'.$_SERVER['PHP_SELF'].'?orderby=code&sort=ASC&page='.$page.'">Код</a></th>';
if ( $orderby == 'title' )
  echo '<th><a href="'.$_SERVER['PHP_SELF'].'?orderby=title&sort='.$tmp.'&page='.$page.'">Наименование</a>&nbsp;<img src="'.$image.'" alt="" /></th>';
else
  echo '<th><a href="'.$_SERVER['PHP_SELF'].'?orderby=title&sort=ASC&page='.$page.'">Наименование</a></th>';
echo '<th>Описание</th>';
if ( $orderby == 'price' )
  echo '<th><a href="'.$_SERVER['PHP_SELF'].'?orderby=price&sort='.$tmp.'&page='.$page.'">Цена</a>&nbsp;<img src="'.$image.'" alt="" /></th>';
else
  echo '<th><a href="'.$_SERVER['PHP_SELF'].'?orderby=price&sort=ASC&page='.$page.'">Цена</a></th>';
echo '</tr>'."\n";
while( $prd = mysql_fetch_array($res) ) {
  echo '<tr>';
  echo '<td>'.$prd['code'].'</td>';
  echo '<td>'.$prd['title'].'</td>';
  echo '<td>'.$prd['description'].'</td>';
  echo '<td>'.$prd['price'].'</td>';   
  echo '</tr>'."\n";
}
echo '</table>'."\n";

// Строим постраничную навигацию
if ( $cnt_pages > 1 )
{
    echo '<div style="margin:1em 0">&nbsp;Страницы: ';
    // Проверяем нужна ли стрелка "В начало"
    if ( $page > 3 )
        $startpage = '<a href="'.$uri.'page=1"><<</a> ... ';
    else
        $startpage = '';
    // Проверяем нужна ли стрелка "В конец"
    if ( $page < ($cnt_pages - 2) )
        $endpage = ' ... <a href="'.$uri.'page='.$cnt_pages.'">>></a>';
    else
        $endpage = '';

    // Находим две ближайшие станицы с обоих краев, если они есть
    if ( $page - 2 > 0 )
        $page2left = ' <a href="'.$uri.'page='.($page - 2).'">'.($page - 2).'</a> | ';
    else
        $page2left = '';
    if ( $page - 1 > 0 )
        $page1left = ' <a href="'.$uri.'page='.($page - 1).'">'.($page - 1).'</a> | ';
    else
        $page1left = '';
    if ( $page + 2 <= $cnt_pages )
        $page2right = ' | <a href="'.$uri.'page='.($page + 2).'">'.($page + 2).'</a>';
    else
        $page2right = '';
    if ( $page + 1 <= $cnt_pages )
        $page1right = ' | <a href="'.$uri.'page='.($page + 1).'">'.($page + 1).'</a>';
    else
        $page1right = '';

    // Выводим меню
    echo $startpage.$page2left.$page1left.'<strong>'.$page.'</strong>'.$page1right.$page2right.$endpage."\n";

    echo '</div>'."\n";
}
?>

Рабочий пример можно посмотреть здесь.

Простейший Интернет-магазин

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

Корзина для Интернет-магазина (файл shop.php):

<?php
$dblocation = "localhost";   // Имя сервера
$dbuser     = "root";        // Имя пользователя
$dbpswrd    = "";            // Пароль
$dbname     = "catalog";     // Имя базы данных

// Соединение с сервером базы данных
$dblink = mysql_connect( $dblocation, $dbuser, $dbpswrd );
mysql_query( 'SET NAMES cp1251' );
// Выбираем базу данных
mysql_select_db( $dbname, $dblink );

// Формируем запрос на извлечение товарных позиций
$query = 'SELECT code, title, description, price
          FROM products
          ORDER BY title'
;
$res = mysql_query( $query );
$cnt = mysql_num_rows( $res );
$goods = '';
for( $i = 1; $prd = mysql_fetch_row( $res ); $i++ ) {
  if ( $i == $cnt )
    $goods = $goods.'  ["'.$prd[0].'","'.$prd[1].'","'.$prd[2].'",'.$prd[3].']'."\n";
  else
    $goods = $goods.'  ["'.$prd[0].'","'.$prd[1].'","'.$prd[2].'",'.$prd[3].'],'."\n";
}
?>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<html>
<title>Интернет-магазин</title>
<meta http-equiv="content-type" content="text/html; windows-1251" />
<script type="text/javascript">
var goods = new Array();
goods = [  
<?php
echo $goods;
?>
];
 
var count = goods.length;
var orders = new Array();
for( var i = 0; i < count; i++ ) orders[i] = 0;

var param = getCookie( "basket" );
for( var i = 0; i < count; i++ ) {
  pos = param.indexOf( ',' );
  if( pos == -1 ) break;
  orders[i] = parseInt( param.substring ( -1, pos ) );
  param = param.substring ( pos+1 );
}

window.onload = function() {
  var table = createTable(goods);   
  document.getElementById("shop").appendChild(table);
}
 
function updateTable()
{
  var totalGoods = 0;
  var totalCosts = 0;
  var basket = "";
  for ( var i = 0; i < count; i++ ) {
    var item = document.getElementById( "item"+i );
    var cnt = parseInt( item.value );
    if ( isNaN(cnt) || cnt < 0 ) {
      cnt = 0;
      item.value = "0";
    }
    if ( cnt > 0 )
      item.parentNode.parentNode.setAttribute("bgcolor", "lightblue");
    else
      item.parentNode.parentNode.removeAttribute("bgcolor")
    orders[i] = cnt;
    basket = basket + cnt + ",";
    totalGoods = totalGoods + cnt;
    totalCosts = totalCosts + cnt*goods[i][3];
  }
  var span = document.getElementById("totalGoods");
  var newTextNode = document.createTextNode(totalGoods);
  span.replaceChild(newTextNode, span.firstChild);
  var span = document.getElementById("totalCosts");
  var newTextNode = document.createTextNode(totalCosts);
  span.replaceChild(newTextNode, span.firstChild);
 
  setCookie("basket", basket);
}

function createTable(data) {
  var table = document.createElement("table");
  table.setAttribute("border", "1");
  table.setAttribute("cellspacing", "0");
  table.setAttribute("cellpadding", "2");
  var thead = document.createElement("thead");
  var tr = document.createElement("tr");
  tr.setAttribute("bgcolor", "lightgrey");
  var head = new Array("Код","Наименование","Описание","Стоимость","Количество");
  for (var i = 0; i < head.length; i++) {
    var th = document.createElement("th");
    var newText = document.createTextNode(head[i]);
    th.appendChild(newText);
    tr.appendChild(th);
  }
  thead.appendChild(tr);
  table.appendChild(thead);

  var totalGoods = 0;
  var totalCosts = 0.0;
 
  var tbody = document.createElement("tbody");
  for (var i = 0; i < data.length; i++) {
    var tr = document.createElement("tr");
    if ( orders[i] > 0 ) tr.setAttribute("bgcolor", "lightblue");
    for (var j=0; j < data[i].length; j++) {
      var td = document.createElement("td");
      var newText = document.createTextNode(data[i][j]);
      td.appendChild(newText);
      tr.appendChild(td);
    }
    var td = document.createElement("td");
    var newInput = document.createElement("input");
    newInput.setAttribute("type", "text");
    newInput.setAttribute("id", "item"+i);
    newInput.setAttribute("value", orders[i]);
    newInput.setAttribute("size", "3");
    newInput.onblur = updateTable;
    td.appendChild(newInput);
    tr.appendChild(td);
    tbody.appendChild(tr);

    totalGoods = totalGoods + orders[i];
    totalCosts = totalCosts + orders[i]*goods[i][3];
  }
  var tr = document.createElement("tr");
  var td = document.createElement("td");
  td.setAttribute("colspan", "3");
  td.setAttribute("align", "right");
  var textNode = document.createTextNode("Итого:");
  td.appendChild(textNode);
  tr.appendChild(td);
  var td = document.createElement("td");
  var span = document.createElement("span");
  span.setAttribute("id", "totalCosts");
  var textNode = document.createTextNode(totalCosts);
  span.appendChild(textNode);
  td.appendChild(span);
  tr.appendChild(td);
  var td = document.createElement("td");
  var span = document.createElement("span");
  span.setAttribute("id", "totalGoods");
  var textNode = document.createTextNode(totalGoods);
  span.appendChild(textNode);
  td.appendChild(span);
  tr.appendChild(td);
  tbody.appendChild(tr);

  table.appendChild(tbody);
  return table;
}

function setCookie(name,value)
{
  document.cookie = name + '=' + value;
}   

function getCookie(name)
{
  var arg = name + "=";
  startpos = document.cookie.indexOf(arg, 0);
  if (startpos == -1)
    return "";
  else
    startpos += arg.length;
  endpos = document.cookie.indexOf(';', startpos);
  if (endpos == -1) endpos = document.cookie.length;
  return document.cookie.substring(startpos, endpos);
}
</script>


</head>
<body>

<div id="shop"></div>

<p><a href="order.php">Оформить заказ</a></p>

</body>
</html>

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

Для оформления заказа покупатель должен заполнить форму:

Скрипт отправки сообщения на e-mail о заказе в магазине (файл order.php):

<?php
session_start();
$admin = 'admin@mail.ru';

$dblocation = "localhost";   // Имя сервера
$dbuser     = "root";        // Имя пользователя
$dbpswrd    = "";            // Пароль
$dbname     = "catalog";     // Имя базы данных

// Соединение с сервером базы данных
$dblink = mysql_connect( $dblocation, $dbuser, $dbpswrd );
mysql_query( 'SET NAMES cp1251' );
// Выбираем базу данных
mysql_select_db( $dbname, $dblink );

// Если не установлена cookie-переменная basket -
// значит пользователь попал сюда по ошибке
if ( !isset( $_COOKIE['basket'] ) ) {
  header( 'Location: shop.php' );
  die();
}

if ( isset($_SESSION['success']) and $_SESSION['success'] ) {
  setcookie ('basket', '', time() - 3600);
  $notShowForm = true;
}

// Если в корзине нет товаров
$cart = explode( ',', $_COOKIE['basket'] );
if ( array_sum( $cart ) == 0 ) {
  echo '<p>Ваша корзина пуста!</p>';
  echo '<p><a href="shop.php">Вернуться к покупкам</a></p>';
  die();
}

// Формируем запрос на извлечение товарных позиций
$query = 'SELECT code, title, description, price
          FROM products
          ORDER BY title'
;
$res = mysql_query($query);
$order = '<table border="1" cellpadding="3" cellspacing="0">';
$order = $order.'<tr>';
$order = $order.'<th>Код</th>';
$order = $order.'<th>Наименование</th>';
$order = $order.'<th>Описание</th>';
$order = $order.'<th>Стоимость</th>';
$order = $order.'<th>Количество</th>';
$order = $order.'</tr>';
$i = 0;
$total = 0;
while( $prd = mysql_fetch_array( $res ) ) {
  if ( $cart[$i] > 0 ) {
    $order = $order.'<tr>';
    $order = $order.'<td>'.$prd['code'].'</td>';
    $order = $order.'<td>'.$prd['title'].'</td>';
    $order = $order.'<td>'.$prd['description'].'</td>';
    $order = $order.'<td align="right">'.$prd['price'].'</td>';
    $order = $order.'<td align="center">'.$cart[$i].'</td>';
    $order = $order.'</tr>';
    $total = $total + $cart[$i]*$prd['price'];
  }
  $i++;
}
$order = $order.'</table>';
$order = $order.'<p style="font-weight:bold">Итого: '.$total.'</p>';

if ( isset( $_SESSION['orderForm'] ) ) {
  echo $_SESSION['orderForm']['error'];
  $name    = htmlspecialchars ( $_SESSION['orderForm']['name'] );
  $email   = htmlspecialchars ( $_SESSION['orderForm']['email'] );
  $phone   = htmlspecialchars ( $_SESSION['orderForm']['phone'] );
  $adress  = htmlspecialchars ( $_SESSION['orderForm']['adress'] );
  unset( $_SESSION['orderForm'] );
} else {
  $name    = '';
  $email   = '';
  $phone   = '';
  $adress  = '';
}

if ( isset( $_POST['sendOrder'] ) ) {
  $name    = substr( $_POST['name'], 0, 64 );
  $email   = substr( $_POST['email'], 0, 64 );
  $phone   = substr( $_POST['phone'], 0, 64 );
  $adress  = substr( $_POST['adress'], 0, 250 );
  // Проверка правильности заполнеия формы
  $error = '';
  if ( empty( $name ) ) $error = $error.'<li>Не заполнено поле "Имя"</li>';
  if ( empty( $email ) ) $error = $error.'<li>Не заполнено поле "E-mail"</li>';
  if ( empty( $phone ) ) $error = $error.'<li>Не заполнено поле "Телефон"</li>';
  if ( empty( $adress ) ) $error = $error.'<li>Не заполнено поле "Адрес"</li>';
  if ( !empty( $email ) and !preg_match( "#^[0-9a-z_\-\.]+@[0-9a-z\-\.]+\.[a-z]{2,6}$#i", $email ) )
    $error = $error.'<li>поле "E-mail" должно соответствовать формату somebody@somewhere.ru</li>';
  // Если при заполнении формы были допущены ошибки
  if ( !empty( $error ) ) {
    $_SESSION['orderForm']['error']   = '<p>При заполнении формы были допущены ошибки:</p><ul>'.$error.'</ul>';
    $_SESSION['orderForm']['name']    = $name;
    $_SESSION['orderForm']['email']   = $email;
    $_SESSION['orderForm']['phone']   = $phone;
    $_SESSION['orderForm']['adress']  = $adress;
    header( 'Location: '.$_SERVER['PHP_SELF'] );
    die();
  }

  $theme   = '=?windows-1251?B?'.base64_encode('Заказ в магазине').'?=';
  $headers = "From: ".$_SERVER['SERVER_NAME']." <".$admin.">\r\n";
  // Отправляем скрытую копию письма администратору сайта
  $headers = $headers."Bcc: <".$admin.">\r\n";
  $headers = $headers."Return-path: <".$admin.">\r\n";
  $headers = $headers."Content-type: text/html; charset=\"windows-1251\"\r\n";
  $headers = $headers."Content-Transfer-Encoding: quoted-printable\r\n\r\n";
 
  $body = '<h1>Заказ в магазине</h1>';
  $body = $body.'<table>';
  $body = $body.'<tr>';
  $body = $body.'<td>Имя:</td><td>'.$name.'</td>';
  $body = $body.'</tr>';
  $body = $body.'<tr>';
  $body = $body.'<td>E-mail:</td><td>'.$email.'</td>';
  $body = $body.'</tr>';
  $body = $body.'<tr>';
  $body = $body.'<td>Телефон:</td><td>'.$phone.'</td>';
  $body = $body.'</tr>';
  $body = $body.'<tr>';
  $body = $body.'<td>Адрес:</td><td>'.$adress.'</td>';
  $body = $body.'</tr>';
  $body = $body.'</table>';
  $body = $body.'<br/><br/>';
  $body = $body.$order;
  $body = $body.'<p>Спасибо за покупку! В ближайшее время наш менеджер свяжется с Вами.</p>';
  $body = quoted_printable_encode( $body );
 
  if ( mail($email, $theme, $body, $headers) ) {
    $_SESSION['success'] = true;
  } else {
    $_SESSION['success'] = false;
  }
  header( 'Location: '.$_SERVER['PHP_SELF'] );
  die();
}
 
function quoted_printable_encode ( $string ) {
   // rule #2, #3 (leaves space and tab characters in tact)
   $string = preg_replace_callback (
   '/[^\x21-\x3C\x3E-\x7E\x09\x20]/',
   'quoted_printable_encode_character',
   $string
   );
   $newline = "=\r\n"; // '=' + CRLF (rule #4)
   // make sure the splitting of lines does not interfere with escaped characters
   // (chunk_split fails here)
   $string = preg_replace ( '/(.{73}[^=]{0,3})/', '$1'.$newline, $string);
   return $string;
}

function quoted_printable_encode_character ( $matches ) {
   $character = $matches[0];
   return sprintf ( '=%02x', ord ( $character ) );
}
?>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Оформить заказ</title>
</head>
<body>

<h1>Оформить заказ</h1>

<?php
if ( isset( $_SESSION['success'] ) ) {
  if ( $_SESSION['success'] ) {
    echo '<p>Спасибо за покупку! В ближайшее время наш менеджер свяжется с Вами.</p>';
  } else {
    echo '<p>Ошибка при отправке заказа, попробуйте повторить попытку.</p>';
  }
  unset( $_SESSION['success'] );
}

if ( !isset( $notShowForm ) ) {
  echo '<form action="'.$_SERVER['PHP_SELF'].'" method="POST">';
  echo '<table>';
  echo '<tr><td>Имя:</td><td><input type="text" name="name" maxlength="64" value="'.$name.'" /></td></tr>';
  echo '<tr><td>E-mail:</td><td><input type="text" name="email" maxlength="64" value="'.$email.'" /></td></tr>';
  echo '<tr><td>Телефон:</td><td><input type="text" name="phone" maxlength="64" value="'.$phone.'" /></td></tr>';
  echo '<tr><td>Адрес:</td><td><textarea name="adress" rows="5" cols="30">'.$adress.'</textarea></td></tr>';
  echo '<tr><td>&nbsp;</td><td><input type="submit" name="sendOrder" value="Отправить" /></td></tr>';
  echo '</table>';
  echo '</form>';
}
?>

<p><a href="shop.php">Вернуться в магазин</a></p>

</body>
</html>

Рабочий пример можно посмотреть здесь.

Алфавитная навигация для каталога товаров

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

После этого достаточно сформировать ссылки с полученными буквами и передать через GET-параметр выбранную посетителем букву:

<?php
session_start();
$_SESSION['navigation'] = array();

$dblocation = "localhost";   // Имя сервера
$dbuser     = "root";        // Имя пользователя
$dbpswrd    = "";            // Пароль
$dbname     = "catalog";     // Имя базы данных

// Соединение с сервером базы данных
$dblink = mysql_connect( $dblocation, $dbuser, $dbpswrd );
mysql_query( 'SET NAMES cp1251' );
// Выбираем базу данных
mysql_select_db( $dbname, $dblink );

// Формируем запрос на извлечение первых букв товарных позиций
$query = 'SELECT SUBSTRING(title,1,1) AS letter
          FROM products
          GROUP BY letter
          ORDER BY letter'
;
$res = mysql_query($query);
// Если имеется хотя бы одна запись - выводим её
if( mysql_num_rows( $res ) > 0 ) {
  echo '<p>Алфавитный указатель:&nbsp;';
  while( $prd = mysql_fetch_array( $res ) ) {
    $_SESSION['navigenion'][] = $prd['letter'];
    echo '<a href="'.$_SERVER['PHP_SELF'].'?letter='.urlencode($prd['letter']).'">'.$prd['letter'].'</a>&nbsp;';
  }
  echo '</p>';
}
echo '<p><a href="'.$_SERVER['PHP_SELF'].'">Все товары</a></p>';
// Если передан параметр letter и он состоит из одного символа -
// выводим содержимое таблицы
if ( isset($_GET['letter']) and in_array($_GET['letter'], $_SESSION['navigenion']) ) {
  $query = "SELECT * FROM products
            WHERE SUBSTRING(title,1,1) = '"
.$_GET['letter']."'
            ORDER BY price"
;
} else {
  $query = "SELECT * FROM products ORDER BY price";
}
$res = mysql_query($query);
// Если имеется хотя бы одна запись - выводим её
if( mysql_num_rows( $res ) > 0 ) {
  $i = 1;
  echo '<table border="1" cellpadding="3" cellspacing="0">';
  echo '<tr><th>№</th><th>Наименование</th><th>Цена</th></tr>';
  while( $prd = mysql_fetch_array( $res ) ) {
    echo '<tr><td>'.$i.'</td><td>'.$prd['title'].'</td><td>'.$prd['price'].'</td></tr>';
    $i++;   
  }
}
?>