2023-04-18
以前的Query可以理解为
- 文章article有一个字段叫channel
- Channel关联到了taxonomy
- 查询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);
再来看一个多层链接的
- 文章article中有一个字段叫channel
- channel关联了taxonomy
- channel中一个字段叫车辆类型:vehicle_type
- vehicle_type又关联了taxonomy
- 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);
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中使用
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-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-01
dpm(\Drupal::service('transliteration')->transliterate('你好啊'));
/**
注入模式
[@transliteration]
__construct(TransliterationInterface $transliteration)
$output = $this->transliteration->transliterate('你好啊')
*/