2023-03-18

关键词:decorates. 比如覆盖系统里logger.factory服务. 

以前的调用方式是

\Drupal::logger('my_channel')->notice('hello world');

比如我们想修改日志的写入方式, 定义新的服务

services:
  my_log:
    parent: logger.factory
    decorates: logger.factory
    class: Drupal\test_code\Mylog

定义Mylog文件和覆盖getChannel代码. 加一个打印信息

<?php

namespace Drupal\test_code;

use Drupal\Core\Logger\LoggerChannelFactory;

class Mylog extends LoggerChannelFactory{

  public function get($channel)
  {
    dpm('run my loger');
    return parent::get($channel); // TODO: Change the autogenerated stub
  }
}

Drupal 9 change services

标签: Drupal Services
2023-03-17

定义服务  

备:创建服务和方法还可以使用: drush gen service:twig-extension

services:
  test_code.twig_extension:
    class: Drupal\test_code\TestCodeTwigExtension
    tags:
      - { name: twig.extension }

定义方法

<?php

namespace Drupal\test_code;

use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

/**
 * Twig extension.
 */
class TestCodeTwigExtension extends AbstractExtension {

  /**
   * {@inheritdoc}
   */
  public function getFunctions() {
    return [
      new TwigFunction('foo', [$this, 'fooReturn']),
    ];
    // 或者.
    return [
      new TwigFunction('foo', function() {
        return 'xiukun foo'
      })
    ];
  }

  public function fooReturn() {
    return 'xiukun foo';
  }
}

测试方法

可以在任意template.twig.html或者views中使用

custom twig func

 

custom twig func output

标签: Twig Drupal9
2023-03-15

有些方法应该是属于Entity本身,而不应该写在外面.  写起来比较繁琐而且难以维护. 比如

$taskEntity->run();
$taskEntity->setFinish();

当然以上entity是我自定义. 这里的扩展主要是扩展系统或者第三方模块提供的Entity

/**
 * extends entity class.
 *
 * @param array $entity_types
 */
function mymodule_entity_type_alter(array &$entity_types) {
  /** @var \Drupal\Core\Entity\EntityTypeInterface[] $entity_types */
  $entity_types['node']->setStorageClass('Drupal\mymodule\ArticleNodeStorage');
}

 

自定一个Storage继承系统的Storage

<?php

namespace Drupal\mymodule;

use Drupal\node\NodeStorage;

class ArticleNodeStorage extends NodeStorage {

  public function getEntityClass(?string $bundle = NULL): string
  {
    return ArticleClass::class;
  }

}

 

编写ArticleClass继承原EntityClass

<?php
namespace Drupal\mymodule;
use Drupal\node\Entity\Node;

class ArticleClass extends Node {

  public function toggleElasticsearchRef() {
    if ($this->isPublished()) {
      $this->pushToElasticsearch();

    } else {
      $this->deleteToElasticsearch();
    }
  }

  // 将文章推送到Elasticsearch.
  public function pushToElasticsearch() {
    $params = [
      'id' => $this->id(),
      'title' => $this->label(),
      'body' => $this->get('body')->value,
    ];
    (new \Drupal\mymodule\Repository())->index($params);
  }

  // 从Elasticsearch上删除文章.
  public function deleteToElasticsearch() {
    try {
      (new \Drupal\mymodule\Repository())->delete($this->id());
    } catch (\Exception $e) {
    }
  }
}

 

接下来就可以这样使用了.

$node = \Drupal\node\Entity\Node::load(1);
$node->pushToElasticsearch();
$node->deleteToElasticsearch();

// Entity更新的时候自动检查是否要删除ES索引
function mymodule_entity_update(Drupal\Core\Entity\EntityInterface $entity) {
  if ($entity->getEntityTypeId() == 'node' && $entity->bundle() == 'article') {
    $entity->toggleElasticsearchRef();
  }
}


// Entity删除的时候删除Es索引
function mymodule_entity_delete(Drupal\Core\Entity\EntityInterface $entity) {
  if ($entity->getEntityTypeId() == 'node' && $entity->bundle() == 'article') {
    $entity->deleteToElasticsearch();
  }
}

 

标签: Drupal9
2023-03-15

经常我们自定义select表单可能需要加载大量的entity. 导致性能很低下. 代码如下. 当我的term address有超过1000个时,页面就需要很久了.

$termStorage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
$options = [];
foreach($termStorage->loadByProperties(['vid' => 'address']) as $entity) {
  $options[$entity->id()] = $entity->label();
}
$form['address'] = [
  '#type' => 'select',
  '#title' => $this->t('Address'),
  '#options' => $options,
  '#required' => TRUE,
];

 

这时我们可以使用select2. 使用select的ajax加载选择项, 下载并启用select2. 我们在里面定义了options来自自定义路由: #autocomplete_route_name = test_code.address_autocomplete. 我们这时只需要定义此路由返回部分数据就行. 因为select2有搜索功能. 我们不需要返回全部

<?php

namespace Drupal\test_code\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\Element\EntityAutocomplete;

/**
 * Provides a test_code form.
 */
class SelectForm extends FormBase {

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'test_code_select';
  }

  public static function AddressAutocompleteCallback(&$element) {
    $complete_form = [];
    $element = EntityAutocomplete::processEntityAutocomplete($element, new FormState(), $complete_form);
    $element['#autocomplete_route_name'] = 'test_code.address_autocomplete';
    return $element;
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {

    $form['address'] = [
      '#type' => 'select2',
      '#select2' => [
        'minimumInputLength' => 0,
      ],
      '#autocomplete' => true,
      '#target_type' => 'taxonomy_term',
      '#autocomplete_route_callback' => self::class . '::AddressAutocompleteCallback',
      '#autocomplete_route_parameters' => [],
      '#title' => $this->t('Address'),
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {

  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {

  }

}

 

定义路由:

test_code.address_autocomplete:
  path: '/test-code/example'
  defaults:
    _title: 'Example'
    _controller: '\Drupal\test_code\Controller\AutocompleteAddress::build'
  requirements:
    _permission: 'access content'

定义controller只返回50条数据。 可以搜索相关更多的数据

<?php

namespace Drupal\test_code\Controller;

use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;

/**
 * Returns responses for test_code routes.
 */
class AutocompleteAddress extends ControllerBase {

  /**
   * Builds the response.
   */
  public function build(Request $request) {

    $keyword = $request->query->get('q');
    $termStorage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
    $query = $termStorage->getQuery();
    // 这里的keyword是select2搜索的关键词.
    if ($keyword) {
      $query->condition('name', "%{$keyword}%", 'LIKE');
    }
    $query->range(0, 50);
    $ids = $query->execute();
    $results = [];
    if ($ids) {
      foreach($termStorage->loadMultiple($ids) as $entity) {
        $results[] = (object)[
          'id' => $entity->id(),
          'text' => $entity->label()
        ];
      }
    }

    return new JsonResponse([
      'results' => $results
    ]);
  }

}

 

标签: Drupal9 性能
2023-03-14

1. dejavu

https://github.com/appbaseio/dejavu

$ docker run -p 1358:1358 -d appbaseio/dejavu

2. kibana

https://www.elastic.co/cn/kibana/

version: "3.0"
services:
  elasticsearch:
    container_name: elasticsearch
    image: elasticsearch:8.6.2
    restart: always
    environment:
      - xpack.security.enabled=false
      - discovery.type=single-node
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    volumes:
      - ./data:/usr/share/elasticsearch/data
      - ./logs:/usr/share/elasticsearch/logs
      - ./plugins:/usr/share/elasticsearch/plugins
    networks:
      - es-net
    ports:
      - 9200:9200

  kibana:
    container_name: kb-container
    image: kibana:8.6.2
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    networks:
      - es-net
    depends_on:
      - elasticsearch
    ports:
      - 5601:5601
networks:
  es-net:
    driver: bridge

 

2023-03-06

比如碰到class有私有或保护方法,不能直接访问,也不可以直接改代码的情况下可以使用

Class代码

class TestClass {

  protected function protectedList() {
    return ['c', 'd'];
  }

  private function privateList() {
    return ['a', 'b'];
  }

  public function publicList() {
    return ['e', 'f'];
  }
}
$class = new TestClass();
$ref = new \ReflectionMethod($class, 'protectedList');
$ref->setAccessible(TRUE);
$list = $ref->invoke($class);
dpm($list);

输出

Array
(
    [0] => c
    [1] => d
)

 

标签: PHP
2023-03-06

通常一般服务会存在多个参数,不能直接 new classServer. 以下两种方法都可以

\Drupal::classResolver(classServer::class)->myserviceFunction();
\Drupal::service('class_resolver')->getInstanceFromDefinition(classServer::class)->myserviceFunction();

 

标签: Drupal9 Services
2023-03-06
$process = Drush::drush($this->siteAliasManager()->getSelf(), 'cache-rebuild');
$process->mustrun();

 

标签: Drush Drupal9
2023-03-05

需要执行的脚本, 里面包含Drush命令, 这个脚本我就放在我站点的bin目录下.  在web上层.

$ cd /var/www/project
$ mkdir bin
$ mkdir logs #用于生成日志
$ cd bin
$ $ vim task.sh

输入脚本内容, drush my-monitor是我自定的一个drush 命令

#!/bin/bash
export HOME=/var/www/project
/var/www/project/vendor/bin/drush my-monitor >> /var/www/project/logs/task.log

# 这里也可以使用   /var/www/project/vendor/bin/drush my-monitor --root=/var/www/project >> /var/www/project/logs/task.log
# 但是我这样出现了错误. 我就用了上面这种方式
# drush可以复制多行,每行为一个进程

需要执行的脚本创建好了, 接下来创建Linux 服务

$ vim /etc/systemd/system/my_monitor.service

输入服务内容

[Unit]
Description=My monitor service
After=network.target

[Service]
ExecStart=/bin/bash /var/www/project/bin/task.sh  # 这里对应脚本目录
Restart=always
RestartSec=1
启动服务和查看状态
$ systemctl start my_monitor.service
$ systemctl status my_monitor.service

 

为什么需要这种脚本,我举几个案例

1. 比如我创建Node以后需要执行一个拉取远程的一个数据, 这个过程相当漫, 你可以创建一个task交给后台的脚本的去跑.

2. 我要从其它服务器同步10万条数据, 如果是batch可能中途会中断,我需要一直盯着. 你也可以创建一个同步的task交给服务,让他在后台跑.

3. 用户下单以后5分钟内不付款, 自动取消订单. 你可以创建一个5分钟以后的task. 让后台去完成这个功能.

4. 用户登陆以后需要从远程服务器更新用户的一些其它信息. 这过程很慢,我们需要交给后台的task去完成

 

标签: Linux Drush Drupal
2023-03-01
dpm(\Drupal::service('transliteration')->transliterate('你好啊'));

/**
注入模式
[@transliteration]
__construct(TransliterationInterface $transliteration)
$output = $this->transliteration->transliterate('你好啊')
*/

 

标签: Drupal9 Drupal