xandeadx.ru Блог музицирующего веб-девелопера

Drupal

DrupalКак расширить функционал фильтр-плагина Views? (добавляем возможность фильтровать числовые поля по нескольким значениям)

Опубликовано в

Пример добавления возможности фильтровать числовые поля по нескольким значениями (добавления оператора "in").

1. В папке своего модуля src/Plugin/views/filter создаём класс и наследуем его от класса, который нужно расширить. В этом классе переопределяем нужные методы и по необходимости добавляем свои. Аннотацию плагина добавлять не нужно.

class ExtendedNumericFilter extends NumericFilter {
 
  /**
   * {@inheritDoc}
   */
  public function operators() {
    $operators = parent::operators();
 
    $operators['in'] = [
      'title' => $this->t('Is one of'),

DrupalAJAX добавление товара в корзину в Commerce 2

Опубликовано в
/**
 * Implements hook_form_BASE_FORM_ID_alter(): commerce_order_item_add_to_cart_form.
 * Alter "Add to cart" form.
 */
function MODULENAME_form_commerce_order_item_add_to_cart_form_alter(array &$form, FormStateInterface $form_state) {
  $product = $form_state->get('product'); /** @var ProductInterface $product */
 
  $form['actions']['submit']['#id'] = 'add-to-cart-button-' . $product->id();
  $form['actions']['submit']['#ajax'] = [
    'callback' => 'MODULENAME_add_to_cart_ajax',
    'event' => 'click',

DrupalВыполнить очередь с помощью Batch API

Опубликовано в

Пример выполнения очереди products_import с помощью Batch API:

class ProductsImportForm extends FormBase {
 
  /**
   * {@inheritDoc}
   */
  public function getFormId() {
    return 'products_import_form';
  }
 
  /**
   * {@inheritDoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Start'),
    ];
 
    return $form;
  }
 
  /**
   * {@inheritDoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {

DrupalДобавить сторонней контент сущности своё базовое поле (base field)

Пример добавления терминам базового поля stored_depth:

// MODULENAME.module
 
/**
 * Implements hook_entity_base_field_info().
 */
function MODULENAME_entity_base_field_info(EntityTypeInterface $entity_type) {
  if ($entity_type->id() == 'taxonomy_term') {
    $fields = [];
 
    $fields['stored_depth'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Depth'))
      ->setSetting('unsigned', TRUE)
      ->setDefaultValue(0);
 
    return $fields;
  }
}

DrupalЗапретить анонимам ставить флаг чаще одного раза в день с одного ip

Опубликовано в

В модуле Flag анонимный юзер может бесконечно флагать одну сущность просто удаляю куку с сессией, что неприемлемо например при реализации лайков. Чтобы это запретить нужно:

1. В админке добавить флагу текстовое поле field_ip.

2. Написать код:

/**
 * Implements hook_ENTITY_TYPE_presave(): flagging.
 */
function MODULENAME_flagging_presave(FlaggingInterface $flagging) {
  // Save user ip
  if ($flagging->hasField('field_ip')) {
    $flagging->set('field_ip', \Drupal::request()->getClientIp());

DrupalСоздание своего текстового фильтра

Опубликовано в

Пример фильтра для оборачивания таблиц в div:

// src/Plugin/Filter/TableWrapperFilter.php
 
/**
 * @Filter(
 *   id = "table_wrapper_filter",
 *   title = @Translation("Table wrapper"),
 *   description = @Translation("Wrap tables to div."),
 *   type = Drupal\filter\Plugin\FilterInterface::TYPE_TRANSFORM_REVERSIBLE,
 *   weight = 20
 * )
 */
class TableWrapperFilter extends FilterBase {
 
  /**
   * {@inheritdoc}
   */
  public function process($text, $langcode) {
    $result = new FilterProcessResult($text);
 
    if (stristr($text, '<table') !== FALSE) {

DrupalОднократное выполнение ajax запроса

Опубликовано в

Часто возникает необходимость, чтобы ajax ссылка с классом use-ajax выполнила запрос ровно один раз, после чего прекратила реакцию на клики (например это ссылка загрузки контента в таб). Реализовать задуманное можно с помощью небольшого костыля подменяющего Drupal.Ajax.prototype.eventResponse:

(function ($, Drupal) {
  /**
   * Override AJAX eventResponse function.
   */
  var originalAjaxEventResponse = Drupal.Ajax.prototype.eventResponse;
 
  Drupal.Ajax.prototype.eventResponse = function (element, event) {
    var $element = $(element);

DrupalРасширить сторонний форматтер своим функционалом

Опубликовано в

Пример добавления для форматтера text_default функционала обрезки текста:

// MODULENAME.module
 
/**
 * Implements hook_field_formatter_third_party_settings_form().
 */
function MODULENAME_field_formatter_third_party_settings_form(FormatterInterface $plugin, FieldDefinitionInterface $field_definition, $view_mode, array $form, FormStateInterface $form_state) {
  $element = [];
 
  if ($plugin->getPluginId() == 'text_default') {
    $element['max_length'] = [
      '#type' => 'number',
      '#title' => t('Max length'),

DrupalОткрывать определённый адрес в административной теме

Опубликовано в

Если роут свой, то просто добавляем для него опцию _admin_route: TRUE:

# modulename.routing.yml
 
modulename.example_route:
  ...
  options:
    _admin_route: TRUE

Если роут объявлен в другом модуле, то альтерим его в EventSubscriber-е:

// src/EventSubscriber/ModulenameRouteSubscriber.php
 
namespace Drupal\modulename\EventSubscriber;
 
use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;
 
class ModulenameRouteSubscriber extends RouteSubscriberBase {

DrupalСделать чтобы форма входа открывалась в диалоге

Опубликовано в

Код добавляет необходимые атрибуты ссылке "Login" в "User account menu", чтобы форма входа открывалась в jQuery UI Dialog.

/**
 * Preprocess function for menu--account.html.twig.
 */
function THEMENAME_preprocess_menu__account(&$vars) {
  if (\Drupal::currentUser()->isAnonymous()) {
    $login_link_url = $vars['items']['user.logout']['url']; /** @var Url $login_link_url */
    $login_link_url->mergeOptions([
      'attributes' => [
        'class' => ['use-ajax'],
        'data-dialog-type' => 'modal',
      ],
    ]);