Оу, оказывается здесь даже есть целый один пост про разработку под Magento. Мне тоже есть что сказать. Интересно, будет ли это кому-нибудь интересно…
Итак, задача — добавить галочку «Получать новости» к одному из шагов чекаута (checkout — «проход через кассу»).
Сразу стало ясно, что придётся писать новый модуль, т.к. модификации дизайнерских файлов здесь не хватит. Как можно добавить своё поле к какому-либо шагу чекаута, можно подсмотреть у модулей Desitex Checkoutnewsletter (он добавляет галочку «подписать на новости» во второй шаг, где надо указать billing address) и у Biebersdorf CustomerOrderComment (он добавляет поле для добавления комментария в последний шаг — страницу подтверждения заказа).
Опускаю процесс копания в указанных модулях и поиска решения :) В итоге, что бы всё получилось, нужно:
Прежде всего создадим модуль. Пусть он будет в пространстве имён
Сначала нужно сказать Magento, что наш модуль есть — создаём файл
Согласно XML, модуль активен и находится в пуле local.
Далее создаём папку где будет находится новый модуль —
Без хелпера модуль не будет работать как надо, а будет вместо этого падать. Поэтому дадим Magento хелпер, пусть и пустой — файл
Файл, рисующий нужную страницу чекаута —
Да, теперь мы видим нашу галочку на странице выбора метода оплаты. Но почему же она неактивна? А потому что она сделана неактивной JS кодом, расположенным в конце файла:
Не разбирался зачем он нужен, но в данном случае он делает неактивными все тэги <input>. Выходит, нам надо активировать нашу галочку после выполнения этого кода:
Теперь галочка стала активна, идём дальше.
Я подсмотрел как это делает Checkoutnewsletter. В конфигурационном файле модуля есть строки, которые видимо перехватывают действия, связанные со всем чекаутом, всеми его страницами:
Стандартный класс
Очевидно, действие
Мы поступим подобным образом — сделаем класс, отнаследуем его от стандартного, и переопределяем только один метод, возникающий при сохранении формы на нашей странице. Метод будет сохранять значение нашей галочки. Класс поместим в файл
Здесь меня немного настиг ступор. Значение галочки находится среди значений формы, но из текущего места у меня нет доступа к этим переменным. Т.е. доступа к объекту Magento Request, хранящему все GET и POST переменные. Доступны разные интересные объекты типа Quote, Checkout и т.д., с разным интересными данными, но не значениями формы. Я почти отчаялся, соображая что переписывать код ядра очень плохо, но потом вспомнил, что это Php, а значит в любом месте доступны переменные $_GET и $_POST :) Проблема была решена.
Теперь скажем Magento, что бы вместо стандартного класса брал наш. Редактируем
Здесь готово. Только видимо есть одно ограничение — переопределить стандартный класс может только один модуль. Мой метод не вызывался, пока я не убрал переопределение у модуля
В отличии от предыдущего «события», оформление заказа это «настоящее» событие
Здесь мы указали какой метод у какого класса вызвать (
Этот код я взял из модуля Checkoutnewsletter, только переделал его что бы он работал. К счастью в Magento есть класс, позволяющий подписывать пользователей на новости. По-сути всё что нужно сделать — вызвать
В итоге получился небольшой модуль, выполняющий поставленную задачу.
Итак, задача — добавить галочку «Получать новости» к одному из шагов чекаута (checkout — «проход через кассу»).
Сразу стало ясно, что придётся писать новый модуль, т.к. модификации дизайнерских файлов здесь не хватит. Как можно добавить своё поле к какому-либо шагу чекаута, можно подсмотреть у модулей Desitex Checkoutnewsletter (он добавляет галочку «подписать на новости» во второй шаг, где надо указать billing address) и у Biebersdorf CustomerOrderComment (он добавляет поле для добавления комментария в последний шаг — страницу подтверждения заказа).
Опускаю процесс копания в указанных модулях и поиска решения :) В итоге, что бы всё получилось, нужно:
- Модифицировать дизайнерский файл, который рисует нужную страницу чекаута — добавить туда галочку;
- Каким-либо образом подписаться на событие «сохранение страницы чекаута», что бы:
- Запомнить состояние галочки в текущей сессии
- Обработать событие «оформление заказа», возникающее когда пользователь уже оформил заказ, перед тем как сайт перенаправит его на сайт для оплаты (например, PayPal, AlliedWallet). Здесь надо извлечь сохранённое значение из сессии и подписать пользователя на рассылку, если он этого хочет.
Создание модуля
Прежде всего создадим модуль. Пусть он будет в пространстве имён
Mage
, а называться будет NewsletterSubscribe
.Сначала нужно сказать Magento, что наш модуль есть — создаём файл
Mage_NewsletterSubscribe.xml
в папке app/etc/modules
:<?xml version="1.0"?> <config> <modules> <Mage_NewsletterSubscribe> <active>true</active> <codePool>local</codePool> </Mage_NewsletterSubscribe> </modules> </config>
Согласно XML, модуль активен и находится в пуле local.
Далее создаём папку где будет находится новый модуль —
app/code/local/Mage/NewsletterSubscribe
, и файл конфигурации config.xml
в папке app/code/local/Mage/NewsletterSubscribe/etc
:<?xml version="1.0"?> <config> <global> <helpers> <newslettersubscribe> <class>Mage_NewsletterSubscribe_Helper</class> </newslettersubscribe> </helpers> </global> </config>
Без хелпера модуль не будет работать как надо, а будет вместо этого падать. Поэтому дадим Magento хелпер, пусть и пустой — файл
Data.php
в папке Helper
:<?php class Mage_NewsletterSubscribe_Helper_Data extends Mage_Core_Helper_Abstract { }
Модификация страницы чекаута
Файл, рисующий нужную страницу чекаута —
app\design\frontend\default\sunnyD\template\checkout\onepage\payment\methods.phtml
. Добавляем галочку:... <?php /* bof Subscribe for newsletter checkbox */ ?> <dt>Join Our Mailing List</dt> <dd> <input type="checkbox" name="NewsletterSubscribe" id="NewsletterSubscribe" checked="checked" /> <label for="NewsletterSubscribe"><?php echo Mage::helper('newslettersubscribe')->__('I would like to receive the Century Supplements newsletter') ?></label> </dd> <?php /* eof Subscribe for newsletter checkbox */ ?> ...
Да, теперь мы видим нашу галочку на странице выбора метода оплаты. Но почему же она неактивна? А потому что она сделана неактивной JS кодом, расположенным в конце файла:
<script type="text/javascript">payment.init();</script>
Не разбирался зачем он нужен, но в данном случае он делает неактивными все тэги <input>. Выходит, нам надо активировать нашу галочку после выполнения этого кода:
<script type="text/javascript">payment.init();</script> <script type="text/javascript">$('NewsletterSubscribe').disabled = false;</script>
Теперь галочка стала активна, идём дальше.
Событие «сохранение страницы чекаута»
Я подсмотрел как это делает Checkoutnewsletter. В конфигурационном файле модуля есть строки, которые видимо перехватывают действия, связанные со всем чекаутом, всеми его страницами:
<?xml version="1.0"?> <config> ... <global> <models> <checkout> <rewrite> <type_onepage>Desitex_Checkoutnewsletter_Model_Checkout_Type_Onepage</type_onepage> </rewrite> </checkout> ... </config>
Стандартный класс
Mage_Checkout_Model_Type_Onepage
заменяется классом модуля Desitex_Checkoutnewsletter_Model_Checkout_Type_Onepage
(который наследуется от оригинального класса Mage_Checkout_Model_Type_Onepage
). В этом классе переопределён всего один метод:<?php class Desitex_Checkoutnewsletter_Model_Checkout_Type_Onepage extends Mage_Checkout_Model_Type_Onepage { public function saveBilling($data, $customerAddressId) { if (isset($data['is_subscribed']) && !empty($data['is_subscribed'])){ $this->getCheckout()->setCustomerIsSubscribed(1); } else { $this->getCheckout()->setCustomerIsSubscribed(0); } return parent::saveBilling($data, $customerAddressId); } }
Очевидно, действие
saveBilling
возникает когда пользователь переходит со страницы ввода billing address (нажимает кнопку Continue). Здесь модуль сохраняет значение своей галочки «подписываться ли на новости» в текущей сессии (или чекауте...). После этого вызывает оригинальный метод стандартного класса.Мы поступим подобным образом — сделаем класс, отнаследуем его от стандартного, и переопределяем только один метод, возникающий при сохранении формы на нашей странице. Метод будет сохранять значение нашей галочки. Класс поместим в файл
Model/Onepage.php
.:<?php class Mage_NewsletterSubscribe_Model_Onepage extends Mage_Checkout_Model_Type_Onepage { public function savePayment($data) { if (isset($_POST['NewsletterSubscribe'])){ $this->getCheckout()->setNewsletterSubscribe((bool) $_POST['NewsletterSubscribe']); } else { $this->getCheckout()->setNewsletterSubscribe(false); } return parent::savePayment($data); } }
Здесь меня немного настиг ступор. Значение галочки находится среди значений формы, но из текущего места у меня нет доступа к этим переменным. Т.е. доступа к объекту Magento Request, хранящему все GET и POST переменные. Доступны разные интересные объекты типа Quote, Checkout и т.д., с разным интересными данными, но не значениями формы. Я почти отчаялся, соображая что переписывать код ядра очень плохо, но потом вспомнил, что это Php, а значит в любом месте доступны переменные $_GET и $_POST :) Проблема была решена.
Теперь скажем Magento, что бы вместо стандартного класса брал наш. Редактируем
etc/config.xml
:<?xml version="1.0"?> <config> <global> <models> <checkout> <rewrite> <type_onepage>Mage_NewsletterSubscribe_Model_Onepage</type_onepage> </rewrite> </checkout> </models> ... </config>
Здесь готово. Только видимо есть одно ограничение — переопределить стандартный класс может только один модуль. Мой метод не вызывался, пока я не убрал переопределение у модуля
Checkoutnewsletter
. Т.е. это не обычное событие, на которое может подписаться произвольное количество слушателей. Потенциальные трудноотлаживаемые проблемы в будущем :(Событие «оформление заказа»
В отличии от предыдущего «события», оформление заказа это «настоящее» событие
checkout_type_onepage_save_order
. Что бы подписаться на него нужно изменить конфиг модуля etc/config.xml
:<?xml version="1.0"?> <config> <global> ... <events> <checkout_type_onepage_save_order> <observers> <mage_newslettersubscribe_observer> <type>singleton</type> <class>newslettersubscribe/observer</class> <method>onOrderSave</method> </mage_newslettersubscribe_observer> </observers> </checkout_type_onepage_save_order> </events> </global> </config>
Здесь мы указали какой метод у какого класса вызвать (
Mage_NewsletterSubscribe_Model_Observer::onOrderSave
), когда пользователь оформит заказ. Теперь создадим этот класс и метод — файл /Model/Observer.php
:<?php class Mage_NewsletterSubscribe_Model_Observer extends Mage_Core_Helper_Abstract { public function onOrderSave($observer) { $isCustomerSubscribed = (bool) Mage::getSingleton('checkout/session')->getNewsletterSubscribe(); if ($isCustomerSubscribed) { $quote = $observer->getEvent()->getQuote(); $session = Mage::getSingleton('core/session'); try { $status = Mage::getModel('newsletter/subscriber')->subscribe($quote->getBillingAddress()->getEmail()); if ($status == Mage_Newsletter_Model_Subscriber::STATUS_NOT_ACTIVE){ $session->addSuccess(Mage::helper('checkoutnewsletter')->__('Confirmation request has been sent regarding your newsletter subscription')); } } catch (Mage_Core_Exception $e) { $session->addException($e, Mage::helper('checkoutnewsletter')->__('There was a problem with the newsletter subscription: %s', $e->getMessage())); } catch (Exception $e) { $session->addException($e, Mage::helper('checkoutnewsletter')->__('There was a problem with the newsletter subscription')); } } return $this; } }
Этот код я взял из модуля Checkoutnewsletter, только переделал его что бы он работал. К счастью в Magento есть класс, позволяющий подписывать пользователей на новости. По-сути всё что нужно сделать — вызвать
Mage::getModel('newsletter/subscriber')->subscribe(<user email>);
Итог
В итоге получился небольшой модуль, выполняющий поставленную задачу.