При построении любого сайта в какой-то момент разработчик сталкивается с требованиями поисковой оптимизации. Традиционно она включает в себя построение правильной адресной структуры, исключение из индексации служебных страниц и неинтересных для поискового робота фрагментов, добавление метаинформации для записей. Более расширенный вариант подразумевает специфическое распределение ссылочного веса и борьбу с дубликатами адресов.
Заголовок окна и метатеги
По правилам хорошего тона каждая наша запись должна сопровождаться корректным заголовком окна, описанием и ключевыми словами. То есть приблизительно так:
<head>
<title>Разведение кроликов | Блог о кроликах</title>
<meta name="description" content="Статья о кроликах" />
<meta name="keywords" content="кролики, разведение, питание" />
</head>
В представлениях мы можем добавлять метатеги как явно, так и как скрипты и стили с помощью компонента
clientScript.Достаточно вписать вручную только формирование заголовка. Для этого в классе
CController уже имеется свойство pageTitle, так что мы без проблем можем использовать его в шаблоне:<head>
<title><?php echo CHtml::encode($this->pageTitle); | <?php echo Yii::app()->name; </title>
</head>
А присваивать этот заголовок и добавлять новые теги можем уже в конкретных представлениях:
$this->pageTitle = $model->pagetitle;
Yii::app()->clientScript->registerMetaTag($model->description, 'description');
Yii::app()->clientScript->registerMetaTag($model->keywords, 'keywords');
<h1><?php echo CHtml::encode($model->title); </h1>
Для хранения значений нужно не забыть добавить соответствующие поля в таблицу и модель наших статей. Если отдельные значения записывать не хочется, то можно генерировать их автоматически:
$this->pageTitle = $model->title;
Yii::app()->clientScript->registerMetaTag(strip_tags(mb_substr($model->text, 0, 200, 'utf-8')) . '...', 'description');
Yii::app()->clientScript->registerMetaTag(implode(', ', CHtml::listData($model->tags, 'id', 'name')), 'keywords');
Хотя это не очень хорошо с точки зрения поисковой оптимизации.
Если конструкция с
Yii::app()->clientScript кажется слишком громоздкой, то можно пойти по стопам свойства pageTitle и ввести новые поля description и keywords в базовый контроллер:class Controller extends CController
{
public $menu = array();
public $breadcrumbs = array();
public $description = '';
public $keywords = '';
}
Теперь в представлениях достаточно присваивать значения этим полям:
$this->pageTitle = $model->pagetitle;
$this->description = $model->description;
$this->keywords = $model->keywords;
<h1><?php echo CHtml::encode($model->title); </h1>
А присвоенные значения уже выводить в шаблоне через
clientScript или вручную:<head>
<title><?php echo CHtml::encode($this->pageTitle); | <?php echo Yii::app()->name; </title>
<meta name="description" content="<?php echo CHtml::encode($this->description); ?>" />
<meta name="keywords" content="<?php echo CHtml::encode($this->keywords); ?>" />
</head>
Теперь для всех страниц и новостей можно будет прописывать метатеги.
Дубли значений метатегов
Какие значения
<title> будет у первых четырёх страниц при паджинации блога про кроликов? Приблизительно такие:Разведение кроликов
Разведение кроликов
Разведение кроликов
Разведение кроликов
Все разделы с перелистыванием страниц будут нести одинаковые имена.
Если вы решили закрыть все кроме первой страницы директивой
Disallow в файле robots.txt, то переживать не стоит.Аналогично с описаниями
description категории. В панели вебмастеров Google вы встретите негодование поисковой системы по этому поводу. Из-за повторов заголовка в индексе будет находиться всего одна страница.Самое лучшее решение в случае необходимости индексации всех страниц заключается в написании уникального заголовка для каждой. Это нам врядли под силу, поэтому проще сделать автоматическое добавление номера страницы:
Разведение кроликов
Разведение кроликов - Страница 2
Разведение кроликов - Страница 3
Разведение кроликов - Страница 4
Для этого достаточно получить номер и добавить его к значениям соответствующих полей:
$page = (int)Yii::app()->request->getQuery('page', 1);
$this->pageTitle = $model->pagetitle . ($page > 1 ? ' - Страница ' . $page : '');
$this->description = $model->description . ($page > 1 ? ' - Страница ' . $page : '');
$this->keywords = $model->keywords;
Удобнее переместить всю логику в помощник:
class PageHelper
{
static public function pageString($param = 'page')
{
$page = (int)Yii::app()->request->getQuery($param, 1);
return $page > 1 ? ' - Страница ' . $page : '';
}
}
и использовать его:
$this->pageTitle = $model->pagetitle . PageHelper::pageString('page');
$this->description = $model->description . PageHelper::pageString('page');
$this->keywords = $model->keywords;
Теперь на сайте не будет дублирующихся заголовков.
Это не так уж хорошо решает проблемы ранжирования, но немного помогает.
Установка rel=nofollow для ссылок из блоков кода
Представим, что у нас есть какой-либо блок текста или облако меток со ссылками. Порой для исключения их из индексации и из распределения ссылочного веса страниц этот блок нужно поместить в
<noindex> и всем ссылкам добавить атрибут rel="nofollow".Для выполнения этой работы в представлении лучше всего подходит виджет. Напишем его:
class DNofollowWidget extends СWidget
{
public function init() {
ob_start();
ob_implicit_flush(false);
}
public function run() {
$html = ob_get_clean();
$html = preg_replace('#<a(\s([^>]+))?\srel="[^"]*"#is', '<a$1', $html);
$html = str_replace('<a ', '<a rel="nofollow" ', $html);
echo $html;
}
}
Мы включаем буферизацию, получаем переданный HTML-код, удаляем атрибут
rel у ссылок и подставляем rel="nofollow".Теперь достаточно окружить любое меню данным виджетом:
<noindex>
<?php $this->beginWidget('NofollowWidget');
<?php $this->beginWidget('zii.widgets.CMenu', array(
'items'=>Tag::model()->getMenuList(),
); ?>
<?php $this->endWidget(); ?>
</noindex>
и облако меток или вспомогательное меню не будет больше распылять ссылочный вес.
Определение текущего маршрута
Порой в представлении или шаблоне бывает нужно узнать, из какого действия и какого контроллера это представление вызвали.
В WordPress для определения текущего положения имеются функции
is_front_page(), is_category и некоторые другие. Это удобно использовать, например, чтобы не использовать ссылку с логотипа на главной странице.Для этих целей в Yii удачно подходит параметр
route контроллера. Он содержит текущий маршрут, состоящий из имени модуля, контроллера и действия. Если модулей в системе нет, то он содержит только контроллер и действие. Это свойство можно использовать для условных конструкций в представлениях.Предположим, что у нас есть какой-либо текст в сайдбаре, и мы хотим закрыть его от индексирования для всех страниц, кроме главной.
Для этого в шаблоне мы можем обрамить данный блок конструкцией
<noindex> (или её валидным вариантом <!--noindex-->) для всех маршрутов, кроме SiteController::actionIndex:if ($this->route != 'site/index'): <!--noindex--><? endif;
<p>Приветствуем Вас!</p>
<?php if ($this->route != 'site/index'): <!--/noindex--><? endif;
Аналогично с условным вызовом рассмотренного ранее виджета можно открыть ссылки во вспомогательном меню только на главной странице:
if ($this->route != 'site/index') $this->beginWidget('DNofollowWidget');
<?php $this->beginWidget('zii.widgets.CMenu', array(
'items'=>Category::model()->getMenuList(),
); ?>
<?php if ($this->route != 'site/index') $this->endWidget(); ?>
Можно, например, вообще закрыть какое-либо меню на странице записи блога (то есть по маршруту
blog/post/view) для избежания утечки ссылочного веса.Аналогично можно использовать другие свойства контроллера:
$this->id;
$this->action->id;
$this->module->id;
Что равносильно прямому вызову соответствующих геттеров:
$this->getId();
$this->getAction()->getId();
$this->getModule()->getId();
С их помощью можно легко определить имя текущего контроллера, действия и модуля.
Находясь в виджете переменная
$this ссылается на сам виджет, а не на контроллер. Нужно сначала получить контроллер, а уже потом обращаться к нему:Yii::app()->controller->id;
Yii::app()->controller->action->id;
Yii::app()->controller->module->id;
Указание rel=nofollow для активного пункта меню
При построении любого меню с помощью виджета
CMenu возникает ситуация, что активный элемент остаётся активной ссылкой и по-прежнему ссылается на текущую страницу. Получается бесконечная циклическая ссылка со страницы, ведущая на саму себя.Переопределим и немного дополним метод
CMenu::normalizeItems в нашем новом классе:Yii::import('zii.widgets.CMenu');
class Menu extends CMenu
{
protected function normalizeItems($items,$route,&$active)
{
foreach($items as $i=>$item)
{
if(isset($item['visible']) && !$item['visible'])
{
unset($items[$i]);
continue;
}
if(!isset($item['label']))
$item['label']='';
if($this->encodeLabel)
$items[$i]['label']=CHtml::encode($item['label']);
$hasActiveChild=false;
if(isset($item['items']))
{
$items[$i]['items']=$this->normalizeItems($item['items'],$route,$hasActiveChild);
if(empty($items[$i]['items']) && $this->hideEmptyItems)
{
unset($items[$i]['items']);
if(!isset($item['url']))
{
unset($items[$i]);
continue;
}
}
}
if(!isset($item['active']))
{
if($this->activateParents && $hasActiveChild || $this->activateItems && $this->isItemActive($item,$route))
$active=$items[$i]['active']=true;
else
$items[$i]['active']=false;
}
elseif($item['active'])
$active=true;
if(isset($items[$i]['active']) && $items[$i]['active'])
$items[$i]['linkOptions']['rel']='nofollow';
}
return array_values($items);
}
}
Мы взяли код из исходного метода и дописали пару строк:
if(!isset($items[$i]['active']) && $items[$i]['active'])
$items[$i]['linkOptions']['rel']='nofollow';
в которых активным ссылкам добавляется соответствующий атрибут.
Теперь вместо
zii.widgets.CMenu можно использовать свой класс Menu:$this->beginWidget('Menu', array(
'items'=>Category::model()->getMenuList(),
); ?>
и активная ссылка будет автоматически снабжена атрибутом
rel="nofollow".Noindex для «хвоста» хлебных крошек
При использовании «хлебных крошек» с включённым выводом текущего заголовка у нас на странице будет дублирование текста. Например, так:
<div class="breadcrumbs">
<a href="/">Главная</a> / <a href="/blog">Блог</a> / <span>Размножение кроликов в дикой природе</span>
</div>
<h1>Размножение кроликов в дикой природе</h1>
Одна и та же фраза (заголовок) написана два раза рядом, что не очень хорошо.
Для борьбы с таким контентным «спамом» можно заменить шаблон последнего элемента:
Comments