Одностраничный магазин с корзиной на Phalcon + AngularJS + Zurb Foundation
Введение Всем привет! Завтра у меня дедлайн по проекту, который я делаю для местной Камчатской компании по доставки еды. И поэтому у меня есть две причины написать эту статью, первая — прокрастинация перед дедлайном, а вторая — я не нашёл на Хабре какого-либо обучающего мануала по написанию корзины товаров на AngularJS.Я нашёл статью на стороннем блоге, которая частично помогла мне решить пару задач, которые стояли передо мной. Но оформление статьи оставляло желать лучшего, да и за 5 лет я уже отвык от кода в блокноте, без подсветки синтаксиса, поэтому нужно было как-то структурировать и сделать более читабельной эту полезную информацию.
Почему был выбран формат одностраничного магазина? Кто-то из вас, наверное уже знает, что на Камчатке существует проблема с интернетом, так как наш полуостров ещё не связан с материком оптоволокном, и весь поток идёт через единственную вышку. К концу 2015 года планируется завершить работы по прокладке оптоволокна по дну Охотского моря, и возможно у нас появится наконец-то стабильный и быстрый интернет.И соответственно чтобы исключить потери клиентов, переходящих по разным категориям блюд, из-за нестабильного канала, была выбрана подобная модель. То есть человеку достаточно один раз загрузить сайт и получить всю необходимую информацию, и сделать заказ.
Так же такой формат позволил избежать хранения товаров корзины в сессиях, localStorage или же в базе данных. Так как мы точно знаем, что человек никуда не уйдёт с этой страницы, мы храним данные корзины в объекте javascript. Ещё одним плюсом стало уменьшение времени заказа, так как нет нужды перемещаться по категориям, и загружать новые страницы. И так как позиций блюд не слишком много, нам даже не пришлось делать Ajax-подгрузку данных при нажатии на категорию, всё подгружалось из кеша базы данных.
База данных После того, как был готов и свёрстан дизайн одностраничного сайта, пришло время создать структуру базы данных категорий и товаров. Это наверно самый быстрый и самый простой этап, учитывая наши потребности и направленность на простоту работы системы и взаимодействия с пользователями. У меня уже был набросок админки на Phalcon PHP Framework, поэтому поправить его для работы с двумя таблицами category и products, не составило особого труда.Таблица category
Таблица products
Вот так выглядит админка сайта
Получаем категории и блюда из базы данных Для работы с базой данных использовалась стандартная ORM система фреймворка Phalcon, если вы с ней не работаете, можете пропустить этот раздел, он для общего развития, чтоб дальше было понятно откуда ноги растут, точнее откуда и как берутся блюда на сайте.В основном контроллере IndexController.php в модуле frontend, я написал функцию, которая сформирует данные нужным нам образом и выведет их на единственную нашу страницу.
public function indexAction () { //Получаем все категории в массив $category = Category: find ()→toArray (); $help = new \Lib\Url (); Перебираем массив и для каждой категории подгружаем блюда foreach ($category as $key => $val) { $category[$key]['url'] = $help→translit_url ($val['name']); //Это не родительская категория? Тогда удаляем из массива if ($val['pid'] != 0) { unset ($category[$key]); } else { //Подгружаем массив подкатегорий, правда в нашем случае только одна категория имеет подкатегории $category[$key]['sub'] = Category: find («pid = '».$val['id'].»'»)→toArray (); //Если есть подкатегории, цепляем к ним блюда, если нет, то цепляем блюда к основном категории if (count ($category[$key]['sub']) > 0) { foreach ($category[$key]['sub'] as $i => $cat) { $category[$key]['sub'][$i]['products'] = Products: find («category = '» . $cat['id'] .»'»)→toArray (); } } else { $category[$key]['products'] = Products: find («category = '» . $val['id'] .»'»)→toArray (); } } } $this→view→setVar ('items',$category); } Возможно есть более изящные решения этой задачи, но у нас не будет больше 200 посетителей за день, и мы подключим кеширование запросов к базе данных, и в принципе не будет такой сильной нагрузки, тем более это Phalcon — «Самый быстрый PHP фреймворк». Но вопрос сейчас не о производительно и оптимизации, это пока рано, главное что пора выводить товар на странице.
Кому доверить рендеринг товаров на странице? AngularJS или Phalcon? Сначала я реализовал всё на AngularJS, ну это было как-то изящнее и красивее, но потом задумался о СЕО-оптимизации, и индексации поисковыми системами, и подумал что лучше наверно не рисковать, и рендеринг доверить нашему старому доброму любимому PHP.Приводить код выводящий товары на странице я тут не буду, как из соображений величины этого кода, так и из этических соображений, всё таки это продукт нашего заказчика. Да и думаю, кто читает Хабр знает как пользоваться функцией foreach в php.
Ну ладно, покажу как я выводил категории блюд, а там уже по примеру каждый разберётся и с товарами.
-
» alt=»= $item['name'] ?>»>
= $item['name'] ?>
Добавление товара в корзину Нажимая на категорию, внизу открывается таб с блюдами из этой категории. Возможно кто-то из вас уже не может смотреть на эти аппетитные картинки, но для продолжения описания работы одностраничного магазина, мне просто необходимо показать вам, как выглядит карточка товара на сайте. Смотрите. Так, а теперь вытирайте слюни с клавиатуры, и поехали дальше, смотреть как же работает основной функционал добавления в корзину и высчитывания конечной суммы заказа.
В основном для понимания основы вам нужно видеть только этот отрезок кода, который выводит кнопку «Добавить» с полем, куда можно ввести количество штук блюда.
Кстати, функция ng-cloak просто выручает в условиях Камчатского интернета, если её не использовать, пока человек будет ждать загрузку страницы, ему будет видна пустая корзина со страшными символами. Для тех кто не знаком с AngularJS, укажу на несколько ключевых моментов.Это нужно, чтобы показывать корзину, только в случае если в ней есть товары.
ng-show=«carts.length > 0» Этот код выводит конечную сумму заказа, форматируя число, и убирая копейки, которые могут получится, при высчитывании 10% скидки, например в случае самовывоза. {{total () | number: 0}} У меня часто возникала задача удалить элемент ассоциативного массива, я каждый раз забывал как это делать, и обращался к Google, но надеюсь после этой публикации я наконец-то запомню, а те кто не знал, узнают. $scope.removeItem = function (carts, item) { carts.splice (item, 1); }; А что, кто-то рассчитывал что будет больше кода? Можно кстати даже в одну строчку написать. Но не будем изгаляться, нам главное читабельность кода. И наверно последнюю функцию которую я хочу привести в этой статье — это подсчёт конечной суммы заказа. Он был выполнен исходя из условий и способов доставки. $scope.total = function () { var total = 0; angular.forEach ($scope.carts, function (item) { total += item.num * item.price; }); var delivery = 0; if ($scope.delivery == 0) { if (total >= 600) { delivery = 0; } else { delivery = 130; } } if ($scope.delivery == 1) { if (total >= 1500) { delivery = 0; } else { delivery = 130; } } if ($scope.delivery == 2) { delivery = 300; } if ($scope.delivery == 3) { delivery = 10/total*100*(-1); } return total + delivery; }; Чем объяснять откуда растут цифры, я просто покажу блок с информацией о доставке, а вы уже сами разгадаете откуда появились разные цифры в коде. Тем более у каждого эти данные будут разные, и будет разное количество районов, поэтому не вижу смысла заострят на этом внимание, тем более статья и так уже получилась достаточно объёмная. Послесловие Главная задача моего поста решена, теперь на Хабре есть материал, о том как сделать добавление товаров в корзину и подсчёт суммы заказа на AngularJS, а в интернете есть хорошо оформленный материал об этом, который дополнит запись из стороннего блога, ссылку на который я привёл в начале статьи. Ну, а так же, я уже достаточно прокрастинировал, поэтому пора приступать снова к работе, и заканчивать отправку заказа нашему заказчику. Надеюсь статья поможет таким же как я. Если у вас всё же возникнут вопросы по приведённому исходному коду, или что-то будет не понятно, я с удовольствием отвечу в комментариях. К сожалению опыт работы с AngularJS всего пол года, поэтому чем смогу, тем помогу. Спасибо за ваше внимание.