PHP.SU

Программирование на PHP, MySQL и другие веб-технологии
PHP.SU Портал     На главную страницу форума Главная     Помощь Помощь     Поиск Поиск     Поиск Яндекс Поиск Яндекс     Вакансии  Пользователи Пользователи


 Страниц (2): [1] 2 »   

> Описание: Как оно делается
DeepVarvar Супермодератор
Отправлено: 31 Января, 2013 - 03:30:51
Post Id



Активный участник


Покинул форум
Сообщений всего: 10419
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




.

Как сделать корзину на сайте

Собственно топик не возник сам собой. На то есть причины.
А причины как всегда просты - повальное желание новичков взять эту "высоту".
И прямо таки в последние две недели - обострение вопросов на данную тему.
И как всегда каждый лепит велосипеды - кто во что горазд.

Так давайте же покажу вам как сделать все быстро просто понятно и красиво.

Сразу определяем задачи:

1) Работаем с MySQL.
2) Нужен учет позиций и их кол-во, т.е. так: "Товар 1 - 6 шт., Товар 2 - 1 шт."
3) Минимум запросов и вообще минимум кода.

Ну так поехали.

Создаем две таблицы.

Первая будет тестовой таблицей с продуктами.
Надо ж на чем то показывать что у нас все работает Закатив глазки
CODE (SQL):
скопировать код в буфер обмена
  1. CREATE TABLE IF NOT EXISTS products (
  2.   id BIGINT NOT NULL AUTO_INCREMENT,
  3.   name TEXT NOT NULL,
  4.   price DECIMAL NOT NULL,
  5.   PRIMARY KEY (id)
  6. )
  7. ENGINE = MYISAM;

Тут у нас айдишник товара, название и цена (DECIMAL) это типа если с копейками будет.

Вторая табличка, собственно, это сама корзина.
Называйте её как хотите, я назвал её basket.
Кому не нравится - дело ваше, именуйте её cart, сути не меняет.
CODE (SQL):
скопировать код в буфер обмена
  1. CREATE TABLE IF NOT EXISTS basket (
  2.   hash CHAR(32) NOT NULL,
  3.   product_id BIGINT NOT NULL
  4. )
  5. ENGINE = MYISAM;

Тут у нас хеш корзины пользака и айдишник товара.
Ну чтобы знать кто добавил и что добавил.

Кому не нравится MYISAM, тоже дело ваше, хотите, ставьте InnoDB.
Я не буду в данной статье вдаваться в подробности различия "движков" для таблиц.

Запиливаем тестовые товары в таблицу товаров.
Штук двести сразу, чтоб было из чего выбирать.
Коннект к БД я опустил, надеюсь что подключаться к базе вы умеете.
А если не умеете, то и эту статью читать вам рано Закатив глазки
Простите уж за запросы в цикле, но в одну обсуждаемую тему не уместить всего того,
что необходимо делать правильно. Вобщем запросы в цикле - это очень плохо.
Надеюсь тут дядю (меня) читатели простят Закатив глазки
Так же простите что использую DEPRECATED mysql_*.
Но обсуждение данного вопроса, я так же намеренно опустил.
PHP:
скопировать код в буфер обмена
  1. for ($i =0; $i < 200; $i++) {
  2.   $name = "Товар № ".mt_rand();
  3.   $price = mt_rand(1,2765) + (mt_rand(0,99)/100);
  4.   mysql_query("INSERT INTO products (id,name,price) VALUES (NULL,'$name',$price)");
  5. }


Теперь, когда тестовые данные готовы, таблицы созданы, и солнце светит ярко...
Создадим наконец наш отдельный файл, который будет являться обработчиком для действий с корзиной basket.php:
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2.  
  3. /**
  4.  * тут коннекты к базе
  5.  * можете использовать своего любимого Ж.Попова:
  6.  * include('blocks/bd.php');
  7.  * считаем что БИДЭ подключено, мои юные сантехники :)
  8.  */
  9.  
  10.  
  11.  
  12. /**
  13.  * для удобства, а не для понтов,
  14.  * создадим функцию-обертку получения результатов из базы
  15.  */
  16.  
  17. function DbQuery($query) {
  18.   $result = mysql_query($query) or die("Смотри куда прёшь: " . mysql_error());
  19.   $arr = array();
  20.   while ($row = mysql_fetch_assoc($result)) {
  21.     $arr[] = $row;
  22.   }
  23.   return $arr;
  24. }
  25.  
  26.  
  27. /**
  28.  * и еще одну для обновления данных
  29.  * INSERT, DELETE или UPDATE
  30.  * возвращать то нам тут ничего не надо
  31.  */
  32.  
  33. function DbSet($query) {
  34.   mysql_query($query) or die("Нее, не прокатит: " . mysql_error());
  35. }
  36.  
  37.  
  38. /**
  39.  * после любых изменений в корзине,
  40.  * надо типа делать редирект на просмотр списка товаров
  41.  * в актуальном виде внутри корзины,
  42.  * чтоб не повадно было коцать добавление товара
  43.  * такой маленький антидидос :)
  44.  */
  45.  
  46. function resetStatus() {
  47.   header("Location: /basket.php");
  48.   die();
  49. }
  50.  
  51.  
  52. /**
  53.  * плюс один товар в позиции
  54.  * проще некуда, добавляет новую запись в таблицу
  55.  */
  56.  
  57. function addToBasket($hash, $id) {
  58.   DbSet("INSERT INTO basket (id,hash,product_id) VALUES (NULL,'$hash',$id)");
  59. }
  60.  
  61.  
  62. /**
  63.  * пытаемся удалить ТОЛЬКО ОДНУ! запись из таблицы
  64.  * если таковая существует
  65.  */
  66.  
  67. function minusInnerBasket($hash, $id) {
  68.   DbSet("DELETE FROM basket WHERE hash = '$hash' AND product_id = $id LIMIT 1");
  69. }
  70.  
  71.  
  72. /**
  73.  * удаляем всю позицию из корзины
  74.  * сколько бы там товаров ни было в количестве
  75.  * где хеш - это хеш пользака,
  76.  * и айди - это айди товара
  77.  */
  78.  
  79. function deleteFromBasket($hash, $id) {
  80.   DbSet("DELETE FROM basket WHERE hash = '$hash' AND product_id = $id");
  81. }
  82.  
  83.  
  84. /**
  85.  * теперь сам "контроллер" описанных выше действий с корзиной
  86.  * собственно это и есть зачаток нормального контроллера
  87.  * но тут он совсем прост
  88.  * и нам не суть разбираться в этих тонкостях прямо сейчас
  89.  */
  90.  
  91. function controller($hash) {
  92.   if (isset($_GET['add']) and (int) $_GET['add'] > 0) {
  93.     // плюс один товар в позиции
  94.     addToBasket($hash, (int) $_GET['add']);
  95.     resetStatus();
  96.   } else if (isset($_GET['minus']) and (int) $_GET['minus'] > 0) {
  97.     // минус один товар в позиции
  98.     minusInnerBasket($hash, (int) $_GET['minus']);
  99.     resetStatus();
  100.   } else if (isset($_GET['del']) and (int) $_GET['del'] > 0) {
  101.     // полное удаление позиции товара
  102.     deleteFromBasket($hash, (int) $_GET['del']);
  103.     resetStatus();
  104.   }
  105. }
  106.  
  107.  
  108. /**
  109.  * теперь собственно запустим все это чудо в работу
  110.  */
  111.  
  112. if (isset($_COOKIE['basket']) and ((string) $_COOKIE['basket'])) {
  113.   // если кука корзины была
  114.   $basket_hash = (string) $_COOKIE['basket'];
  115. } else {
  116.   // ну или если куки корзины не было
  117.   $basket_hash = md5("не забудьте посолить" . microtime());
  118. }
  119.  
  120. // обновляем куку в любом случае
  121. setcookie("basket", $basket_hash, time() + 26400);
  122.  
  123.  
  124. /**
  125.  * а вот тут дергаем наш "контроллер"
  126.  * передав ему хеш корзины текущего пользака
  127.  */
  128.  
  129. controller($basket_hash);
  130.  
  131.  
  132. /**
  133.  * тут или контроллер отработает
  134.  * и сделает редирект на /basket.php без параметров
  135.  * или то, что идет ниже, уже есть /basket.php без параметров :)
  136.  * получам список товаров В КОРЗИНЕ и их кол-во ОДНИМ ЗАПРОСОМ
  137.  * сортируем сразу по кол-ву товаров в позиции
  138.  * первыми будут идти те позиции,
  139.  * в которых большее кол-во товаров
  140.  */
  141.  
  142. $productsInBasket = DbQuery("
  143.    SELECT b.product_id, COUNT(b.product_id) cnt, p.name, p.price
  144.      FROM basket b
  145.      JOIN products p ON p.id = b.product_id
  146.      WHERE b.hash = '$basket_hash'
  147.      GROUP BY b.product_id
  148.      ORDER BY cnt DESC
  149. ");
  150.  
  151.  
  152. /**
  153.  * получам просто список товаров для теста
  154.  * ну штук десять например
  155.  */
  156.  
  157. $products = DbQuery("
  158.    SELECT * FROM products ORDER BY price DESC LIMIT 10;
  159. ");
  160.  
  161.  
  162. /**
  163.  * и тут наверное уже 70% подумало "а где же эти <TR><TD>?"
  164.  * спокойно, сейчас они появятся :)
  165.  * все данные получены, теперь их можно выводить
  166.  * подключаем шаблон вывода
  167.  */
  168.  
  169. require_once "basket_template.html";

Вот он, basket_template.html:
CODE (htmlphp):
скопировать код в буфер обмена
  1. <html>
  2. <head>
  3. <meta http-equiv="content-language" content="ru" />
  4. <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
  5. <title>Корзина</title>
  6. </head>
  7. <body>
  8.  
  9.   <h1>Просто продукты:</h1>
  10.   <?php foreach ($products as $product) { ?>
  11.     <div style="float: left; margin: 4px; width: 150px; height: 250px; text-align: center;">
  12.       <h4><?=$product['name']?></h4>
  13.       <h5><?=$product['price']?> руб.</h6>
  14.       <a href="/basket.php?add=<?=$product['id']?>">в корзину</a>
  15.     </div>
  16.   <?php } ?>
  17.   <div style="clear: both;"></div>
  18.  
  19.   <hr />
  20.  
  21.   <h1>Корзина:</h1>
  22.  
  23.   <ul>
  24.     <?php foreach ($productsInBasket as $product) { ?>
  25.       <li>
  26.         <b><?=$product['name']?></b>
  27.         <?=$product['cnt']?> шт.
  28.         (<?=($product['price'] * $product['cnt'])?> руб.)
  29.         [<a href="/basket.php?add=<?=$product['product_id']?>">+1</a>] /
  30.         [<a href="/basket.php?minus=<?=$product['product_id']?>">-1</a>] /
  31.         [<a href="/basket.php?del=<?=$product['product_id']?>">удалить</a>]
  32.       </li>
  33.     <?php } ?>
  34.   </ul>
  35.  
  36.   <?php if (!$productsInBasket) { ?>
  37.     <h3>Корзина пуста</h3>
  38.   <?php } ?>
  39.  
  40. </body>
  41. </html>


Вопросы? Закатив глазки

.
 
 Top
KingStar
Отправлено: 31 Января, 2013 - 06:28:19
Post Id



Участник


Покинул форум
Сообщений всего: 1889
Дата рег-ции: Авг. 2011  
Откуда: Беларусь


Помог: 69 раз(а)




ну это если пользователь, а если гость??? в основном таки все магазы так и работаю - вначале корзина, после как доходит дело до заказа - рега, или даже если зареген, но не авторизован - та же проблема. ИМХО айпишник нужно юзать в качестве принадлежности кукис и записей в базе


-----
То что программа работает, не означает что она написана правильно!
 
 Top
KingStar
Отправлено: 31 Января, 2013 - 06:30:48
Post Id



Участник


Покинул форум
Сообщений всего: 1889
Дата рег-ции: Авг. 2011  
Откуда: Беларусь


Помог: 69 раз(а)




хотя... может и зря я Подмигивание принцип то тот же Хорошо
(Добавление)
посмотрел, смотря что понимать под хешем Улыбка у меня как раз таки хеш есть хеш
(Добавление)
я бы сделал тип таблицы int(10) unsigned и кидал бы IP с INET_ATON


-----
То что программа работает, не означает что она написана правильно!
 
 Top
DeepVarvar Супермодератор
Отправлено: 31 Января, 2013 - 06:42:59
Post Id



Активный участник


Покинул форум
Сообщений всего: 10419
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




KingStar пишет:
IP с INET_ATON
Ну тут еще и заказчик может начать решать. Нужна ли ему жесткая привязка к IP или нет.
Может и не нужна. А так на деле всеравно манагер перезванивает покупателю и уточняет-подтверждает заказ.
И если покупатель не подтвердил лично - заказ аннулируется.
Смысл привязываться к IP?

Так же нет смысла держать какие-то сессии корзины у залогиненого пользака.
Когда дело дойдет до формы заказа - просто подставляем ФИО из профиля.

Вопросы скидок и спец-условий я тут не рассматривал.
Это уже сложнее, и может быть не понято с ходу Закатив глазки
 
 Top
KingStar
Отправлено: 31 Января, 2013 - 07:44:24
Post Id



Участник


Покинул форум
Сообщений всего: 1889
Дата рег-ции: Авг. 2011  
Откуда: Беларусь


Помог: 69 раз(а)




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

Цитата:
Так же нет смысла держать какие-то сессии корзины у залогиненого пользака.


так же как и нет смысла держать в базе заказы гостя, пока он не авторизуется или не зарегится


-----
То что программа работает, не означает что она написана правильно!
 
 Top
DeepVarvar Супермодератор
Отправлено: 31 Января, 2013 - 07:48:19
Post Id



Активный участник


Покинул форум
Сообщений всего: 10419
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




KingStar пишет:
так же как и нет смысла держать в базе заказы гостя
Одно от другого же Закатив глазки
Или так или так.
 
 Top
Мелкий Супермодератор
Отправлено: 31 Января, 2013 - 07:59:14
Post Id



Активный участник


Покинул форум
Сообщений всего: 11573
Дата рег-ции: Июль 2009  
Откуда: Россия, Санкт-Петербург


Помог: 563 раз(а)




DeepVarvar пишет:
цена (FLOAT) это типа если с копейками будет.

Деньги в float'ах хранить нельзя. decimal на что? Не говоря о том, что можно просто в копейках хранить и делить на 100 при выводе.

DeepVarvar пишет:
_query("$query")

Ох я в печали... Берегись, это, видимо, заразно.

Нельзя заказы к ip привязывать. Зайдёт пара человек с одного NAT'а - и будут мучаться, потому что в корзине будут товары обоих сразу.


-----
PostgreSQL DBA
 
 Top
DeepVarvar Супермодератор
Отправлено: 31 Января, 2013 - 08:02:29
Post Id



Активный участник


Покинул форум
Сообщений всего: 10419
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




Мелкий пишет:
Деньги в float'ах хранить нельзя. decimal на что? Не говоря о том, что можно просто в копейках хранить и делить на 100 при выводе.
Отличное замечание.
Мелкий пишет:
Ох я в печали... Берегись, это, видимо, заразно.
Аааа, копипаст - зло! Исправил.
 
 Top
KingStar
Отправлено: 31 Января, 2013 - 08:06:49
Post Id



Участник


Покинул форум
Сообщений всего: 1889
Дата рег-ции: Авг. 2011  
Откуда: Беларусь


Помог: 69 раз(а)




Мелкий согласен - в этом минус, но по другому то как привязать товар к гостю???
(Добавление)
СТАПЕ!!! я не говорил чтобы товар привязывали в базе к IP, я говорю про кукисы - именно там на время сохраняем карзину без внесения данных в базу, а после заказа, как пользователь согласен будет на заказ - то он регится или авторизуется, и на этом этапе уже товар добавляется в базу и привязывается конкретно к пользователю


-----
То что программа работает, не означает что она написана правильно!
 
 Top
DeepVarvar Супермодератор
Отправлено: 31 Января, 2013 - 08:12:21
Post Id



Активный участник


Покинул форум
Сообщений всего: 10419
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




KingStar пишет:
я говорю про кукисы - именно там на время сохраняем карзину
и запиливаем кучу ненужных циклов с добавляньями, удаляньями позиций товаров.
А табличку и почистить можно, по крону... Что ей пару тысяч записей ненужных понакапливать?
 
 Top
KingStar
Отправлено: 31 Января, 2013 - 08:22:28
Post Id



Участник


Покинул форум
Сообщений всего: 1889
Дата рег-ции: Авг. 2011  
Откуда: Беларусь


Помог: 69 раз(а)




DeepVarvar пишет:
и запиливаем кучу ненужных циклов с добавляньями, удаляньями позиций товаров.
А табличку и почистить можно, по крону... Что ей пару тысяч записей ненужных понакапливать?


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


-----
То что программа работает, не означает что она написана правильно!
 
 Top
DeepVarvar Супермодератор
Отправлено: 31 Января, 2013 - 08:30:44
Post Id



Активный участник


Покинул форум
Сообщений всего: 10419
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




KingStar пишет:
По моему ты сейчас просто уперся
Не уперся. Просто изначально предлагаю концепт без привязки к пользователю. А вот ты, возможно уперся Радость
KingStar пишет:
вообще не понял о чем ты
Большую часть функционала по расчетам, добавлениям, удалениям, сортировке - я перенес на твердые плечи БД. В твоем же случае придется писать несколько костыликов с циклами по массиву корзинки на стороне приложения. Не так ли?
 
 Top
KingStar
Отправлено: 31 Января, 2013 - 08:36:35
Post Id



Участник


Покинул форум
Сообщений всего: 1889
Дата рег-ции: Авг. 2011  
Откуда: Беларусь


Помог: 69 раз(а)




все же правильней было бы разноплановый концепт Подмигивание


-----
То что программа работает, не означает что она написана правильно!
 
 Top
DeepVarvar Супермодератор
Отправлено: 31 Января, 2013 - 08:43:35
Post Id



Активный участник


Покинул форум
Сообщений всего: 10419
Дата рег-ции: Дек. 2008  
Откуда: Альфа Центавра


Помог: 353 раз(а)




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

http://javascript[dot]ru/unsorted/id
 
 Top
KingStar
Отправлено: 31 Января, 2013 - 09:13:08
Post Id



Участник


Покинул форум
Сообщений всего: 1889
Дата рег-ции: Авг. 2011  
Откуда: Беларусь


Помог: 69 раз(а)




ну прям уж страшилище Радость Радость

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


-----
То что программа работает, не означает что она написана правильно!
 
 Top
Страниц (2): [1] 2 »
Сейчас эту тему просматривают: 1 (гостей: 1, зарегистрированных: 0)
« Обсуждение статей »


Все гости форума могут просматривать этот раздел.
Только зарегистрированные пользователи могут создавать новые темы в этом разделе.
Только зарегистрированные пользователи могут отвечать на сообщения в этом разделе.
 



Powered by PHP  Powered By MySQL  Powered by Nginx  Valid CSS  RSS

 
Powered by ExBB FM 1.0 RC1. InvisionExBB