Как создать страницы на основе 2х словарей без создания нод?

Аватар пользователя VasyOK VasyOK 28 марта в 17:23

Допустим сайт объявлений с 2-мя словарями: города и услуги.
Есть ли в Drupal 8 возможность создания страниц на основании этих словарей?

Не в смысле создать ноды и указывать в них 2 словаря, а в мысле, что создаем новый термин Грузоперевозки. И сразу же есть страницы:
Грузоперевозки в Москве
Грузоперевозки в Твери
Грузоперевозки в Саратове

Если добавить новый регион - добавятся все услуги в этом регионе:
Выгул собак в Регионе
Проведение праздников в Регионе

Комментарии

Аватар пользователя ivnish ivnish 28 марта в 17:24

Ох уже эти SEO-задачки

Заюзай хук создания нового термина. Там получай список городов и создавай ноды в цикле

Аватар пользователя Semantics Semantics 28 марта в 19:24

Есть ли в Drupal 8 возможность создания страниц на основании этих словарей?

Аргументы умел вьюс даже в пятом друпале.
А в шестом и панели.
В седьмом умели почти все.
А восьмой умеет всё.

Жди девятого, он сам за тебя всё сделает

Аватар пользователя marassa marassa 28 марта в 19:39

Не очень понятно что понимается под "страницами".
Делается вьюха с двумя контекстными фильтрами. В принципе вызов вьюхи с любым сочетанием значений этих двух фильтров есть новая "страница". То есть создавать-то ничего и не надо, кроме одной вьюхи и двух словарей. Ну и само собой самих предложений услуг в виде типа материала с двумя ER-полями на эти словари.

Аватар пользователя gun_dose gun_dose 29 марта в 1:20

Да, нужна вьюха с двумя аргументами. Но если нужны ещё и красивые урлы, то можно к примеру в препроцессе вьюхи менять урлы на алиасы, создавая их, если их нет.

PS: не факт, что красивые урлы сейчас хоть как-то влияют на сео, т.к. во всю идут разговоры о том, чтобы в браузере по умолчанию показывать только домен.

Аватар пользователя vlucas vlucas 29 марта в 12:06

Но есть же ещё хлебные крошки, которые на основе урлов по умолчанию строятся.
Плюс в браузере - да. Но ссылку же можно копировать и куда-то вставить, например в документ и там она уже вся видна будет.

Аватар пользователя gun_dose gun_dose 29 марта в 20:26

На крошки можно свой билдер сделать и воротить, что угодно.

Что касается того, что ссылка всё же видна при копировании - это вообще пофиг. Среди людей старше сорока большинство даже не знает, что такое ссылка. А каждый третий не знает, что такое браузер, хоть и пользуется им каждый день. И вот реально очень сомнительно, что поисковики могут учитывать при ранжировании столь малозначительный фактор, который в принципе может увидеть только кучка задротов вроде нас.

Аватар пользователя VasyOK VasyOK 7 апреля в 17:21

Допустим мне нужно не столько фильтр, сколько страницы. Вот вьюха. Выводит материалы по термину (список заказов в городе).

Все работает, если перейти на адрес
sitename.com/zakazy/3257
что сделать, чтобы работало по адресу
sitename.com/zakazy/moskva
?

Аватар пользователя VasyOK VasyOK 7 апреля в 18:29

Может и поможет, но пока не понял как. Установил. Во вьюхе выше указал:

sitename.com/zakazy/moskva не находит ничего Sad

Аватар пользователя marassa marassa 7 апреля в 18:43

Значит не повезло...
В описании упомянуто, что должно работать с nodes, а про термины ничего не сказано. При этом в коде не вижу никаких завязок на node, то есть по идее должно бы работать...

Аватар пользователя marassa marassa 7 апреля в 19:16

Я глянул в код модуля - там ошибка дурацкая. Попробуй заменить содержимое файла src/Plugin/views/argument_validator/UrlPath.php вот на этот код:

<?php

namespace Drupal\views_url_path_arguments\Plugin\views\argument_validator;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\views\Plugin\views\argument_validator\ArgumentValidatorPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Convert an entity id to its url path.
 *
 * @ViewsArgumentValidator(
 *   id = "views_url_path",
 *   title = @Translation("Entity ID from URL path alias")
 * )
 */

class UrlPath extends ArgumentValidatorPluginBase {

  /**
   * The route match.
   *
   * @var \Drupal\Core\Routing\RouteMatchInterface
   */

  protected $routeMatch;

  /**
   * Language manager for retrieving the default langcode when none is specified.
   *
   * @var \Drupal\Core\Language\LanguageManagerInterface
   */

  protected $languageManager;

  /**
   * Constructs a new Tid instance.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $pluginId
   *   The plugin_id for the plugin instance.
   * @param mixed $pluginDefinition
   *   The plugin implementation definition.   *
   * @param \Drupal\Core\Routing\RouteMatchInterface $routeMatch
   *   The route match.
   * @param \Drupal\Core\Language\LanguageManagerInterface $languageManager
   *   The language manager.
   */

  public function __construct(array $configuration, $pluginId, $pluginDefinition, RouteMatchInterface $routeMatch, LanguageManagerInterface $languageManager) {
    parent::__construct($configuration, $pluginId, $pluginDefinition);

    $this->routeMatch = $routeMatch;
    $this->languageManager = $languageManager;
  }

  /**
   * {@inheritdoc}
   */

  public static function create(ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition) {
    return new static(
      $configuration,
      $pluginId,
      $pluginDefinition,
      $container->get('current_route_match'),
      $container->get('language_manager')
    );
  }

  /**
   * {@inheritdoc}
   */

  protected function defineOptions() {
    $options = parent::defineOptions();

    $options['provide_static_segments'] = ['default' => TRUE];
    $options['segments'] = ['default' => ''];

    return $options;
  }

  /**
   * {@inheritdoc}
   */

  public function buildOptionsForm(&$form, FormStateInterface $formState) {
    $form['provide_static_segments'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Provide a static url segment as the prefix to the alias?'),
      '#default_value' => $this->options['provide_static_segments'],
    ];
    $form['segments'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Segments'),
      '#description' => $this->t('Without leading and/or trailing slashes.'),
      '#default_value' => $this->options['segments'],
      '#states' => [
        'visible' => [
          ':input[name="options[argument_default][views_url_path][provide_static_segments]"]' => ['checked' => TRUE],
        ],
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */

  public function validateArgument($argument) {

    // Is it already the entity id?
    if (ctype_digit($argument)) {
      $this->argument->argument = $argument;
      return TRUE;
    }

    $alias = '/';
    if ($this->options['provide_static_segments']) {
      $alias .= $this->options['segments'] . '/';
    }

    $alias .= $argument;
    $langcode = $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_URL)->getId();

    $canonicalPath = '';
    if (\Drupal::hasService('path_alias.repository')) {

      if ($alias = \Drupal::service('path_alias.repository')->lookupByAlias($alias, $langcode)) {
        $canonicalPath = $alias['path'];
      }
    }
    else {

      $canonicalPath = \Drupal::service('path.alias_storage')->lookupPathSource($alias, $langcode);
    }

    $entity_id = substr($canonicalPath, strrpos($canonicalPath, '/') + 1);
    if (ctype_digit($entity_id)) {
      $this->argument->argument = $entity_id;
      return TRUE;
    }
    else {
      return FALSE;
    }
  }

}

Если заработает, сделаю патч и закину на орг.

Аватар пользователя marassa marassa 7 апреля в 20:14

Послал патч на орг и его немедленно закомиттили в dev, так что теперь уж проще установить dev.

Аватар пользователя VasyOK VasyOK 7 апреля в 22:50

Поставил дев версию. update.php/selection пишет:

The website encountered an unexpected error. Please try again later.
Drupal\Core\Database\DatabaseExceptionWrapper: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'mydb.url_alias' doesn't exist: SELECT ua.source AS source, ua.alias AS alias FROM {url_alias} ua; Array ( ) in views_url_alias_rebuild() (line 214 of modules\contrib\views_url_alias\views_url_alias.module).

views_url_alias_rebuild()
call_user_func_array('

views_url_alias_rebuild', Array) (Line: 403)
Drupal\Core\Extension\ModuleHandler->invokeAll('
rebuild') (Line: 1079)
drupal_flush_all_caches() (Line: 350)
Drupal\system\Controller\DbUpdateController->selection(Object) (Line: 164)
Drupal\system\Controller\DbUpdateController->handle('
selection', Object)
call_user_func_array(Array, Array) (Line: 115)
Drupal\Core\Update\UpdateKernel->handleRaw(Object) (Line: 76)
Drupal\Core\Update\UpdateKernel->handle(Object) (Line: 28)

Изменений не вижу. На моем последнем скрине никаких галок снимать ставить не надо?
Контекстный фильтр и путь вьюхи правильно прописаны?

Аватар пользователя marassa marassa 8 апреля в 8:39
1

Упс, значит совет поставить дев был неправильным - там что-то ещё наколбашено... А если всё же поставить мой патч на релиз? Там всего-то нужно убрать несколько символов в двух местах, из которых нам важно одно.
У меня работает. Настройки правильные за исключением последней галки "Provide a static URL segment", которая кстати может и быть причиной неработы! Я бы прежде всего попробовал убрать дев, вернуть релиз и для начала просто убрать лишнюю галку. На свежей версии ядра может заработать и без моего патча (я пробовал на 8.6 где всё немножко иначе с алиасами).

Аватар пользователя VasyOK VasyOK 15 апреля в 15:36

Спасибо, marassa!

Ваш патч уже в последней желтой версии и все работает!

Попробовал этим модулем сделать вьюху берущую аргументы от 2-х словарей (тип работ и регион).
Путь вьюхе оставил тот же: zakazy/%taxonomy_term и ввожу еще один аналогичный контекстный фильтр.

После этого становятся доступными страницы:
zakazy/moskva
zakazy/gruzopervozki
zakazy/moskva/gruzopervozki
zakazy/gruzopervozki/moskva

Надеюсь эту возможность не уберут в следующих версиях.

Аватар пользователя voviko voviko 7 апреля в 19:11

Недавно связывал два словаря. Ссылка типа /brands/alias_brand/alias_catalog
В общем дали мне вьюху где через агрегацию и php поле были сформированы ссылки:


<?php print drupal_get_path_alias("taxonomy/term/$row->tid"); ?>
<?php
$full_url 
drupal_get_path_alias("taxonomy/term/$row->tid_1"); 
$url_array explode('/',$full_url);
echo 
'/'.$url_array['1'];
if (!empty(
$url_array['2'])) {echo '/'.$url_array['2'];}
if (!empty(
$url_array['3'])) {echo '/'.$url_array['3'];}
if (!empty(
$url_array['4'])) {echo '/'.$url_array['4'];}
?>

А так как views работать любит с tid, то потребовалось по алиасу выловить ИД термина и вместо алиаса с сформированной ссылки в аргумент поставить ИД термина

<?php
/*
 * hook_views_pre_view
 */
function custom_views_pre_view(&$view, &$display_id, &$args)
{
    if (
$view->name == 'brand_facet' && $view->current_display == 'page_2') {
        
$arg arg();
        
$view->args[0] = custom_viewsarg_viewsbrand_brand($arg);
        
$view->args[1] = custom_viewsarg_viewsbrand_catalog($arg);
    }
}

//Поиск ид термина для аргумента 1 /brands/%/%
function custom_viewsarg_viewsbrand_brand($arg) {
    
$brand_sin 'brands/'.$arg[1];
    foreach (
$arg as $ar_k) {
        if (!
preg_match("#^[aA-zZ0-9\-_]+$#",$ar_k)) {
            exit(
'error adress');
        }
    }
    
$result db_select('url_alias''n')
        ->
fields('n', array('source'))
        ->
condition('n.    alias'$brand_sin)
        ->
execute()
        ->
fetchField();
    if(isset(
$result) and !empty($result)) {
        
$term_sourse explode('/'$result);
        return  
$term_sourse[2];
    }
    return;
}
?>