Сегодня я расскажу вам про мой интернет-магазин. Как я решил его сделать и потом переделывал. Как я мучался над архитектурой и организацией логики. Как я рыскал по сети в поисках подходящих юзабилити решений, и что в итоге придумал. Под катом довольно много интересных технических и не очень подробностей. Чего это я вдругНачну с того, что живу я в славном городе суровых мужиков — в Челябинске. И вот, однажды вечером (около года назад), я подумал, что очень хочу пользоваться интернет-магазином продуктов с доставкой на дом. Просто в холодильнике внезапно всё кончилось, а кодинг шёл в полную силу — с дымом от клавиатуры, и отрываться не хотелось. А в городе у нас, между тем, нет представительства ни одной фирмы, занимающейся чем-то подобным (типа Ашана или Утконоса). Службы доставки с заказом по телефону не рассматриваю как несерьёзные. Причины такого положения не ясны. То ли они не видят перспектив, то ли далековато до нас, то ли я слишком наивен и у меня всё рухнет и нет никаких шансов, а они уже всё это просчитали и поэтому не лезут =). Короче, кто не рискует — тот не пьёт шампанского. Поэтому будем кодить.
Первый блинОбдумав эту идею, отдал её младшему брату. Сказал выбрать простую бесплатную систему, разобраться, поставить, настроить и запустить. Просто бесплатной системой оказалась OSCommerce. Я как бы и раньше слышал, что сия система не то чтобы очень. Однако ж среди других альтернатив она казалась более-менее доступной, понятной и расширяемой. Боже, как я ошибался… Короче брат возился с этим !#$^@% несколько месяцев, после чего сайт мы всё-таки запустили. Это было ужасно. Всё скрипело и тормозило, разваливалось и вообще не поддавалось какому бы то ни было расширению и улучшению для удержания посетителей. Короче атас. Поработав в таком виде пару месяцев, было решено снести всё нафиг и начать с нуля.
My first loveВзгляд как-то сразу упал на CakePHP, несмотря на статьи на хабре, восхваляющие Code Ignighter (которого я пока так и не посмотрел). Мне трудно описать словами тот восторг, который я испытывал создавая по документации что-то типа блога. Это было просто волшебно. Встроенные компоненты, automagic, обработка форм… А возможности связей таблиц — это просто экстаз! Здесь всё уже написано и разрулено до нас — нужно просто сесть и фигачить. Короче, вы поняли. Отдельно стоит заметить, что в этот раз кодить мы сели вдвоём.
База данныхИтак, магазин продуктов. Опустим, пожалуй, такие тривиальные вещи как пользователи, корзины, категории и т.д. Что действительно интересно — так это организация продуктов. В OSCommerce всё было просто и понятно. Одна таблица, тупо список. id#name#price
Таким образом, таблица была заполнена чем-то вроде этого: Чипсы 90г бекон Чипсы 120г бекон Чипсы 90г сыр Чипсы 120г сыр Чипсы 240г сыр
Где красота, где рациональность? Надо как-то это улучшить. Самое простое — все вариции засунуть в атрибуты, и придумать механизм их обработки.
Неудачный вариант таблица products: id#name#image#base_price
//price_change предполагалось указывать как изменение цены относительно базовой, например 1.0 или 2.2
attr_type — это тип атрибута, например вес или вкус.
Почему неудачный? Потому что здесь не учтены возможности группировки атрибутов. Например, есть в таблице вкус: Бекон вкус: Сыр вес: 120г вес: 240г
И как нам указать, что есть товар «Бекон 120г», а «Бекон 240г» нет? Значит нужно указывать правила группировки атрибутов. Развитие этой мысли привело к следующему решению.
Гениальный вариант mould (англ.) — шаблон, форма для литья. таблица moulds: id#name#image //обратите внимание — здесь нет цены
таблица attributes: id#attr_type#name //больше нет product_id и price_change
Теперь товар формируется очень гибко. Начём с таблицы attributes. Она заполнена у меня чем-то вроде этого: 1#вес#100г 2#вес#120г 3#вес#150г 4#вес#240г 5#вес#1кг 6#объём#0,33л 7#объём#0,5л 8#объём#1л 9#вкус#бекон 10#вкус#сыр 11#вкус#клубника
Понятно, что attr_type содержит только идентификатор, который указывает на таблицу имён атрибутов — просто здесь вынес для ясности.
Пример записи в moulds 1#Чипсы Lays#lays.jpg
А теперь следите за руками! Who wants to see some magic? Записи в products 1#1#12 2#1#12 3#1#24
Записи в products_to_attributes 1#1#2 2#1#9 3#2#2 4#2#10 5#3#4 6#3#10
Объясняю. Мы создаём три продукта в соответствующей таблице, указывая mould, который они наследуют, и цену. И тут же назначаем атрибуты. Продукт #1, например, получает вес 120г и вкус бекона, а продукт #3 имеет вес 240г и вкус сыра. В такой системе можно описать абсолютно любой товар, с любым числом вариаций. Можно даже сделать mould «Cola», и от него создать продукты со вкусами «Mirinda» или «Sprite».
ПоискПоиск занимает особое место в юзабилити моего ресурса (об этом ниже), поэтому к реализации его нужно подойти со всей серьёзностью. В начале я решил поизучать сторонние проекты. Порадовал sphinx, но оказалось что его нужно ставить на машину, а у меня пока только вирт. хостинг. Потом смотрел на Zend Lucene (отдельное спасибо хабраюзеру , который нашёл время выслать мне собственную standalone сборку этой штуки). Смотрел, пробовал, тыкал, ставил, но запустить нормально не смог. Потом также развлекался с fulltext поиском самой mysql, однако тоже безуспешно. Пришлось писать самому.
Тут ничего, как мне кажется, особо гениального нет, но это всё сделано мной лично, а не по чьим-то умным статьям. Поэтому расскажу, пожалуй. Для организации поиска делаем две таблицы: searchindex: id#stem
Общий принцип работы вполне очевиден. При индексации указываются модели и поля, которые нужно обработать. Затем в цикле выбираются текстовые поля и разбиваются по словам. У каждого слова эвристически извлекаем корень (стемминг), и смотрим — есть ли уже такой же корень в базе. Дописываем в searchcont найденный стем, модель, поле, идентификатор ряда и позицию, на которой находится слово (это нам понадобится для сортировки по релевантности).
При поиске запрошенная строка разбивается на слова, у каждой ищутся совпадения. Затем, если несколько слов найдены в одной записи модели, считаем релевантность. Помните про поле position? Ну так вот, я решил просто посчитать дисперсию набора значений. Грубо говоря, это разлёт значений от среднего. То есть чем кучнее находятся совпавшие слова в записи, тем сама запись релевантнее. Считается по формуле
— это среднее арифметическое всех значений.
Да, согласен, метод довольно грубый. Но у меня поисковая фраза в основном состоит из одного-двух, максимум трёх слов, так что такая оценка вполне приемлема.
ЮзабилитиВ процессе проектирования излазил кучу сайтов — искал советы, варианты, готовые решения. Нашёл много всего полезного и не очень. Многое внедрил, а кое-что только планирую. То, что уже работает (здесь не только классические юзабилити фишки, но ещё и некоторые идеи по организации работы с магазином):
Поскольку каталог у меня довольно объёмный (одних только категорий больше 200) было решено сделать упор не на браузинг по каталогу, а на использование формы поиска, которая выделена. Плюс к этому в сайдбаре представлены не все возможные категории, а только популярные.
Возможность совершения покупки без регистрации
Помощь на месте у каждого пункта
Интерфейс с упором на функционал. Из графики только логотип, градиентик на заднем плане и иконки.
Связь с оператором прямо из браузера на основе виджета от google talk
Аяксовая корзина
История покупок с возможностью частично или полностью добавить старый заказ в новую корзину
Наличие сущности «пакетов товаров». Грубо говоря, это заранее собранная корзина, имеющая label (название, чтоб не потеряться). Её тоже можно добавить в текущую формируемую либо целиком, либо по частям. Зарегистрированные пользователи могут сами создавать пакеты товаров.
Множество тонкостей, недостойных отдельных пунктов: удобная форма регистрации, линки на просмотр корзины и оформление заказа со всех страниц (если пользователь имеет корзину в статусе формирования), все контакты крупно в шапке, кнопка «Купить» у каждого продукта при любом представлении списка товаров (внутри категории, результаты поиска)
То, что планируется:
Пользователи, которые купили этот товар, также выбрали...
Написать FAQ и инструкцию «Как купить».
Есть идея «любимых товаров». Хотя, возможно, это лишнее.
Что сейчас на сайте. Кто и что покупает.
Надо выдумать какую-то ещё систему взаимодействия пользователей.
В реализации чего я не уверен:
Товары представлены «плиткой», по два в ряду. Текущее представление содержимого плитки не очень нравится.
Над личной панелью можно (нужно?) работать очень много.
?
Собственно ссылка: ку Надеюсь, хабрасообществом это не будет вопринято как реклама. Я всего-лишь хвастаюсь своим творением, как разработчик. Как думаете, может этот магазин стать идеальным? Я имею ввиду как пример реализации интернет-магазина. habrahabr.ru/blogs/webdev/44338/