Система краудфандинга на miniShop2

Добрый день.
Собираю краудфандинг систему на всеми нами любимом miniShop2 с минимальными изменениями. Да, это полноценный рабочий вариант наподобие кикстартера, индигого и планета.ру. Уже есть, чем поделиться, но хотелось бы получить от вас советов, дополнений. Вообщем, совместными усилиями доработать, буду премного благодарен. Почти все есть в свободном доступе в этом сообществе, так что осталось дело за малым – собрать все воедино.

Для постинга проектов пользователями выбрал Tickets. Если создавать проекты, как товары miniShop2, то задание гораздо упрощается, но мне очень нравятся тикеты, хоть убейте.

Далее будет информация о
сниппетах
get_backers_page – вывод списка пользователей, поддержавших проект, а также суммы пожертвования
get_backers_count – сколько всего раз пожертвовали данному проекту
get_backers_sum – сумма всех пожертвованных денег проекту
get_backers_percentage – процент пожертвованных денег от нужной суммы

плагинах
pricefree – обработка произвольной цены на странице оплаты
priceselect – radio кнопки с выбором пожертвования

а также немного js и модификаций miniShop2
Добро пожаловать под кат :)


Шаг 1. Создаем продукт с любым названием, цена:1, вес:1

Шаг 2. Добавляем кнопку на страницу проекта
2.1 Выводим созданный продукт
[[pdoPage? &element=`msProducts` &parents=`89` &tpl=`button-go`]]
чанк button-go
<div class="button-block ms2_product">
<form method="post" class="ms2_form">
<button class="button-go" type="submit" name="ms2_action" value="cart/add">Помочь проекту</button>
<input type="hidden" name="id" value="[[+id]]">
<input type="hidden" name="count" value="[[*id]]">
</form>
</div>
* В значение count у нас идет id проекта

2.2 Переход сразу к оплате после нажатия кнопки
<script type="text/javascript">
$(document).ready(function() {
miniShop2.Callbacks.Cart.add.response.success = function() {
document.location = 'payment';
}
});
</script>

Шаг 3. TV требуемой суммы
Присваиваем шаблону проекта TV project-price. В форме создания проекта во фронтенде добавьте input, чтобы пользователь мог вводить нужную сумму. Также при оформлении страницы проекта сделайте вывод этого tv (как это выглядит у других можно подсмотреть в гугле по поиску crowdfunding)

Шаг 4. Сниппеты для оформления страницы проекта
В нужном месте на странице проекта ставим некешируемый вывод сниппета
[[!имясниппета?]]

4.1 get_backers_page
вывод списка пользователей, поддержавших проект, а также суммы пожертвования

<?php
$current = $modx->resource->get('id');//берет id текущей страницы (проекта)
$output = '';
$sql = "SELECT * FROM modx_ms2_orders WHERE weight=$current ORDER BY `modx_ms2_orders`.`id` DESC";
foreach ($modx->query($sql) as $row) {
$output .= $modx->getChunk('backedby', $row);
}
return $output;

* Сразу скажу, что id проекта будет передаваться в miniShop2 в поле weight заказа. Это нужно будет для второй части статьи, в которой, надеюсь, у меня получится вывести pagetitle проекта по этому значению. Чтобы не сильно изменять miniShop2, пришлось задействовать только weight и count.

Чанк backedby, мне интересно, целесообразно ли делать такой вывод, либо лучше вывести значения процессорами modx?
[[!pdoUsers?
&users=`[[+user_id]]`
&tpl=`@INLINE <p><a href="tiim/[[+user_id]]/"><b>@[[+username]]</b> <small>[[+fullname]]</small></a> + [[+cart_cost]]€</p>`
]]

** У меня не получилось сделать вывод пожертвований через pdoPage element get_backers_page, но в идеале должна быть пагинация. Может есть у кого-нибудь идеи, как это осуществить?

4.2 get_backers_count
сколько всего раз пожертвовали данному проекту

<?php
$current = $modx->resource->get('id');
$results = $modx->query("select count(id) as allcount from modx_ms2_orders where weight=$current");
if (!is_object($results)) {
return '0';
}
else {
$r = $results->fetch(PDO::FETCH_ASSOC);
return $r['allcount'];
}

4.3 get_backers_sum
сумма всех пожертвованных денег проекту

<?php
$current = $modx->resource->get('id');
$results = $modx->query("select sum(cart_cost) as allsum from modx_ms2_orders where weight=$current");
if (!is_object($results)) {
return '0';
}
else {
$r = $results->fetch(PDO::FETCH_ASSOC);
return $r['allsum'];
}

4.4 get_backers_percentage
процент пожертвованных денег от нужной суммы

<?php
$current = $modx->resource->get('id');
$needed = $modx->resource->getTVValue('project-price');
$results = $modx->query("select sum(cart_cost) as allsum from modx_ms2_orders where weight=$current");
if (!is_object($results)) {
return '0';
}
else {
$r = $results->fetch(PDO::FETCH_ASSOC);
$sum = $r['allsum'];
}
$percentage = ($sum/$needed)*100;
return round($percentage, 0, PHP_ROUND_HALF_UP);

Тут появляется значение tv с нужной суммой проекта project-price из шаг3.
Получаемый процент округляется к бОльшему числу и нужен, например, для визуализации остаточной стоимости пожертования
||||||||||||||||||_____| 65%

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

Шаг 5. Модификация miniShop2
Тут самое важное не накосячить, а то придется переустанавливать miniShop2,
либо делать копипейст с гитхаба

5.1 Сниппет msCart
находим поля
$row['weight'] = $miniShop2->formatWeight($v['weight']);
$row['cost'] = $miniShop2->formatPrice($v['count'] * $v['price']);
меняем на
$row['weight'] = $v['count'];
$row['cost'] = $v['price'];

далее
$outer['total_cost'] +=  $v['count'] * $v['price'];
$outer['total_weight'] += $v['count'] * $v['weight'];
меняем на
$outer['total_cost'] =  $v['price'];
$outer['total_weight'] = $v['count'];

и еще
$outer['total_weight'] = $miniShop2->formatWeight($outer['total_weight']);
на
$outer['total_weight'] = $outer['total_weight'];

Об этих перестановках написано в пункте 4.1

5.2 core/components/minishop2/model/minishop2/mscarthandler.class.php

В районе 260 строчки ищем
$status['total_cost'] += $item['price'] * $item['count'];
$status['total_weight'] += $item['weight'] * $item['count'];
и заменяем на
$status['total_cost'] = $item['price'];
$status['total_weight'] = $item['count'];
* вот опять этот count, в котором передавали id ресурса по кнопке «пожертвовать»

Как видим, изменения несущественные, поэтому, если делать бекапы, то можно обновляться на новую версию miniShop2, а потом добавлять модификации. На все это займет от силы 2 минуты

Шаг 6. Страница оплаты
6.1 Вызываем сниппет msCart со своими темплейтами

tplOuter нужно убрать все ненужное
tplRow примерно такой чанк:

<div id="[[+key]]">
<!--Наименование проекта и ссылка на него-->
<a href="[[!pdoField? &id=`[[+weight]]` &field=`uri`]]">[[!pdoField? &id=`[[+weight]]` &field=`pagetitle`]]</a>

<form method="post" class="count ms2_form" role="form">
<input type="hidden" name="key" value="[[+key]]" />
<input type="hidden" name="count" value="[[+count]]" max-legth="1" />
<!--Поле для вывода произвольной цены-->
<input type="number" name="freeprice" value="[[+price:replace=` ==`]]" >

<!--Выбор суммы пожертвования-->
<div class="prices">
<label for="price_1"><input type="radio" id="price_1" name="selectprice" value="1" checked /><span>1€</span></label>
<label for="price_5"><input type="radio" id="price_5" name="selectprice" value="5"/><span>5€</span></label>
<label for="price_10"><input type="radio" id="price_10" name="selectprice" value="10"/><span>10€</span></label>
<label for="price_25"><input type="radio" id="price_25" name="selectprice" value="25"/><span>25€</span></label>
<label for="price_50"><input type="radio" id="price_50" name="selectprice" value="50"/><span>50€</span></label>
<label for="price_100"><input type="radio" id="price_100" name="selectprice" value="100"/><span>100€</span></label>
</div>

<button class="btn btn-default" type="submit" name="ms2_action" value="cart/change"></button>
</form>

<form method="post" class="remove ms2_form">
<input type="hidden" name="key" value="[[+key]]">
<!--Можно оставить, а можно и нет, так как все же есть кнопка очистки корзины-->
<button class="btn btn-default" type="submit" name="ms2_action" value="cart/remove" title="Отказаться от помощи"></button>
</form>
</div>

6.2 Создаем плагин pricefree на событие msOnBeforeChangeInCart для обработки произвольной цены (найден в сообществе)
<?php
$tmp = $cart->get();
$price = intval(str_replace(' ', '', $_POST['freeprice']));
if($price>0){
$tmp[$key]['price'] = $price;
}
$cart->set($tmp);

и на его основе, создаем аналогичный плагин priceselect на то же событие
<?php
$tmp = $cart->get();
$price = intval(str_replace(' ', '', $_POST['selectprice']));
if($price>0){
$tmp[$key]['price'] = $price;
}
$cart->set($tmp);

добавляем в шапку скрипт*
<script type="text/javascript">
$(document).on('change', 'input[name=freeprice]', function() {
$(this).closest(miniShop2.form).submit();
});

$(document).on('change', 'input[name=selectprice]', function() {
$(this).closest(miniShop2.form).submit();
});
</script>

* Сейчас для теста именно так, но на готовом проекте нужно убирать скриптом значение checked у selectprice при нажатии на инпут freeprice, а также очищать freeprice при выборе суммы пожертвования в selectprice. Это очень важно! Чуть позже дополню статью

6.3 Добавьте вызов сниппета msOrder

Шаг 7. Дополнительно
7.1 В настройках miniShop2 измените формат веса на []

7.2 Поменяйте структуру таблицы modx_ms2_orders
  • cart_cost / decimal(12,0)
  • weight / mediumtext / NULL
Шаг 8. Стилизуйте все на свое усмотрение по опыту других краудфандинговых платформ

NB! ЧТО НУЖНО ЕЩЕ СДЕЛАТЬ *** Статья будет дополнена
Надеюсь на вашу помощь, но сам буду тоже изучать эти моменты:

1. Плагин, который очищает корзину перед добавлением в нее проекта, чтобы в корзине мог находится всегда только 1 проект
2. Пагинация через pdoPage со списком помощников
3. Предлагайте свои идеи в комментариях

Во второй части статьи хочу поделиться соображениями, как все более или менее нормально вывести в бэкенд с минимальными правками miniShop2.

Спасибо за внимание!
Andrei D.
30 апреля 2016, 12:44
modx.pro
7
3 567
+6

Комментарии: 7

Yar
Yar
30 апреля 2016, 16:35
0
Сильно
    Алексей Федоров
    30 апреля 2016, 17:02
    +1
    Вау! Краудфандинговая платформа это круто, но после всех правок, насколько понимаю, обычная продажа товаров черех miniShop2 перестанет работать. Надеюсь, что ошибаюсь.
    Для большей части краудфандинговых платформ характерна функция возврата средств жертвователям в случае неудачи по сбору средств. Плюс время, которое выделяется на сбор средств.

    P.S.: Неужели нельзя сделать это дополнением, без правок файлов самого магазина? Например, группа (категория) товаров — различные типы взносов для проекта краудфандинга.
    1) Нужен плагин для подсчета общей суммы (будет перехватывать показатель после изменения статуса товара из краудфандинговых категорий на «оплачен»), на которую куплены эти товары (из одной категории) и сравнения с заданной в ТВ. Процент сбора и число участников проекта это, конечно, важные моменты.
    2) Плюс сниппет для подсчета суммы, на которую отдельный пользователь купил товары из конкретной «категории». Время на сбор средств может быть задано вручную — число + период (дней/недель/месяцев/лет), либо неограниченным.
    3) Через сам минишоп проводить только оплату без правки ядра и родных сниппетов.
      Andrei D.
      30 апреля 2016, 17:22
      +1
      Поделился решением, которое сделал для своего сайта, на котором других продаж не планируется.
      Можно сделать и без правок, передавая тот же id через опцию. Как известно, опции хранятся уже в другой таблице ms2_order_products, причем в формате {«optionname»:«123»}. Поэтому запрос нужно делать с джойнами этой таблицы, что может означать больше времени на запрос. Поправьте меня, если я ошибаюсь.

      По поводу вашего первого пункта, я предложил сниппет get_backers_sum

      Плюс такое решение планировал для того, чтобы потом во второй части статьи нормально вывести все пожертвования в бекенде.

      В любом случае, это открытый топик, в котором каждый может внести свой вклад. Если совместно создать компонент и выложить его в официальный репозиторий, который бы при загрузке подтягивал минишоп – вот это, я считаю, круто
      Иван Климчук
      01 мая 2016, 11:51
      +1
      Оставлю свое мнение, но оно автору не понравится. Оно конечно похвально, что автор постарался и таки решил проблему (хоть и частично), но с кучей условий и ограничений, что в реальном мире выглядит как что-то, подпретое костылями, чтобы хоть как-то работало. Очень важно для решаемой задачи в самом начале подобрать правильные инструменты, чтобы еще на старте максимально уменьшить технический долг. Первым звоночом стала фраза про использование Tickets, потому что очень нравится этот компонент. А нужно было сделать нормальный анализ задачи и подумать, что какой компонент может, а что нет. Для задачи краудфантинга не подходит ни тикетс, ни miniShop, даже если их пытаться как-то объединять. Решение здесь одно — нормально спроектировать структуры данных (т.е. таблицы и из связи) для решаемой задачи и оформить это в виде отдельного компонента, который будет работать так, как ожидается, его можно будет легко дорабатывать и он будет независим от обновлений тикетс и минишоп, которые могут все сломать в любой момент, потому что автор этих дополнений ждет, что их используют по назначению, а не в каких-то изощренных схемах. Как я и говорил, мое мнение не понравится автору. Так вот, будущего в этом проекте я не вижу. На старте все здорово, но через какое-то время сложность и стоимость (время) поддержки всего количества «костылей» настолько превысит рентабельность, что ни у кого не будет желания этим заниматься.
        Andrei D.
        01 мая 2016, 12:11
        0
        Иван, я полностью согласен с вашим мнением. Мне нужен был именно такой функционал, как в статье, я им и поделился. По поводу Tickets, то уже тысячу раз использовал этот компонент не по назначению и без проблем обновлялся. Мне очень важны рейтинги и другие фишки, встроенные в тикеты, так как платформу делаю небольшую и ламповую, причем, для Эстонии. Сами понимаете, что уровень трафика России и Эстонии несоизмерим :) Я разрабатываю прототип для себя, в котором все работает, как вы правильно заметили, на костылях. В дальнейшем проект, возможно, уйдет на yii, либо на drupal, где второй вариант даже предпочтительнее, так как уже был опыт экспорта тикетов туда + уже готовые эстонские методы оплаты и возможность входа по ID-карте.
        Согласитесь, собрать рабочий прототип на ModX занимает в разы меньше времени, чем на других платформах. А с альфа версией можно еще и затормозить на год-два, так как ModX все равно будет работать, как часы и держать трафик
        Ruslan
        06 июня 2021, 10:27
        0
        Добрый день!
        Увидел вашу великолепную работу по Система краудфандинга на miniShop2
        Хочу Вас попросить сделать для меня примерно аналогичный проект да оплату за вашу работу! Если у Вас есть время реализовать аналогичный мой проект?
        напишите пожалуйста в телеграмм casetar или напишите пожалуйста в личку!
          Andrei D.
          07 июня 2021, 12:22
          0
          привет, уже больше трех лет не работаю с modx, ушел в сторону headless
          в основном юзаю стек nuxt+moleculer.services/laravel
          Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
          7