2025-06-18

在sites/default/settings.php中加入

$config['system.logging']['error_level'] =  'verbose';
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
ini_set('error_reporting', E_ALL);
$settings['http_client_config']['verify'] = false;
标签: Drupal
2025-04-14

Drupal的序列化和反序列化用的是 symfony/serializer

服务

$serializer = \Drupal::service("serializer");

 

Node序列化成xml.

$node = node_load(1);

$serializer = \Drupal::service("serializer");
$xml = $serializer->serialize($node, 'xml');

// 再解析回来
$deserializedData = $serializer->deserialize($xml, $node::class, 'xml');
dpm($deserializedData);

解析XML为数组

$data = <<<EOF
<?xml version="1.0"?>
<response><userid>1</userid><username>xiukunabcd</username></response>
EOF;

$serializer = \Drupal::service("serializer");
$deserializedData = $serializer->decode($data, 'xml');

dpm($deserializedData);

 

定义csv解析和反解析, 先定义一个服务:

services:
  mymodule.encoder.csv:
    class: Drupal\mymodule\Encoder\CsvEncoder
    tags:
      - { name: encoder, format: csv }

 

目录在src/Encoder/CsvEncoder.php

<?php
namespace Drupal\rest_output\Encoder;

class CsvEncoder extends \Symfony\Component\Serializer\Encoder\CsvEncoder {
  protected $format = 'csv';

  public function decode(string $data, string $format, array $context = []): mixed
  {
    $context[CsvEncoder::AS_COLLECTION_KEY] = false;
    return parent::decode($data, $format, $context); // TODO: Change the autogenerated stub
  }
}

 

测试

$node = node_load(1);

$serializer = \Drupal::service("serializer");
$xml = $serializer->serialize($node, 'csv');


$deserializedData = $serializer->deserialize($xml, $node::class, 'csv');
dpm($deserializedData);

在目录中vendor/symfony/serializer/Encoder还可能看到有yaml的解析。 可以使用上面代码复写

 

标签: Drupal
2025-04-14

关键词: autowire: true

更新日志: https://www.drupal.org/node/3218156

在默认的服务器中如果想要传入参数都需要在服务中使用arguments. autowire可以自动加载服务

比如以下:

services:
  Drupal\mymodule\Helper:
    autowire: true

 

服务在初始化的时候会有参数EntityTypeManagerInterface.

<?php
namespace Drupal\mymodule;

use Drupal\Core\Entity\EntityTypeManagerInterface;


final class Helper {

  public function __construct(
    private readonly EntityTypeManagerInterface $entityTypeManager
  ) {}


  public function getHello() {
    return 'hello';
  }


}

 

但是这并不会生效。自动装载装载的是当前服务中有EntityTypeManagerInterface这个服务才会自动装载。现在来改一个原来的services.yml

services:
  Drupal\mymodule\Helper:
    autowire: true
  Drupal\Core\Entity\EntityTypeManagerInterface: '@entity_type.manager'

 

 

标签: Drupal
2025-04-14

1.hook_views_pre_render

function MYMODULE_views_pre_render(&$view) {
  if ($view->name == 'view_myviewname') {
    // Set the view title.
    $view->setTitle('New title');
    $result = $view->result;
    // Set the ro title
    foreach ($result as $i => $row) {
      $view->_entity->set('title', 'New titile for row');
      $row->_entity->set('field_name', "new value");    
    }
  }
}

2. function hook_preprocess_views_view_field(&$variables)

function hook_preprocess_views_view_field(&$variables) {
  $view = $variables['view'];
  $field = $variables['field'];
  $row = $variables['row'];

  if ($view->storage->id() == 'view_name' && $field->field == 'field_name') {

    $variables['output'] = 'News output';
    // Example of inline styling

    $value = $field->getValue($row);

    $markup = [

      '#type' => 'inline_template',

      '#template' => '{{ yourvar }} {{ yourhtml | raw }}',

      '#context' => [

        'yourvar' => $value,

        'yourhtml' => '<span style="color:red;">Your HTML</span>',

      ],

    ];

    $variables['output'] = $markup;
  }
}

 

 

 

标签: Drupal
2025-04-14
  logger.factory:
    class: Drupal\Core\Logger\LoggerChannelFactory
    arguments: ['@request_stack', '@current_user']
    tags:
      - { name: service_collector, tag: logger, call: addLogger }

以上代码的定义中name是说这个服务会进行服务收集. 收集的服务名为 logger.  收集的服务将会调用Drupal\Core\Logger\LoggerChannelFactory::addLogger这个函数

 

https://www.drupal.org/docs/drupal-apis/services-and-dependency-injection/service-tags

 

标签: Drupal
2025-04-14
$header = []
foreach ($result as $ip) {
      $row = [];
      $links = [];
      $links['delete'] = [
        'title' => $this->t('Delete'),
        'url' => Url::fromRoute('myrouter'),
      ];
      $row[] = [
        'data' => [
          '#type' => 'operations',
          '#links' => $links,
        ],
      ];
      $rows[] = $row;
}
[
      '#type' => 'table',
      '#header' => $header,
      '#rows' => $rows,
]

 

标签: Drupal
2025-04-14

1.增加索引到自定义Entity

下面我们来使用代码定义一个entity.

下面定义了一个Request的Entity: mymodule/src/Entity/Request.php

<?php

namespace Drupal\mymodule\Entity;

use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;

/**
 *
 * @ContentEntityType(
 *   id = "requests",
 *   label = @Translation("Requests"),
 *   handlers = {
 *     "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
 *     "list_builder" = "Drupal\Core\Entity\EntityListBuilder",
 *     "views_data" = "Drupal\views\EntityViewsData",
 *     "form" = {
 *       "default" = "Drupal\Core\Entity\ContentEntityForm",
 *       "add" = "Drupal\Core\Entity\ContentEntityForm",
 *       "edit" = "Drupal\Core\Entity\ContentEntityForm",
 *       "delete" = "Drupal\Core\Entity\ContentEntityDeleteForm",
 *     },
 *     "route_provider" = {
 *       "html" = "Drupal\Core\Entity\Routing\AdminHtmlRouteProvider",
 *     },
 *     "access" = "Drupal\Core\Entity\EntityAccessControlHandler",
 *   },
 *   admin_permission = "administer requests",
 *   base_table = "requests",
 *   translatable = FALSE,
 *   entity_keys = {
 *     "id" = "id",
 *     "uuid" = "uuid"
 *   },
 * )
 */
class Requests extends ContentEntityBase {

  /**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
    $fields = parent::baseFieldDefinitions($entity_type);

    $fields['method'] = BaseFieldDefinition::create('string')
      ->setLabel(t('method'));

    $fields['url'] = BaseFieldDefinition::create('string')
      ->setLabel(t('url'));


    return $fields;
  }

}

 

添加4条数据作为测试

$storage = \Drupal::entityTypeManager()->getStorage('requests');
$storage->create([
    'method' => 'POST',
    'url' => 'https://www.a.com'
  ])->save();

$storage->create([
    'method' => 'GET',
    'url' => 'https://www.b.com'
  ])->save();

$storage->create([
    'method' => 'PUT',
    'url' => 'https://www.c.com'
  ])->save();

$storage->create([
    'method' => 'DELETE',
    'url' => 'https://www.d.com'
  ])->save();

 

打开PHPMyAdmin查看会看到有4条数据.

 

现在执行一个自定义SQL查询数据

SELECT * FROM `requests` WHERE method="POST"

这里会很正常的显示一条.

然后我们这边使用EXPLAIN来查看SQL查询细节

我们会发现我们想要的其实是其中一条数据. 但是SELECT他进行了全表扫描. 

其主要原因是因为我没有对字段增加索引, 如果你要对表进行搜索你必须对其增加索引. 不增加就会全表扫描.

我们需要修改一下entity定义. 增加一个storage_schema

 *   handlers = {
 *     "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
 *     "list_builder" = "Drupal\Core\Entity\EntityListBuilder",
 *     "storage_schema" = "Drupal\mymodule\RequestsStorageSchema",

接着我们定义 mymodule/src/RequestsStorageSchema.php

<?php
namespace Drupal\mymodule;

use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema;

/**
 * Defines the Requests schema handler.
 */
class RequestsStorageSchema extends SqlContentEntityStorageSchema {
  /**
   * {@inheritdoc}
   */
  protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $reset = FALSE) {
    $schema = parent::getEntitySchema($entity_type, $reset);
    if ($data_table = $this->storage->getBaseTable()) {
      $schema[$data_table]['indexes'] += [
        'requests__url' => ['url'],
        'requests__method' => ['method'],
      ];
    }

    return $schema;
  }
}

接着我们只运行:  drush entity-updates, 此功能: https://www.drupal.org/project/devel_entity_updates

我们再运行上面的sql进行测试:

EXPLAIN SELECT * FROM `requests` WHERE method="POST"

这时候可以看到这里的rows已经变成了1

 

2.对Node Body字段和自定义字段增加索引

在系统的Node中. 我们经常需要对body也进行搜索. 但是body字段并没有索引所以就可能导致当文章很多的时候搜索会很漫.

自定义字段也是一样. 不会有索引. 我会在后台创建一个field_myname的字段

我们查询1条数据. 实际上他也是进行了全表扫描.

 

这里直接使用hook_entity_field_storage_info_alter

 
function mymodule_entity_field_storage_info_alter(&$fields, \Drupal\Core\Entity\EntityTypeInterface $entity_type) {
  if ($entity_type->id() == 'node' && !empty($fields['body'])) {
    $fields['body']->setIndexes(['value' => ['value']]);
    $fields['field_myname']->setIndexes(['value' => ['value']]);
  }
}

 

然后运行drush entity-updates 

接着测试sql

暂时没有模块可以提供可以增加索引的功能. 有一个field_index的模块但是并不支持drupal10, 还有一个模块可以提示views中搜索字段没有增加索引views_index_hint

 

标签: Drupal mysql
2025-04-14

首先定义一个事件服务.

mymodule.services.yml

services
  mymodule.event_subscriber:
    class: Drupal\mymodule\EventSubscriber\MariadbProfileSubscriber
    tags:
      - { name: event_subscriber }

定义服务内容

<?php
declare(strict_types=1);
namespace Drupal\mymodule\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
 * @todo Add description for this subscriber.
 */
final class MariadbProfileSubscriber implements EventSubscriberInterface {
  /**
   * Kernel request event handler.
   */
  public function onKernelRequest(RequestEvent $event): void {
    // 开启profiles
    \Drupal::database()->query('set profiling=1')->execute();
  }
  public function onResponse(ResponseEvent $event):void {
    if ($event->isMainRequest()) {
      $is_debug_more = true;
      $strings = '';
      // 通过show profiles查看页面所有query的相应速度.
      $query_list = \Drupal::database()->query('show profiles;')->fetchAll();
      uasort($query_list, function($a, $b) {
        return ($a->Duration < $b->Duration) ? 1 : -1;
      });
      foreach ($query_list as $item) {
        $strings .= <<<EOF
QueryID = {$item->Query_ID}
Query = {$item->Query}
Duration = {$item->Duration}
EOF;
        if ($is_debug_more) {
          // 通过select INFORMATION_SCHEMA.PROFILING表来查看单条sql为什么会漫.
          // 设置query id.
          \Drupal::database()->query('set @query_id=' . $item->Query_ID);
          $query = <<<EOF
  SELECT STATE, SUM(DURATION) AS Total_R,
      ROUND(
      100* SUM(DURATION)/
      (SELECT SUM(DURATION) FROM INFORMATION_SCHEMA.PROFILING WHERE QUERY_ID = @query_id), 2) AS Pct_R,
      COUNT(*) AS Calls,
      SUM(DURATION)/COUNT(*) AS "R/Ca11"
  FROM INFORMATION_SCHEMA.PROFILING
    WHERE QUERY_ID = @query_id
    GROUP BY STATE
    ORDER BY Total_R DESC;
  EOF;
          $res = \Drupal::database()->query($query)->fetchAll();
          foreach ($res as $sub_item) {
            $sub_item = (array)$sub_item;
            $strings .= <<<EOF
  STATE:{$sub_item["STATE"]}, Total_R:{$sub_item["Total_R"]}, Pct_R:{$sub_item["Pct_R"]}, Calls:{$sub_item["Calls"]}, R/Ca11:{$sub_item['R/Ca11']}
  EOF;
            $strings .= "\r\n";
          }
        }
        $strings .= "\r\n\r\n";
      }
      \Drupal::logger('debug_sql')->notice("<pre>{$strings}</pre>");
    }
  }
  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents(): array {
    return [
      KernelEvents::REQUEST => ['onKernelRequest'],
      KernelEvents::RESPONSE => ['onResponse']
    ];
  }
}

标签: Drupal mysql
2024-05-07

ECA模块全名(事件 - 条件 - 行动)有点像Rules。 但是他更强大。只需要拖拖拽拽就可以完成。 

这边主要介绍一下 ECA+BPMN.iO模块. 

ECA

  1. 图中可以添加事件. 由事件启动.
  2. 引线是条件。
  3. 圆角正方型是要做的事。也就是行动

 

像上面的图是,当执行cron的时候。 切换到管理员(由于cron是匿名用户执行的).   获取views查询数据,这里做了一个活动报名的view. 将对即将要开始的活动提前一天对所有报名的用户发送邮件。 views只是用来查出所有需要发送邮件的用户. 获取到数据以后将数据保存成变量。后面循环执行下面那一个新的自定义事件. 发送邮件.

 

这里有更详细的视频介绍:https://www.youtube.com/watch?v=foL8V6MCXrM

eca.tar
标签: ECA Drupal
2024-04-09
  1. 先导出配置
  2. 找到导出的配置文件field_ui.settings.yml
  3. 修改配置文件后再导入
_core:
  default_config_hash: Q1nMi90W6YQxKzZAgJQw7Ag9U4JrsEUwkomF0lhvbIM
langcode: zh-hans
field_prefix: field_

修改为

_core:
  default_config_hash: Q1nMi90W6YQxKzZAgJQw7Ag9U4JrsEUwkomF0lhvbIM
langcode: zh-hans
field_prefix: ''

 

标签: Drupal