
Сразу стало ясно, что придётся писать новый модуль, т.к. модификации дизайнерских файлов здесь не хватит. Как можно добавить своё поле к какому-либо шагу чекаута, можно подсмотреть у модулей 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>);
Статья классная доходчивая, автор молодец!!!, вот только один вопросик - что такое Magento?
ОтветитьУдалить