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
2024-04-01

服务器需要安装: rsyslog

启用drupal核心模块:syslog

在这里配置admin/config/development/logging日志前缀. 一般选择默认的就行, 如果有多个站点。可以第一个站点选择local0, 第二站点选择local1

LOG_LOCAL0
LOG_LOCAL1
LOG_LOCAL2
LOG_LOCAL3
LOG_LOCAL4
LOG_LOCAL5
LOG_LOCAL6
LOG_LOCAL7

配置rsyslog

vim /etc/rsyslog.d/50-default.conf

添加行:

local0.* /var/log/drupal.log

 

这里的local0和你上面的选择有关系. 

 

重新启动rsyslog服务

service rsyslog restart

 

禁用db log模块

测试

1. 打开一个404页面

2. \Drupal::logger('debug_test')->notice('hello world');

 

标签: Drupal
2024-01-17

在定义Entity的handlers中自定义views_data

*   handlers = {
*     "views_data" = "Drupal\mymodule\Entity\MymoduleViewsData",

创建MymoduleViewsData字段

<?php

namespace Drupal\mymodule\Entity;

use Drupal\views\EntityViewsData;

class MymoduleViewsData extends EntityViewsData {

  /**
   * {@inheritdoc}
   */
  public function getViewsData() {
    $data = parent::getViewsData();
    $data['my_entity_type']['my_field'] = [
      'title' => $this->t('test field'),
      'help' => $this->t('test field'),
      'field' => [
        'id' => 'field_id',
      ],
    ];
    return $data;
  }

}

如果是已存在Entity可以这样

/**
 * Implements hook_views_data().
 */
function mymodule_views_data() {
  $data['my_entity_type']['my_field'] = [
      'title' => $this->t('test field'),
      'help' => $this->t('test field'),
      'field' => [
        'id' => 'field_id',
      ],
  ];

  return $data;
}

 

在模块src/Plugin/views/field下面创建文件MyField.php

<?php

namespace Drupal\my_module\Plugin\views\field;

use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\ResultRow;

// 这里的field_id和上面的field_id对应
/**
 * @ViewsField("field_id")
 */
class MyField extends FieldPluginBase {

  /**
   * {@inheritdoc}
   */
  public function query() {}

  /**
   * {@inheritdoc}
   */
  public function render(ResultRow $values) {
    // 自定义输出.
    $entity = $this->getEntity($values);
    return $entity->label();
  }

}


 

标签: Drupal Views
2023-06-27

由于上次写的Entity扩展改起来比较复杂。并且也不是太好用。所以这次提供了一个简单的模块来实现这个功能,已经上传到Drupal.org
https://www.drupal.org/project/entity_expand

标签: Drupal Entity
2023-06-16

在很多时候比如写抓取API代码。一般像这种是通过cron去跑. 而cron一般是匿名用户. 如果直接用admin方式来测试可能会有很多的Bug. 就比如profile一般都需要加accessCheck(false).  所以在写类似代码的时候一定要加上这段方便测试

 

$switcher = \Drupal::service('account_switcher');
$switcher->switchTo(new \Drupal\Core\Session\AnonymousUserSession());

// 你的逻辑代码

// 结尾加上这句回到当前登陆用户.
$switcher->switchBack();

 

标签: Drupal
2023-04-18

以前的Query可以理解为

  1. 文章article有一个字段叫channel
  2. Channel关联到了taxonomy
  3.  查询taxonomy中的checkout_status字段值为1
// 获取一个Entity类型的Storage
$node_storage = \Drupal::entityTypeManager()->getStorage('node');
// 获取Query
$query = $node_storage->getQuery();
$query->condition('type', 'article');
$query->condition('channel.entity:taxonomy_term.checkout_status', 1);
$ids = $query->execute();
dpm($ids);

 

再来看一个多层链接的

  1. 文章article中有一个字段叫channel
  2. channel关联了taxonomy
  3. channel中一个字段叫车辆类型:vehicle_type
  4. vehicle_type又关联了taxonomy
  5. vehicle_type的名字叫汽车运输的
$node_storage = \Drupal::entityTypeManager()->getStorage('node');
$query = $node_storage->getQuery();
$query->condition('type', 'article');
$query->condition('channel.entity:taxonomy_term.vehicle_type.entity:taxonomy_term.name', '汽车运输');
$ids = $query->execute();
dpm($ids);

 

标签: Drupal9 Drupal
2023-04-09

在使用entity中有些事情比较繁琐, 比如

1. 获取关联的entity

$entity->get('field')->entity->toArray()

这里有一个弊端.如果entity已经被删除了或者entity不存在都会报错.

 

2. 获取多值的target_id, 需要有多步操作.

$ids = [];
if (!$entity->get('field')->isEmpty()) {
  $ids = $entity->get('field')->getValue();
  $ids = array_column($ids, 'target_id');
}

3. 获取多值的广本字段.也需要有多步操作

$values = [];
if (!$entity->get('field')->isEmpty()) {
  $values = $entity->get('field')->getValue();
  $values = array_column($values, 'value');
}

 

4. 获取lis字段的label

$value = '';
if ($entity->get('field')->isEmpty()) {
  $allow_values = $entity->get('field')->getSetting('allowed_values');
  $value = $allow_values[$entity->get('field')->value];
}

以下是一个简单扩展

<?php
namespace Drupal\mymodule;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\FieldItemListInterface;

/**
 * entity helper.
 *
 * Class Entity
 */
class Entity {
  /**
   * @var EntityInterface
   */
  private EntityInterface $entity;

  /**
   * init entity.
   *
   * Entity constructor.
   * @param EntityInterface $entity
   */
  public function __construct(EntityInterface $entity) {
    $this->entity = $entity;
  }

  /**
   * get field use custom.
   *
   * @param $field
   * @return FieldItem
   */
  public function get($field):FieldItem {
    return new FieldItem($this->entity, $field);
  }

  /**
   * bundle key
   * node key is type
   * profile key is profile_type
   * @return false|string
   */
  public function bundleKey() {
    return $this->entity->getEntityType()->getKey('bundle');
  }

  /**
   * call entity old functions.
   *
   * @param $name
   * @param $arguments
   * @return false|mixed
   */
  public function __call($name, $arguments)
  {
    if (method_exists($this->entity, $name)) {
      return call_user_func_array(array(
        $this->entity,
        $name
      ), $arguments);
    }
    return false;
  }

  /**
   * call entity old property.
   *
   * @param $name
   * @return FieldItem
   */
  public function __get($name) {
    return new FieldItem($this->entity, $name);
  }
}

/**
 * entity field item.
 * Class FieldItem
 * @package Drupal\dyniva_helper
 */
class FieldItem {

  /**
   * @var FieldItemListInterface
   */
  private FieldItemListInterface $field;

  /**
   * FieldItem constructor.
   * @param EntityInterface $entity
   * @param $field
   */
  public function __construct(EntityInterface $entity, $field) {
    $this->field = $entity->get($field);
  }

  /**
   * set call field old functions.
   *
   * @param $name
   * @param $arguments
   * @return false|mixed
   */
  public function __call($name, $arguments) {
    if (method_exists($this->field, $name)) {
      return call_user_func_array(array(
        $this->field,
        $name
      ), $arguments);
    }
    return false;
  }

  /**
   * set call field old property.
   *
   * @param $name
   * @return mixed
   */
  public function __get($name) {
    return $this->field->{$name};
  }

  public function emptyValue($default = '') {
    if (!$this->field->isEmpty()) {
      return $this->field->getString();
    }
    return $default;
  }

  /**
   * get select field allow values.
   *
   * @return mixed
   */
  public function getAllowValues() {
    return $this->field->getSetting('allowed_values');
  }

  public function selectLabel() {
    $allowvalues = $this->getAllowValues();
    return $allowvalues[$this->field->value] ?? '';
  }

  /**
   * get ref entity.
   *
   * @return false|EntityInterface
   */
  public function ref() {
    if (!$this->field->isEmpty() && $this->field->entity) {
      return new Entity($this->field->entity);
    }
    return false;
  }

  /**
   * get refs entity.
   *
   * @return false|array
   */
  public function refs() {
    if (!$this->field->isEmpty()) {
      $entitys = $this->field->referencedEntities();
      foreach ($entitys as $key => $entity) {
        $entitys[$key] = new Entity($entity);
      }
      return $entitys;
    }
    return [];
  }

  /**
   * get field targets.
   *
   * @return array
   */
  public function targets() {
    if ($this->field->isEmpty()) {
      return [];
    }
    return array_column($this->field->getValue(), 'target_id');
  }

  /**
   * get field values.
   *
   * @return array
   */
  public function values() {
    if ($this->field->isEmpty()) {
      return [];
    }
    return array_column($this->field->getValue(), 'value');
  }
}


 

使用方法

$node = node_load(1);
$entity = new \Drupal\mymodule\Entity($node);

$entity->uid->refs(); 获取多个关联entity
$entity->uid->ref();  获取单个关联的entity
$entity->uid->targets();  获取多个target_id
$entity->get('titlte')->values();  获取多个value
$entity->get('select_field')->selectLabel();  获取list select字段的label. 经常在使用value的时候只会获取Key

 

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-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