Собственно вопрос, по реализации магазина и как я буду его оценивать.
Для начала разберем реальный магазин, как он работает, какие функции там реализованы и его стоимость.
И так итернет-магазин на битрикс можно реализовать на 2х редакциях это малый бизнес и бизнес.
В чем основная разница именно для магазина?
1. Нет модуля рекламы - целесообразность данного модуля можно поставить под сомнение его можно реализовать на инфоблоках и будет это быстро и неплохо работать, да и сам модуль тяжелый в некоторых случаях вообще не целесообразный
2. Сам модуль интернет магазин отличается тем, что в бизнесе нет ограничений по количеству цен для товара, и нет наборов и комплектов, в малом бизнесе только один склад, нет аффилатов (вопрос спорный надо ли в большинстве случаев) и нельзя продавать электронный контент остальное в редакциях магазинов одинаково работает.
Что мы имеем, битрикс полностью интегрирован с 1С, малый бизнес - хорош если нет складов и разных цен к примеру оптовых и за товар. Бизнес - монстрозубый мгазин который готов для любых предприятий.
И цена: 9 000 и 19 000 разница ощутимая.
Старт стоит 1 400 т.е. собсвенно остальные дополнения 7 600 и 17 600 соответсвенно.
В среднем на бирже труда работа программиста на php равняесть 5-10$ в час т.е. 130 грн - 260 грн. 58 и 29 часов соответсвенно т.е. при нормальном рабочем часе в день 4-7 дней. Реально ли?
Давайте разберемся, что надо реализвать в первую очередь.
1. Каталог товаров
2. Корзина
2.1. Скидки
3. Оформление заказ
3.1. Способы оплаты
3.2. Способы доставки
Ну что же упростим для начала:
Битрикс старт
Любая правильная разработка в битрикс начинается с отдельного модуля в котором будет все свое хранится и не мешать всему остальному.
Разработка модуля итнернет магазина начинается со стандартного "пустого модуля"
создаем папку local в корне сайта и создаем в нем папку modules/NAMESPACE в моем случаи будет это binliz.sale для того чтобы битрикс определил что это модуль надо создать пару папочек и файликов это все написано в курсе по битрикс но я буду писать именно что я делаю
создаем папку install , файлики install/index.php, include.php, options.php
/** * User: binliz */ use Bitrix\Main\Localization\Loc; Loc::loadMessages(__FILE__); if (class_exists("binliz_sale")) { return; } class binliz_sale extends CModule { public $MODULE_ID = "binliz.sale"; public $MODULE_VERSION = "0.1"; public $MODULE_VERSION_DATE = "2016-09-19"; public $MODULE_NAME = "Модуль binliz sale"; public $MODULE_DESCRIPTION = "Модуль магазина, служит для автоподключение классов. Использует движок D7."; public $MODULE_GROUP_RIGHTS = "N"; public $PARTNER_NAME = "binliz"; public $PARTNER_URI = "http://сайт"; public function DoInstall() { global $APPLICATION; RegisterModule($this->MODULE_ID); } public function DoUninstall() { global $APPLICATION; UnRegisterModule($this->MODULE_ID); } }
Вот и все, идем в Рабочий стол > Marketplace > Установленные решения и устанавливаем наш модуль, делать мы это будем часто устанавиливать и уаалять чтобы точно знать что наш модуль развернется из коробки.
Первым делом, что нам нужно организовать это возможность создание каталога в админке, для удобства сделаем его так же как и у битрикса т.е. не выдумывая новой структры - добавим к элементам инфоблока свои вкладки с торговым каталогом.
Как же это организовать? Попробуем для начала создать таблицу в которой будем хранить наши инфоблоки которые станут каталогами.
CREATE TABLE `b_binliz_sale_catalog` (`ID` int(11) NOT NULL, `IBLOCK_ID` int(11) NOT NULL, `OFFER_IBLOCK_ID` int(11) NOT NULL, `IS_CATALOG` int(1) NOT NULL, `IS_OFFERS` int(1) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; ALTER TABLE `b_binliz_sale_catalog` ADD PRIMARY KEY (`ID`), ADD UNIQUE KEY `IBLOCK_ID` (`IBLOCK_ID`), ADD UNIQUE KEY `OFFER_IBLOCK_ID` (`OFFER_IBLOCK_ID`); ALTER TABLE `b_binliz_sale_catalog` MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT;
Создадим в ОРМ представление данной таблице, создадим папку lib и в нее положим файл salecatalog.php
Namespace Binliz\Sale; use Bitrix\Main, Bitrix\Main\Localization\Loc; Loc::loadMessages(__FILE__); /** * Class SaleCatalogTable * * @package Binliz\Sale **/ class SaleCatalogTable extends Main\Entity\DataManager { /** * Returns DB table name for entity. * * @return string */ public static function getTableName() { return "b_binliz_sale_catalog"; } /** * Returns entity map definition. * * @return array */ public static function getMap() { return array("ID" => array("data_type" => "integer", "primary" => true, "autocomplete" => true, "title" => Loc::getMessage("SALE_CATALOG_ENTITY_ID_FIELD"),), "IBLOCK_ID" => array("data_type" => "integer", "required" => true, "title" => Loc::getMessage("SALE_CATALOG_ENTITY_IBLOCK_ID_FIELD"),), "OFFER_IBLOCK_ID" => array("data_type" => "integer", "required" => true, "title" => Loc::getMessage("SALE_CATALOG_ENTITY_OFFER_IBLOCK_ID_FIELD"),), "IS_CATALOG" => array("data_type" => "integer", "required" => true, "title" => Loc::getMessage("SALE_CATALOG_ENTITY_IS_CATALOG_FIELD"),), "IS_OFFERS" => array("data_type" => "integer", "required" => true, "title" => Loc::getMessage("SALE_CATALOG_ENTITY_IS_OFFERS_FIELD"),),); } }
И можно в приниципе и локализацию положить lang/ru/lib/salediscount.php
$MESS["SALE_CATALOG_ENTITY_ID_FIELD"] = ""; $MESS["SALE_CATALOG_ENTITY_IBLOCK_ID_FIELD"] = ""; $MESS["SALE_CATALOG_ENTITY_OFFER_IBLOCK_ID_FIELD"] = ""; $MESS["SALE_CATALOG_ENTITY_IS_CATALOG_FIELD"] = ""; $MESS["SALE_CATALOG_ENTITY_IS_OFFERS_FIELD"] = "";
пока я ничего не переопредил просто положил файлик, табличка создана пора создавать вкладку и задавать ей методы работы с нею, так как у нас битрикс, он имеет систему событий лучший и правильный способ это организовать классы которые будут навешиватся на эти самые события и решать наши задачи, для этого я предпочел руководсвоватся принципом один класс одно решение и конструкторе само определение того на, что навешиваемся. Первым делом будем решать задачу определения каталогов. Для этого нам надо в разделе редактирования инфоблока создать вкладку в которой будет наша галочка, что это каталог и пока этого будет достаточно мы не будем заморачиватся пока торговыми предложениями мы сможем это сделать попозже. Создадим абстрактный класс для событий в котором создадим массив передающий следующие данные:
модуль на который навешиваемся, событие, и метод класса который за это отвечает. Для этого в папке lib создадим папку events и туда будем складывать наши события. И так абстрактный класс.
Namespace Binliz\Sale\Events; abstract class Base{ protected $eventlist = ; public final function __construct(){ $eventManager = \Bitrix\Main\EventManager::getInstance(); foreach($this->eventlist as $event){ $eventMethods = get_called_class()."::".$event["method"]; $eventManager->addEventHandler($event["module"],$event["event"],$eventMethods); } } }
И класс для работы с инфоблоком:
Namespace Binliz\Sale\Events; class SaleCatalogIblockEdit extends Base{ protected $eventlist = [ ["module"=>"main","event"=>"onAdminTabControlBegin","method"=>"onAdminTabControlBegin"], ["module"=>"iblock","event"=>"onAfterIblockAdd","method"=>"onAdminIblock"], ["module"=>"iblock","event"=>"onAfterIblockUpdate","method"=>"onAdminIblock"], ["module"=>"iblock","event"=>"OnIBlockDelete","method"=>"onAdminIblockDelete"], ]; static public function getApp(){ $application = \Bitrix\Main\Application::getInstance(); $context = $application->getContext(); $request = $context->getRequest(); $uri = $request->getRequestedPage(); return array("application"=>$application,"context"=>$context,"request"=>$request,"uri"=>$uri); } public function onAdminTabControlBegin(&$form){ $ells = self::getApp(); if($ells["uri"] == "/bitrix/admin/iblock_edit.php") { if($ells["request"]->getQuery("ID")>0) $results = \Binliz\Sale\SaleCatalogTable::GetByIblock($ells["request"]->getQuery("ID")); ob_start(); include(__DIR__."/templates/iblock_edit.php"); $str = ob_get_contents(); ob_end_clean(); $form->tabs = array("DIV" => "BINLIZ_SALE_CATALOG", "TAB" => "Торговый каталог Binliz.Sale", "ICON"=>"main_user_edit", "TITLE"=>"Каталог настройки", "CONTENT"=>$str); } } public function onAdminIblock($arFields){ $ells = self::getApp(); file_put_contents(__DIR__."/fields.txt",var_export(array($arFields,$ells["request"]->getPostList(),$ells["request"]->getPost("IS_BINLIZ_CATALOG")),1)); if($arFields["RESULT"]!=false){ $ID = $arFields["ID"]; $fields = ["IBLOCK_ID"=> $ID,"IS_CATALOG"=>$ells["request"]->getPost("IS_BINLIZ_CATALOG")=="Y"?1:0]; \Binliz\Sale\SaleCatalogTable::AddByIblock($ID,$fields); } } public function onAdminIblockDelete($ID){ \Binliz\Sale\SaleCatalogTable::DeleteByIblock($ID); } }
Соответсвенно добавим наши функции к модели таблицы:
Public function GetByIblock($IBLOCK_ID){ $query = new \Bitrix\Main\Entity\Query(self::getEntity()); $query->setSelect(array("*")); $query->setFilter(array("=IBLOCK_ID" => $IBLOCK_ID)); $result = $query->exec(); $ells = $result->fetch(); if(isset($ells["ID"])) return $ells; else return false; } public function AddByIblock($IBLOCK_ID,$arFields){ $query = new \Bitrix\Main\Entity\Query(self::getEntity()); $query->setSelect(array("IBLOCK_ID","ID")); $query->setFilter(array("=IBLOCK_ID" => $IBLOCK_ID)); $result = $query->exec(); $ells = $result->fetch(); if(isset($ells["ID"])) self::Update($ells["ID"],$arFields); else $res = self::Add($arFields); } public function DeleteByIblock($IBLOCK_ID){ $query = new \Bitrix\Main\Entity\Query(self::getEntity()); $query->setSelect(array("IBLOCK_ID","OFFER_IBLOCK_ID","ID","IS_CATALOG","IS_OFFERS")); $query->setFilter(array("=IBLOCK_ID" => $IBLOCK_ID)); $result = $query->exec(); $ells = $result->fetch(); if(isset($ells["ID"]) && $ells["OFFER_IBLOCK_ID"]==null) {self::Delete($ells["ID"]); return true;} }
И файл шаблона с галочкой который мы выводим:
Ура, вот и получилась у нас рабочая галочка которая определяет каталог это у нас или нет.
Часто возникает ситуация, что необходимо расширить функционал метода emarket basket(), который отвечает за добавление товара в корзину. В версии системы 2.8.4.3 и ниже в методе basket еще не прописано событие для кастомизации данного действия, поэтому для решения задачи из нашего примера вынесем метод basket в кастомный файл.
Задача: необходимо при добавлении товара в корзину дать покупателю оставить небольшой комментарий к товару, который он положит в корзину, также этот комментарий необходимо видеть в административной части при просмотре параметров заказа. Также, в наш новый метод мы добавим функционал, который позволит увеличивать или уменьшать на единицу количество любого элемента заказа.
Решение: Скопируем код метода basket из файла __purchasing.php , в файл classes/modules/emarket/__custom.php и переименуем метод в basket_custom . В новом методе, строку:
$order = self :: getBasketOrder (! in_array ($mode , array ("put" , "remove" )));
необходимо изменить на:
$order = $this -> getBasketOrder (! in_array ($mode , array ("put" , "remove" )));
После строки:
if ($itemType == "element" ) {
$orderItem -> dop_info = htmlspecialchars (getRequest ("dop_info" ));
данный код будет добавлять в элемент заказа переданное значение из поля dop_info .
Для настройки функционала увеличения или уменьшения количества какого-либо элемента заказа, необходимо:
после строки:
$options = getRequest ("options" );
добавить:
$mode_change = (int ) getRequest ("change_item" );
и также после строки:
$amount = $amount ? $amount : ($orderItem -> getAmount () + 1 );
добавить:
if ($mode_change ) $amount = ($mode_change == 1 ) ? $orderItem -> getAmount () + 1 : $orderItem -> getAmount () - 1 ;
Если говорить на примере TPL-шаблонизатора, то при использовании в шаблоне tpls/emarket/default.tpl , в блоке order_item , ссылок:
+ -
к которым можно прикрепить кнопки, можно увеличивать или уменьшать количество любого элемента заказа на единицу.
array ("basket_custom" )); ?>
В шаблонах сайта, должна быть создана форма для добавления товара в корзину, action формы, стандартный, за исключением того, что вместо метода basket необходимо прописать basket_custom. Также в саму форму необходимо добавить input:
< input name = "dop_info" style = "width: 100px;" type = "text" />
В административой части, в модуле Шаблоны данных необходимо открыть на редактирование тип данных Заказ , получить его xml представление, добавив в конце адресной строки браузера: .xml
В полученных данных, будет примерно следующий блок:
в которых guide-id="73" , тогда открываем на редактирование тип данных с type_id = 73, по ссылке /admin/data/type_edit/73/ . В группу "Свойства наименования" добавим еще одно поле типа Строка, со строковым идентификатором dop_info . Для отображения сохраненного значения, при подробном просмотре заказа, переназначим некоторые шаблоны из файла \styles\skins\mac\data\modules\emarket\order-edit.xsl , если быть точнее добавим к имени элемента заказа, вывод значения из поля dop_info. Для реализации данной части заказа добавим файл: \styles\skins\mac\data\modules\emarket\form.modify.custom.xsl со следующим содержанием:
После выполнения всех описанных действий, при добавлении покупателем элемента заказа, он может указать краткий комментарий к добавляемому товару, а администратор магазина, при подробном просмотре заказа, этот комментарий сможет прочитать.
Содержимое включаемых областей хранится в отдельных PHP или HTML файлах. Области для страниц или разделов сохраняются с некоторым суффиксом. Например, в поставляемых файлах продукта в качестве обозначения включаемой области для страницы используется суффикс _inc (например, index_inc.php ), а включаемая область для раздела сайта сохраняется в файле с именем sect и добавлением к нему суффикса (например, sect_inc.php ).
Важно! Файл с включаемой областью должен быть сохранен в той же директории, что и страница, для которой он был создан. Включаемая область для раздела - в папке этого раздела.
Подключение областей в шаблоне дизайна сайта выполняется с помощью компонента Вставка включаемой области либо с помощью функции .
Суффикс, используемый для обозначения включаемых областей, определяется одноименной опцией в настройках компонента Вставка включаемой области . Компонент можно размещать не только в шаблоне дизайна, но и страницах сайта с условием, что суффикс файла должен быть задан отличным от того, который используется в шаблоне.
Один компонент может быть настроен только на один из вышеописанных вариантов отображения. Кроме того, они могут отображаться на страницах сайта в соответствии с любыми другими условиями. Например, только на главной странице сайта или только для авторизованных посетителей, и т.д.
Примечание: определяется опцией Показывать включаемую область .
Если компонент расположить в шаблоне дизайна сайта, то информация из файла будет выводиться на всем сайте. Установка параметра доступна только пользователю с правами edit_php .
Для размещения включаемой области выполните следующее:
Использование включаемых областей при интеграции в дизайн имеет ограничение. Оно связано с размером, отведенным под ячейку, в которой размещен компонент. Если вводимый текст, картинка, либо что-то еще по своим размерам больше, чем отведенное компоненту место, то дизайн «поедет», то есть исказится.
Использование включаемых областей позволяет управлять не только текстом. Можно поместить в эту область картинку вместо текста (или компонент Случайное фото ) и получить индивидуальный вид каждого раздела. При этом индивидуальность будет «динамическая», изменяемая.
Создание включаемых областей может быть выполнено:
Аналогично перейти к редактированию включаемых областей можно:
Внимание!
Если в качестве включаемой области будет использоваться вариант из файла
, то необходимо проверить, что файл подключен из системы, а не вызван напрямую. Делается это с помощью следующей строки:
.
Пример содержимого включаемого файла:
Шаблоны включаемых областей
Включаемые области создаются на основе шаблонов, хранящихся в папках с именем /page_templates/ :
Чтобы в визуальном редакторе можно было выбирать шаблон, на основе которого создается редактируемая область, список шаблонов для редактируемых областей должен быть добавлен в файл .content.php .
Файл .content.php хранится в папке /page_templates/ в каталоге соответствующего шаблона сайта.
Пример содержимого файла:
if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true)die(); IncludeTemplateLangFile(__FILE__); $TEMPLATE["standard.php"] = Array("name"=>GetMessage("standart"), "sort"=>1); $TEMPLATE["page_inc.php"] = Array("name"=>GetMessage("page_inc"), "sort"=>2); $TEMPLATE["sect_inc.php"] = Array("name"=>GetMessage("sect_inc"), "sort"=>3); ?>
Следует обратить внимание на то, что имя шаблона может быть передано параметром при подключении редактируемой области в шаблоне сайта ("TEMPLATE"=>"page_inc.php" в примере ниже).
Если подключение редактируемых областей выполняется с помощью PHP функции IncludeFile() , помещаемой в соответствующие места шаблона дизайна, то код может иметь такой вид: