Весь контент admin
- 
	
		
		Cамое важное о мета тегах: title, description, keywords (оптимизация, длина, примеры)
		
		Для каждого отважного начинающего блогера или интернет-бизнесмена одной из самый первых и важных задач является навести порядок в хаосе терминов и их актуальности. Title, Description и Keywords это именно то, с чего следует начинать. Три маленьких слова, которые либо приведут ваш сайт на вершину поисковой пирамиды, либо похоронят под завалами других, неухоженных страниц, про которые никто никогда не узнает. Если это и есть ваш ночной кошмар, и вы чувствуете, что готовы немного потрудиться чтобы расставить всё на свои места – off we go. Для начала вспомним, что такое мета теги или meta tags. Это такие-себе HTML-теги для веб-документов, которые нужны для отображения мета данных вашей страницы. Указывать их нужно в заголовке в самом начале. Свою главную роль они играют при ранжировании в поиске, поисковые системы используют их для считывания и обработки данных с вашей страницы и определяют вам место в поисковых запросах. Чем лучше и грамотней прописаны мета теги, тем выше ваш результат окажется при поиске. Название, описание и ключевые слова – три столба, на которые опирается популярность вашей страницы. TITLE Теперь попробуем разобраться, что это все значит и с чем его едят. Title это тот же заголовок веб-сайта, который виден в браузере и при поиске. Его принято считать самым главным показателем релевантности сайта. Если для поисковых роботов заголовок будет максимально соответствующим по смыслу запросу, страница получит достаточно высокую позицию при поиске. Но не забывайте о том, что Title должен быть привлекательным не только для поисковых систем, а и для людей. Точно так же как в газетах и журналах, заголовок должен пробуждать интерес и обещать важную информацию. Так что не стоит писать его опустив рукава, пару лишних минут на создание мощного заголовка с лихвой вам окупятся, обещаю. ОСНОВНЫЕ ТРЕБОВАНИЕ К ЗАГОЛОВКУ Оптимальная длина заголовка это уже половина успеха. Больше 60 – поисковые системы не покажут дальше 60 символа, а слишком короткие чаще всего выглядят несерьезно и неубедительно. В идеале 45-60 символов. Заголовок для каждой страницы должен быть уникальным. Постарайтесь поставить ключевые слова в самом начале заголовка. Точка в конце запроса не нужна, точно так же, как и вопросительный знак. Забудьте про повторения. Получится каша вместо привлекательного заголовка. И маленькая подсказка: используйте только те заголовки, по теме которых действительно сможете дать нужную информацию. Иначе толку от высоких позиций не будет, а ваши клиенты или читатели покинут сайт недовольные и никогда больше не вернутся. Сравните сами: Here you can get the best WordPress themes for your business. - звучит неплохо, но слишком длинный заголовок будет обрезан, главные слова стоят ближе к концу и точка в конце лишняя. Best WordPress Business Themes - четко и ясно, поисковые системы пропустят по длине, точки в конце нет. Этот заголовок очень точно говорит о том, что ожидает посетителей на этой странице. Искали wordpress business themes, это и нашли. DESCRIPTION Description или описание - это мета-тег, который говорит о содержимом странице. Название говорит само за себя: короткое описание, которое видит пользователь, совершая поиск. Правильно составленное описание – маленькая SEO-деталь, которая может дать движение всему огромному механизму. Или же нет. Не стоит вписывать сюда название компании или контактную информацию. Пары интересных и грамотных предложений хватит для того чтобы задержать внимание потенциального читателя или клиента и превратить его в постоянного пользователя вашего ресурса. И куда же без правил? Длина Description должна быть около 150-165 символом, этого будет достаточно чтобы предоставить основную информацию о вашей странице и не усыпить ваших посетителей долгими и нудными описаниями. Описание должно быть уникальным. То есть просто скопировать информацию с сайта и вставить в строку будет недостаточно. Description должно отличаться от Titel. В конце ставится точка. Можете использовать ключевую фразу несколько раз, разбавляя её синонимами. Это ваш шанс написать о ваших сильных сторонах, то что выделяет вас среди сотен таких же блогов или компаний. Не упустите этот шанс. И еще. Взывайте к эмоциям и призывайте к действию, не стесняйтесь. Использование таких слов как «зайдите», «узнайте», «купите», «выберите», «прочитайте» только приветствуется. Убедитесь в этом сами: Here you can get the best WordPress themes for your business. Find the best one for your needs.слишком коротко и не цепляет, непонятно почему стоит выбирать именно эти шаблоны, в чем их преимущество. Need a website for your business that is easily customizable and yet feature rich and trendy looking? Choose best premium business WordPress themes from the web design masters at Template Monster.есть описание сильных сторон шаблонов компании, призыв к действию и описание достаточно многообещающее. KEYWORDS В последнее время ведется много споров о том, нужны ли ключевые слова для продвижения сайтов. Одни кричат, что век ключевых слов закончился еще в далеком 2002, другие все же советуют включить пару-тройку для очищения SEO-совести. Попробуем разобраться и в этом Изначально, ключевые слова отыгрывали такую же важную роль как и остальные мета теги, но особо хитрые начали использовать это далеко не в благих целях, а пичкать в эту строку самые частые запросы. То, что они чаще всего совершенно не отвечали действительности, никого не волновало, сайты ведь были в топе, хотя и целевую аудиторию за собой не приводили. Реакция на такие выходки не заставила себя долго ждать и умные поисковые системы просто начали банить за такое и само собой важность этих самых keywords значительно упала. Так что же теперь делать честным блогерам и предпринимателям, которые просто хотят продвинуть свой сайт и донести свой качественный контент до своей аудитории? Правду нужно искать где-то посередине. Ключевые слова в малых количествах точно не навредят, но, вероятнее всего и большой магии не сделают. Если же вы все же чувствуете острую необходимость использовать keywords, то постарайтесь соблюдать следующие правила: Используйте слова, которые действительно связанные с вашей страницей. Если вы говорите о шаблонах и бизнесе, то не стоит писать о революции в мире программирования. Пишутся ключевые слова как правильно в начале документа. Не переусердствуйте. Парочки запросов будет достаточно. И еще раз, никакого спама, это грозит долгим и не очень увлекательным путешествием вашей страницы по самым низам поисковых списков. Теперь вы еще на шаг ближе к созданию чего-то действительно большого. Учитывая все эти правила вам не составит никакого труда создать качественные и цепляющие заголовки, описания и ключевые слова. Руководствуясь честностью и соблюдая уникальность, вы сможете не только поднять свою страницу на самый верх при поиске, но и заполучить внимание важной для вас аудитории. Удачи!
 - 
	
		
		Открытие и скрытие блока при клике по ссылке
		
		Как то в одном моем проекте мне надо было реализовать скрытие и отображение части текста — кликая по ссылке. Учитывая то, что заказчик сам выбирает что скрывать, мне нужен был скрипт jquery, который не будет зависеть от места его применения. Для начала надо продумать все так, что бы неподготовленный человек смог использовать данный функционал. Конечно же, все должно быть просто и редактироваться в визуальном редакторе или при минимальном копании в html коде описания или статьи. Поэтому реализовал я это следующим образом. Когда заказчику надо было скрыть часть текста, он перед скрываемым блоком писал Слово-ссылку (например “Подробнее”), и присваивал этому слову заголовок h6, после которого все что надо было скрыть — помещали в контейнер div с классом hide. Получалась вот такая конструкция html кода: <h6>Подробнее</h6> <div class='hide'> <p>Скрытый текст</p> <p>Скрытый текст</p> </div> Для скрытия текста, нам надо в css файл стилей вписать .hide{display:none;} после чего блок будет недоступен для просмотра. Для открытия блока по клику на текст Подробнее, где в тех же css стилях его можно украсить под ссылку, нам надо добавить javascript код на jquery. Для этого нам надо добавить на страницу скрипт: $(function(){ $('h6').toggle(function(){ $(this).next("div.hide").slideDown(); $('h6').html("Закрыть"); }, function(){ $(this).next("div.hide").slideUp(); $('h6').html("Подробнее"); }); }); После этих дополнений мы можем скрывать и открывать блоки с контентом по клику на ссылку или текст. Обратите внимание еще на такой момент. Скрывая текст от пользователя, таким образом, Вы рискуете, что поисковые системы посчитают это за черный метод SEO, за что могут быть применены санкции до сайта. Но если это делать в меру, например скрывать табличные данные, и писать текст ссылки как посмотреть характеристики , то оно будет удобно, и вероятность что сайт залетит под фильтры поисковых систем сводятся к нулю.
 - 
	
		
		Кнопка показать еще в категории Opencart
		
		В категории Opencart есть пагинация. С помощью нее мы переходим на следующие страницы в каталоге товаров. Но часто многим пагинация не нужна. В категории Opencart есть пагинация. С помощью нее мы переходим на следующие страницы в каталоге товаров. Но часто многим пагинация не нужна, и всем известно что переход на следующие страницы порождает дубли категорий в опенкарте. Есть альтернативный вариант показать пользователю больше товаров не переходя на постраничную навигацию. Этот способ мы назвем подгрузка товаров или же Кнопка показать больше или показать еще 20 товаров, где 20 - это лимит на страницу. В чем преимущества? Более быстрая подгрузка товаров, чем переходя на по страницам Отсутствуют дубли категорий (если убрать пагинацию) Покупатель не покидает товары с первой страницы, очень удобно если надо сравнить товары или вернуть к какому-то повторно. (Смотрите решение - продвинутое добавление в закладки) Как видим преимуществ много. Итак для внедрения этого функционала надо открыть catalog/controller/product/category.php и найти строку $product_total = $this->model_catalog_product->getFoundProducts(); или вместо getFoundProducts будет getTotalProduct(); после этой строки добавить $this->data['ttl'] = $product_total; Таким образом мы из контроллера передаем данные о количестве товара в категории. Далее открываем шаблон категории catalog/view/theme/default/template/product/category.tpl и находим пагинацию. В каждом шаблоне по разному, но смотрите такой код <?php echo $pagination; ?> после, или перед этой строкой вставляем код кнопки подгрузки: (саму пагинацию можно убрать или уставить, как будет удобно) <?php if($ttl > count($products)){ ?> <input type="hidden" value="<?php echo $this->config->get('config_catalog_limit') + count($products); ?>" name="limit" id="pagin_limit"> <?php if(($ttl - count($products)) >= $this->config->get('config_catalog_limit')){ ?> <div class="category_load">Показать еще <?php echo $this->config->get('config_catalog_limit'); ?></div> <?php }else{ ?> <div class="category_load">Показать еще <?php echo $ttl-count($products); ?></div> <?php } ?> <script> $(".category_load").on("click", function(){ product_div = '.products'; lim = $("#pagin_limit").val(); $("." + product_div).load('index.php?route=product/category&path=<?php echo $this->request->get['path']; ?>&limit=' + lim + ' ' + product_div + '>*', function(){ $("#pagin_limit").val(lim); }); }); </script> <?php } ?> Где переменная product_div отвечает за блок в котором находятся товары. Детально расписывать работу функции подгрузки не буду, но она очень простая. При клике на кнопку показать еще у нас идет обработчик с запуском функции загрузки страницы с лимитом как первая + вторая страница и т.п. Если Количество еще не подгруженых фото больше за лимит на странице - отображается в кнопке лимит на странице. Если меньше - в кнопке показывает реально количество товара, которое еще не подгрузилось. Это очень удобно. И на последней странице подгрузки кнопка не будет отображаться т.к. уже все подгружено на страницу и она не нужна. Недостаток такого подхода в том что он не понимает сортировки товаров, лимиты и т.п. фильтрации в категории. Но основа есть и при желании расширить функционал это не составит труда.
 - 
	
		
		Как включить и отключить несколько продуктов OpenCart 2.1.x, 2.2.x и 2.3.x
		
		Работа администратора в интернет-магазине занимает довольно много времени. Лучше нас это никто не знает. А поскольку iSenseLabs призвана облегчить жизнь пользователям OpenCart, мы решили создать краткое руководство по программированию, которое добавит дополнительную функцию в ваш магазин. Это бесплатно, его легко реализовать, и он может сэкономить массу времени при редактировании нескольких продуктов. Вам нужно включить или отключить продукты в вашем магазине? Выполнение этого отдельно для каждого продукта может потребовать огромных затрат времени. Однако, внедрив этот фрагмент кода в OpenCart, вы заметите немедленное изменение — две дикие кнопки «Включить и отключить» внезапно появятся в поле управления продуктами вашей панели администратора. Посмотрим, как реализована эта функция. Если у вас есть любимый текстовый редактор (Скобки, Блокнот, Блокнот ++ и т. д.), смело открывайте его и вставляйте пример кода. Поскольку пример кода составляет более 200 строк, мы решили превратить его в файл Важное примечание : эта функция была протестирована и работает на версиях OpenCart 2.1.x, 2.2.x и 2.3.x. How to Enable and Disable Multiple Products
 - 
	
		
		Включить/отключить продукты oc302
		
		Включить/отключить продукты oc302 enable-disable-products-oc302
 - 
	
		
		Изображение по умолчанию для продуктов
		
		Изображение по умолчанию для продуктов default_product_image.xml
 - 
	
		
		Как удалить «Сравнить этот товар» и «Добавить в список желаний» в OpenCart 2.0
		
		«Сравнить этот товар» и «Добавить в список желаний» — отличные функции OpenCart, однако для некоторых магазинов они просто не нужны. К сожалению, нет функций или настроек, позволяющих отключить эти две функции. В этом уроке я покажу вам, как удалить кнопки «Сравнить этот продукт» и «Добавить в список желаний» из всех мест в вашем магазине OpenCart. В этом уроке я буду использовать чистую установку OpenCart 2.0.1.1 с включенной темой по умолчанию. Чтобы удалить кнопки «Сравнить этот продукт» и «Добавить в список желаний», нам нужно изменить несколько шаблонов OpenCart. Мы можем внести изменения непосредственно в файлы шаблонов, что я не рекомендую, или мы можем сделать их как новую модификацию OCmod. Создание модификаций с использованием системы модификаций OCmod намного безопаснее, потому что файлы ядра не изменяются напрямую, плюс мы можем в любое время вернуть функциональность по умолчанию. Создание нового файла OCmod Мы можем использовать практически любой код или текстовый редактор и создать новый пустой файл XML. Как только мы создадим файл, мы должны создать скелет нашего файла OCmod. Вы можете скопировать/вставить приведенный ниже код и изменить имя, версию, ссылку, автора и код с помощью вашей личной информации. <modification> <name>Type a name of this modification</name> <version>The version of the modification in numbers (ex. 1.0)</version> <link>http://yourwebsite.com</link> <author>Your Name</author> <code>unique_identifier_for_the_modification</code> </modification> После того, как мы сохраним файл, мы должны добавить расширение «.ocmod» после имени файла — например. модификация.ocmod.xml. Применение изменений Теперь, когда файл OCmod готов, мы начинаем добавлять модификации одну за другой. Модификации добавляются перед закрывающим тегом </modification>. 1. Удалите «Сравнить этот продукт» и «Добавить в список желаний» из «Бестселлеры», «Рекомендуемые», «Новинки», «Специальные предложения». Начнем с удаления кнопок «Сравнить этот продукт» и «Добавить в список желаний» из модулей OpenCart по умолчанию — «Бестселлеры», «Рекомендуемые», «Новинки» и «Специальные предложения». <file path="catalog/view/theme/default/template/module/{bestseller,featured,latest,special}*.tpl"> <operation> <search><![CDATA[<button type="button" data-toggle="tooltip" title="<?php echo $button_wishlist; ?>" onclick="wishlist.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-heart"></i></button>]]></search> <add position="replace"><![CDATA[]]></add> </operation> <operation> <search><![CDATA[<button type="button" data-toggle="tooltip" title="<?php echo $button_compare; ?>" onclick="compare.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-exchange"></i></button>]]></search> <add position="replace"><![CDATA[]]></add> </operation> </file> 2. Удалите «Сравнить этот продукт» и «Добавить в список желаний» со страницы категории. Кнопки «Сравнить этот продукт» и «Добавить в список желаний» есть для каждого продукта на странице категорий, как для представлений «Сетка», так и для представлений «Список». Удалим их. <file path="catalog/view/theme/default/template/product/category.tpl"> <operation> <search><![CDATA[<button type="button" data-toggle="tooltip" title="<?php echo $button_wishlist; ?>" onclick="wishlist.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-heart"></i></button>]]></search> <add position="replace"><![CDATA[]]></add> </operation> <operation> <search><![CDATA[<button type="button" data-toggle="tooltip" title="<?php echo $button_compare; ?>" onclick="compare.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-exchange"></i></button>]]></search> <add position="replace"><![CDATA[]]></add> </operation> </file> 3. Удалите «Сравнить этот продукт» и «Добавить в список желаний» со страницы продукта. <file path="catalog/view/theme/default/template/product/product.tpl"> <operation> <search><![CDATA[<button type="button" data-toggle="tooltip" class="btn btn-default" title="<?php echo $button_wishlist; ?>" onclick="wishlist.add('<?php echo $product_id; ?>');"><i class="fa fa-heart"></i></button>]]></search> <add position="replace"><![CDATA[]]></add> </operation> <operation> <search><![CDATA[<button type="button" data-toggle="tooltip" class="btn btn-default" title="<?php echo $button_compare; ?>" onclick="compare.add('<?php echo $product_id; ?>');"><i class="fa fa-exchange"></i></button>]]></search> <add position="replace"><![CDATA[]]></add> </operation> </file> 4. Удалите «Сравнить этот продукт» и «Добавить в список желаний» со страницы поиска. <file path="catalog/view/theme/default/template/product/search.tpl"> <operation> <search><![CDATA[<button type="button" data-toggle="tooltip" title="<?php echo $button_wishlist; ?>" onclick="wishlist.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-heart"></i></button>]]></search> <add position="replace"><![CDATA[]]></add> </operation> <operation> <search><![CDATA[<button type="button" data-toggle="tooltip" title="<?php echo $button_compare; ?>" onclick="compare.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-exchange"></i></button>]]></search> <add position="replace"><![CDATA[]]></add> </operation> </file> 5. Убрать ссылку "Список желаний" из шапки. После того, как вы удалили все кнопки «Добавить в список желаний» из своего магазина, ссылка «Список желаний» в верхней панели заголовка больше не нужна. Удалим и его. <file path="catalog/view/theme/default/template/common/header.tpl"> <operation> <search><![CDATA[<li><a href="<?php echo $wishlist; ?>" id="wishlist-total" title="<?php echo $text_wishlist; ?>"><i class="fa fa-heart"></i> <span class="hidden-xs hidden-sm hidden-md"><?php echo $text_wishlist; ?></span></a></li>]]></search> <add position="replace"><![CDATA[]]></add> </operation> </file> Мы добавили все модификации, которые удалят все кнопки «Сравнить этот продукт» и «Добавить в список желаний». Теперь у нас есть сохранение файла и установка в магазине с помощью установщика расширений. Однако эта модификация вызовет небольшой сбой в местах, где у нас есть кнопка «Добавить в корзину», сгруппированная с кнопками «Сравнить этот продукт» и «Добавить в список желаний». Например, у нас есть такая группа кнопок в модуле Featured и на странице категории. Глюк вызван тем, что эта группа кнопок имеет определенную ширину, а ширина кнопки «Добавить в корзину» установлена на 60%, что означает, что она не будет занимать всю ширину этой группы кнопок. Чтобы исправить этот сбой, мы должны внести небольшую модификацию в файл stylesheet.css, который находится в каталоге/представлении/теме/по умолчанию/stylesheet/stylesheet.css. Мы должны найти следующий селектор CSS «.product-thumb .button-group button» и изменить его ширину с 60% до 100%. .product-thumb .button-group button { width: 100%; ... } Заключение Это руководство сделано специально для темы OpenCart по умолчанию, но я думаю, что такой же результат может быть достигнут и для других пользовательских тем после небольших изменений в модификациях. Однако функции «Сравнить этот продукт» и «Добавить в список желаний» могут повлиять на ваши продажи, поэтому подумайте дважды, прежде чем отключать их. remove_wishlist_and_compare_buttons.ocmod.xml
 - 
	
		
		Как скрыть кнопку «Добавить в корзину» и цену товара в OpenCart 2.0.x
		
		Как владелец магазина OpenCart, вы хорошо знаете кнопку «Добавить в корзину» и цену продукта, которые отображаются рядом с каждым продуктом в вашем магазине. Есть несколько веских причин, по которым вы можете захотеть удалить эти два, но, к сожалению, OpenCart не дает вам возможности отключить их из панели администратора. Вот почему мы подготовили для вас этот краткий учебник, который покажет вам, как вы можете сделать это самостоятельно. Мы создадим два файла OCmod, которые будут вносить необходимые изменения без необходимости изменения основных файлов. Итак, продолжайте читать, чтобы узнать, как: 1. Удалите кнопку «Добавить в корзину» со страницы вашего продукта, категории и поиска, а также из заголовка вашего интернет-магазина. 2. Удалите цены продуктов из вашего продукта, категории и страниц поиска. *Обратите внимание, что руководство предназначено для темы OpenCart 2.0.x по умолчанию, поэтому, если вы используете пользовательскую тему, изменения могут не применяться. Удалите кнопку "Добавить в корзину" Чтобы превратить ваш сайт из магазина в каталог и убрать кнопку «Добавить в корзину», а также ссылку «Корзина» из шапки, вам необходимо выполнить следующие шаги: 1. Создайте новый файл OCmod, открыв предпочитаемый вами текстовый редактор и сохраните файл под именем nameofyoupreference.ocmod.xml. 2. Скопируйте и вставьте следующий код, который фактически является открытием и закрытием документа: <modification> <name>Hide Add to Cart Button</name> <version>1.0</version> <link>https://isenselabs.com</link> <author>iSenseLabs</author> <code>isenselabs_hide_add_to_cart</code> {body} </modification> 3. На место {body} скопируйте и вставьте следующие фрагменты кода, чтобы: Убрать кнопку "Добавить в корзину" со страницы товара <file path="catalog/view/theme/*/template/product/product.tpl"> <operation> <search><![CDATA[<button type="button" id="button-cart" data-loading-text="<?php echo $text_loading; ?>" class="btn btn-primary btn-lg btn-block"><?php echo $button_cart; ?></button>]]></search> <add position="replace"><![CDATA[ <button type="button" style="display:none;" id="button-cart" data-loading-text="<?php echo $text_loading; ?>" class="btn btn-primary btn-lg btn-block"><?php echo $button_cart; ?></button> ]]></add> </operation> <operation> <search><![CDATA[<button type="button" onclick="cart.add('<?php echo $product['product_id']; ?>');"><span class="hidden-xs hidden-sm hidden-md"><?php echo $button_cart; ?></span> <i class="fa fa-shopping-cart"></i></button>]]></search> <add position="replace"><![CDATA[ <button type="button" style="display:none;" onclick="cart.add('<?php echo $product['product_id']; ?>');"><span class="hidden-xs hidden-sm hidden-md"><?php echo $button_cart; ?></span> <i class="fa fa-shopping-cart"></i></button> ]]></add> </operation> <operation> <search><![CDATA[<button type="button" data-toggle="tooltip" title="<?php echo $button_wishlist; ?>" onclick="wishlist.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-heart"></i></button>]]></search> <add position="replace"><![CDATA[ <button type="button" style="width:50%;" data-toggle="tooltip" title="<?php echo $button_wishlist; ?>" onclick="wishlist.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-heart"></i></button> ]]></add> </operation> <operation> <search><![CDATA[<button type="button" data-toggle="tooltip" title="<?php echo $button_compare; ?>" onclick="compare.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-exchange"></i></button>]]></search> <add position="replace"><![CDATA[ <button type="button" style="width:50%;" data-toggle="tooltip" title="<?php echo $button_compare; ?>" onclick="compare.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-exchange"></i></button> ]]></add> </operation> </file> Удалите кнопку «Добавить в корзину» со страницы категории: view sourceprint? <file path="catalog/view/theme/*/template/product/category.tpl"> <operation> <search><![CDATA[<button type="button" onclick="cart.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-shopping-cart"></i> <span class="hidden-xs hidden-sm hidden-md"><?php echo $button_cart; ?></span></button>]]></search> <add position="replace"><![CDATA[ <button type="button" style="display:none;" onclick="cart.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-shopping-cart"></i> <span class="hidden-xs hidden-sm hidden-md"><?php echo $button_cart; ?></span></button> ]]></add> </operation> <operation> <search><![CDATA[<button type="button" data-toggle="tooltip" title="<?php echo $button_wishlist; ?>" onclick="wishlist.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-heart"></i></button>]]></search> <add position="replace"><![CDATA[ <button type="button" style="width:50%;" data-toggle="tooltip" title="<?php echo $button_wishlist; ?>" onclick="wishlist.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-heart"></i></button> ]]></add> </operation> <operation> <search><![CDATA[<button type="button" data-toggle="tooltip" title="<?php echo $button_compare; ?>" onclick="compare.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-exchange"></i></button>]]></search> <add position="replace"><![CDATA[ <button type="button" style="width:50%;" data-toggle="tooltip" title="<?php echo $button_compare; ?>" onclick="compare.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-exchange"></i></button> ]]></add> </operation> </file> Удалите кнопку «Добавить в корзину» со страницы поиска: <file path="catalog/view/theme/*/template/product/search.tpl"> <operation> <search><![CDATA[<button type="button" onclick="cart.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-shopping-cart"></i> <span class="hidden-xs hidden-sm hidden-md"><?php echo $button_cart; ?></span></button>]]></search> <add position="replace"><![CDATA[ <button type="button" style="display:none;" onclick="cart.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-shopping-cart"></i> <span class="hidden-xs hidden-sm hidden-md"><?php echo $button_cart; ?></span></button> ]]></add> </operation> <operation> <search><![CDATA[<button type="button" data-toggle="tooltip" title="<?php echo $button_wishlist; ?>" onclick="wishlist.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-heart"></i></button>]]></search> <add position="replace"><![CDATA[ <button type="button" style="width:50%;" data-toggle="tooltip" title="<?php echo $button_wishlist; ?>" onclick="wishlist.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-heart"></i></button> ]]></add> </operation> <operation> <search><![CDATA[<button type="button" data-toggle="tooltip" title="<?php echo $button_compare; ?>" onclick="compare.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-exchange"></i></button>]]></search> <add position="replace"><![CDATA[ <button type="button" style="width:50%;" data-toggle="tooltip" title="<?php echo $button_compare; ?>" onclick="compare.add('<?php echo $product['product_id']; ?>');"><i class="fa fa-exchange"></i></button> ]]></add> </operation> </file> Удалите ссылку и кнопку «Корзина» из шапки: <file path="catalog/view/theme/*/template/common/header.tpl"> <operation> <search><![CDATA[<li><a href="<?php echo $shopping_cart; ?>" title="<?php echo $text_shopping_cart; ?>"><i class="fa fa-shopping-cart"></i> <span class="hidden-xs hidden-sm hidden-md"><?php echo $text_shopping_cart; ?></span></a></li>]]></search> <add position="replace"><![CDATA[ <li style="display:none;"><a href="<?php echo $shopping_cart; ?>" title="<?php echo $text_shopping_cart; ?>"><i class="fa fa-shopping-cart"></i> <span class="hidden-xs hidden-sm hidden-md"><?php echo $text_shopping_cart; ?></span></a></li> ]]></add> </operation> <operation> <search><![CDATA[<div class="col-sm-3"><?php echo $cart; ?></div>]]></search> <add position="replace"><![CDATA[ <div class="col-sm-3" style="display:none;"><?php echo $cart; ?></div> ]]></add> </operation> </file> Имейте в виду, что все фрагменты кода должны быть после строки <code>isenselabs_hide_add_to_cart</code> и перед закрывающим тегом </modification> . Если вы хотите убрать кнопку «Добавить в корзину» не из всех, а только из некоторых ранее упомянутых мест, включите только соответствующие фрагменты кода. 4. Сохраните и загрузите файл через установщик расширений в панели администратора => Расширения => Установщик . Затем перейдите в « Расширения» => «Модификации» и « Обновите », нажав кнопку «Обновить». Удалить цены на продукты Для того, чтобы убрать цены на ваши товары, вам нужно будет выполнить те же действия, что и раньше, но изменить код в файле OCmod. 1. Начните с создания файла OCmod и назовите его nameofyoupreference.ocmod.xml. 2. Скопируйте и вставьте следующий фрагмент кода с открывающим и закрывающим тегами: <modification> <name>Hide Product Price</name> <version>1.0</version> <link>https://isenselabs.com</link> <author>iSenseLabs</author> <code>isenselabs_hide_product_price</code> <wbr> {body} </modification> * КОД каждой модификации должен быть уникальным, поэтому убедитесь, что имя в тегах <code></code> не используется. Также обратите внимание, что если вы загружаете модификацию более одного раза, вам придется сначала удалить старую модификацию, ОЧИСТИТЬ модификации и только после этого загрузить новую. 3. Замените {body} следующими кодами, чтобы: Удалите продукт и цену опциона со страницы продукта: <file path="catalog/view/theme/*/template/product/product.tpl"> <operation> <search><![CDATA[<?php if ($price) { ?>]]></search> <add position="before"><![CDATA[ <div style="display:none"> ]]></add> </operation> <operation> <search><![CDATA[ <div id="product">]]></search> <add position="before"><![CDATA[ </div> ]]></add> </operation> <operation> <search><![CDATA[<?php foreach ($option['product_option_value'] as $option_value) { ?>]]></search> <add position="after"><![CDATA[<?php unset($option_value['price']); $option_value['price'] ="";?>]]> </add> </operation> <operation> <search><![CDATA[<p class="price">]]></search> <add position="replace"><![CDATA[<p class="price" style="display:none;">]]> </add> </operation> </file> Удалите цену продукта со страницы категории: <file path="catalog/view/theme/*/template/product/category.tpl"> <operation> <search><![CDATA[<p class="price">]]></search> <add position="replace"><![CDATA[<p class="price" style="display:none;">]]> </add> </operation> </file> Удалите цену продукта со страницы поиска: <file path="catalog/view/theme/*/template/product/search.tpl"> <operation> <search><![CDATA[<p class="price">]]></search> <add position="replace"><![CDATA[<p class="price" style="display:none;">]]> </add> </operation> </file> 4. Сохраните и загрузите файл через установщик расширений в панели администратора => Расширения => Установщик . Затем перейдите в « Расширения» => «Модификации» и « Обновите », нажав кнопку «Обновить». hide_add_to_cart.xml hide_product_price.xml
 - 
	
		
		Как добавить логотип магазина и изображения товаров в счета в OpenCart 2.x
		
		В этом сообщении блога мы покажем вам, как добавить логотип вашего магазина и изображения продуктов в ваши счета, изменив шаблон счета OpenCart 2.x по умолчанию. К сожалению, в OpenCart нет встроенного редактора для редактирования шаблона счета в админке, и нам приходится менять его, модифицируя сам код. Примечание: эта модификация работает только с OpenCart 2.x, и мы предполагаем, что вы уже установили ее. Модификация OCMod Поскольку изменение файлов ядра не рекомендуется из-за сбоев в работе других модулей, мы будем использовать модификацию OCMod для редактирования шаблона для счетов. Этот метод не изменяет основные функции OpenCart, поэтому мы собираемся использовать его. Первое, что вы должны сделать, это создать новый файл XML и назвать его yourcustommodificationname.ocmod.xml . Пожалуйста, убедитесь, что ваш файл имеет расширение .ocmod.xml , иначе он не будет распознан встроенным установщиком расширений в OpenCart 2.x. Добавить логотип в шапку Если мы хотим включить логотип в заголовок счета-фактуры, нам нужно добавить эти строки в файл: <modification> <name>Add images to invoice by iSenseLabs</name> <version>1.0 (Initial)</version> <link>https://isenselabs.com</link> <code>isense_invoice</code> <author>iSenseLabs</author> <file path="admin/controller/sale/order.php"> <operation> <search><![CDATA[public function invoice() {]]></search> <add position="after"><![CDATA[ // ISENSELABS.COM CODE STARTS HERE $this->load->model('tool/image'); // ISENSELABS.COM CODE ENDS HERE ]]></add> </operation> <operation> <search index="0"><![CDATA[$this->response->setOutput($this->load->view('sale/order_invoice]]></search> <add position="before"><![CDATA[ // ISENSELABS.COM CODE STARTS HERE if (isset($this->request->server['HTTPS']) && (($this->request->server['HTTPS'] == 'on') || ($this->request->server['HTTPS'] == '1'))) { $server = $this->config->get('config_ssl'); } else { $server = $this->config->get('config_url'); } if ($this->config->get('config_logo') && file_exists(DIR_IMAGE . $this->config->get('config_logo'))) { $data['logo'] = $server . 'image/' . $this->config->get('config_logo'); } else { $data['logo'] = ''; } // ISENSELABS.COM CODE ENDS HERE ]]></add> </operation> </file> <file path="admin/view/template/sale/order_invoice.tpl"> <operation> <search><![CDATA[<div style="page-break-after: always;">]]></search> <add position="after"><![CDATA[ <!-- ISENSELABS.COM CODE STARTS HERE --> <?php if(isset($logo) && !empty($logo)) { ?> <img src="../<?php echo $logo ?>" style="margin: 15px 0 15px 0;" /> <?php } ?> <!-- ISENSELABS.COM CODE ENDS HERE --> ]]></add> </operation> </file> </modification> Затем мы должны перейти на вашу страницу администрирования магазина OpenCart> Расширения> Установщик расширений и загрузить файл. После того, как мы получим сообщение «Успешно: вы установили расширение!», нам нужно перейти в «Расширения»> «Модификации» и нажать кнопку «Обновить», чтобы применить изменения. Счета-фактуры должны выглядеть так: Добавьте изображения товаров Если мы хотим также добавить изображения продуктов в наши счета-фактуры, мы должны добавить этот код в наш текущий файл модификации или мы можем создать новый: <file path="admin/controller/sale/order.php"> <operation> <search><![CDATA[public function invoice() {]]></search> <add position="after"><![CDATA[ // ISENSELABS.COM CODE STARTS HERE $this->load->model('catalog/product'); // ISENSELABS.COM CODE ENDS HERE ]]></add> </operation> <operation> <search index="0"><![CDATA[$product_data[] = array(]]></search> <add position="before"><![CDATA[ // ISENSELABS.COM CODE STARTS HERE $width = 100; $height = 100; $product_info = $this->model_catalog_product->getProduct($product['product_id']); if (version_compare(VERSION, '2.3', '>=')) { $this->load->model('tool/image'); } if ($product_info['image']) { $image = $this->model_tool_image->resize($product_info['image'], $width, $height); } else { $image = $this->model_tool_image->resize('placeholder.png', $width, $height); } // ISENSELABS.COM CODE ENDS HERE ]]></add> </operation> <operation> <search index="0"><![CDATA[$product_data[] = array(]]></search> <add position="after"><![CDATA[ 'image' => $image, // ISENSELABS.COM ]]></add> </operation> </file> <file path="admin/view/template/sale/order_invoice.tpl"> <operation> <search><![CDATA[<?php echo $product['name']; ?>]]></search> <add position="replace"><![CDATA[ <!-- ISENSELABS.COM CODE STARTS HERE --> <?php if(isset($product['image']) && !empty($product['image'])) { ?> <img src="<?php echo $product['image'] ?>" /> <?php } ?> <!-- ISENSELABS.COM CODE ENDS HERE --> <?php echo $product['name']; ?> ]]></add> </operation> </file> После этого нам нужно загрузить файл модификации в наш магазин OpenCart и окончательный результат будет таким: Изменить размер изображения продукта Здесь мы покажем вам, как настроить код в файле OCMod. Эта модификация не большая, но можно внести небольшие изменения. Если вам нужны изображения товаров меньшего или большего размера, вы можете изменить эти две строки: $width = 100; $height = 100; После внесения изменений необходимо сохранить файл .ocmod.xml. Перед загрузкой модификации вам необходимо удалить старую из «Расширения» > «Модификации», отметить модификацию «Добавить изображения в счет от iSenseLabs» и нажать красную кнопку «Удалить». Вам также необходимо очистить и обновить модификации. После этого вы сможете загрузить новый файл .ocmod.xml, используя встроенный установщик расширений в OpenCart 2.x. invoice.ocmod.xml
 - 
	
		
		Адаптивное изменение размеров изображений (crop, resize) в OpenCart
		
		Хотя с обработкой изображений в Opencart дела обстоят не очень плохо, но все же, примерно в 50% случаев, возникает необходимость сначала обрезать, а уже потом уменьшать изображения до заданных в настройках размеров. Таким образом, в не зависимости от пропорций исходного изображения, при выводе изображений мы всегда получаем изображение, занимающее максимум от возможного места без белых полей по краям. Это решение будет оптимально если большинство изображений на сайте имеют фон, отличный от белого (наверное актуально в основном для стандартной темы). За ресайз изображений в Opencart отвечает класс ModelToolImage, находящийся в catalog/model/tool/image.php. Вариант 1. Если необходимо, чтобы абсолютно все изображения обрабатывались по нашей схеме, то замените полностью метод resize на public function resize($filename, $width, $height, $type = "") { if (!file_exists(DIR_IMAGE . $filename) || !is_file(DIR_IMAGE . $filename)) { return; } $info = pathinfo($filename); $extension = $info['extension']; $old_image = $filename; $new_image = 'cache/' . utf8_substr($filename, 0, utf8_strrpos($filename, '.')) . '-' . $width . 'x' . $height . $type .'.' . $extension; if (!file_exists(DIR_IMAGE . $new_image) || (filemtime(DIR_IMAGE . $old_image) > filemtime(DIR_IMAGE . $new_image))) { $path = ''; $directories = explode('/', dirname(str_replace('../', '', $new_image))); foreach ($directories as $directory) { $path = $path . '/' . $directory; if (!file_exists(DIR_IMAGE . $path)) { @mkdir(DIR_IMAGE . $path, 0777); } } list($width_orig, $height_orig) = getimagesize(DIR_IMAGE . $old_image); if ($width_orig != $width || $height_orig != $height) { $scaleW = $width_orig/$width; $scaleH = $height_orig/$height; $image = new Image(DIR_IMAGE . $old_image); if ($scaleH > $scaleW) { $_height = $height * $scaleW; $top_x = 0; $top_y = ($height_orig - $_height) / 2; $bottom_x = $width_orig; $bottom_y = $top_y + $_height; $image->crop($top_x, $top_y, $bottom_x, $bottom_y); } elseif ($scaleH < $scaleW) { $_width = $width * $scaleH; $top_x = ($width_orig - $_width) / 2; $top_y = 0; $bottom_x = $top_x + $_width; $bottom_y = $height_orig; $image->crop($top_x, $top_y, $bottom_x, $bottom_y); } $image->resize($width, $height, $type); $image->save(DIR_IMAGE . $new_image); } else { copy(DIR_IMAGE . $old_image, DIR_IMAGE . $new_image); } } if (isset($this->request->server['HTTPS']) && (($this->request->server['HTTPS'] == 'on') || ($this->request->server['HTTPS'] == '1'))) { return $this->config->get('config_ssl') . 'image/' . $new_image; } else { return $this->config->get('config_url') . 'image/' . $new_image; } } Вариант 2. Если такая обработка подходит не всегда, а только в отдельных случаях, то добавьте следующий код перед определением метода resize public function myResize($filename, $width, $height, $type = "") { if (!file_exists(DIR_IMAGE . $filename) || !is_file(DIR_IMAGE . $filename)) { return; } $info = pathinfo($filename); $extension = $info['extension']; $old_image = $filename; $new_image = 'cache/' . utf8_substr($filename, 0, utf8_strrpos($filename, '.')) . '-' . $width . 'x' . $height . $type .'.' . $extension; if (!file_exists(DIR_IMAGE . $new_image) || (filemtime(DIR_IMAGE . $old_image) > filemtime(DIR_IMAGE . $new_image))) { $path = ''; $directories = explode('/', dirname(str_replace('../', '', $new_image))); foreach ($directories as $directory) { $path = $path . '/' . $directory; if (!file_exists(DIR_IMAGE . $path)) { @mkdir(DIR_IMAGE . $path, 0777); } } list($width_orig, $height_orig) = getimagesize(DIR_IMAGE . $old_image); if ($width_orig != $width || $height_orig != $height) { $scaleW = $width_orig/$width; $scaleH = $height_orig/$height; $image = new Image(DIR_IMAGE . $old_image); if ($scaleH > $scaleW) { $_height = $height * $scaleW; $top_x = 0; $top_y = ($height_orig - $_height) / 2; $bottom_x = $width_orig; $bottom_y = $top_y + $_height; $image->crop($top_x, $top_y, $bottom_x, $bottom_y); } elseif ($scaleH < $scaleW) { $_width = $width * $scaleH; $top_x = ($width_orig - $_width) / 2; $top_y = 0; $bottom_x = $top_x + $_width; $bottom_y = $height_orig; $image->crop($top_x, $top_y, $bottom_x, $bottom_y); } $image->resize($width, $height, $type); $image->save(DIR_IMAGE . $new_image); } else { copy(DIR_IMAGE . $old_image, DIR_IMAGE . $new_image); } } if (isset($this->request->server['HTTPS']) && (($this->request->server['HTTPS'] == 'on') || ($this->request->server['HTTPS'] == '1'))) { return $this->config->get('config_ssl') . 'image/' . $new_image; } else { return $this->config->get('config_url') . 'image/' . $new_image; } } Далее там, где это необходимо - заменить вызов $this->model_tool_image->resize(...), на $this->model_image_tool->myResize(...).
 - 
	
		
		Универсальный config.php для Opencart
		
		При переносе интернет-магазина на Opencart всегда приходится править конфигурационные файлы config.php и admin/config.php, а, именно, приписывать новые адреса хостов и пути к директориям не говоря уже о параметрах БД. Есть решение, которое может свести к минимуму действий при переносе сайта на Opencart на другой домен или хостинг. config.php <?php // HTTP $host = $_SERVER['HTTP_HOST']; define('HTTP_SERVER', 'https://'.$host.'/'); define('HTTP_IMAGE', 'https://'.$host.'/image/'); define('HTTP_ADMIN', 'https://'.$host.'/admin/'); // HTTPS define('HTTPS_SERVER', 'https://'.$host.'/'); define('HTTPS_IMAGE', 'https://'.$host.'/image/'); // DIR $dir = dirname(__FILE__); define('DIR_APPLICATION', $dir . '/catalog/'); define('DIR_SYSTEM', $dir . '/system/'); define('DIR_DATABASE', $dir . '/system/database/'); define('DIR_LANGUAGE', $dir . '/catalog/language/'); define('DIR_TEMPLATE', $dir . '/catalog/view/theme/'); define('DIR_CONFIG', $dir . '/system/config/'); define('DIR_IMAGE', $dir . '/image/'); define('DIR_CACHE', $dir . '/system/cache/'); define('DIR_DOWNLOAD', $dir . '/download/'); define('DIR_LOGS', $dir . '/system/logs/'); // DB define('DB_DRIVER', 'mysql'); define('DB_HOSTNAME', 'localhost'); define('DB_USERNAME', 'mysql_user'); define('DB_PASSWORD', 'mysql_password'); define('DB_DATABASE', 'mysql_db'); define('DB_PREFIX', 'oc_'); ?> admin/config.php <?php // HTTP $host = $_SERVER['HTTP_HOST']; define('HTTP_SERVER', 'https://'.$host.'/admin/'); define('HTTP_CATALOG', 'https://'.$host.'/'); define('HTTP_IMAGE', 'https://'.$host.'/image/'); // HTTPS define('HTTPS_SERVER', 'https://'.$host.'/admin/'); define('HTTPS_CATALOG', 'https://'.$host.'/'); define('HTTPS_IMAGE', 'https://'.$host.'/image/'); // DIR $dir = dirname(dirname(__FILE__)); define('DIR_APPLICATION', $dir . '/admin/'); define('DIR_SYSTEM', $dir . '/system/'); define('DIR_DATABASE', $dir . '/system/database/'); define('DIR_LANGUAGE', $dir . '/admin/language/'); define('DIR_TEMPLATE', $dir . '/admin/view/template/'); define('DIR_CONFIG', $dir . '/system/config/'); define('DIR_IMAGE', $dir . '/image/'); define('DIR_CACHE', $dir . '/system/cache/'); define('DIR_DOWNLOAD', $dir . '/download/'); define('DIR_LOGS', $dir . '/system/logs/'); define('DIR_CATALOG', $dir . '/catalog/'); // DB define('DB_DRIVER', 'mysql'); define('DB_HOSTNAME', 'localhost'); define('DB_USERNAME', 'mysql_user'); define('DB_PASSWORD', 'mysql_password'); define('DB_DATABASE', 'mysql_db'); define('DB_PREFIX', 'oc_'); ?>
 - 
	
		
		Уведомление о согласии на использование файлов cookie в OpenCart
		
		Что такое закон о файлах cookie? Закон о файлах cookie — это директива закона о конфиденциальности, которая была одобрена Европейским Союзом в мае 2011 года и действует во всех странах ЕС. Закон о конфиденциальности требует, чтобы все веб-сайты, работающие и предназначенные для пользователей в странах, входящих в ЕС, запрашивали разрешение на сохранение файлов cookie в браузере или просто уведомляли пользователя о том, что веб-сайт использует файлы cookie. Как я могу сделать свой сайт совместимым? В этой статье вы узнаете, как легко реализовать свое собственное уведомление о согласии на использование файлов cookie и привести свой веб-сайт в соответствие с законом Европейского Союза о файлах cookie. По умолчанию OpenCart хранит несколько типов файлов cookie: Google Analytics, AddThis, идентификатор сеанса PHP клиента, а также настройки языка и валюты. Это означает, что вы можете только уведомить клиента о том, что веб-сайт использует файлы cookie, фактически не запрашивая разрешения на их сохранение в браузере. Для магазинов, использующих OpenCart 2.0, используйте OCMod cookie_consent_notification.ocmod.xml, который вы можете скачать После загрузки файла расширения вам необходимо установить его с помощью установщика расширений OpenCart. Откройте вашу администрацию OpenCart. Перейдите в Расширения -> Установщик расширений . Нажмите кнопку «Загрузить» и найдите загруженный файл cookie_consent_notification.ocmod.xml . OpenCart установит его автоматически, и вы получите сообщение об успешном завершении. Перейдите в Расширения -> Модификации и нажмите кнопку [Обновить]. Вот и все! cookie_consent_notification.ocmod.xml cookie_consent_notification.ocmod_bottom.xml cookie_consent_notification.xml cookie_consent_notification_bottom.xml
 - 
	
		
		3 способа улучшения опций в OpenCart
		
		В этом посте мы расскажем вам о трех способах, которые с легкостью помогут вам изменить карточку товара: 1. Добавление описания для опции 2. Добавление количества на складе для опций продукта 3. Удаление цены в опциях Предложенная реализация придаст вашей странице продукта более четкий, информативный и аккуратный вид, который, несомненно, произведет положительное впечатление на ваших клиентов. Добавление описания для опции Добавление описания к опциям продукта является хорошим способом для того, чтобы дать своим клиентам четкое представление о различных видах продукции, которую может предложить ваш интернет-магазин. Обеспечение клиентов информацией - это всегда хорошая идея, потому что это даст им более глубокое понимание того, что ожидать, когда они закажут вашу продукцию. Это, с другой стороны, приведет к увеличению доверия и уверенности в вашем магазине. Ниже мы покажем вам, как можно реализовать эту функцию в простой и быстрой форме. Создание OCMod файла Процедура начинается с создания ocmod файла, используя любой текстовый редактор. Сохраните файл как namebyyourchoice.ocmod.xml и не забывайте, что расширение ocmod.xml является обязательным. <modification> <name>Add description to product options</name> <version>1.0</version> <link></link> <author></author> <code>add_description_to_product_options</code> <file path="admin/language/*/catalog/option.php"> <operation> <search><![CDATA[$_['entry_sort_order'] = 'Sort Order';]]></search> <add position="after"><![CDATA[$_['entry_description'] = 'Description:';]]></add> </operation> </file> <file path="admin/controller/catalog/option.php"> <operation> <search><![CDATA[$data['entry_sort_order'] = $this->language->get('entry_sort_order');]]></search> <add position="after"><![CDATA[ $data['entry_description'] = $this->language->get('entry_description'); ]]></add> </operation> <operation> <search><![CDATA[public function index() {]]></search> <add position="after"><![CDATA[ $query = $this->db->query("DESC " . DB_PREFIX . "option_value_description description"); if (!$query->num_rows) { $this->db->query("ALTER TABLE " . DB_PREFIX . "option_value_description ADD description TEXT CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL"); } ]]></add> </operation> </file> <file path="admin/model/catalog/option.php"> <operation> <search><![CDATA[ $this->db->query("INSERT INTO " . DB_PREFIX . "option_value_description SET option_value_id = '" . (int)$option_value_id . "', language_id = '" . (int)$language_id . "', option_id = '" . (int)$option_id . "', name = '" . $this->db->escape($option_value_description['name']) . "'"); ]]></search> <add position="replace"><![CDATA[ $this->db->query("INSERT INTO " . DB_PREFIX . "option_value_description SET option_value_id = '" . (int)$option_value_id . "', language_id = '" . (int)$language_id . "', option_id = '" . (int)$option_id . "', name = '" . $this->db->escape($option_value_description['name']) . "', description = '" . $this->db->escape($option_value_description['description']) . "'"); ]]></add> </operation> <operation> <search><![CDATA[ $option_value_description_data[$option_value_description['language_id']] = array('name' => $option_value_description['name']); ]]></search> <add position="replace"><![CDATA[ $option_value_description_data[$option_value_description['language_id']] = array('name' => $option_value_description['name'], 'description' => $option_value_description['description']); ]]></add> </operation> </file> <file path="admin/view/template/catalog/option_form.tpl"> <operation> <search><![CDATA[ <label class="col-sm-2 control-label" for="input-sort-order"><?php echo $entry_sort_order; ?></label> ]]></search> <add position="before"><![CDATA[ <label class="col-sm-2 control-label" for="input-sort-order"><?php echo $entry_description; ?></label> ]]></add> </operation> <operation> <search><![CDATA[ <td colspan="3"></td> ]]></search> <add position="replace"><![CDATA[ <td colspan="4"></td> ]]></add> </operation> <operation> <search><![CDATA[ <td class="text-left required"><?php echo $entry_option_value; ?></td> ]]></search> <add position="after"><![CDATA[ <td class="text-left">Option Value Description</td> ]]></add> </operation> <operation> <search><![CDATA[ <td class="text-left required"><?php echo $entry_option_value; ?></td> ]]></search> <add position="replace"><![CDATA[ <td class="text-left required" style="width:30%;"><?php echo $entry_option_value; ?></td> ]]></add> </operation> <operation> <search><![CDATA[ <?php echo $footer; ?> ]]></search> <add position="after"><![CDATA[ <script type="text/javascript"> <?php foreach ($languages as $language) { ?> $("#description-<?php echo $option_value_row; ?>-<?php echo $language['language_id']; ?>").summernote({height: 100}); <?php } ?> </script> ]]></add> </operation> <operation> <search><![CDATA[ <td class="text-left"><a href="" id="thumb-image<?php echo $option_value_row; ?>" data-toggle="image" class="img-thumbnail"><img src="<?php echo $option_value['thumb']; ?>" alt="" title="" data-placeholder="<?php echo $placeholder; ?>" /></a> ]]></search> <add position="before"><![CDATA[ <!--ProductOption Description Start--> <td class="text-left"> <div class="tabbable"> <div class="tab-navigation"> <ul class="nav nav-tabs mainMenuTabs"> <?php $class="active"; foreach ($languages as $language) { ?> <li class="<?php echo $class; ?>"><a href="#tab-<?php echo $language['code']; ?>" data-toggle="tab"><img src="view/image/flags/<?php echo $language['image']; ?>"/></a></li> <?php $class="";}?> </ul> </div> <div class="tab-content"> <?php $class=" active"; foreach ($languages as $language) { ?> <div id="tab-<?php echo $language['code']; ?>" language-id="<?php echo $language['language_id']; ?>" class="row-fluid tab-pane<?php echo $class; ?> language"><div> <textarea language-id="<?php echo $language['language_id']; ?>" name="option_value[<?php echo $option_value_row; ?>][option_value_description][<?php echo $language['language_id']; ?>][description]" id="description-<?php echo $option_value_row; ?>-<?php echo $language['language_id']; ?>"><?php echo isset($option_value['option_value_description'][$language['language_id']]) ? $option_value['option_value_description'][$language['language_id']]['description'] : ''; ?></textarea> <?php $class="";} ?> </div> </div> </td> <script type="text/javascript"> <?php foreach ($languages as $language) { ?> $("#description-<?php echo $option_value_row; ?>-<?php echo $language['language_id']; ?>").summernote({height: 100}); <?php } ?> </script> <!--ProductOption Description End--> ]]></add> </operation> <operation> <search><![CDATA[ html += ' <td class="text-left"><a href="" id="thumb-image' + option_value_row + '" data-toggle="image" class="img-thumbnail"><img src="<?php echo $placeholder; ?>" alt="" title="" data-placeholder="<?php echo $placeholder; ?>" /></a><input type="hidden" name="option_value[' + option_value_row + '][image]" value="" id="input-image' + option_value_row + '" /></td>'; ]]></search> <add position="before"><![CDATA[ html += '<td class="text-left">'; html += ' <div class="tabbable">'; html += ' <div class="tab-navigation">'; html += ' <ul class="nav nav-tabs mainMenuTabs">'; <?php $class="active"; foreach ($languages as $language) { ?> html += ' <li class="<?php echo $class; ?>"><a href="#tab-<?php echo $language['code']; ?>" data-toggle="tab"><img src="view/image/flags/<?php echo $language['image']; ?>"/></a></li>'; <?php $class="";}?> html += ' </ul>'; html += ' </div>'; html += ' <div class="tab-content">' <?php $class="active"; foreach ($languages as $language) { ?> html += ' <div id="tab-<?php echo $language['code']; ?>" language-id="<?php echo $language['language_id']; ?>" class="row-fluid tab-pane<?php echo $class; ?> language"><div>'; html += ' <textarea language-id="<?php echo $language['language_id']; ?>" name="option_value[<?php echo $option_value_row; ?>][option_value_description][<?php echo $language['language_id']; ?>][description]" id="description-<?php echo $option_value_row; ?>-<?php echo $language['language_id']; ?>"><?php echo isset($option_value['option_value_description'][$language['language_id']]) ? $option_value['option_value_description'][$language['language_id']]['description'] : ''; ?></textarea>'; <?php $class="";} ?> html += ' </div>'; html += ' </div>'; html += '</td>'; ]]></add> </operation> </file> <file path="catalog/controller/product/product.php"> <operation> <search><![CDATA[$data['text_tags'] = $this->language->get('text_tags');]]></search> <add position="after"><![CDATA[ $data['text_view'] = $this->language->get('text_view'); ]]></add> </operation> <operation> <search><![CDATA['name' => $option_value['name'],]]></search> <add position="after"><![CDATA[ 'description' => html_entity_decode($option_value['description'], ENT_QUOTES, 'UTF-8'), ]]></add> </operation> </file> <file path="catalog/language/*/product/product.php"> <operation> <search><![CDATA[$_['text_error'] = 'Product not found!';]]></search> <add position="after"><![CDATA[ $_['text_view'] = 'view more'; ]]></add> </operation> </file> <file path="catalog/model/catalog/product.php"> <operation> <search><![CDATA['name' => $product_option_value['name'],]]></search> <add position="after"><![CDATA[ 'description' => (isset($product_option_value['description']) ? $product_option_value['description'] : ''), ]]></add> </operation> </file> <file path="catalog/view/theme/*/template/product/product.tpl"> <operation> <search><![CDATA[(<?php echo $option_value['price_prefix']; ?><?php echo $option_value['price']; ?>)]]></search> <add position="after"><![CDATA[ <?php } ?> <?php if(isset($option_value) && $option_value['description']) { ?> <a class="view" id="<?php echo $option_value['option_value_id']; ?>"> View more </a> <div id="desc_<?php echo $option_value['option_value_id']; ?>" style="display:none"> <?php echo $option_value['description'] ?> </div> ]]></add> </operation> <operation> <search><![CDATA[ <?php echo $footer; ?> ]]></search> <add position="before"><![CDATA[ <script> $(document).ready(function(){ $('.view').hover(function(){ $("#desc_".concat(this.id)).fadeIn(500) },function(){ $("#desc_".concat(this.id)).fadeOut(500) }) }); </script> ]]></add> </operation> </file> </modification> Добавление количества на складе для опций продукта В дополнение к описанию параметров продукта, добавление количества товара к вашей продукции также увеличивает количество информации о продукции доступной для Ваших клиентов. Кроме того, эта реализация может иметь положительное влияние на показатели переходов. А ограниченный объем количества создает ощущение срочности, уменьшает колебания и, таким образом, повышает готовность к покупке. <modification> <name>Add product quantity to product options</name> <version>1.0</version> <link></link> <author></author> <code>quantity_to_product_options</code> <file path="catalog/view/theme/*/template/product/product.tpl"> <operation> <search><![CDATA[ (<?php echo $option_value['price_prefix']; ?><?php echo $option_value['price']; ?>)]]></search> <add position="after"><![CDATA[ <?php } if (isset($option_value['quantity']) && $option_value['quantity'] > 10) { ?>  [<?php echo $option_value['quantity']; ?> <?php echo $text_pcs; ?>] <?php } elseif ($option_value['quantity']) { ?>  [<span style="color: #F00"><?php echo str_replace('{quant}', $option_value['quantity'], $text_only); ?></span>] ]]></add> </operation> </file> <file path="catalog/controller/product/product.php"> <operation> <search><![CDATA['name' => $option_value['name'],]]></search> <add position="after"><![CDATA['quantity' => $option_value['quantity'],]]></add> </operation> <operation> <search><![CDATA[$data['entry_qty'] = $this->language->get('entry_qty');]]></search> <add position="after"><![CDATA[ $data['text_pcs'] = $this->language->get('text_pcs'); $data['text_only'] = $this->language->get('text_only'); ]]></add> </operation> </file> <file path="catalog/language/*/product/product.php"> <operation> <search><![CDATA[$_['entry_qty'] = 'Qty';]]></search> <add position="after"><![CDATA[ $_['text_pcs'] = 'pcs.'; $_['text_only'] = ' Only {quant} pcs. left '; ]]></add> </operation> </file> </modification> Изменение цвета Когда наличие товара - менее 10 штук, цвет количества становится красным. Это может быть изменено путем добавления цвета в следующем фрагменте кода: <span style="color: #F00"><?php echo str_replace('{quant}', $option_value['quantity'],$text_only); ?></span> Удаление цены в опциях Еще одно небольшое изменение - это удаление цены у опции товара. Для большинства розничных продавцов эта функция весьма позитивна. Однако, для некоторых покупателей эта функция нежелательна, ведь когда они делают заказ и выбирают опцию товара - в корзине видят цену выше основной. Тем не менее, если вы готовы "принять этот риск», необходимо следовать ранее упомянутым шагам и использовать следующий код: <modification> <name>Remove option price</name> <version>1.0</version> <link></link> <author></author> <code>remove_option_price</code> <file path="catalog/view/theme/*/template/product/product.tpl"> <operation> <search><![CDATA[<?php foreach ($option['product_option_value'] as $option_value) { ?>]]></search> <add position="after"><![CDATA[<?php unset($option_value['price']); $option_value['price'] ="";?>]]> </add> </operation> </file> </modification> Вывод: Конечный результат, который вы получите после применения трех модификаций, должен быть похож на этот: Мы надеемся, что вы будете применять данные этапы для настройки ваших вариантов продукции. Они являются полезным и эффективным способом для достижения конечной цели - удовлетворения потребностей ваших клиентов и увеличения продаж. prod_options_add_descr.xml prod_options_add_quantity.xml prod_options_remove_price.xml
 - 
	
		
		Ограничение доступа к админ панели по IP
		
		Быстрый и мощный совет по обеспечению безопасности вашей админ панели, который ограничит доступ по определенному IP адресу. Это решение идеально подходит для администраторов и менеджеров вашего интернет-магазина. В следующих шагах мы опишем простой метод реализации для всех версий OpenCart. ШАГ 1 - Узнаем свой IP-адрес Первый шаг, узнаем свой публичный IP, который мы позже внесем в белый список разрешающих адресов и ограничим доступ в админ панель. Узнать IP можете здесь или здесь ШАГ 2 - Создаем файл .htaccess Подключаемся к серверу, где хранятся файлы вашего сайта, с помощью любого FTP клиента (например, FileZilla). Для того, чтобы соединиться с FTP сервером, вам понадобятся доступы. Узнать их можно у своего хостинг-провайдера. После успешного подключения перейдите к папке admin и создайте новый файл с именем .htaccess. Если такой файл уже существует, просто откройте его для редактирования. Добавьте следующий код ниже в файле .htaccess, заменяя 1.2.3.4 из кода ниже на ваш IP-адрес (который вы получили ранее). Вы можете разрешить несколько IP-адресов, добавляя с новой строки Allow from с нужным IP. Order deny,allow Deny from all Allow from 1.2.3.4 Затем сохраните и загрузите файл. ШАГ 3 – Проверяем работоспособность Теперь откройте веб-браузер и перейдите в админ панель. Она должна выглядеть таким же образом, как и раньше, без каких-либо изменений. Теперь вернитесь к пункту 2 и измените разрешенный IP-адрес на отличающийся от вашего, и попробуйте снова войти в админ панель. Ваш доступ должен быть запрещен. Хорошая работа! Верните обратно свой IP адрес, и наслаждайтесь новой защитой.
 - 
	
		
		Не работает OCMOD?
		
		Иногда система OCMOD на OpenCart может перестать работать во фронтенде, и вы можете потратить какое-то время в поисках причины. Кажется, это довольно частая проблема, потому вот несколько советов по тому, как самостоятельно решить проблему, связанную с OCMOD. Что вызывает эту проблему? В 100% случаев, которые возникали у нас, проблема была вызвана присутствием константы DIR_CATALOG в файле config.php. Поскольку по умолчанию эта константа присутствует только в панели администратора, функция модификации проверяет, определена ли она и, если да, то ищет модификации в папке администратора. Таким образом, когда эта константа присутствует в части каталога, OpenCart путается и ищет модификации в папке администратора, но они обычно не присутствуют там, или эти модифицированные файлы имеют совершенно другую логику, и начинают возникать ошибки. Как решить эту проблему? Будет лучше, если вы знаете, какое расширение/настройка используют константу DIR_CATALOG в каталоге, и свяжетесь с его разработчиком и попросите их внести исправления в расширение и убрать параметр из файла config.php. Таким образом вы сохраните более чистую версию OpenCart, что поможет в будущем, когда вам нужно будет обновить систему. Ваш второй вариант состоит в том, чтобы модифицировать файл system/startup.php. Откройте его и найдите функцию модификации. Она должна находиться где-то около середины файла и выглядит так: function modification($filename) { if (!defined('DIR_CATALOG')) { $file = DIR_MODIFICATION . 'catalog/' . substr($filename, strlen(DIR_APPLICATION)); } else { $file = DIR_MODIFICATION . 'admin/' . substr($filename, strlen(DIR_APPLICATION)); } if (substr($filename, 0, strlen(DIR_SYSTEM)) == DIR_SYSTEM) { $file = DIR_MODIFICATION . 'system/' . substr($filename, strlen(DIR_SYSTEM)); } if (is_file($file)) { return $file; } return $filename; } Измените следующую строку: if (!defined('DIR_CATALOG')) { на if (basename(DIR_APPLICATION) == ‘catalog’) { и это должно решить проблему.
 - 
	
		
		Увеличиваем лимит загружаемых файлов в PHP для OpenCart
		
		Есть два условия, которые диктуют максимальный объем загружаемого файла в OpenCart на основе php: 1. post_max_size 2. upload_max_filesize post_max_size Эта переменная определяет общий размер POST данных, который включает в себя файлы, как часть этих данных. Например: post_max_size 10M означает, что вы не можете загрузить больше, чем 10 Мб данных методом POST. upload_max_filesize Вторая переменная устанавливает максимальный допустимый размер загружаемых файлов. Учтите, что upload_max_filesize не может быть больше, чем post_max_size. Например: если post_max_size 10M и upload_max_filesize 2М, вы загрузите 5 файлов размером 2 Мб, но вы не сможете загрузить файл размером 3MB. Если upload_max_filesize больше post_max_size, вы можете загрузить только файл, который меньше или равен post_max_size. Эти две переменные, как правило, управляются из 3 общих мест: cPanel htaccess php.ini Важно! Новые изменения могут вступить в силу только после перезагрузки сервера или php-интерпретатора.
 - 
	
		
		Как убрать обязательное поле модель у товара в OpenCart
		
		Поле модель часто является не нужным для заполнение в карточке товара. Мы покажем один из способов убрать это условие на обязательное заполнение. 1. Вам нужно перейти в корень вашего сайта, выглядит это обычно так: 2. Далее найти и открыть файл admin/controller/catalog/product.php 3. Сделать поиск по строке "protected function validateForm()". Поиск можно открыть комбинацией клавиш CTR+F. Результат поиска и код который нужно стереть: 4. Готово, теперь поле модель не будет являться обязательным для заполнения!
 - 
	
		
		Разбираем класс прокси в OpenCart
		
		Чаще всего мы принимаем что-то как должное. Если что-то работает так, как ожидалось, мы не будем беспокоиться о его внутренней работе, чтобы понять лежащий в основе механизм. Или, говоря иначе, мы не копаемся в чем-то, пока не будем в беде! Точно так же мне всегда было интересно узнать о нескольких концепциях OpenCart, которые использовались внутри фреймворка, и одним из них был класс Proxy. Мне потребовалось некоторое время, чтобы понять его, и я подумал, что с тобой приятно будет делиться, так как всегда интересно узнать что-то новое. Что такое класс прокси? Хотя вы найдете различные материалы в Интернете, которые определяют термин прокси, определение из Википедии довольно легко понять. Прокси-сервер в его самой общей форме - это класс, функционирующий как интерфейс к чему-то другому. Таким образом, прокси делегирует элемент управления объекту, который он намеревается использовать, и таким образом действует от имени фактического класса, который был создан. Фактически, шаблон прокси-дизайна является очень популярным шаблоном, который используется популярными фреймворками по мере необходимости. Учитывая тот факт, что обсуждение прокси-метода само по себе является такой широкой темой и выходит за рамки этой статьи, я быстро подытожу, как он используется большую часть времени: Действует как обертка, чтобы обеспечить дополнительную функциональность. Задержка создания дорогостоящих объектов, также называемая ленивой загрузкой. В контексте OpenCart можно сказать, что шаблон прокси используется для добавления функциональности в базовый прокси-класс. Сказав это, базовый прокси-класс сам по себе не предоставляет ничего, кроме нескольких волшебных методов! Как мы увидим в следующем разделе, прокси-класс обогащается во время выполнения, добавляя к нему свойства. Прежде чем перейти к следующему разделу, давайте быстро взглянем на класс прокси. Он находится в system/engine/proxy.php. <?php class Proxy { public function __get($key) { return $this->{$key}; } public function __set($key, $value) { $this->{$key} = $value; } public function __call($key, $args) { $arg_data = array(); $args = func_get_args(); foreach ($args as $arg) { if ($arg instanceof Ref) { $arg_data[] =& $arg->getRef(); } else { $arg_data[] =& $arg; } } if (isset($this->{$key})) { return call_user_func_array($this->{$key}, $arg_data); } else { $trace = debug_backtrace(); exit('<b>Notice</b>: Undefined property: Proxy::' . $key . ' in <b>' . $trace[1]['file'] . '</b> on line <b>' . $trace[1]['line'] . '</b>'); } } } Как вы можете видеть, он реализует три магических метода: __get(), __set() и __call(). Среди них реализация метода __call() является важной, и мы вернемся к ней довольно скоро. Как работает прокси-класс с моделью В этом разделе я объясню, как именно вызов типа $this->model_catalog_category->getCategory($category_id) работает из коробки. Фактически, история начинается со следующего утверждения. $this->load->model('catalog/category'); Во время начальной загрузки оболочка OpenCart хранит все общие объекты в объекте Registry, чтобы их можно было получить по желанию. В результате этого вызов $this->load возвращает объект Loader из реестра. Класс Loader предоставляет различные методы для загрузки разных компонентов, но нас интересует метод model. Давайте быстро взглянем на фрагмент метода model из system/engine/ oader.php. public function model($route) { // Sanitize the call $route = preg_replace('/[^a-zA-Z0-9_\/]/', '', (string)$route); // Trigger the pre events $this->registry->get('event')->trigger('model/' . $route . '/before', array(&$route)); if (!$this->registry->has('model_' . str_replace(array('/', '-', '.'), array('_', '', ''), $route))) { $file = DIR_APPLICATION . 'model/' . $route . '.php'; $class = 'Model' . preg_replace('/[^a-zA-Z0-9]/', '', $route); if (is_file($file)) { include_once($file); $proxy = new Proxy(); foreach (get_class_methods($class) as $method) { $proxy->{$method} = $this->callback($this->registry, $route . '/' . $method); } $this->registry->set('model_' . str_replace(array('/', '-', '.'), array('_', '', ''), (string)$route), $proxy); } else { throw new \Exception('Error: Could not load model ' . $route . '!'); } } // Trigger the post events $this->registry->get('event')->trigger('model/' . $route . '/after', array(&$route)); } Учитывая вышеупомянутый пример, значением аргумента $route является catalog/category. Во-первых, значение переменной $route очищается и после этого запускает событие before, чтобы другие слушатели модуля могли изменить значение переменной $route. Затем он проверяет наличие запрошенного объекта модели в реестре. Если реестр содержит запрошенный объект, дальнейшая обработка не требуется. Если объект не найден, следует загрузить интересную процедуру, и это фрагмент, который мы ищем в контексте этой статьи. Для начала он подготавливает путь к файлу запрашиваемой модели и загружает ее, если она существует. ... $file = DIR_APPLICATION . 'model/' . $route . '.php'; $class = 'Model' . preg_replace('/[^a-zA-Z0-9]/', '', $route); if (is_file($file)) { include_once($file); ... } ... После этого он создает объект Proxy. $proxy = new Proxy(); Теперь обратите внимание на следующий цикл for: он намного больше, чем кажется. foreach (get_class_methods($class) as $method) { $proxy->{$method} = $this->callback($this->registry, $route . '/' . $method); } В нашем случае значение $class должно быть ModelCatalogCategory. Фрагмент get_class_methods($class) загружает все методы класса ModelCatalogCategory и проходит через него. Что он делает в цикле? Давайте посмотрим внимательно. В цикле он вызывает метод callback того же класса. Интересно отметить, что метод обратного вызова возвращает функцию, вызываемую для объекта $proxy с ключом в качестве имени метода. Конечно, прокси-объект не обладает такими свойствами; он будет создан на лету, используя магический метод __set()! Затем объект $ proxy добавляется в реестр, чтобы его можно было получить позже, когда это необходимо. Посмотрите на ключевой компонент метода set. В нашем случае это должна быть model_catalog_category. $this->registry->set('model_' . str_replace(array('/', '-', '.'), array('_', '', ''), (string)$route), $proxy); В конце он вызовет событие after, чтобы другие слушатели модуля могли изменить значение переменной $route. Это одна часть истории. Давайте рассмотрим, что происходит, когда вы используете следующее в своем контроллере. $this->model_catalog_category->getCategory($category_id); Фрагмент $this->model_catalog_category пытается найти соответствие для ключа model_catalog_category в реестре. Если вам интересно, просто изучите определение класса Controller в файле system/engine/controller.php - он предоставляет магический метод __get(), который это делает. Как мы только что обсуждали, это должно вернуть объект $proxy, назначенный этому конкретному ключу. Затем он пытается вызвать метод getCategory для этого объекта. Но класс Proxy не реализует такой метод, так как это будет работать? Магический метод __call() приходит на помощь! Всякий раз, когда вы вызываете метод, который не существует в классе, элемент управления передается магическому методу __call(). Давайте рассмотрим его подробно, чтобы понять, что происходит. Откройте файл класса Proxy и обратите внимание на этот метод. В $key содержится имя функции, которая называется-getCategory. С другой стороны, $args содержит аргументы, переданные методу, и он должен быть массивом из одного элемента, содержащего идентификатор категории, который передается. Далее, существует массив $arg_data, который хранит ссылки на аргументы. Честно говоря, я не уверен, что код $arg instanceof Ref имеет смысл. Если кто-нибудь знает, почему он там, я был бы рад узнать. Кроме того, он пытается проверить существование свойства $key в объекте $proxy, и это приводит к чему-то вроде этого. if (isset($this->getCategory)) { Напомним, что ранее мы назначили все методы класса ModelCatalogCategory как свойства объекта $proxy с использованием цикла for. Для вашего удобства я снова вставлю этот код. ... foreach (get_class_methods($class) as $method) { $proxy->{$method} = $this->callback($this->registry, $route . '/' . $method); } ... Таким образом, он должен быть там, и он также должен вернуть нам функцию, которая вызывается! И, наконец, он вызывает эту функцию, вызываемую с помощью функции call_user_func_array, передавая аргументы метода. Теперь давайте обратим наше внимание на само вызываемое функцией определение. Я возьму фрагмент из метода callback, определенного в system/engine/ oader.php. ... function($args) use($registry, &$route) { static $model = array(); $output = null; // Trigger the pre events $result = $registry->get('event')->trigger('model/' . $route . '/before', array(&$route, &$args, &$output)); if ($result) { return $result; } // Store the model object if (!isset($model[$route])) { $file = DIR_APPLICATION . 'model/' . substr($route, 0, strrpos($route, '/')) . '.php'; $class = 'Model' . preg_replace('/[^a-zA-Z0-9]/', '', substr($route, 0, strrpos($route, '/'))); if (is_file($file)) { include_once($file); $model[$route] = new $class($registry); } else { throw new \Exception('Error: Could not load model ' . substr($route, 0, strrpos($route, '/')) . '!'); } } $method = substr($route, strrpos($route, '/') + 1); $callable = array($model[$route], $method); if (is_callable($callable)) { $output = call_user_func_array($callable, $args); } else { throw new \Exception('Error: Could not call model/' . $route . '!'); } // Trigger the post events $result = $registry->get('event')->trigger('model/' . $route . '/after', array(&$route, &$args, &$output)); if ($result) { return $result; } return $output; }; ... Поскольку это анонимная функция, она сохранила значения в виде переменных $register и $route, которые были переданы ранее методу callback. В этом случае значение переменной $route должно быть catalog/category/getCategory. Кроме того, если мы посмотрим на важный фрагмент этой функции, он создает экземпляр объекта ModelCatalogCategory и сохраняет его в статическом массиве $model. ... // Store the model object if (!isset($model[$route])) { $file = DIR_APPLICATION . 'model/' . substr($route, 0, strrpos($route, '/')) . '.php'; $class = 'Model' . preg_replace('/[^a-zA-Z0-9]/', '', substr($route, 0, strrpos($route, '/'))); if (is_file($file)) { include_once($file); $model[$route] = new $class($registry); } else { throw new \Exception('Error: Could not load model ' . substr($route, 0, strrpos($route, '/')) . '!'); } } ... И вот фрагмент, который захватывает имя метода, которое нужно вызвать, используя переменную $route. $method = substr($route, strrpos($route, '/') + 1); Таким образом, мы имеем ссылку на объект и имя метода, которое позволяет нам вызывать его с помощью функции call_user_func_array. Следующий фрагмент делает именно это! ... if (is_callable($callable)) { $output = call_user_func_array($callable, $args); } else { throw new \Exception('Error: Could not call model/' . $route . '!'); } ... В конце метода полученный результат возвращается через переменную $output. И да, это еще одна часть истории! Я намеренно игнорировал код pre и post events, который позволяет вам переопределять методы основных классов OpenCart. Используя это, вы можете переопределить любой метод основного класса и настроить его в соответствии с вашими потребностями. Но давайте сделаем в другой раз, так как это уже будет слишком много всего, чтобы вписаться в одну статью! Так вот как это работает. Я надеюсь, что вы должны быть более уверенны в этих коротких вызовах OpenCart и об их внутренней работе. Заключение То, что мы только что обсуждали, является одной из интересных и неоднозначных концепций в OpenCart: использование метода Proxy для поддержки сокращенных соглашений для вызова методов модели. Надеюсь, статья была достаточно интересна и обогатила ваши знания OpenCart.
 - 
	
		
		Пользовательские контроллеры в OpenCart 2
		
		Сегодня мы рассмотрим концепцию контроллеров в OpenCart 2.x. С выпуском версии OpenCart 2.x они внедрили изменения фреймворка, требующие обновления, если вы сделали какие-либо пользовательские модули в более ранней версии - OpenCart 1.x. Мы рассмотрим практический пример того, что мы будем называть гостевой книгой в этом учебнике. Прежде чем двигаться дальше в этой статье, вы можете рассмотреть это как продолжение того, что я написал ранее. В этой статье я объяснил, как создать пользовательскую страницу в OpenCart 1.x, и если вы уже последовали этой статье, вы можете быстро пропустить большинство разделов сегодня! Конечно, это OpenCart 2.x, который будет обсуждаться сегодня, поэтому убедитесь, что вы внимательно следите за кодом. Если вы хотите узнать больше о шаблоне и контроллерах OpenCart, вы можете пройти пару первых разделов этой статьи. Зачем вообще пользовательский контроллер? То, что вы можете спросить в первую очередь - почему пользовательский контроллер? Давайте быстро поймем, что такое контроллер в OpenCart, прежде чем мы займемся этим. В контексте OpenCart контроллер является незаменимым компонентом фреймворка, который напрямую связан с процессом маршрутизации и отображает пользовательский интерфейс. В этом процессе он рассматривает другие важные компоненты, такие как язык, модель и представление, чтобы построить окончательный результат. Когда вы открываете доступ к любой странице OpenCart, фреймворка OpenCart ищет соответствующий контроллер и делегирует ему дальнейшую обработку. Будучи модульным по своей природе, фреймворк OpenCart предоставляет несколько контроллеров, которые имеют дело с логически сгруппированными функциональными возможностями. Например, группа account содержит контроллеры, которые занимаются регистрацией, входом, профилем и аналогичными вариантами использования. Аналогично, группа контроллеров checkout обрабатывает процесс создания заказа. В двух словах, когда вы хотите создать функцию, которая не находится в ядре OpenCart, и если она использует новый URL-адрес в терминологии OpenCart, вы должны перейти на пользовательский контроллер. Он даст вам полный контроль над процессом создания страницы - какие элементы вы хотите отображать на своей странице. Создание пользовательского контроллера Сегодня мы реализуем базовую функциональность Guestbook, которая демонстрирует концепцию пользовательских контроллеров. В процессе этого мы создадим интерфейс, который позволит гостевым пользователям отправлять свои отзывы, введя свое имя и сообщение. Прежде чем продолжить, убедитесь, что у вас есть рабочая установка OpenCart 2.3.x. Для тех, кто не знаком с фреймворком OpenCart, местом для поиска интерфейсных контроллеров является catalog/controller. Это каталог, который управляет всеми контроллерами по группам, основываясь на предоставляемых ими функциях. В нашем случае мы создадим отдельную группу под названием guestbook. Идем дальше и создаем каталог catalog/controller/guestbook. Внутри этого каталога создайте файл entry.php со следующим содержимым. Это файл контроллера, который обрабатывает логику приложения и логику отправки наших функций гостевой книги. <?php class ControllerGuestbookEntry extends Controller { private $error = array(); public function index() { $this->load->language('guestbook/guestbook'); $this->document->setTitle($this->language->get('heading_title')); if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) { $this->load->model('guestbook/guestbook'); $data['subject'] = sprintf('New guestbook entry submitted by %s', $this->request->post['guest_name']); $data['message'] = $this->request->post['guest_message']; $this->model_guestbook_guestbook->processGuestbookEntry($data); $this->session->data['success'] = $this->language->get('text_success'); $this->response->redirect($this->url->link('guestbook/entry', '', true)); } $data['success'] = ''; if (isset($this->session->data['success'])) { $data['success'] = $this->session->data['success']; unset($this->session->data['success']); } $data['breadcrumbs'] = array(); $data['breadcrumbs'][] = array( 'text' => $this->language->get('text_home'), 'href' => $this->url->link('common/home') ); $data['breadcrumbs'][] = array( 'text' => $this->language->get('heading_title'), 'href' => $this->url->link('guestbook/entry', '', true) ); $data['heading_title'] = $this->language->get('heading_title'); $data['entry_guest_name'] = $this->language->get('entry_guest_name'); $data['entry_guest_message'] = $this->language->get('entry_guest_message'); $data['entry_submit'] = $this->language->get('entry_submit'); if (isset($this->error['guest_name'])) { $data['error_guest_name'] = $this->error['guest_name']; } else { $data['error_guest_name'] = ''; } if (isset($this->error['guest_message'])) { $data['error_guest_message'] = $this->error['guest_message']; } else { $data['error_guest_message'] = ''; } $data['action'] = $this->url->link('guestbook/entry', '', true); if (isset($this->request->post['guest_name'])) { $data['guest_name'] = $this->request->post['guest_name']; } else { $data['guest_name'] = ''; } if (isset($this->request->post['guest_message'])) { $data['guest_message'] = $this->request->post['guest_message']; } else { $data['guest_message'] = ''; } $data['column_left'] = $this->load->controller('common/column_left'); $data['column_right'] = $this->load->controller('common/column_right'); $data['content_top'] = $this->load->controller('common/content_top'); $data['content_bottom'] = $this->load->controller('common/content_bottom'); $data['footer'] = $this->load->controller('common/footer'); $data['header'] = $this->load->controller('common/header'); $this->response->setOutput($this->load->view('guestbook/entry', $data)); } protected function validate() { if (utf8_strlen(trim($this->request->post['guest_name'])) < 1) { $this->error['guest_name'] = $this->language->get('error_guest_name'); } if (utf8_strlen(trim($this->request->post['guest_message'])) < 1) { $this->error['guest_message'] = $this->language->get('error_guest_message'); } return !$this->error; } } Согласно соглашениям об именах OpenCart, имя класса начинается с ключевого слова Controller, за которым следует имя каталога, Guestbook в нашем случае, в которой находится файл класса. Кроме того, имя файла класса, Entry в нашем случае, добавляется в конце. Каждый класс контроллера предоставляет де-факто метод index, который обрабатывает большую часть логики контроллера. Затем мы рассмотрим код в методе index, и при необходимости создадим другие файлы. В большинстве случаев мы начнем с включения языкового файла для конкретной группы. Таким образом OpenCart управляет ярлыками статического языка во всем приложении. Конечно, это делает внедрение многоязычных сайтов легче. $this->load->language('guestbook/guestbook'); Прежде чем двигаться вперед, давайте создадим соответствующий языковой файл, чтобы наш контроллер мог его найти. Создайте файл catalog/language/en-gb/guestbook/guestbook.php со следующим содержимым. <?php // Heading $_['heading_title'] = 'Guestbook'; // Entry $_['entry_guest_name'] = 'Your Name'; $_['entry_guest_message'] = 'Message'; $_['entry_submit'] = 'Submit'; $_['text_success'] = 'Success: Your entry has been successfully submitted.'; // Error $_['error_guest_name'] = 'Please enter Your Name!'; $_['error_guest_message'] = 'Please enter Message!'; Как вы можете видеть, мы просто назначаем метки своими значениями в массиве языков. Вернемся к нашему контроллеру. Следующее - настроить тег заголовка HTML для нашей страницы. $this->document->setTitle($this->language->get('heading_title')); Внимательные читатели уже заметили, что мы использовали языковую переменную heading_title, определенную в языковом файле, созданном минуту назад. Чтобы понять следующий фрагмент кода, нам нужно создать файл модели. Поэтому на мгновение я отвлеку вас на создание файла модели в catalog/model/guestbook/guestbook.php со следующим содержимым. <?php class ModelGuestbookGuestbook extends Model { public function processGuestbookEntry($data) { // send email notification to store admin $mail = new Mail(); $mail->protocol = $this->config->get('config_mail_protocol'); $mail->parameter = $this->config->get('config_mail_parameter'); $mail->smtp_hostname = $this->config->get('config_mail_smtp_hostname'); $mail->smtp_username = $this->config->get('config_mail_smtp_username'); $mail->smtp_password = html_entity_decode($this->config->get('config_mail_smtp_password'), ENT_QUOTES, 'UTF-8'); $mail->smtp_port = $this->config->get('config_mail_smtp_port'); $mail->smtp_timeout = $this->config->get('config_mail_smtp_timeout'); $mail->setTo($this->config->get('config_email')); $mail->setFrom($this->config->get('config_email')); $mail->setSender(html_entity_decode($this->config->get('config_name'), ENT_QUOTES, 'UTF-8')); $mail->setSubject($data['subject']); $mail->setText($data['message']); $mail->send(); } } В OpenCart модель отвечает за обработку бизнес-логики приложения. Если вы хотите реализовать любую логику, которая включает базу данных, модель как раз для этого. Соглашение об именовании класса модели аналогично стандарту класса контроллера. Чтобы все было просто, мы внедрили метод processGuestbookEntry, который уведомляет администратора магазина по электронной почте, когда пользователь представляет запись в гостевой книге. Довольно просто, да? Вернемся к нашему контроллеру и рассмотрим следующий фрагмент кода в очереди. if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) { $this->load->model('guestbook/guestbook'); $data['subject'] = sprintf('New guestbook entry submitted by %s', $this->request->post['guest_name']); $data['message'] = $this->request->post['guest_message']; $this->model_guestbook_guestbook->processGuestbookEntry($data); $this->session->data['success'] = $this->language->get('text_success'); $this->response->redirect($this->url->link('guestbook/entry', '', true)); } Он проверяет действительный запрос POST и выполняет базовую проверку данных, отправленных пользователем, вызывая метод validate. Код $this->load->model('guestbook/guestbook') используется для загрузки модели, которую мы определили мгновение назад. Сразу же после этого мы готовим массив $data на основе ввода пользователя и вызываем метод processGuestbookEntry, который уведомляет администратора магазина о новой записи в гостевой книге. Наконец, мы перенаправляем пользователя обратно на страницу ввода гостевой книги. Двигаясь вперед в контроллере, следующий фрагмент устанавливает сообщение об успешном завершении, которое будет отображаться при отправке формы. $data['success'] = ''; if (isset($this->session->data['success'])) { $data['success'] = $this->session->data['success']; unset($this->session->data['success']); } После этого есть фрагмент, который используется для создания набора ссылок для страницы. $data['breadcrumbs'] = array(); $data['breadcrumbs'][] = array( 'text' => $this->language->get('text_home'), 'href' => $this->url->link('common/home') ); $data['breadcrumbs'][] = array( 'text' => $this->language->get('heading_title'), 'href' => $this->url->link('guestbook/entry', '', true) ); Следующий фрагмент является важным, и это то, что вы будете использовать большую часть времени для передачи информации из метода контроллера в шаблон представления. $data['heading_title'] = $this->language->get('heading_title'); $data['entry_guest_name'] = $this->language->get('entry_guest_name'); $data['entry_guest_message'] = $this->language->get('entry_guest_message'); $data['entry_submit'] = $this->language->get('entry_submit'); Аналогично назначению переменных OpenCart инициализирует общие элементы заголовка страницы, нижнего колонтитула и тому подобного, как показано в следующем фрагменте. $data['column_left'] = $this->load->controller('common/column_left'); $data['column_right'] = $this->load->controller('common/column_right'); $data['content_top'] = $this->load->controller('common/content_top'); $data['content_bottom'] = $this->load->controller('common/content_bottom'); $data['footer'] = $this->load->controller('common/footer'); $data['header'] = $this->load->controller('common/header'); Наконец, он вызывает шаблон представления, чтобы отобразить фактическую страницу! $this->response->setOutput($this->load->view('guestbook/entry', $data)); Конечно, мы еще не создали шаблон представления. Это идеальное время для этого! Идем дальше и создаем файл catalog/view/theme/default/template/guestbook/entry.tpl со следующим содержимым. <?php echo $header; ?> <div class="container"> <ul class="breadcrumb"> <?php foreach ($breadcrumbs as $breadcrumb) { ?> <li><a href="<?php echo $breadcrumb['href']; ?>"><?php echo $breadcrumb['text']; ?></a></li> <?php } ?> </ul> <?php if ($success) { ?> <div class="alert alert-success"><i class="fa fa-check-circle"></i> <?php echo $success; ?></div> <?php } ?> <div class="row"><?php echo $column_left; ?> <?php if ($column_left && $column_right) { ?> <?php $class = 'col-sm-6'; ?> <?php } elseif ($column_left || $column_right) { ?> <?php $class = 'col-sm-9'; ?> <?php } else { ?> <?php $class = 'col-sm-12'; ?> <?php } ?> <div id="content" class="<?php echo $class; ?>"><?php echo $content_top; ?> <h1><?php echo $heading_title; ?></h1> <form action="<?php echo $action; ?>" method="post" enctype="multipart/form-data" class="form-horizontal"> <div class="form-group required"> <label class="col-sm-2 control-label" for="input-guest-name"><?php echo $entry_guest_name; ?></label> <div class="col-sm-10"> <input type="text" value="<?php echo $guest_name; ?>" name="guest_name" placeholder="<?php echo $entry_guest_name; ?>" id="input-guest-name" class="form-control" size="10" /> <?php if ($error_guest_name) { ?> <div class="text-danger"><?php echo $error_guest_name; ?></div> <?php } ?> </div> </div> <div class="form-group required"> <label class="col-sm-2 control-label" for="input-guest-message"><?php echo $entry_guest_message; ?></label> <div class="col-sm-10"> <textarea name="guest_message" placeholder="<?php echo $entry_guest_message; ?>" id="input-guest-message" class="form-control"><?php echo $guest_message; ?></textarea> <?php if ($error_guest_message) { ?> <div class="text-danger"><?php echo $error_guest_message; ?></div> <?php } ?> </div> </div> <div class="form-group required"> <label class="col-sm-2"> </label> <div class="col-sm-10"> <input type="submit" value="<?php echo $entry_submit; ?>" class="btn btn-primary" /> </div> </div> </form> <?php echo $content_bottom; ?></div> <?php echo $column_right; ?></div> </div> <?php echo $footer; ?> Это наш основной файл шаблона, который отвечает за отображение содержимого нашей страницы гостевой книги. В этом файле мы только что использовали переменные, которые были настроены в методе контроллера index. Важно отметить, что отзывчивость - это то, что поставляется с новейшими версиями OpenCart, поддерживаемыми платформой Bootstrap. Помимо этого, это довольно обычный HTML, который довольно просто понять. Итак, это то что касается настройки файлов. У нас все готово, но как вы получите доступ к этому из front-end? На фронтенде вы можете получить доступ к странице «Гостевая книга», добавив переменную переадресации маршрута, поэтому URL-адрес должен быть похож на http://your-opencart-store-url/index.php?route=guestbook/entry. Давайте разбираться, как OpenCart сопоставляет любой URL-адрес с конкретным файлом контроллера. Формат переменной маршрута - {directory}/{filename}/{methodname}. Компонент {directory} отображается в каталог catalog/controller. {filename} сопоставляется с именем файла контроллера в catalog/controller/{directory}. Наконец, он будет искать метод контроллера с именем {methodname}, если он указан в маршруте, иначе он вызовет метод index по умолчанию.‘ Вот как выглядит конечный результат. Конечно, вы можете пойти дальше и протестировать его, отправив форму, и вы должны получить уведомление по электронной почте на адрес электронной почты, зарегистрированный в качестве администратора магазина. Заключение В любом фреймворке всегда интересно продолжить реализацию собственных пользовательских функций. Это стало предметом сегодняшнего учебника, в котором мы расширили OpenCart и разработали пользовательские контроллеры, создав довольно простую, но полезную функциональность гостевой книги. В этом процессе мы поняли, что речь идет не только о контроллерах - было несколько других ключевых элементов, которые были неизбежны. Поэтому мы также создали их: модель, язык и представление. В конце было приятно видеть рабочий прецедент того, что я обещал в начале статьи.
 - 
	
		
		Интеграция внешних библиотек в OpenCart с помощью Composer
		
		В настоящее время почти каждый фреймворк имеет встроенную поддержку Composer, потрясающего инструмента управления зависимостями в PHP, и OpenCart не является исключением. Из этого руководства вы узнаете, как использовать Composer для интеграции внешних библиотек в OpenCart. Роль Composer в OpenCart После представления OpenCart 2.2 поддерживается рабочий процесс на основе Composer. Так что, идите и возьмите последнюю версию OpenCart; как написано это — 2.3.0.2. Убедитесь, что вы установили и настроили последнюю загруженную версию, так как это пригодится нам позже в этой статье. Посмотрите структуру каталогов, и вы заметите некоторые отличия от более ранних версий OpenCart. Касательно этой статьи интересными кандидатами будут файл composer.json и каталог vendor. Давайте быстренько пройдемся по файлу composer.json. { "name": "opencart/opencart", "type": "project", "description": "OpenCart", "keywords": ["opencart", "ecommerce", "framework", "opensource"], "homepage": "https://www.opencart.com", "license": "GPL-3.0+", "require": { "cardinity/cardinity-sdk-php": "^1.0", "braintree/braintree_php" : "3.2.0", "leafo/scssphp": "0.0.12", "divido/divido-php": ">=1.1.1", "klarna/kco_rest": "^2.2", "php": ">=5.4.0" } } Хотя обсуждение синтаксиса Composer выходит за рамки этой статьи, давайте быстро рассмотрим то, что говорится в определениях для непрофессионала. Во-первых, сам проект OpenCart теперь доступен как библиотека, поэтому вы можете установить его, используя Composer, не загружая его с сайта вручную. Кроме того, также требуется, чтобы другие сторонние библиотеки работали должным образом, такие как divo, leafo и т.д. Конечно, вам не нужно беспокоиться об этом, поскольку они будет обработанный автоматически при выполнении соответствующих команд Composer. Когда вы устанавливаете новую библиотеку, в файл composer.json будет добавлена соответствующая запись. Связанные файлы библиотеки помещаются в каталог vendor на том же уровне. Просто просмотрите этот каталог, и вы увидите, что библиотеки уже установлены! Каталог vendor также содержит файл autoload.php, сгенерированный самим Composer, который обеспечивает автоматическую загрузку библиотек в OpenCart, поэтому вы можете сразу использовать его. Конечно, OpenCart включает autoload.php при начальной загрузке проекта. Итак, это краткое введение в то, как Composer работает с OpenCart. В демонстрационных целях мы установим популярную библиотеку PHPMailer, используя Composer. Установка PHPMailer с помощью Composer PHPMailer — популярная библиотека PHP, которая используется для отправки электронных писем. Мы установим его в OpenCart с помощью Composer. Для этого, перейдите в свой терминал и измените каталог, чтобы вы находились на том же уровне, где находятся каталог vendor и файл composer.json. Теперь запустите команду composer require phpmailer/phpmailer и нажмите enter! Полагая, что все идет хорошо, ответ должен выглядеть следующим образом. $composer require phpmailer/phpmailer Using version ^5.2 for phpmailer/phpmailer ./composer.json has been updated Loading composer repositories with package information Updating dependencies (including require-dev) - Installing phpmailer/phpmailer (v5.2.16) Downloading: 100% phpmailer/phpmailer suggests installing league/oauth2-google (Needed for Google XOAUTH2 authentication) Writing lock file Generating autoload files Вот и всё! PHPMailer успешно загружен и установлен, в этом и есть вся прелесть Composer! Убедитесь, что заглянув в каталог vendor, вы найдете его установленным в каталог phpmailer/phpmailer. Также, давайте откроем composer.json, чтобы посмотреть как он выглядит. { "name": "opencart/opencart", "type": "project", "description": "OpenCart", "keywords": ["opencart", "ecommerce", "framework", "opensource"], "homepage": "http://www.opencart.com", "license": "GPL-3.0+", "require": { "cardinity/cardinity-sdk-php": "^1.0", "braintree/braintree_php" : "3.2.0", "leafo/scssphp": "0.0.12", "divido/divido-php": ">=1.1.1", "klarna/kco_rest": "^2.2", "php": ">=5.4.0", "phpmailer/phpmailer": "^5.2" } } Как видите, запись "phpmailer/phpmailer": "^5.2" добавлена в раздел require. Это означает, что для правильной работы вашему проекту нужeн PHPMailer Давайте предположим, что вы работаете с другими разработчиками и вам нужно регулярно делиться вашей работой. В этом случае, вам нужно передать только ваш файл composer.json, а обо всём остальном позаботиться Composer! Им потребуется только выполнить команду composer update, это обеспечит установку требуемых зависимостей в их копии! Ну вот, мы установили PHPMailer используя Composer, но как его использовать? Не беспокойтесь, я не оставлю вас так быстро — это точный рецепт нашего следующего раздела! Как использовать библиотеку PHPMailer? Вы уже сделали себе одолжение, использовав Composer для установки библиотеки PHPMailer, и вы будете свидетелем того, как в этом разделе мы рассмотрим то, насколько просто его использовать в примерах кода. В целях примера, мы создадим довольно простой файл с пользовательским контроллером, который вы можете вызывать для отправки email. Откройте ваш любимый текстовый редактор и создайте example/email.php внутри каталога catalog/controller со следующим содержимым. <?php class ControllerExampleEmail extends Controller { public function index() { // just instantiate the mailer object, no need to include anything to use it. $objPhpMailer = new PHPMailer(); $objPhpMailer->From = "sender@example.com"; $objPhpMailer->FromName = "Sajal Soni"; $objPhpMailer->AddAddress("receiver@example.com"); $objPhpMailer->WordWrap = 50; $objPhpMailer->IsHTML(true); $objPhpMailer->Subject = "Subject"; $objPhpMailer->Body = "<h2>HTML Body</h2>"; $objPhpMailer->AltBody = "Plain Body"; if(!$objPhpMailer->Send()) { echo "Message could not be sent. <p>"; echo "Mailer Error: " . $objPhpMailer->ErrorInfo; exit; } echo "Message has been sent"; exit; } } В методе index, вы видите, что мы создали экземпляр объекта PHPMailer без каких-либо указаний include, которые должны были включить требуемый класс PHPMailer, если бы мы не использовали рабочий процесс на основе Composer. Вы все поняли правильно, OpenCart Напомним, что autoload.php в каталоге vendor осуществляет всю эту магию! Вследствие этого есть некая довольно стандартная вещь, требуемая PHPMailer для отправки email. Конечно, я пытался сделать пример настолько просто насколько возможно, так как для обсуждения PHPMailer потребуется отдельная статья. Так что, это было быстрое и простое введение в то, как вы можете использовать Composer с OpenCart для интеграции со сторонними библиотеками. Выводы В этой статье мы только вскарабкались на поверхность рабочего процесса на основе Composer в OpenCart, чтобы использовать сторонние библиотеки в вашем проекте. Не говоря уже о том, что Composer — это будущее инструментов управления зависимостями в PHP. Поэтому всегда хорошо запачкать в этом ваши руки, поскольку это становится стандартом во всех популярных фреймворках.
 - 
	
		
		Вывод ошибок PHP на экран
		
		При работе с сайтом каждый разработчик сталкивался с ситуацией, когда при включении модуля, добавления своего кода или другого изменения на сайте, возникал так называемый WHITE SCREEN OF DEATH (белый экран смерти), который обычно вызван ошибкой PHP. Первое действие разработчика, естественно, откатить изменения, повлёкшие за собой эту ошибку. Но ведь ошибку-то исправлять надо, а значит, надо посмотреть что именно является причиной её возникновения. Включение вывода ошибок на экран через .htaccess В корне имеется файл .htaccess, который регулирует процессы загрузки страниц. Чтобы включить вывод сообщений, откройте его и добавьте следующие две строки: php_flag display_errors on php_flag display_startup_errors on
 - 
	
		
		Событие в opencart 3
		
		Думаю многие разработчики сталкивались с ситуацией, когда нужно выполнить какое-то действие при определенном событии на сайте. Многие сразу делают врезку через модификатор в нужный контроллер или модель со своей логикой. Но на мой взгляд это в корне не правильно, учитывая тот факт, что в опенкарт давно уже есть функционала событий который позволяет переопределить данные или выполнить свой скрипт в момент определенного события на сайте. Предлагаю разобрать как все работает на реальном примере. Реализуем простую отправку данных о заказе на сторонний сервис. Создаем модуль Для того что бы мы могли создать событие нужно для начала создать самый простой модуль и повесить на его активацию установку нашего события. Создаем контроллер модуля в админке Идем в папку admin/controller/extension/module и создаем файл mymodule.php со следующим содержимым: <?php class ControllerExtensionModuleMymodule extends Controller { //Сработает только 1 раз при установке public function install() { $this->load->model('setting/event'); $this->model_setting_event->addEvent('sm-send-order-1c', 'catalog/model/checkout/order/addOrder/after', 'extension/module/mymodule/Send'); } //Сработает только 1 раз при удалении (удалит действие) public function uninstall() { $this->load->model('setting/event'); $this->model_setting_event->deleteEvent('sm-send-order-1c'); } private $error = array(); public function index() { $this->load->language('extension/module/mymodule'); $this->document->setTitle($this->language->get('heading_title')); $this->load->model('setting/setting'); if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate()) { $this->model_setting_setting->editSetting('module_mymodule', $this->request->post); $this->session->data['success'] = $this->language->get('text_success'); $this->response->redirect($this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true)); } if (isset($this->error['warning'])) { $data['error_warning'] = $this->error['warning']; } else { $data['error_warning'] = ''; } $data['breadcrumbs'] = array(); $data['breadcrumbs'][] = array( 'text' => $this->language->get('text_home'), 'href' => $this->url->link('common/dashboard', 'user_token=' . $this->session->data['user_token'], true) ); $data['breadcrumbs'][] = array( 'text' => $this->language->get('text_extension'), 'href' => $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true) ); $data['breadcrumbs'][] = array( 'text' => $this->language->get('heading_title'), 'href' => $this->url->link('extension/module/mymodule', 'user_token=' . $this->session->data['user_token'], true) ); $data['action'] = $this->url->link('extension/module/mymodule', 'user_token=' . $this->session->data['user_token'], true); $data['cancel'] = $this->url->link('marketplace/extension', 'user_token=' . $this->session->data['user_token'] . '&type=module', true); if (isset($this->request->post['module_mymodule_status'])) { $data['module_mymodule_status'] = $this->request->post['module_mymodule_status']; } else { $data['module_mymodule_status'] = $this->config->get('module_mymodule_status'); } $data['header'] = $this->load->controller('common/header'); $data['column_left'] = $this->load->controller('common/column_left'); $data['footer'] = $this->load->controller('common/footer'); $this->response->setOutput($this->load->view('extension/module/mymodule', $data)); } protected function validate() { if (!$this->user->hasPermission('modify', 'extension/module/mymodule')) { $this->error['warning'] = $this->language->get('error_permission'); } return !$this->error; } } Сосредоточимся на двух функциях: public function install() { $this->load->model('setting/event'); $this->model_setting_event->addEvent('sm-send-order-1c', 'catalog/model/checkout/order/addOrder/after', 'extension/module/mymodule/Send'); } Тут мы регистрируем наше событие в момент установки модуля. Вызываем метод “addEvent” и передаем туда 3 параметра: 1.'sm-send-order-1c' - название события (произвольное) 2.'catalog/model/checkout/order/addOrder/after' - путь к контроллеру, модели, языку или загрузчику шаблона. У нас мы указали путь к модели создания заказа. Обратите внимание данный параметр состоит с двух частей 2.1. catalog/model/checkout/order/addOrder - непосредственно пусть к модели 2.2 after - время выполнения вашего нового события. Если after то ваш код будет выполнен уже после выполнения основного кода Функции "addOrder". Если вы укажете before - то сначала выполниться ваш код, а потом уже остальной код. Нас интересует выполнение уже после добавления заказа. Поэтому мы указали after. 3. 'extension/module/mymodule/Send' - путь к функции нашего контроллера где будет описана вся логика которую надо выполнить после создания заказа (отправка данных о заказе на сторонний сервис) Вторая функция - это функция удаления события. public function uninstall() { $this->load->model('setting/event'); $this->model_setting_event->deleteEvent('sm-send-order-1c'); } Создаем файл шаблона модуля twig Создаем файл mymodule.twig в папке admin\view\template\extension\module\mymodule.twig с кодом: {{ header }}{{ column_left }} <div id="content"> <div class="page-header"> <div class="container-fluid"> <div class="pull-right"> <button type="submit" form="form-module" data-toggle="tooltip" title="{{ button_save }}" class="btn btn-primary"><i class="fa fa-save"></i></button> <a href="{{ cancel }}" data-toggle="tooltip" title="{{ button_cancel }}" class="btn btn-default"><i class="fa fa-reply"></i></a></div> <h1>{{ heading_title }}</h1> <ul class="breadcrumb"> {% for breadcrumb in breadcrumbs %} <li><a href="{{ breadcrumb.href }}">{{ breadcrumb.text }}</a></li> {% endfor %} </ul> </div> </div> <div class="container-fluid"> {% if error_warning %} <div class="alert alert-danger alert-dismissible"><i class="fa fa-exclamation-circle"></i> {{ error_warning }} <button type="button" class="close" data-dismiss="alert">×</button> </div> {% endif %} <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title"><i class="fa fa-pencil"></i> {{ text_edit }}</h3> </div> <div class="panel-body"> <form action="{{ action }}" method="post" enctype="multipart/form-data" id="form-module" class="form-horizontal"> <div class="form-group"> <label class="col-sm-2 control-label" for="input-status">{{ entry_status }}</label> <div class="col-sm-10"> <select name="module_mymodule_status" id="input-status" class="form-control"> {% if module_mymodule_status %} <option value="1" selected="selected">{{ text_enabled }}</option> <option value="0">{{ text_disabled }}</option> {% else %} <option value="1">{{ text_enabled }}</option> <option value="0" selected="selected">{{ text_disabled }}</option> {% endif %} </select> </div> </div> </form> </div> </div> </div> </div> {{ footer }} Здесь я думаю все всем понятно и по сути нечего объяснять. Дальше нужно создать файл переводов в папке admin\language\ru-ru\extension\module\mymodule.php с кодом: <?php // Heading $_['heading_title'] = 'Мой новый модуль с событием отправки заказа'; $_['text_module'] = 'Модули'; $_['text_success'] = 'Настройки успешно изменены!'; $_['text_edit'] = 'Редактирование'; // Entry $_['entry_status'] = 'Статус'; // Error $_['error_permission'] = 'У вас недостаточно прав для внесения изменений!'; Проверка установки события После создания нашего нового модуля идем и активируем его в разделе Дополнения-модули. После этого если все сделано правильно наше событие “sm-send-order-1c” должно появиться в списке событий в разделе “Разширения-События” Если все так. То идем дальше. Создаем сам обработчик события Создаем файл catalog\controller\extension\module\mymodule.php с кодом: <?php class ControllerExtensionModuleSm1c extends Controller { public function Send(&$route, &$args, &$output) { $this->log->write(print_r($route,true)); $this->log->write(print_r($args,true)); $this->log->write(print_r($output,true)); } } В функции ” Send” я просто вывел данные в журнал логов опенкарта. Но тут можно делать с данными все что угодно. В переменной $args будут все данные по заказу которые вы можете смело отправлять на сторонний сервис например при помощи CURL.
 - 
	
		
		API Opencart 3.* от А до Я как работает и как использовать
		
		Речь в этой статтье пойдет о API opencart 3.* Насколько я понял , то само АПИ 3.* и 2.3 не особо отличаются, но я разбирал именно opencart 3.0.2.1 с большой вероятностью в ocstore 3.0 апи точно такой же и эта статья подойдет и для тех кто использует русскую сборку данного движка. Но все же могут быть отличия так-что разбирайтесь сравнивайте и реализуйте. И так, ближе к делу, как говорится. Поступила заявка реализовать обновление количества товаров в магазине с 1С. Причем я уже делал данную процедуру, но только магазин выступал клиентом а 1С – сервером. Тоесть я с магазина делал запрос к API 1C и в ответ получал данные которые обрабатывал уже в своем скрипте. Но сейча какраз все наоборот. 1С делает запрос к REST API opencart и отдает мне данные с которыми я должен работать. Сначала опишу в двух словах как это работает пошагово. Шаг №1 Подключение со стороны 1С к сайту Шаг №2 Сайт проверяет логин и пароль и в ответ отдает токен сесии Шаг 3 1С делает повторный запрос но уже к контроллеру обновления даннх товаров По сути все довольно просто и можно было бы написать самостоятельно все нужные контроллеры. НО зачем? Ведь в опенкарт 3 уже есть REST API в которого уже есть функционал авторизации. И осталось только написать обновление товаров. Но не тут то было. Разработчики движка наверное уж большо спешили выпустить новый релиз и наделали ошибок. Да и в документации по использованию API привелм примеры не на php, как мне бы хотелось, а на piton ) Ну ничего. Сейчас со всем разберемся. Но перед тем как начать писать свое АПИ подключение надо бы поправить баги которые допустили разработчики в opencart 3/ БУдем надеятся что они хи исправят но поканужно внести пару парвок. Если этого не сделать то в момент подключения вы будете получать ошибку Notice: Undefined index: api_token in /catalog/controller/startup/session.php on line 8. И так : идем в файл /catalog/controller/startup/session.php и ищем строку $this->db->query(“DELETE FROM `” . DB_PREFIX . “api_session` WHERE TIMESTAMPADD(HOUR, 1, date_modified) < NOW()”); и перед этой сторокой надо добавить: if (!isset($this->request->get[‘api_token’])) { $this->request->get[‘api_token’] = ”; } 2. идем в файл catalog/model/account/api.php и ищем строку: $query = $this->db->query(“SELECT * FROM `” . DB_PREFIX . “api` WHERE `username` = ‘” . $this->db->escape($username) . “‘ AND `key` = ‘” . $this->db->escape($key) . “‘ AND status = ‘1’”); [/php] в этой строке также допущена ошибка пропущен and. Ниже я указал правильную строку, просто замените на нее: [php] $query = $this->db->query("SELECT * FROM `" . DB_PREFIX . "api` WHERE `username` = '" . $this->db->escape($username) . "' AND `key` = '" . $this->db->escape($key) . "' AND status = '1'"); 3. Последний шаг и можно приступать. Идем в файл /catalog/controller/api/login.php и ишем там строку : $session = new Session($config->get('session_engine'), $registry); И тут разработчики чуток на путали. правильный синтаксис такой : $session = new Session($this->config->get('session_engine'), $this->registry); Вот и все. Можно идти дальше и сделать само потключение. И так поехали. Представим что у нас есть Сайт-1 – сайт где надо обновить данные по товарам Сайт-2 – откуда надо подключится и выслать те самые данные. Подключение к API opencart 3 Для начала на Сайте-2 (откуда мы хотим подключится) создадим простой файл в корне (разместить вы его можете где угодно, но для примера я выбрал именно корень сайта). И так создадим файл connect.php. Я решил использовать для соединения с другим сайтом CURL. И в этом файле описываю саму функцию присоединения к удаленному сайту – Сайт-1 текст файл выглядит так: $apiKey = "r9DkS8KmRO5YNCHoQFh16ZQwHW5f3yf9ClETja06un2dk17Q078AoSWoAjLCx7zNUhbT2oSsq8xdYg7iUt0267aRPoaCCA4jdkHEjXI1RZ9IcVWHQE7lClm5dCHpdwo6gAfJC9pJUUNVA7WZkl1jDDicTchVsGkcXXzgKjULfBLuSpdkbtOFUEl3wmy9OWzFR01r0zGsp6gmjfNBmjlUtxtS8RxydlsSyrO0effEBWJkU4sfNDPfbmF6LiFOAyoa"; //Whatever you put in System -> Users -> API $url = "http://opencart3.myopencart.club/index.php?route=api/login"; $curl = curl_init($url); $post = array ( 'username' => 'Default', 'key' => $apiKey ); curl_setopt_array( $curl, array( CURLOPT_RETURNTRANSFER=> TRUE, CURLOPT_POSTFIELDS => $post ) ); $raw_response = curl_exec( $curl ); //var_dump($raw_response); $response = json_decode($raw_response); curl_close($curl); $api_token = $response->api_token; // создем масив товаров для обновления информации $products = Array ( '123' => Array ( 'price' => 350, 'quantity' => 10, 'product_discount' => 100 ), '555' => Array ( 'quantity' => 44 ) ); // подключаемся к сайту 2 и к вновь созданному файлу которій будет отвечать за обновление товаров. Создание его я опишу ниже. $url = "http://opencart3.myopencart.club/index.php?route=api/updateproduct&api_token=".$api_token; $post = $products; $curl = curl_init($url); curl_setopt_array( $curl, array( CURLOPT_RETURNTRANSFER=> TRUE, CURLOPT_POSTFIELDS => $post ) ); $raw_response = curl_exec( $curl ); var_dump($raw_response); Имя пользователя и пароль берем в админке Сайта-1. Идем в раздел System -> Users -> API И так на данный момент у нас готова часть которая отвечает за подключени и отправку данных по API. Теперь осталось создать файл который будет обрабатывать полученные данный с сайта-2 на сайте-1. Итак идем в папку catalog/controller/api на Сайте-1 и создаем файл updateproduct.php Саму логику обновлеия данных по товарам я не буду описывать. Просто приведу пример получения данных и отправки ответа. <?php class ControllerApiUpdateproduct extends Controller { public function index() { $this->load->language(‘api/currency’); $json = array(); if (!isset($this->session->data[‘api_id’])) { $json[‘error’] = $this->language->get(‘error_permission’); } else { $this->load->model(‘catalog/product’); if ($this->request->post[‘product’]) { // Здесь будет ваша логика обработки полученных товаров $json[‘success’] = $this->language->get(‘text_success’); } else { $json[‘error’] = $this->language->get(‘error_currency’); } } if (isset($this->request->server[‘HTTP_ORIGIN’])) { $this->response->addHeader(‘Access-Control-Allow-Origin: ‘ . $this->request->server[‘HTTP_ORIGIN’]); $this->response->addHeader(‘Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS’); $this->response->addHeader(‘Access-Control-Max-Age: 1000’); $this->response->addHeader(‘Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With’); } $this->response->addHeader(‘Content-Type: application/json’); $this->response->setOutput(json_encode($json)); } } Вот и все.
 - 
	
		
		OCMOD opencart создание модуля
		
		С версии opencart 2 в поставке CMS уже встроена система модификации файлов OCMOD. Она пришла на замену VQMOD и имеет некоторые отличия как в принципах формирования файлов xml так и в установке файлов модификаций. Очень большим плюсов OCMOD является тот, что он уже встроен в opencart и не нуждается в дополнительной установке. Структура папок модуля для OCMOD Весь модуль должен быть упакаван в zip архів и иметь название xxx.ocmod.zip где ххх – любое слово латиницей. Внутренняя структура архива: папка upload - обязательная папка в которой находяться файлі модуля с подпапками папка admin - папка с файлами модуля папка catalog - папка с файлами модуля и так далее - другие папки файл instal.xml - файл с изменениями которые нужно внести в файлы ядра opencart файл instal.php - php файл с функциями которые надо выполнить во время установки модуля файл instal.sql - sql файл с командами insert, update или delete которые надо выполнить во время установки модуля Подробнее о структуре файла instal.xml Данный файл отвечает за внесение изменений в любые файлы ядра движка, при этом сами файлы не изменяются, а делается их копия с внесенными изменениями. Данные копии используются при загрузке сайта вместо стандартных. Просмотреть все созданные файлы можно в папке system/storage/modification в данной папке повторяется полная структура папок ядра где размещается модифицированый файл. Поговорим о самой структуре файла. Пример файла: <?xml version=”1.0″ encoding=”utf-8″?> <modification> <name>Modification Default</name> <version>1.0</version> <author>OpenCart Ltd</author> <link>http://www.opencart.com</link> <file path=”catalog/controller/common/home.php”> <operation> <search><![CDATA[ $data[‘column_left’] = $this->load->controller(‘common/column_left’); ]]></search> <add position=”replace”><![CDATA[ test123 ]]></add> </operation> </file> </modification> Атрибуты: “name” - название модификатора должно быть уникалное в разрезе всех установленных модификаторов. Обязательно к заполнению. “version” - версия вашего модификатора/модуля. Не обязательно но желательно к заполнению. “author” - автор модуля/модификатора.Не обязательно но желательно к заполнению. “link” - линк на любой сайт. Можете указать свой сайт ии оставить пустым. Теги: “file“ - в данном теге прописывается путь и название файла в который мы будем вносить изменения. <file path=”catalog/controller/common/home.php”> изменения будут вноситься в файл home.php который находится в папке catalog/controller/common <file path=”catalog/controller/{common,product}/home.php”> поиск файла home.php будет происходить уже в двух папках в catalog/controller/common и catalog/controller/product. То есть можно укозать несколько папок для поиска файла. Для єтого нужно просто указать все папки между двух "{}" через запятую. Если указать путь к файлу так: <file path=”catalog/controller/*/home.php”> То поиск файла home.php будет происходить во всех подпапках папки controller. Так же есть возможность задать несколько файлов в которые мы хотим внести изменения. Для нескольких файлов : <file path=”catalog/controller/common/{home,header}*.php”> Для всех файлов с разширением .php директории “common": <file path=”catalog/controller/common/*.php”> “operation“ - тег в котором заключено описание что именно искать и что делать с найденным элементом. Для каждлго задания данный тег открывается и закрывается отдельно. В теге может быть указан атрибут: error = "skip" - (работает только начина с версии opencart - 2.3) позволят пропускать задачу при возникновении ошибки, без прерывания выполнения всех задач. <operation error = “skip”> … </operation> “search” – в данном теге указываем элемент поиска. То, что нужно найти в файле/файлах указаных в теге “file” В теги serch можно указывать несколько атрибутов: trim="(true|false)" - удалять пробелы в указаном искомом тексте regex="(true|false)" - используется только когда поиск текста происходит через регулярное выражение index="(number)" - указывает к какому по очереди искомому тексту применять изменения описанные в теге "add"(о нем ниже). Тоесть если указаный код выглядит так: <search index=”2″><![CDATA[$data[‘column_left’]]]></search> то, если в указаном файле, код – “$data[‘column_left’]” встречается несколько раз, то изменения будут применены только ко второму по счету совпавшему тексту. "add" - в данном теге указывается текст который надо вставить в искомый файл. Место куда именно внужно вставить указывается атрибутами: trim="(true|false)" - перед вставкой уода будут удалены все ненужные пробелы. (не обязателен) position="(Replace|Before|After)" - обязателен атрибут. replace - заменить найденый текст (который указаный в теге "search") на текст который указан в теге "add" before - вставить текст с теге "add" над котом указаный в теге "search" after - вставить текст с теге "add" под котом указаный в теге "search" offset="(число)" - атрибут указывает сколько строчек отступить от кода указаного в теге "search" и только потом вставить новый код. Если тег "add" будет иметь атрибут position="before" и offset="5" <add position=”before” trim=”true” offset=”5″> . То будет найдена искомая строка, потом от нее будет отсчитано 5 строк вверх и после пятой строки встится новый код. Обратите внимание если указать <add position=”replace ” trim=”true” offset=”5″> то система найдет искомый код отступить от него в низ 5 строчек и заменить сам искомый текст и эти 5 строк на новый код. Все тексты которые мы ищим и те которые хотим вставить должны заключаться в специальные теги в начале “<![CDATA[” и закрываться в конце “]]>". Пример: <search><![CDATA[ искомый текст ]]></search> <add position="replace"><![CDATA[ добавляемый текст ]]></add> И так, давайте теперь разберем код который я наводил в начале статьи: <?xml version=”1.0″ encoding=”utf-8″?> <modification> <name>Modification Default</name> <version>1.0</version> <author>OpenCart Ltd</author> <link>http://www.opencart.com</link> <file path=”catalog/controller/common/home.php”> <operation> <search><![CDATA[ $data[‘column_left’] = $this->load->controller(‘common/column_left’); ]]></search> <add position=”replace”><![CDATA[ test123 ]]></add> </operation> </file> </modification> Что он делает? Ответ: ищем файл catalog/controller/common/home.php в данном файле ищем строку $data['column_left'] = $this->load->controller('common/column_left'); найденную строку замеяем на ” test123″ Надеюсь я хоть чуточку улучшил вам понимание в даном моде и в том как с ним работать.
 - 
	
		
		Замена Изображения товара на картинку опции
		
		Как мы уже знаем, а те кто не знает, сейчас будут проинформированы , начиная с версии opencart 2.2* в CMS появился новый тип опций “image”, который позволяет добавлять картинку к каждой опции. И вот недавно мне задали вопрос – “Как сделать так чтобы при выборе опции данного типа, изображение товара заменялось на изображение опции?”. Не долго размышляя, я начал искать в google, но к моему удивлению ничего похожего не нашел. По этой причине пришлось все делать самостоятельно. Вот об это я и будет данная статья. Первое, что нужно сделать – это добавить новую опцию с типом “image”. Заходим в административную часть своего сайта в раздел “Каталог”/”Опции” и жмем “Добавить”. Заполняем все поля и добавляем каждой опции свою картинку Далее добавляем данную опцию на товар, как любую другую. В результате мы должны получить вот такой вид страницы товара Как мы видим, на странице появилась возможность выбрать нашу опцию и возле каждого значения вывелось изображение, которое мы добавили раннее. Это довольно удобно. Например каждая опция – это товар в разном цвете и таким образом, покупатель может увидеть например какой вид будет иметь товар о выбранном им цвете. Но есть один недостаток, изображение возле опции, слишком маленькое. Давайте сделаем так, что бы при выборе опции, главная картинка товара, заменялась на изображение опции. Создаем модель в которой будем получать изображение опции в исходном размере по id опции. Создаем файл smoption.php в папке корневая_папка_сайта/catalog/model/module/smoption.php с текстом : <?php class ModelModuleSmoption extends Model { public function GetImageById($product_option_id, $product_option_value_id) { $query = $this->db->query(“select * FROM ” . DB_PREFIX . “option_value ov, (SELECT pov.option_value_id FROM ” . DB_PREFIX . “product_option_value pov WHERE pov.product_option_id like ” . $product_option_id . ” and pov.product_option_value_id like ” . $product_option_value_id.”) t WHERE ov.option_value_id = t.option_value_id”) ; return $query->row; } } ?> Создаем контроллер который будет обрабатывать все данные Создаем файл smoption.php в папке корневая_папка_сайт/catalog/controller/module/smoption.php и добавим туда код : <?php class ControllerModuleSmoption extends Controller { public function GetImage() { $this->load->model(‘module/smoption’); $this->load->model(‘tool/image’); //получаем исходное изображение опции $result = $this->model_module_smoption->GetImageById($this->request->post[‘option_id’],$this->request->post[‘option_val_id’]); // если изображение есть, то создаем две копии данного изображения с нужными размерами(размеры берутся с настроек магазина) if($result){ $images = array( ‘popup’ => $this->model_tool_image->resize($result[‘image’], $this->config->get(‘config_image_popup_width’), $this->config->get(‘config_image_popup_height’)),// изображение всплывающее при клике ‘thumb’ => $this->model_tool_image->resize($result[‘image’], $this->config->get(‘config_image_thumb_width’), $this->config->get(‘config_image_thumb_height’)) // главное изображение товара ); } $this->response->setOutput(json_encode($images)); } } ?> Пишем скрипт который будет, при клике по опции, заменят изображение Идем в папку корневая_папка_сайт/catalog/view/theme/напка с названием вашей темы/template/product/product.tpl Ищем код: <?php echo $footer; ?> и перед ним вставляем : <script> $(‘.form-group label img’).click(function(){ option_id = (parseInt($(this).prev().attr(“name”).replace(/[^0-9]*/g, “”),10)); option_val_id =$(this).prev().val(); $.ajax({ url:’index.php?route=module/smoption/GetImage’, type: ‘post’, data: ‘option_id=’+option_id+’&option_val_id=’+option_val_id, dataType: ‘json’, success:function(json){ popup = json.popup; thumb = json.thumb; $(‘.thumbnails .thumbnail’).first().attr(‘href’,popup) $(‘.thumbnails img’).first().attr(‘src’,thumb) } }); }); </script> После чего сохраняем файл. Вот и все. Теперь если вы все сделали правильно, то при клике на изображение опции, главная картинка товара будет подменяться на картинку опции.