Я думаю, каждый друпалер использует на своих сайтах модули Views и Pathauto. Да, на некоторых сайтах можно без них обойтись, но, фактически, оба этих модуля давно входят в джентльменский набор при запуске нового сайта, поэтому нет нужды объяснять, для чего они используются. Некоторое время назад я столкнулся с проблемой, которая не то, чтобы была критичной, но беспокоила моё эстетическое чувство и не давала мне считать свою работу выполненной. Дело было связано с тем, что views и pathauto по-разному представляют пользователю одни и те же вещи — как-то заголовки материалов и имена пользователей.
До некоторых пор это не особенно бросается в глаза, но представим себе следующую ситуацию — у вас есть вьюс, с помощью которого вы выводите список материалов по адресу example.net/content. Если задан аргумент, вьюс выводит только материалы за авторством конкретного пользователя. Самый простой способ сделать это — указать в качестве аргумента User:Uid. В результате получается, что при запросе вида example.net/content/1 будут отображаться все материалы, созданные суперпользователем, при запросе example.net/content/150, пользователем с uid 150 и так далее. Это хороший способ, у которого есть один минус — URL example.net/content/150 «некрасив» и мало, о чём говорит стороннему наблюдателю (хотя я часто использую такие конструкции для вьюсов, «закрытых» для пользователя).
Сделать URL «более говорящим» можно достаточно легко, если использовать в качестве аргумента не User:Uid, а User:Name. В результате мы получаем красивые адреса вроде example.net/content/ingumsky, едва взглянув на которые можно понять, что содержимое страницы будет иметь отношение к тому или иному пользователю (другое дело, какое отношение они к нему имеют :). В принципе, такого решения уже вполне достаточно, если бы не одно «но»...
Например, представим, что на нашем сайте есть пользователи с именами Ingumsky, John Smith, jack_brown, S.White, He is Awesome, Паша. Мы включаем на сайте модуль pathauto и с его помощью создаём для каждого пользователя красивые адреса страниц профилей: example.net/users/ingumsky, example.net/users/john-smith и так далее. и здесь обнаруживается, что в адресах страниц пользователей, на страницах блогов и так далее синонимы имён пользователей отличаются от того, что принимает views в качестве аргумента. Сравните, как выглядят аргументы во Views и синонимы Pathauto [в квадратных скобках] для каждого из перечисленных выше пользователей (обращаю внимание, что в настройках аргумента включены опции «lowercase» и «turn spaces into dashes»:
example.net/content/ingumsky — материалы пользователя Ingumsky [ingumsky]
example.net/content/john-smith — материалы пользователя John Smith [john-smith]
example.net/content/jack_brown — материалы пользователя jack_brown [jackbrown]
example.net/content/s.white — материалы пользователя S.White [swhite]
example.net/content/he-is-awesome — материалы пользователя He is Awesome [he-awesome]
example.net/content/паша — материалы пользователя Паша [pasha]
Как видно, Views принимает в качестве аргумента именно имя пользователя, которое при указанных чуть выше настройках, просто переведено в нижний регистр и избавлено от пробелов (они заменены на дефисы). Но Pathauto при создании синонима вносит больше изменений в исходный текст. В результате в тех случаях, когда в именах пользователей встречаются знаки подчёркивания, точки, кириллица или даже некоторые английские слова, которые неважны для SEO (is, are и тому подобное), аргумент отличается от того синонима, который нам предлагает Pathauto.
Как я написал в самом начале записи, это не критичная проблема, но она... м-м-м... раздражает. Хочется, чтобы всё было красиво и «единообразно». Вопрос состоял в следующем: Как добиться того, чтобы скармливаемый вьюсу аргумент всегда выглядел так же, как выглядит ник в URL, созданном Pathauto?
Первоначальный поиск по этим вашим энтернетам не дал нужного мне результата, и я даже обратился за помощью к сообществу. Тот вариант решения, который мне посоветовали, показался мне излишне сложным для данного случая — к тому моменту я уже понял, в каком направлении нужно копать, и вскоре решение было найдено. Так как я уверен, что данная задача наверняка интересовала не только меня, предлагаю своё решение и вам:
1. В качестве аргумента мы будем использовать не User: Name, а User: Uid, пусть это прозвучит и несколько странно Теперь нам надо сделать так, чтобы из аргумента, который принимает наш вьюс, мы смогли вытащить uid;
2. В качестве «Что делать, если аргумент не указан» ставим «Показывать всё»;
3. Проверять аргумент мы будем с помощью php, поэтому смело выбираем в списке валидаторов «PHP Code»;
4. В текстовом поле пишем:
<?php
// Проверяем, не число ли дано в качестве аргумента
// Если число, аргумент подходит.
// Это особенно удобно, чтобы проверять, работает ли ваш вьюс с таким валидатором
if (is_numeric($argument)) {
return TRUE;
}
// Смотрим, не указано ли в качестве аргумента «all»
// Если указано, аргумент подходит, и мы покажем все материалы
elseif ($argument == 'all') {
return TRUE;
} else {
// У меня синонимы профилей пользователей выглядят как users/username
// Здесь мы подставляем наш аргумент, чтобы получить такой же синоним
// В вашем случае может потребоваться заменить 'users/' на что-то другое
$dru_alias = 'users/'.$argument;
// С помощью функции drupal_lookup_path ищем, каков внутренний адрес,
// соответствующий синониму — в нашем случае получится 'user/uid'
$dru_path = drupal_lookup_path('source',$dru_alias);
// Разбираем внутренний адрес, вытаскивая из него uid
$uid = substr($dru_path, strrpos($dru_path, '/') + 1);
// Отдаём uid в качестве аргумента и сообщаем, что всё верно.
$handler->argument = $uid;
return TRUE;
}
?>
5. Profit!
В результате мы получаем views, который правильно обрабатывает аргументы, если они выглядят так же, как наши синонимы. Этот вьюс принимает так же и непосредственно uid в качестве аргумента. Это удобно, но, если у вас есть пользователи с числовыми именами, лучше избавиться от первой части условия в if.
Возможно, гуру Друпал засмеют меня и скажут, что решение было очевидным. Что ж, возможно и так. Я до этого решения дошёл далеко не сразу да и то только по наводке комментаторов с drupal.org, благодаря которым я понял, что надо смотреть в сторону валидатора аргумента. Надеюсь, что это решение поможет кому-нибудь ещё.
Комментарии
Красава, все бы делились хотябы частью освоенного в подробном (подробном) виде, а то ведь многие даже [РЕШЕНО] написать не могут.
з.ы. Ливер чемпион ???
а что за вид подробный (подробный)?
а так, да, решения всегда очень интересно и полезно почитать, спасибо.
каким образом эти урлы создаются?
Спасибо, сталкивался с этой проблемой!
Тему в закладки.
Давно таких статей не было... и туда, и туда надо поместить...
Ливер — это печень А так Ливерпуль — чемпион, да. В перспективе...
Представление вида «page» c заданным путём content. Если в качестве аргумента принимать User:Name, будут формироваться URL example.net/content/username.
All
Спасибо за отзывы. Рад, что материал пригодился.
А при включенном кэшировании работать будет?
При включённом кешировании чего?
Вот уж что-то, а очевидные решения с вьюсами часто рождаются после нескольких дней траходрома.
Кстати, будут ли актуальны следующие темы с views: Двухуровневый каталог с картинками и блекджеком, Views+точки на карте?
Второе актуально.
Если на сайте используется модуль subpath_alias, то в настройках views будет достаточно указать путь страницы "user/%/content"
тогда материалы пользователя будут доступны точно за такой же страницей что и профиль с приставкой "/content" (то есть, например, для пользователя S.White это users/swhite и users/swhite/content)
не знаю как в Вашем случаи, но в большинстве этот вариант и проще и лучше
enjoy your Drupal
Это точно.
Думаю, будут актуальны. views+точки на карте у меня есть тут: http://www.liverbird.ru/pubs Решение с темизацией шаблонов у меня, наверное, не самое изящное, но работает.
Я в курсе, но, есть причины не пользоваться им. Во-первых, это ещё один модуль — зачем ставить ещё один модуль, решающий общую задачу, когда требуется решить одну небольшую частную задачу? Во-вторых, с точки зрения семантики, «ваш» алиас и «мой» дают разный по смыслу результат: у меня, например, есть топ лучших материалов:
http://example.net/top
Я хочу сделать ещё топ для каждого пользователя:
http://example.net/users/ingumsky/top — выглядит неплохо, но конкретно для top не очень хорошо.
У меня сделано так:
http://example.net/top/by/ingumsky — по-моему, так лучше.
Только Pathauto ИМХО лучше не использовать. Лучше custom_url_rewrite_inbound()/inbound, тогда алиасов будет мало, и можно будет все подгрузить за один раз.
В моём случае pathauto используется по всему сайту, так что без него всё равно никуда. Или я вас неправильно понял?
Бывает наверное, когда сложно обойтись без pathauto, но надо стараться Пример: захотелось заменить node/xx на более понятное content/xx. Можно было использовать pathauto, но я сделал с помощью custom_url_rewrite_inbound()/inbound. В результате алиасов всего несколько, и можно сделать патч для drupal_lookup_path - загружать все алиасы один раз. Получим один запрос вместо 30-40 (столько раз на страницу у меня вызывается drupal_lookup_path()). Большого прироста наверное не будет (запросы простые), но все равно приятно.
P.S. Сам патч еще не сделал - просто такая идея возникла.
А как вы сделали custom_url_rewrite_inbound ? Если не сложно, выложите пожалуйста. Или ссылочку )
<?php
// Изменяем входящие пути (от пользователя).
function custom_url_rewrite_inbound(&$result, $path, $path_language) {
if (preg_match('|^content/(.+)|i', $result, $matches)) {
$result = 'node/'. $matches[1];
}
} // Изменяем исходящие пути (ссылки, генерируемые Друпалом).
function custom_url_rewrite_outbound(&$path, &$options, $original_path) {
if (strtolower(substr($path, 0, 4)) == 'node') {
if (preg_match('|^node/+(.+)|i', $path, $matches)) {
$path = 'content/'. $matches[1];
}
else if (preg_match('|^node/*$|i', $path)) {
$path = '';
}
}
}
?>
Потом еще потестирую и может доработаю. Я это по-быстрому написал и пока оставил так.
А вот и ссылочка: http://api.drupal.org/api/drupal/developer--hooks--core.php/function/cus...
Большое спасибо, понял!
Идея в том чтобы отказаться от запросов (и к БД) в drupal_lookup_path, а ссылки менять на лету на основе правил преобразования? Кстати, в семерке нет такого хука, кажется.
Очень актуально!
«Наоборот - в 7-ке это сделали хуками, что очень удобно»
Точно, hook_url_inbound_alter. Спасибо за подробные ответы!
2 shp@drupal.org
Вот модуль Ильи Азарова path_extend с этим функционалом.
Пасибо, полезно. Свежий модуль, буквально на днях вышел )