Уже давно у нас в в Magento был внедрён Wordpress, специальным модулем-плагином для Magento. Но, используя такой метод внедрения, есть проблема с адресами в Wordpress - можно использовать только простые адреса, вида
?p=<номер поста>
,
?cat=<категория>
и т.д. Это связано с тем, что обработку всех адресов берёт на себя Magento, т.е. все адреса должны быть в специальном формате -
<module name>/<controller name>/<controller action>[/<param1>/<value1>/<param2>/<value2>...]
. Если же в Wordpress включить ЧПУ адреса, например, вида
<year>/<month>/<post title>
, такой адрес придёт в Magento, которая попытается найти модуль
<year>
, в нём найти контроллер
<month>
, и выполнить метод
<post title>
. Естественно ничего такого в Magento нет, поэтому будет ошибка "страница не найдена".
Тем не менее, в Magento можно использовать ЧПУ. Для этого надо писать реврайты. Magento сама создаёт по реврайту на каждый продукт и категорию. Rewrite говорит, какой произвольный адрес перенаправить на какой корректный "Magento-адрес". Например, адрес
/benq-2220hd
нужно заменить адресом
/catalog/product/view/id/496
.
Реврайты бывают "внтуренние" и "с редиректом". Внутренние работают внутри :) Т.е. пользователь ввёл один адрес, он видит его в адресной строке, а Magento внутри себя вызывает другой адрес для обработки (более точно - другое действие другого контроллера). Реврайт с редиректом наоборот сразу бросается в глаза - браузер пользователя перенаправляется с одного адреса на другой.
Но даже с реврайтами есть проблема. Модуль, использующийся для интеграции Wordpress в Magento, не позволял использовать ЧПУ. Я его немного переделал, и теперь стало возможным написать адрес вида
blog/index/index/param1/value1/param2/value2
, и эти параметры будут переданы Wordpress, который на их основе выполнит запрос. Т.е. раньше приходилось писать реврайты с редиректом на не-ЧПУ адрес (например, на
/blog/?p=123
), сейчас же можно писать "внутренние" реврайты. А подсмотреть, какие нужно передавать параметры, можно в документации на Wordpress. Т.е., если перейти по адресу
/blog/index/index/p/123
, Wordpress покажет пост с id=123. Кроме p есть другие параметры, cat - для категорий, tag - для тэгов, m - для архивов, и т.д.
Сначала мы писали реврайты для постов вручную. Но это очень неудобно, медленно, не расширяемо и т.д., и я подумал что это дело можно автоматизировать. Сейчас будем этим заниматься :)
Мы напишем модуль, который сам будет создавать реврайты на все посты/категории/тэги/архивы блога/rss. Делать это будет специальная волшебная кнопка "пыщь" в админке :)
Создание модуля
Создадим модуль Blogrewrite в пространстве имён Mage. Что бы сделать свою страницу в админке нужно иметь такой config.xml:
<?xml version="1.0"?>
<config>
<global>
<helpers>
<Blogrewrite>
<class>Mage_Blogrewrite_Helper</class>
</Blogrewrite>
</helpers>
</global>
<admin>
<routers>
<Blogrewrite>
<use>admin</use>
<args>
<module>Mage_Blogrewrite</module>
<frontName>Blogrewrite</frontName>
</args>
</Blogrewrite>
</routers>
</admin>
<adminhtml>
<menu>
<catalog module="catalog">
<children>
<Blogrewrite translate="title" module="Blogrewrite">
<title>Blog Rewrite</title>
<action>Blogrewrite/adminhtml_Blogrewrite</action>
</Blogrewrite>
</children>
</catalog>
</menu>
</adminhtml>
</config>
В секции admin мы говорим, что наш модуль будет доступен по адресу
Blogrewrite
(
www.example.com/Blogrewrite
). Т.о. контроллер нашего модуля -
Mage_Blogrewrite_Adminhtml_BlogrewriteController
.
В секции adminhtml мы добавляем новый пункт меню, в меню Catalog, и говорим, какое действие нужно выполнить, когда пользователь нажмёт на этом пункте меню - модуль
Blogrewrite
, контроллер
Mage_Blogrewrite_Adminhtml_BlogrewriteController
, действие по-умолчанию - index.

Действие index всего лишь отображает блок
Mage_Blogrewrite_Block_Adminhtml_Blogrewrite
:
public function indexAction() {
$this->_initAction();
$this->getLayout()->getBlock('head')
->setCanLoadRulesJs(true);
$this->_addContent($this->getLayout()->createBlock('Blogrewrite/adminhtml_Blogrewrite')
->setCanLoadRulesJs(true));
$this->renderLayout();
}
Этот блок находится в файле
Block/Adminhtml/Blogrewrite.php
:
<?php
class Mage_Blogrewrite_Block_Adminhtml_Blogrewrite extends Mage_Adminhtml_Block_Template {
public function __construct() {
parent::__construct();
$this->setTemplate('Blogrewrite/index.phtml');
}
}
Блок всего лишь выводит шаблонный файл index.phtml, который должен быть в app/design/adminhtml/default/default/template/Blogrewrite/index.phtml. В шаблоне - одна кнопка, которая вызывает действие makerewrites нашего контроллера:
<div class="content-header">
<table cellspacing="0">
<tr>
<td>
<h3 class="head-dashboard"><?php echo $this->__('Blog Rewrite') ?></h3>
</td>
</tr>
</table>
</div>
<form action="<?php print $this->getUrl('*/*/makerewrites'); ?>">
<button type="submit">Make Blog Rewrites</button>
</form>
Итак, модуль готов, он виден в админке, форма с кнопкой отображается. Надо делать собственно добавление реврайтов
Создаём rewrites
Как создать реврайт? Для этого есть модель
core/url_rewrite
, мы можем получить её так:
Mage::getModel('core/url_rewrite');
Можно загрузить данные из базы по некоторым параметрам, нам вполне хватит загрузки по Id Path:
Mage::getModel('core/url_rewrite')->loadByIdPath($idPath);
Если запись с таким Id Path есть в базе, она загрузится в модель, иначе нет :)
После того как мы загрузили данные из базы (или если данных нет), нужно задать параметры реврайта:
Mage::getModel('core/url_rewrite')
->loadByIdPath($idPath)
->setIdPath($idPath)
->setRequestPath($requestPath)
->setTargetPath($targetPath)
->setDescription('Automagically generated, Blogrewrite module')
->setIsSystem(0);
И, наконец, сохраняем реврайт:
->save();
Для удобства вынесем создание/обновление реврайта в отдельную функцию:
protected function _makeRewrite($idPath, $requestPath, $targetPath, $permamentRedirect = false) {
$this->_urlRewrite->loadByIdPath($idPath);
$this->_urlRewrite->setIdPath($idPath)
->setRequestPath($requestPath)
->setTargetPath($targetPath)
->setOptions($permamentRedirect ? 'RP' : '')
->setDescription('Automagically generated, Blogrewrite module')
->setIsSystem(0);
$this->_urlRewrite->save();
}
Ещё я добавил новый параметр $permanentRedirect, который, соответственно, делает реврайт редиректом (по-умолчанию реврайты создаются "внутренними").
Если сохранить реврайт так, как выше, без указания магазина (Store Id), он добавится для всех магазинов. Это хорошо :)
makerewritesAction
Создание реврайтов будет происходить в действии makerewrites контроллера Mage_Blogrewrite_Adminhtml_BlogrewriteController:
<?php
class Mage_Blogrewrite_Adminhtml_BlogrewriteController extends Mage_Adminhtml_Controller_Action {
...
public function makerewritesAction() {
...
}
...
}
?>
Что бы создать реврайт, нужно знать, для чего его создавать. Т.е. нужно перебрать все посты, категории, архивы, и т.д., узнать их адреса, и создать по реврайту, перенаправляя эти адреса на правильные.
Сейчас будет в основном Wordpress часть.
Wordpress
Мы будем пользоваться функциями Wordpress для получения ЧПУ постов/категорий и т.д. Отсюда следует два замечения:
Перебор постов
Для перебора постов можно воспользоваться "циклом", loop. Но по-умолчанию Wordpress загружает лишь несколько постов в цикл. А нам надо все. Это делается вызовом метода query_posts. После этого запускаем цикл, перебирающий все посты. На каждой итерации добавляем/обновляем реврайт:
// Query all published posts
query_posts(array('post_status' => 'publish', 'showposts' => -1));
while (have_posts()) {
the_post();
// get_permalink returns full absolute url. We need to remove domain info.
// Also remove trailing slash, it's important.
// E.g. was http://www.example.com/blog/2009/02/title
// become blog/2009/02/title
$this->_makeRewrite('blog/' . get_the_ID(),
trim(substr(get_permalink(), strlen($blogUrl) - 4), '/'),
'blog/index/index/p/' . get_the_ID());
$posts++;
}
Перебор категорий
Для получения всех категорий достаточно вызвать функцию
get_categories
. Для получения адреса категории есть функция
get_category_link
.
// Update rewrites for categories
$cats = get_categories();
foreach ($cats as $_c) {
$this->_makeRewrite('blog_cat/' . $_c->term_id,
trim(substr(get_category_link($_c->term_id), strlen($blogUrl) - 4), '/'),
'blog/index/index/cat/' . $_c->term_id);
}
Перебор тэгов
Тэги так же доступны вызовом всего одной функции
get_tags
, а для получения адреса есть функция
get_tag_url
:
// Update rewrites for tags
$tags = get_tags();
foreach ($tags as $_t) {
$this->_makeRewrite('blog_tag/' . $_t->term_id,
trim(substr(get_tag_link($_t->term_id), strlen($blogUrl) - 4), '/'),
'blog/index/index/tag/' . $_t->term_id);
}
Перебор архивов
Я посмотрел, как архивы выводятся у нас на сайте. Это задаётся в шаблонном файле app/design/frontend/default/sunnyD/template/blog/menu.phtml, а именно - вызов функции
wp_get_archives('type=monthly')
. Эта функция возвращает уже отформатированный html. К сожалению, нет нормального способа выбрать нужные мне ссылки. Пришлось скопировать код этой функции к себе и немного его переделать:
// Update rewrites for archive (from Wordpress core file wp-includes\general-template.php,
// function wp_get_archives, from line 753)
global $wpdb;
$defaults = array(
'type' => 'monthly', 'limit' => '',
'format' => 'html', 'before' => '',
'after' => '', 'show_post_count' => false,
'echo' => 1
);
$r = wp_parse_args('', $defaults);
$where = apply_filters('getarchives_where', "WHERE post_type = 'post' AND post_status = 'publish'", $r);
$join = apply_filters('getarchives_join', "", $r);
$query = "SELECT DISTINCT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY post_date DESC $limit";
$archive = $wpdb->get_results($query);
if ($archive) {
$afterafter = $after;
foreach ((array) $archive as $_a) {
$this->_makeRewrite('blog_month/' . $_a->year . $_a->month,
trim(substr(get_month_link($_a->year, $_a->month), strlen($blogUrl) - 4), '/'),
'blog/index/index/m/' . $_a->year . $_a->month);
}
}
RSS
Последние ссылки без реврайтов - подписка на новости. Их две - подписка на новые посты, и новые комментарии.
$feeds = trim(substr(get_bloginfo('rss2_url'), strlen($baseUrl)), '/');
$feedsComments = trim(substr(get_bloginfo('comments_rss2_url'), strlen($baseUrl)), '/');
$this->_makeRewrite('blog_feeds', $feeds, 'blog/index/index/feed/rss2');
$this->_makeRewrite('blog_comments_feeds', $feedsComments, 'blog/index/index/feed/comments-rss2');
Заключение
Итак, мы сделали по реврайту на посты, категории, тэги, месячные архивы и rss. Теперь пользователи будут видеть ЧПУ, относящиеся к блогу, в адресной строке браузера. Так же адреса, связанные с Wordpress, выводимые на странице магазина, будут ЧПУ (т.к. это указано в настройках Wordpress).
Но всё же есть одна неприятность. Страницы просмотра категории, или тэга, могут иметь несколько страниц. Например, blog/my-category/page/2. Такой адрес выдаст страницу "не найдено" :(