Оу, оказывается здесь даже есть целый один пост про разработку под 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>);Итог
В итоге получился небольшой модуль, выполняющий поставленную задачу.
