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
}
}
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中使用
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();
}
}
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
]);
}
}
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
)
2023-03-06
通常一般服务会存在多个参数,不能直接 new classServer. 以下两种方法都可以
\Drupal::classResolver(classServer::class)->myserviceFunction();
\Drupal::service('class_resolver')->getInstanceFromDefinition(classServer::class)->myserviceFunction();
2023-03-06
$process = Drush::drush($this->siteAliasManager()->getSelf(), 'cache-rebuild');
$process->mustrun();
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去完成
2023-03-01
dpm(\Drupal::service('transliteration')->transliterate('你好啊'));
/**
注入模式
[@transliteration]
__construct(TransliterationInterface $transliteration)
$output = $this->transliteration->transliterate('你好啊')
*/