?Вышла новая сборка магазина ShopModxBox-2.1.0-beta.
Список изменений
- Обновлен пакет shopModx до версии 0.2.0-beta.
- Добавлено TV-поле keywords. Теперь легко можно указать ключевые слова для документа.
- Полностью переписан класс modMgrOrdersProductsAddProcessor компонента billing. Удален метод getInstance(). Теперь логика вызова Add- или Create- процессора выполнена прям в методе process() текущего класса. Это позволяет гораздо легче расширять логику на уровне классов.
- Улучшен компонент basket:
- Механизм корзины полностью переписан на основе базы данных. В сессии хранится только id заказа, и то исключительно для того, чтобы поддерживать механизм корзины для неавторизованных пользователей. Если пользователь авторизованный, то будет автоматически получен id его актуальной корзины. В остальном все данные хранятся в базе данных.
- Созданы новые basket/web/orders/update и basket/web/orders/status/update процессоры. В update-процессоре прописана проверка, действительно ли данный заказ принадлежит текущему пользователю.
- Переписан процессор очистки корзины. Теперь дополнительно еще проверяется, чтобы статус корзины (заказа) был только 1 — то есть новый. После того, как он оформлен, ее уже нельзя очистить. В дальнейшем еще будет дописан процессор orders/cancel, чтобы пользователь имел возможность отменить уже оформленный заказ, но не взятый в работу менеджерами.
- Все процессоры теперь не в папке processors/, а в папке processors/basket/. Это необходимо, чтобы не было конфликтов имен классов с процессорами других компонентов (например биллинга). Дело в том, что механизм MODX-а устроен так, что лучше использовать автоматически создаваемые имена классов (MODX имена генерирует). И если у нас процессоры будут лежать в одинаковых папках, но в разных модулях, то имена классов могут совпасть, и будет фатальная php-ошибка. А там процессоры именуются по маске modBasketWeb… и modBasketMgr... Старые процессоры оставлены для обратной совместимости, но они все помечены как _depricated. От их использования необходимо отказываться, изменив пути в вызовах процессоров. Обновляется basket простой заменой файлов.
- Различные багфиксы и мелкие улучшения.
- Значительно улучшенный модуль оформления заказов. подробности по нему смотрите в записи с прошлого вебинара.
Под катом подробная инструкция по обновлению существующих магазинов на ShopModxBox-2.0.1-beta.
Обновление работающего сайта на ShopModxBox-2.0.1
Через пакет ShopModxBox не обновить, так как это пакет всего сайта, и он затрет текущую версию сайта. Тем не менее я приложил максимум усилий для обратной совместимости со старой версией и чтобы легко было обновить существующий сайт. В целом цель достигнута, как правило за счет вот подобных замен старых процессоров:
require_once dirname(dirname(dirname(dirname(__FILE__)))) . '/basket/web/orders/empty.class.php'; class modEmptyProcessor_depricated extends modBasketWebOrdersEmptyProcessor{} class modEmptyProcessor extends modEmptyProcessor_depricated{ public function initialize(){ $this->modx->log(xPDO::LOG_LEVEL_ERROR, "[- Basket -] Class '". __CLASS__ ."' is depricated. ".__FILE__.":".__LINE__); return parent::initialize(); } } return 'modEmptyProcessor';
Здесь сразу и обозначение, что он устаревший, и сохранение старого названия (на случай, если вы где-то его переопределили и явно указали имя класса, и чтобы переименование процессора не привело к фатальной ошибке), и сразу логирование в MODX, что процессор устарел, и что от него надо избавляться.
Процесс избавления очень просто: в том месте, где вызывается этот процессор, меняете вызываемый процессор на актуальный, то есть тот, который расширяет depricated (да, меня уже поправляли, что правильно deprecated, но сейчас буду писать так, как в коде, чтобы не путать. В новой версии переименую или удалю нафиг :-) ). В нашем случае это modBasketWebOrdersEmptyProcessor. Путь к нему указан выше.
Итак, перейдем непосредственно к процессу обновления.
1. Обновите пакет shopModx до последней версии.
Текущая актуальная версия — shopModx-0.2.0-beta.
Это необходимо сделать, так как если не сделать, то процессор modxsite/processors/web/getdata.class.php почти наверняка развалится по причине того, что новый процессор требует метода в родительском процессоре, который появился как раз в shopModx-0.2.0.
2. Скачайте и распакуйте у себя git-репозиторий ShopModxBox
Да, мы выложили гит-репозиторий сборки :-) Адрес: https://github.com/Fi1osof/ShopModxBox
Конечно, это не вся сборка, а только самые основные пакеты (basket, billing, modxsite), и то не установочные, а только их файлы, тем не менее это во-первых, позволит мне же гораздо проще тыкать в нужную строчку кода, чтобы показать где что происходит, а во вторых у нас сборка на 98% основывается на файлах (процессоры, xPDO-объекты и т.п.), так что всегда можно будет их репозитория быстро обновиться.
Если вы умеете работать с гитом, то конечно же лучше всего как раз делать клон проекта. Но если нет, или какие-то сложности, то можно и просто скачать zip-архив репозитория.
?
3. Залейте поверх новые файлы
Целевые папки:
- assets/componensts/basket/
- core/components/basket/
- core/components/billing/
- core/components/modxsite/ (Убедитесь, что у вас не используется default шаблон. Переименуйте его в core и в assets и укажите смените настройку шаблона в modxSmarty).
4. Добавьте новый MODX-сервис basket.
Для этого в консоли выполните следующий код:
<?php $modx->removeExtensionPackage('basket'); $modx->addExtensionPackage('basket', '[[++core_path]]components/basket/model/', array( "serviceName" =>"basket", "serviceClass"=>"Basket", ));
Теперь у вас всегда будет доступен новый объект-сервис компонента basket. Пока в нем мало что есть, но как минимум есть следующее:
$modx->basket->getActiveOrderID();
То есть в любом месте можно получить id текущей корзины (именно новой, не оформленной корзины, которую набивает пользователь товарами).
5. Добавьте в свой Smarty-шаблон новые шаблоны писем
В новом модуле оформления заказов появились отдельные письма для конечных пользователей, и для менеджеров. Эти письма отправляются, когда пользователь оформляет заказ на сайте.
Скопируйте в свой Smarty-шаблон папку core/components/modxsite/templates/default/message
Там два шаблона: contractor.tpl и manager.tpl
6. Обновите вызовы старых процессоров.
После обновления скорее всего вам в MODX-логи начнут сыпаться сообщения по поводу устаревших процессоров. Я про это написал выше. Просто обновите вызовы этих процессоров.
UPD: Нашел место, где параметр один старый потерян. То есть он устаревший, но вот надо шаблон поправить. Шаблон shop/basket/overview/index.tpl
Надо заменить $object.order_price на $object.price, и значение key подправить
gist.github.com/Fi1osof/ed7d055071b2e80fd84e/revisions
Это на странице просмотра корзины.
7. Подредактировать плагин basket.
Там появились две переменные:
$basket_processor и $basket_namespace.
В параметрах этого процессора надо указать basket/web/public/action и basket соответственно.
?
Эти параметры для того, чтобы можно было изменить вызываемый роутер-класс.
Вот, собственно, и все. Обновление прошло :-)
Если у кого-то возникнут сложности, пишите прям здесь.
P.S. Если кому помогает наша работа, не стесняйтесь слать донейты ;-) Больше донейтов — больше нашей занятости над сборкой. Координаты указаны в верхнем правом углу.
Кстати, с обновлением процессоров маленькая хитрость: активируйте плагин Debug, и на странице будете видеть вот такие уведомления (только когда авторизованы в админке или во фронте как админ):
?
А логи загрузки страницы по памяти и времени выводятся? Тоже было бы не лишним.
Ты про время генерации страницы? Так там же есть плагин memory_get_usage. Активировал его и все. Только если для всех надо, то закомментировать проверку прав.
А там была проверка прав? Я просто думал, что нет :)
Точно нет :-)
Но в любом случае, плагин выключен. Надо включать и все.
Добрый день.
Я наконец-то начал делать магазин.
Мне необходимо задать для товаров кроме цен еще размеры (разные для разных товаров). как это лучше и проще сделать?
Правильно ли сделать это в виде доп.параметров (как в shopkeeper)?
Все дополнительное сейчас делается на дополнительных полях. Только имей ввиду, что каждый товар надо будет заводить в отдельности, даже если это одинаковые товары, только отличаются размерами. Пока так.
Николай, добрый день. Переношу дизайн. Шаблон элемента в списке товаров выглядит так:
{* Шаблон для вывода карточки товара в списке *} {if $object.image} {assign var=src value=$object.image} {else} {assign var=src value=$object.imageDefault} {/if} <li class="ajax_block_product {$cls}"> <div class="featured_li"> <a class="product_image" href="{$object.uri}" title="{$object.pagetitle}"> <img src="assets/images/{$object.tvs.image.value}" alt="{$object.pagetitle}" class="vky" width="230"/> <img src="assets/images/{$object.tvs.image2.value}" alt="{$object.pagetitle}" style="display:none;" class="v" width="230"/> </a> <div data-smodx-productcls="listproduct"> <form action="" method="post"> <input type="hidden" name="product_id" value="{$object.product_id}"/> <input type="hidden" name="action" value="add_product"/> <input type="hidden" name="quantity" value="1"/> <h5><a class="product_link" href="{$object.uri}" title="{$object.pagetitle}">{$object.pagetitle}</a></h5> <span class="price">{$object.sm_price|number_format:2:".":" "} руб.</span> <br /> <button class="exclusive ajax_add_to_cart_button" type="submit"><b></b>Купить</button> <a class="button" href="{$object.uri}" title="View">Подробнее</a> </form> </div> </div> </li>
почему-то к значению переменной картинки нужно добавить родительские каталоги:
<img src="assets/images/{$object.tvs.image.value}"...
и, самое главное, при покупке возвращается ответ 500. парасетры в ajax передаются верно, в логе ошибок пусто. Что я сделал не так?
почему-то к значению переменной картинки нужно добавить родительские каталоги: <img src=«assets/images/{$object.tvs.image.value}»…
Потому что в товаре когда картинку выбираешь, там же не из корня сайта идет раздел картинок, а в assets/images/. Для этого там используется Источник файлов.
Но $object.tvs.image.value — это чистое значение параметра. А в процессоре уже прописано формирование полного пути на картинку, при чем как от корня, так и абсолютный с протоколом и хостом. github.com/Fi1osof/ShopModxBox/blob/master/core/components/modxsite/processors/web/catalog/products/getdata.class.php#L60
В процессоре есть метод, который получает путь для медиасурса: github.com/Fi1osof/ShopModxBox/blob/master/core/components/modxsite/processors/web/getdata.class.php#L122
Можно УРЛ получить, а можно путь на диске (если передать $callback = 'getBasePath').
и, самое главное, при покупке возвращается ответ 500.
Какая версия php? Не 5.2? Вот это не спасает? modxclub.ru/blog/voprosy-spetsyalistov/255.html#comment2293
Какая версия php? Не 5.2?
Да, 5.2.17
на timeweb только он. 5.4 только через cgi, а, насколько я понимаю, с этим связываться не стоит.
сделал изменения в
- return $className::getInstance($modx,$className,$properties);
помогло, спасибо!
Не за что.
Коля, добрый вечер.
Решил добавить свои ajax-плюшки, написал процессор (.../basket/public/...), все работает.
Возник вопрос: я не могу из этого процессора запустить smarty (чтобы через .tpl переписать часть странички). Как мне это сделать?
Саша, привет!
Вопрос: ты хочешь при Ajax-запросе оформить ответ в HTML? То есть тебе там нужен шаблонизатор?
да. Я хочу вывести фильтр и через ajax обновлять список товаров при изменении критериев. Тоже самое хочу навесить и на постраничность
Понял тебя.
Ну смотри: обычные запросы к сайту обрабатывают классы modRequest и modResponse. Там вызываются различные системные события, на которые вешаются плагины, в том числе и modxSmarty. А вот запросы на коннектор обрабатывают modConnectorRequest и modConnectorResponse, которые этих событий не вызывают, и соответственно нет и $modx->smarty.
Есть два варианта для тебя:
1. В процессоре в initialize() или типа того прописать:
if(empty($this->modx->smarty)){ $this->modx->invokeEvent('OnHandlerRequest'); }
Все, modxSmarty сработает. Только контекст обязательно должен быть не mgr (там проверка и возврат).
Но этот вариант имеет минусы — в плагинах на это событие надо обязательно проверять наличие объекта $modx->resource (что логично, но не все делают). Иначе будет ошибка.
2. Более предпочтительный: создать пустую неиндексируемую страницу, и в ней уже вызывать basket/public/-процессор, и оформлять в смарти все, что хочешь.
Все, modxSmarty сработает.
То есть проинициализируется, и тебе будет доступен объект $this->modx->smarty.
создать пустую неиндексируемую страницу
я так и начал делать, но почему-то в конце выводится 1. как этого избежать?
Разобрался. Нужно не через print, а через return данные выводить :)
Как с помощью процессора можно сделать фильтрацию одновременно по нескольким TV?
Так вот же недавно как раз обсуждали этот вопрос. modxclub.ru/blog/voprosy-spetsyalistov/256.html
Ага, это верно.
Извините за глупый вопрос, несилён в программировании! А где (как) на главной выведены новинки? Обыскался…
Сергей, если не сильны в программировании, то наш движок скорее всего будет сложен для вас. Но посмотрите демку из первого анонса: modxclub.ru/blog/vehicles/230.html
И вообще по тегу не мало топиков с видюшками: modxclub.ru/tag/ShopModxBox/
Там и вебинары записи есть. Наверняка многое проясните для себя.
Вот и хочу научиться и конкретно для этого движка, мне его за глаза должно хватить. Логику уже примерно понимаю. Всё равно спасибо! Видео уже все посмотрел, видимо где-то упустил.
Если я не скопировал шаблон для редактирования, а ковыряю дефолтный, он при обновлении сотрётся?
P. S. В шаблоне продукта [[*content]] не выведен (в этом разобрался, исправил).
Если я не скопировал шаблон для редактирования, а ковыряю дефолтный, он при обновлении сотрётся?
Сборка в целом не рассчитана на обновление, об этом говорилось сразу. Но новый релиз я сопровождаю подробной инструкцией по ручному обновлению, так что если шаблон не затрете, то ничего не пропадет. Но лучше переименуйте его и не парьтесь. В настройках имя смените и все.
P. S. В шаблоне продукта [[*content]] не выведен (в этом разобрался, исправил).
Да, есть там такое дело. Только рекомендую осваивать именно Смарти-теги. В данном случае аналог: {field name=content}
Николай, добрый день. На сайте
shop. ex-addicts. ru
добавил ajax-фильтры и ajax-getpage, перерисовываются товары с формами добавления в корзину (по запросу возвращается html). Все вроде работает, но есть две проблемы:
1. после изменения списка через ajax+js отваливается js-обвязка магазина и по кнопке «купить» происходит переход, а не добавление товара в корзину. Как это победить?
2. при переходе на другую страничку в getpage через ajax location в адресной строке не меняется, и при обновлении страницы соответственно грузится первая страница. как-то можно сделать так, чтобы в адресной строке location менялся, но переход не происходил?
1. после изменения списка через ajax+js отваливается js-обвязка магазина и по кнопке «купить» происходит переход, а не добавление товара в корзину. Как это победить?
Это уже к Сергею proxyfabio вопрос, как это победить, но вообще логично, что не работает. Дело в том, что когда у тебя страница загружается, инициализируются нужные элементы, а когда у тебя ajax-ово подгружаются и по сути создаются новые, на них не навешано событие обработки. В ранних версиях jQuery был такой метод live(), который работал почти что как bind() с той лишь разницей, что он создавал обработчик, который всегда срабатывал на элементах с заданным селектом. То есть это бы тебя спасло именно в этой ситуации. Создается новый элемент, но для него всегда есть обработчик.
Сергей, есть ли у нас сейчас какой-то аналог метода live()?
2. при переходе на другую страничку в getpage через ajax location в адресной строке не меняется, и при обновлении страницы соответственно грузится первая страница. как-то можно сделать так, чтобы в адресной строке location менялся, но переход не происходил?
Опять-таки логично. getPage отталкивается от текущего ресурса для формирования адреса. У тебя всегда одна и та же страница для обработки Ajax-запросов. Верно? Поэтому и адрес один. Нам повезло на Сергеев, и Сергей husband написал замену getPage-у на Smarty. Спроси в чате у него про эти наработки и используй их.
1. После обновления списка товаров попробуйте вызывать ShopMODX.widgets.Product.bindEvents()
2. Проще всего воспользоваться HTML5 History API habrahabr.ru/post/200106/
Спасибо за советы!
Этот вопрос надо обсуждать с Сергеем proxyfabio . Надо дорабатывать API, чтобы обработчик событий работал как положено.
Ребята привет!
Установил вроде по инструкции (1-й вариант) ставил сразу ShopModxBox-2.1.0-beta на чистый modx. В результате вылетает ошибка 1-й строкой на главной странице:
Notice: Undefined variable: tagPropString in /home/compuche/public_html/satpro.com.ua/core/components/modxsmarty/smarty_plugins/function.field.php on line 29
Собственно открывается только главная страница с товаром, больше ничего не пашет.
Куда глядеть? Адрес сайта в строке с ошибкой :(
Notice: Undefined variable: tagPropString in /home/compuche/public_html/satpro.com.ua/core/components/modxsmarty/smarty_plugins/function.field.php on line 29
Это просто нотис, то есть не критическое предупреждение. Про этот момент написано здесь: modxclub.ru/blog/voprosy-spetsyalistov/255.html
А то, что другие страницы не пашут — скорее всего просто не настроен .htaccess или чем там на вашем сервере подмена УРЛов обеспечивается. Попробуйте переименовать в корне сайта ht.access в .htaccess (должно помочь).
Спасибо Fi1osof! Про УРЛ… действительно без настроек, а про ошибку сейчас почитаем!
Николай, добрый вечер!
Что-то не могу сообразить, куда и как вставить сортировку по цене товара. Не подскажешь?
или по tv?
С sm_price разобрался. А как по TV отсортировать?
Пожалуйста!
Саша, вопрос актуальный еще? Как раз вот недавно этот момент разбирали: modxclub.ru/blog/dokumentatsiya-dlya-spetsialistov/147.html#comment2441
Добрый день, спасибо за ответ. Я именно так и сделал.
Кстати, все собирался спросить:
В многих местах я вижу подобные вызовы
$c->innerJoin('ShopmodxProduct', 'Product');
т.е. не записано условие соединения. оно где-то в другом месте прописано? Я не смог найти ничего конкретного по этой теме.
Ну и замечательно.
Материала на эту тему написано на самом деле не мало уже. Один из топиков: modxclub.ru/blog/dokumentatsiya-dlya-spetsialistov/86.html
Это xPDO-связи объектов, и в топике есть ссылка на документацию. Описывается это в map-файле.
github.com/Fi1osof/shopModx/blob/b27b497031262d7c1bda373abf7f8f43a26858ec/core/components/shopmodx/model/shopModx/mysql/shopmodxresourceproduct.map.inc.php#L13
Спасибо, теперь разобрался.
Пожалуйста.
И бери на вооружение, потом что это очень мощный механизм. Правда есть баги :-)
При попытке скачать из репозитория выходит ошибка failure без объяснения причин. Остальные вещи качаются с репозитория Вашего нормально. В чем может быть проблема?
Если это спустя какое-то время происходит, то скорее всего просто не успевает скачать файл из-за ограничений канала на вашем хостинге. Можете скачать файл напрямую: 2.1.0.shopmodxbox.modxdev.webtm.ru/shopmodxbox-2.1.0-beta.transport.zip
Потом сделаю вывод файлов на сайте репозитория.
Коля, привет. Столкнулся с проблемой: если регистрируюсь, а затем оформляю заказ, то спокойно перехожу к оплате. А вот если оформляю без регистрации, то при переходе на оплату выскакивает сообщение «Не был получен заказ». При этом сам заказ в базе появляется и пользователь регистрируется. Ковырялся сутки, так и не смог понять, где проблема. Процессор выдает пустой объект.
Саша, привет.
Да, раньше все работало, но вот в последней версии это дело сломалось. При чем, чтобы это корректно работало, там надо принять дополнительные меры безопасности (хотя там никаких угроз кроме шалостей вряд ли предполагается). Сейчас посмотрю и маякну на счет решения.
И еще я не совсем понял: пользователь автоматом регистрируется при оформлении заказа, заносится в базу. А как он потом сможет войти? через восстановление пароля? Мне кажется, надо бы ему еще письмо скидывать с данными авторизации. Как думаешь?
WHERE ( `Order`.`id` = 71 AND (( `Order`.`createdby` = 1 AND `Order`.<strong>`status_id` = 1</strong> ) OR `Order`.`contractor` = 1 ) AND `Order`.<strong>`status_id` != 1</strong> )
Возможно я ошибаюсь, но не получается тут противоречие с условиями отбора по status_id?
(SQL, сгерированный в web/orders/getlist, ns=basket)
Так я не против. Мы пока не дописали, так как были более важные дела. Но ты запросто можешь для себя это дописать, если надо. У нас это в новой сборке скорее всего будет реализовано.
Нет. Там же условие OR. В данном случае условие буквально `Order`.`id` = 71 AND `Order`.`contractor` = 1 AND `Order`.`status_id` != 1.
То есть это твой заказ с id=71 и статусом != 1
В общем, проблема была как раз там, где я и предполагал. Дело в том, что вэбовский процессор по соображениям безопасности выл дописан таким образом, что получить данные можно только по заказам текущего пользователя. Следовательно, нет пользователя — нечего и получать. А вот получение прочих заказов предполагалось делать через mgr-процессоры, к которым априори внимание по безопасности должно быть выше, то есть использовать их надо очень аккуратно.
Но вот в новой версии движка как раз вот этот участок мы и проглядели. Если помнишь, мы ведь все процессоры перевели в папку processors/basket/(web|mgr), а тут еще просто processors/web.
Все это явно указывает, что там не по новой системе. Переработается это только в новой версии магазина, а пока просто код-патч шаблона:
{* Оплата *} {$modx->regClientCSS("{$template_url}css/robokassa.css")} {if !$smarty.get.order_id} <h3 class="error">Не был указан ID заказа</h3> {else} {* Пытаемся получить заказ *} {assign var=params value=[ "order_id" => $smarty.get.order_id ]} {*processor action="web/orders/getlist" ns="basket" params=$params assign=result*} {processor action="basket/mgr/orders/products/getdata" ns="basket" params=$params assign=result} {if $result.success && $result.object} {*assign var=order value=$result.object[0]*} {assign var=order value=$result} {snippet name="robokassa.getButton" params="shp_order=`{$smarty.get.order_id}`&out_sum=`{$order.sum}`"} {else} <h3 class="error">Не был получен заказ</h3> {/if} {/if}
Заработало! Спасибо!
Отлично!
Не за что!
Всех с новым годом!
Решил перевести сайт на ShopModxBox-2.1.0-beta (как смог, я не программист)
на мой взгляд долго подгружаются страницы с товаром.
Запустил плагин memory_get_usage проверить скорость загрузки:
Memory: 12 Mb
TotalTime: 1.9567 s
так должно быть, или я что-то не так сделал?!
Добрый день. И вас с Новым годом!
1. Если вы не программист, скорее всего пока не стоит эту сборку на вооружение брать, так как она выставляет определенные требования к уровню знаний веб-технологий.
2. Скорее всего у вас хостинг слабый. Сам по себе MODX Revolution довольно требовательный к ресурсам, и если хостинг слабенький, работать он быстро никак не будет. Для боле менее нормального хостинга обычное время генерации страницы — 0,3-0,6 сек
3. По умолчанию в сборке во всех шаблонах стоит параметр phptemplates.non-cached = true. Из-за этого даже на кешированных страницах все равно шаблон отрабатывается каждый раз. Надо зайти в шаблоны и в каждом, где стоит в параметрах Да, изменить на нет.
?
2. Скорее всего у вас хостинг слабый. Сам по себе MODX Revolution довольно требовательный к ресурсам, и если хостинг слабенький, работать он быстро никак не будет. Для боле менее нормального хостинга обычное время генерации страницы — 0,3-0,6 сек
изначально так и было 0,3-0,6 пока не добавил товар и не поменял шаблон. Возможно из-за шаблона или из-за ....
1. Если вы не программист, скорее всего пока не стоит эту сборку на вооружение брать, так как она выставляет определенные требования к уровню знаний веб-технологий.
но все равно спасибо Вам за ответ и сборку ShopModxBox-2.1.0-beta
Если вы не добавили более нескольких тысяч товаров, вряд ли вы должны были ощутить разницу в производительности.
При этом обязательно обратите внимание на кеширование самого шаблона (я писал третьим пунктом выше). Отключение кеширования может в производительности сыграть в два и более раз.
Пожалуйста!