История


Немного предыстории. Первая версия Shopkeeper вышла в 2009 году. Тогда это был модуль для CMS MODX, а точнее для первой его ветки, которая сейчас называется Evolution. В то время у MODX был всего один подобный модуль, но его качество меня не устраивало, поэтому я решил написать свой. Плюс нужен был какой-то проект для наращивания опыта в программировании на PHP. Т.к. конкуренции почти не было, мой компонент стал самым популярным для той версии MODX.

Страшная картинка


Сначала Shopkeeper это был только компонент для создания корзины товаров и управления заказами. Позже добавился функционал для управления каталогом (создание и редактирование товаров и т.п.).

Позднее вышел MODX Revolution. Для него я тоже разработал новую версию своего компонента.

Так выглядел интерфейс управления заказами Shopkeeper 2.0


Затем я немного отошел от разработки на MODX и углубился во Front-end разработку. В основном в то время я работал на AngularJS 1.x. Когда пришло время обновить свой компонент для создания интернет-магазина, я принял решение разрабатывать его на AngularJS.

Shopkeeper 3.0


3.2.7-pl3 — Последняя на данный момент версия Shopkeeper из ветки 3.x.

На основной работе я часто использую Symfony, Angular 2.0+ и MongoDB, поэтому новую версию SHK я разрабатываю с использованием этих инструментов.

Обзор возможностей


Shopkeeper 4.0 — это не просто компонент для CMS, это самостоятельный движок. Данное приложение я стараюсь сделать максимально простым, но в то же время гибким. Планируется возможность интеграции с другими CMS. В первую очередь будет сделана интеграция для MODX Revolution. Я считаю, что нет смысла создавать узкоспециализированный компонент для одной CMS. Ведь всё равно приходится реализовывать не только корзину товаров, но ещё и управление каталогом и т.д. Нужно стараться создавать приложения максимально универсальными.

На данный момент Shopkeeper 4.0 находится в разработке, недавно вышла бета-версия. Уже реализованы почти все основные возможности, которые были запланированы, но есть мелкие недоработки, а так же планирую добавить больше Ajax.




Возможности:

— Можно создавать разные типы товаров с разными наборами параметров (полями). Для каждого поля можно выбрать тип ввода и вывода (как в MODX).
— Товары можно сохранять в разные коллекции (таблицы) базы данных MongoDB.
— Управление пользователями для администратора.
— Управление заказами для администратора.
— Интерфейс для управления валютами, способами доставки, способами оплаты товаров и т.д.
— Для покупателя есть личный кабинет, где он может отслеживать статус заказа и редактировать свои контактные данные.
— Вывод товаров с возможностью фильтрации по параметрам.
— Параметры товаров, которые могут влиять на цену.
— Шаблон с отзывчивой версткой с использованием Bootstrap 4.
… другое.







Да, я знаю, что на Symfony уже есть как минимум один движок для создания интернет-магазина — Sylius. Но его реализация мне лично не очень понравилась. Я думаю, что вы сразу увидите разницу в подходе. Например, там нет Angular, TypeScript и MongoDB, а для меня это важно.

Техническая информация


В свойствах типа контента можно добавить поле и выбрать тип ввода и тип вывода для этого поля. Есть интерфейс для создания своих типов ввода и вывода.




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

Вывод сырых данных:

<span class="text-secondary">
{{ currentPage.price }}
</span>

Вывод данных с использованием шаблона типа вывода:

{{ renderOutputTypeField(currentPage, fields, 'price') | raw }}

Аргументы:

1. Массив со всеми данными страницы.
2. Массив с данными полей типа контента.
3. Системное имя поля.

Далее я не буду перечислять аргументы, эту информацию можно получить в документации на GitHub.

Вывод данных поля по названию чанка:

{{ renderOutputTypeChunk(currentPage, fields, 'price') | raw }}

Чанк — понятие взято из MODX. Здесь чанк используется не только как название шаблона, но так же как метка для поиска нужных полей. Например, по этой метке определяется из какого поля нужно брать цену товара при добавлении в корзину.

Вывод всех полей, в свойствах которых включен флажок «Показывать в списке».

{{ renderOutputTypeArray(currentPage, fields, 'prefix_') | raw }}

Для удобства сделано много Twig-функций. Например, так выводится меню категорий каталога:

{{ categoriesTree(0, 'menu_dropdown', null, true) }}

Здесь 4-й аргумент отвечает за управление кэшированием (по умолчанию выключено).

Если включено кэширование, то весь HTML код меню будет сохранен в файле. Но при этом есть возможность отмечать текущий пункт меню. Более подробно в документации.

Так в шаблоне выводится корзина товаров:

{{ shopCart('shop_cart_edit', 'shop_cart_edit_empty') }}

Здесь первый параметр — название шаблона, второй — название шаблона пустой корзины (указывать не обязательно).

Так выглядит код шаблона простой корзины, где выводится только число выбранных товаров и общая цена:

<div class="shop-cart-bottom">
    <div class="shop-cart-bottom-b">
        <div class="container">
            <div class="float-md-left">
                {{ 'Selected' | trans }}:
                <span class="badge badge-pill badge-light big mx-2">
                    {{ countTotal }}
                </span>
                {{ 'product with a total cost|products with a total cost' | transchoice(countTotal) }}
                <span class="badge badge-light big mx-2">
                    {{ priceTotal | price }}
                </span>
                {{ currency }}
            </div>
                <div class="float-md-right mt-3 mt-md-0">
                    <a class="btn btn-outline-light" href="{{ path('shop_cart_edit') }}">
                        {{ 'Proceed to checkout' | trans }}
                    </a>
                    <a class="btn btn-outline-light ml-1" href="{{ path('shop_cart_clear') }}" data-toggle="tooltip" data-placement="top" title="{{ 'Empty cart' | trans }}">
                        <i class="icon-cross"></i>
                    </a>
                </div>
            <div class="clearfix"></div>
        </div>
    </div>
</div>

Для автоматического изменения размеров изображений используется LiipImagineBundle. Пример вывода изображения товара:

<img src="{{ imageUrl(data) | imagine_filter('thumb_small') }}" alt="">

Здесь «thumb_small» — это название одного из наборов фильтров, которые созданы в файле конфигурации. По умолчанию созданы три таких набора: thumb_small, thumb_medium, thumb_big. Подробнее в документации.

Пример настроек набора фильтров:

thumb_small:
    quality: 85
    filters:
        relative_resize:
            widen: 200
        thumbnail: { size: [200, 200], mode: inset }
        background: { size: [200, 200], position: center, color: '#ffffff' }

Ссылку на демо-сайт давать не буду, т.к. боюсь «хабра-эффекта». Но при желании её легко найти.
Буду благодарен за ваши комментарии, критику и пулл-реквесты.

Последняя версия на данный момент: beta3.
Проект на GitHub
Документация

Комментарии (16)


  1. iproger
    17.06.2018 08:08

    Интересно было посмотреть что внутри. После второй мадженты все такое простое…
    Открываю контроллер Cart. В методе addAction написано 100 строк кода и вот эти mongo->cache. Не знаю зачем в сотый раз нужно это писать. Сейчас, например, прошла мода на создание так называемых витрин для магазинов. Сама витрина работает на vue, а магазин на чем угодно. Вот за этим будущее, имхо.
    Не увидел у вас api. А как расширять систему, модулей как-то не видно. Монга выбрана потому что так надо? Я не работал с ней в проектах, разве там можно обеспечить целостность?

    Хочу, нет, мечтаю увидеть магазин на ddd с по-настоящему универсальными модулями. У которых будут отдельно интерфейсы и реализация. Чтобы в будущем 1 раз написали глобальный модуль товаров и весь мир его использовал в любых системах с любыми реализациями. Ведь нельзя же изобретать Log в десятитысячный раз.


    1. Stan_1
      17.06.2018 13:39

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


    1. Andchir Автор
      17.06.2018 14:00

      > Открываю контроллер Cart. В методе addAction написано 100 строк кода и вот эти mongo->cache.

      Спасибо за замечание. Согласен, что выглядит немного пугающе. Позже вынесу некоторые куски кода в отдельные методы. Там происходит довольно много действий. Нужно определить в каком поле находится цена товара, т.к. вы его можете назвать как угодно по своему желанию. Но нужно поставить метку (об этом писал в статье). Товар уже может быть в корзине, поэтому нужно его найти, сравнить параметры и изменить количество, если параметры сходятся… и т.д. Содержимое корзины сохраняется в БД, чтобы можно было оформить покупку хоть через несколько дней (проверка актуальности цены будет добавлена позже). Для этого хорошо подошел стандартный для Symfony механизм кэширования в БД.

      Ну и я пока не хочу отказываться от поддержки PHP 5.x, поэтому код получается более многословным. В PHP 7, например, появился оператор "??", который я использовать не могу.

      > Не знаю зачем в сотый раз нужно это писать. Сейчас, например, прошла мода на создание так называемых витрин для магазинов…

      Не понял к чему это.

      > А как расширять систему, модулей как-то не видно.

      Для модулей админки есть абстрактный класс (StorageControllerAbstract), в котором реализованы большинство методов, которые могут понадобиться в других модулях. Для стандартного модуля достаточно контроллер наследовать от этого класса и добавить методы валидации данных и создания/редактирования сущности. А для добавления функционала нужно использовать систему бандлов Symfony. По-моему этот механизм очень удобен.

      > Монга выбрана потому что так надо? Я не работал с ней в проектах, разве там можно обеспечить целостность?

      Забыл об этом написать в статье. Спасибо за вопрос. MongoDB выбрана для удобства. Например, там очень легко менять структуру данных. Так же по идеологии считается нормальным сохранять в ДБ вложенные коллекции. К примеру, в реляционных БД нужно создавать кучу отдельных таблиц и потом вытаскивать данные через JOIN. Но, если задуматься, эти данные отдельно друг от друга нужны в очень редких случаях. В данном случае у меня все данные типов контента хранятся в одной коллекции.

      Как это выглядит


    1. alexeydg
      18.06.2018 14:56

      когда витрина на vue, как решается вопрос с индексацией поисковыми роботами таких страниц?


  1. mouze1976
    17.06.2018 09:34
    +1

    Спасибо за то что вы делаете. Считаю нужно поощрять разработку продуктов под опенсоурс. Уважаемый iproger и вам спасибо за то что даёте своё виденье на то как улучшить продукт. А если решите его реализовать в предлагаемом варианте, то сообщество будет вам благодарно!


  1. Alexufo
    17.06.2018 11:03
    +1

    Это очень и очень хорошо. Но для популярности необходимо учитывать популярность развёртывания. Если продукт нельзя развернуть на обычном 150 рублевом хостинге, если используются бд типа монго (как вышел mysql 5.7 с поддержкой json полей в монго вообще по-моему смысла все меньше и меньше, а в расчёте на популярность точно), то стоит ожидать обхода со стороны потенциальных пользователей, которые обходят битрикс потому что у заказчиков нет 35тр на движек.


    1. hector
      17.06.2018 14:01

      35 т.р. — это меньше чем пол месяца работы программиста. Стоимость движка — это очень малая часть ИМ (работа, дизайн, обслуживание).
      Другое дело, что выбирая новое решение, владельцу ИМ необходимо будет брать дополнительные риски связанные с поиском программиста\команды с нужной компетенцией.


      1. Alexufo
        17.06.2018 20:05

        Стоимость движка — это очень малая часть ИМ (работа, дизайн, обслуживание).

        Ну кто спорит. Только вы мыслите как настоящий маркетолог. Многие программисты своим знакомым, девушкам, друзьям, родителям бесплатно поднимают ИМ при условии что не надо вкладывать никакие деньги — просто попробовать, дать людям возможность, и 35тр ради попробовать ой не надо. Автором именно и движет любовь к ближнему, а не к бизнесу.

        В некоторой степени для общества тут проблема, малый бизнес (если он остался) обдирает IT своей монополией.

        То есть суть то вобщем про любовь. Бирикс это про бабки. В итоге у нас остаются либо облака, либо адские динозавры типа опенкарта или престашопа, которые тоже обросли платными модулями, делающих их них что-то похожее на магазин.
        По факту сейчас нет современного бесплатного движка ИМ (про Юпи не знаю особо ничего) который я мог бы посоветовать поставить — советую облака.


        1. oxidmod
          17.06.2018 22:02

          Не устаю советовать OXID eShop


    1. Andchir Автор
      17.06.2018 14:08

      У MongoDB есть свой облачный сервис. Бесплатный сервер там, конечно, очень медленный, но цены не очень кусаются. Для магазина их цены вполне приемлемы. Я думаю когда появится спрос, тогда появится и предложение.
      Сейчас хостинг с VDS стоит очень не дорого и развернуть там MongoDB так же просто как и MySQL.


      1. Alexufo
        18.06.2018 01:17

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


        1. VolCh
          18.06.2018 08:16
          +1

          Даже обычный шаред-хостинг не каждому клиенту отдашь с потрохами. Даже не каждый SaaS. Всё зависит от компетентности клиента или желания её повышать до уровня, минимизирующего внешнюю поддержку.


  1. VolCh
    17.06.2018 16:16
    +1

    > Copyright © 2004-2017 Fabien Potencier

    Поправьте файл LICENSE :)


  1. bond1768
    17.06.2018 17:02

    «планирую добавить больше Ajax» — че, ангуляр юзать передумали?


    1. Andchir Автор
      17.06.2018 17:06

      Имеется ввиду во внешней части сайта. Ангуляр используется только в админке.


      1. bond1768
        17.06.2018 17:10

        ага, понял, спасибо.