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-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-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-01
dpm(\Drupal::service('transliteration')->transliterate('你好啊'));

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

 

标签: Drupal9 Drupal