API Documentation
This guide provides technical documentation for developers working with the Knowledge module. It covers the module's API, services, plugins, hooks, and integration points.
Table of Contents
- Architecture Overview
- Entities
- Services
- Hooks
- Plugins
- Field Types
- Storage and Schema
- Migration Support
- Testing
- Extending the Module
Architecture Overview
Entity Structure
The Knowledge module provides four main content entities:
knowledge (Content Entity)
├── Knowledge entity links incidents to content
├── Revisioable, publishable, ownable
└── Bundles: Knowledge Types (Config Entity)
knowledge_competency (Content Entity)
├── Tracks user competency across roles
├── Revisionable with editorial workflow
└── No bundles
knowledge_adherence (Content Entity)
├── Tracks adherence to knowledge linking practices
├── Not revisionable
└── No bundles
knowledge_quality (Content Entity)
├── Quality assessment of knowledge articles
├── Revisionable with editorial workflow
└── No bundles
Service Architecture
knowledge.manager
├── Core knowledge management functionality
├── Entity creation and manipulation
└── Statistics aggregation
knowledge.statistics
├── Statistical calculations
├── Frequency tracking (short/medium/long)
└── Citation vs. direct knowledge tracking
knowledge.competency
├── Competency calculation
├── Role progression logic
└── Proposal and approval workflows
knowledge.leader
├── Leader assignment
└── Coach/learner relationships
knowledge.link_builder
├── Generates knowledge link render arrays
└── Permission checking
Entities
Knowledge Entity
Entity Type ID: knowledge
Class: Drupal\knowledge\Entity\Knowledge
Interface: Drupal\knowledge\KnowledgeInterface
Base Fields
// Entity identification
$fields['kid'] // Knowledge ID (primary key)
$fields['uuid'] // Universally unique identifier
$fields['knowledge_type'] // Bundle (knowledge type)
$fields['revision_id'] // Revision ID
// Ownership and status
$fields['uid'] // Author user ID
$fields['status'] // Published status (0/1)
$fields['created'] // Creation timestamp
$fields['changed'] // Last modification timestamp
// Linking information
$fields['field_name'] // Field name through which knowledge was added
$fields['entity_type'] // Target entity type
$fields['entity_id'] // Target entity ID
$fields['entity_revision'] // Target entity revision
$fields['incident_type'] // Incident entity type
$fields['incident_id'] // Incident entity ID
// Classification
$fields['citation'] // Is this a citation? (boolean)
Key Methods
// Get linked entities
$knowledge->getKnowledgeedEntity(); // Returns target entity
$knowledge->getKnowledgeedEntityId(); // Returns target entity ID
$knowledge->getKnowledgeedEntityTypeId(); // Returns target entity type
// Get incident entities
$knowledge->getIncidentEntity(); // Returns incident entity
$knowledge->getIncidentEntityId(); // Returns incident ID
$knowledge->getIncidentEntityTypeId(); // Returns incident type
// Field operations
$knowledge->getFieldName(); // Returns field name
$knowledge->setFieldName($field_name); // Sets field name
// Metadata
$knowledge->getAuthorName(); // Returns author display name
$knowledge->getCreatedTime(); // Returns creation timestamp
$knowledge->permalink(); // Returns URL with fragment
Creating Knowledge Programmatically
use Drupal\knowledge\Entity\Knowledge;
$knowledge = Knowledge::create([
'knowledge_type' => 'support_to_article',
'uid' => \Drupal::currentUser()->id(),
'status' => 1,
'field_name' => 'field_knowledge',
'entity_type' => 'node',
'entity_id' => $article_nid,
'incident_type' => 'node',
'incident_id' => $ticket_nid,
]);
$knowledge->save();
Querying Knowledge
$query = \Drupal::entityTypeManager()
->getStorage('knowledge')
->getQuery()
->accessCheck(TRUE);
// Filter by knowledge type
$query->condition('knowledge_type', 'support_to_article');
// Filter by target entity
$query->condition('entity_type', 'node');
$query->condition('entity_id', $nid);
// Filter by incident
$query->condition('incident_type', 'node');
$query->condition('incident_id', $ticket_id);
// Filter by author
$query->condition('uid', $uid);
// Filter by date range
$query->condition('created', strtotime('-30 days'), '>=');
// Filter by citation status
$query->condition('citation', 0); // Direct knowledge only
$knowledge_ids = $query->execute();
$knowledge_entities = Knowledge::loadMultiple($knowledge_ids);
Knowledge Type Entity
Entity Type ID: knowledge_type
Class: Drupal\knowledge\Entity\KnowledgeType
Interface: Drupal\knowledge\KnowledgeTypeInterface
Configuration
use Drupal\knowledge\Entity\KnowledgeType;
$knowledge_type = KnowledgeType::create([
'id' => 'support_to_article',
'label' => 'Support Ticket to Article',
'description' => 'Links support tickets to knowledge base articles.',
'target_entity_type_id' => 'node',
'incident_entity_type_id' => 'node',
]);
$knowledge_type->save();
Key Methods
$knowledge_type->getTargetEntityTypeId(); // Returns target entity type
$knowledge_type->getIncidentEntityTypeId(); // Returns incident entity type
$knowledge_type->getDescription(); // Returns description
Knowledge Competency Entity
Entity Type ID: knowledge_competency
Class: Drupal\knowledge\Entity\Competency
Interface: Drupal\knowledge\KnowledgeCompetencyInterface
Base Fields
$fields['id'] // Competency ID
$fields['user_id'] // Learner user ID
$fields['roles'] // Multi-value competency role field
$fields['correct'] // Total correct competencies
$fields['total'] // Total competencies required
$fields['completed'] // Completion timestamp
$fields['created'] // Creation timestamp
$fields['changed'] // Modification timestamp
// Legacy role fields (auto-populated from roles)
$fields['candidate_correct']
$fields['candidate_total']
$fields['contributor_correct']
$fields['contributor_total']
$fields['contributor_proposed']
$fields['contributor_approved']
$fields['contributor_coach']
$fields['contributor_leader']
$fields['publisher_correct']
$fields['publisher_total']
$fields['publisher_proposed']
$fields['publisher_approved']
$fields['publisher_coach']
$fields['publisher_leader']
Working with Competencies
use Drupal\knowledge\Entity\Competency;
// Load user's competency
$competency = \Drupal::entityTypeManager()
->getStorage('knowledge_competency')
->loadByProperties(['user_id' => $uid]);
if (!empty($competency)) {
$competency = reset($competency);
// Get overall progress
$correct = $competency->get('correct')->value;
$total = $competency->get('total')->value;
$percentage = ($total > 0) ? ($correct / $total) * 100 : 0;
// Check for pending proposals
$pending_role = $competency->isPending();
if ($pending_role) {
// User has pending proposal for advancement
}
// Check completion
if ($competency->get('completed')->value) {
$completed_time = $competency->get('completed')->value;
// All competencies achieved
}
}
Knowledge Adherence Entity
Entity Type ID: knowledge_adherence
Class: Drupal\knowledge\Entity\KnowledgeAdherence
Base Fields
$fields['id'] // Adherence ID
$fields['name'] // Label/description
$fields['uid'] // Creator user ID
$fields['learner'] // Learner being evaluated
$fields['incident_id'] // Incident entity reference
$fields['incident_type'] // Incident entity type
$fields['knowledge_id'] // Knowledge entity reference
$fields['article_id'] // Article entity reference
$fields['article_type'] // Article type
$fields['linked'] // Was article linked? (nullable boolean)
$fields['reused'] // Created or reused? (nullable boolean)
$fields['accurate'] // Was article accurate? (nullable boolean)
$fields['improve'] // Needs improvement? (nullable boolean)
$fields['required'] // Was article required? (nullable boolean)
$fields['found'] // Could find article? (nullable boolean)
$fields['disposition'] // Incident disposition
$fields['created'] // Creation timestamp
$fields['changed'] // Modification timestamp
Knowledge Quality Entity
Entity Type ID: knowledge_quality
Class: Drupal\knowledge\Entity\KnowledgeQuality
Base Fields
$fields['id'] // Quality ID
$fields['user_id'] // Reviewer user ID
$fields['entity_type'] // Reviewed entity type
$fields['entity_id'] // Reviewed entity ID
$fields['entity_vid'] // Reviewed entity revision
$fields['correct'] // Criteria marked as correct
$fields['total'] // Total criteria
$fields['status'] // Published status
$fields['created'] // Review timestamp
$fields['changed'] // Modification timestamp
Key Methods
$quality->getTotal(); // Returns total criteria count
$quality->getCorrect(); // Returns correct criteria count
$quality->getScore(); // Returns calculated quality score
Calculating Quality Scores
use Drupal\knowledge\Entity\KnowledgeQuality;
// Score formula: 100 * ((correct - incorrect) / total)
$score = $quality->getScore();
// Score ranges:
// 90-100: Excellent
// 70-89: Good
// 50-69: Fair
// <50: Poor
Services
knowledge.manager
Class: Drupal\knowledge\KnowledgeManager
Service ID: knowledge.manager
Main service for knowledge management operations.
$knowledge_manager = \Drupal::service('knowledge.manager');
Methods
// Get knowledge field for an entity
$knowledge_manager->getFields($entity_type_id);
// Get display mode for knowledge field
$knowledge_manager->getDisplayMode($entity_type_id, $bundle);
// Check if knowledge is enabled for entity
$knowledge_manager->isEnabled($entity);
// Get knowledge form
$knowledge_manager->getForm($entity);
knowledge.statistics
Class: Drupal\knowledge\KnowledgeStatistics
Service ID: knowledge.statistics
Handles knowledge statistics calculation and tracking.
$statistics = \Drupal::service('knowledge.statistics');
Methods
// Update statistics for a knowledge entity
$statistics->update($knowledge);
// Get statistics for an entity
$stats = $statistics->read($entity_id, $entity_type);
// Returns array:
// - total_count: Total knowledge links
// - short_count: Links in short period
// - medium_count: Links in medium period
// - long_count: Links in long period
// - total_citation: Total citations
// - short_citation: Citations in short period
// - medium_citation: Citations in medium period
// - long_citation: Citations in long period
// Delete statistics for an entity
$statistics->delete($entity_id, $entity_type);
// Get top knowledge entities
$top = $statistics->getTopKnowledge($limit = 10);
knowledge.competency
Class: Drupal\knowledge\Service\CompetencyService
Service ID: knowledge.competency
Manages competency calculations and workflows.
$competency_service = \Drupal::service('knowledge.competency');
Methods
// Calculate competencies for a user
$competency_service->calculate($user);
// Propose user for role advancement
$competency_service->propose($competency, $role);
// Approve role advancement
$competency_service->approve($competency, $role);
// Pre-save operations
$competency_service->doPreSave($roles, $original_roles);
// Post-save operations
$competency_service->doPostSave($user, $roles, $original_roles);
knowledge.leader
Class: Drupal\knowledge\Service\KnowledgeLeaderService
Service ID: knowledge.leader
Manages leader and coach assignments.
$leader_service = \Drupal::service('knowledge.leader');
Methods
// Get user's leader
$leader = $leader_service->getLeader($user);
// Get user's coach
$coach = $leader_service->getCoach($user);
// Get all users for a leader
$followers = $leader_service->getFollowers($leader);
// Get all users for a coach
$learners = $leader_service->getLearners($coach);
Hooks
hook_knowledge_links_alter()
Alter knowledge link render arrays before display.
/**
* Implements hook_knowledge_links_alter().
*/
function mymodule_knowledge_links_alter(array &$links, KnowledgeInterface $entity, array &$context) {
// Add custom link
$links['mymodule'] = [
'#theme' => 'links__knowledge__mymodule',
'#attributes' => ['class' => ['links', 'inline']],
'#links' => [
'knowledge-report' => [
'title' => t('Report Issue'),
'url' => Url::fromRoute('mymodule.report', [
'knowledge' => $entity->id()
]),
],
],
];
// Modify existing links
if (isset($links['knowledge']['#links']['knowledge-delete'])) {
// Change delete link text
$links['knowledge']['#links']['knowledge-delete']['title'] = t('Remove');
}
// Remove links based on context
if ($context['view_mode'] == 'teaser') {
unset($links['knowledge']['#links']['knowledge-edit']);
}
}
Entity Hooks
Standard Drupal entity hooks work with knowledge entities:
/**
* Implements hook_knowledge_presave().
*/
function mymodule_knowledge_presave(EntityInterface $entity) {
// Act before knowledge entity is saved
if ($entity->bundle() == 'support_to_article') {
// Custom logic for specific knowledge type
}
}
/**
* Implements hook_knowledge_insert().
*/
function mymodule_knowledge_insert(EntityInterface $entity) {
// Act after new knowledge entity is created
\Drupal::logger('mymodule')->notice('Knowledge @id created', [
'@id' => $entity->id(),
]);
}
/**
* Implements hook_knowledge_update().
*/
function mymodule_knowledge_update(EntityInterface $entity) {
// Act after knowledge entity is updated
}
/**
* Implements hook_knowledge_delete().
*/
function mymodule_knowledge_delete(EntityInterface $entity) {
// Act after knowledge entity is deleted
// Clean up custom data
}
Plugins
Field Type: Knowledge
Plugin ID: knowledge
Class: Drupal\knowledge\Plugin\Field\FieldType\KnowledgeItem
Storage Settings
[
'knowledge_type' => 'support_to_article', // Knowledge type ID
'short_period' => 15, // Days for short frequency
'medium_period' => 90, // Days for medium frequency
'long_period' => 365, // Days for long frequency
]
Field Settings
[
'per_page' => 50, // Links per page
'anonymous' => 0, // Anonymous linking permission
'form_location' => 1, // Form display location
]
Field Type: Knowledge Competency Role
Plugin ID: knowledge_competency_role
Class: Drupal\knowledge_field\Plugin\Field\FieldType\KnowledgeCompetencyRoleItem
Multi-value field tracking competency per role.
Properties
[
'role' => 'knowledge_contributor', // Role machine name
'correct' => 5, // Correct competencies
'total' => 10, // Total competencies
'proposed' => 1234567890, // Proposed timestamp
'approved' => 1234567891, // Approved timestamp
'proposer' => 3, // Proposer user ID
'approver' => 2, // Approver user ID
]
Migrate Destination Plugins
EntityKnowledge
Migrate destination for knowledge entities.
id: my_knowledge_migration
migration_tags:
- Knowledge
source:
plugin: csv
path: 'path/to/knowledge.csv'
ids:
- id
process:
knowledge_type: 'support_to_article'
uid:
plugin: migration_lookup
migration: user_migration
source: user_id
entity_type: 'node'
entity_id:
plugin: migration_lookup
migration: article_migration
source: article_id
incident_type: 'node'
incident_id:
plugin: migration_lookup
migration: ticket_migration
source: ticket_id
field_name: 'field_knowledge'
status: 1
destination:
plugin: entity:knowledge
EntityKnowledgeType
Migrate destination for knowledge types.
id: knowledge_type_migration
source:
plugin: csv
path: 'path/to/knowledge_types.csv'
process:
id: machine_name
label: label
description: description
target_entity_type_id: target_entity_type
incident_entity_type_id: incident_entity_type
destination:
plugin: entity:knowledge_type
Field Types
Creating a Custom Knowledge Field Widget
namespace Drupal\mymodule\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of custom knowledge widget.
*
* @FieldWidget(
* id = "mymodule_knowledge_custom",
* label = @Translation("Custom Knowledge Widget"),
* field_types = {
* "knowledge"
* }
* )
*/
class CustomKnowledgeWidget extends WidgetBase {
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element['status'] = [
'#type' => 'select',
'#title' => $this->t('Knowledge Status'),
'#options' => [
0 => $this->t('Disabled'),
1 => $this->t('Read only'),
2 => $this->t('Read/Write'),
],
'#default_value' => $items[$delta]->status ?? 2,
];
// Add custom elements
$element['custom_field'] = [
'#type' => 'textfield',
'#title' => $this->t('Custom Field'),
];
return $element;
}
}
Creating a Custom Knowledge Field Formatter
namespace Drupal\mymodule\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
/**
* Plugin implementation of custom knowledge formatter.
*
* @FieldFormatter(
* id = "mymodule_knowledge_custom",
* label = @Translation("Custom Knowledge Formatter"),
* field_types = {
* "knowledge"
* }
* )
*/
class CustomKnowledgeFormatter extends FormatterBase {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = [];
foreach ($items as $delta => $item) {
$elements[$delta] = [
'#markup' => $this->t('Knowledge status: @status', [
'@status' => $item->status,
]),
];
}
return $elements;
}
}
Storage and Schema
Database Tables
knowledge
Main knowledge entity table.
CREATE TABLE `knowledge` (
`kid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`uuid` VARCHAR(128) NOT NULL,
`revision_id` INT(10) UNSIGNED,
`knowledge_type` VARCHAR(32) NOT NULL,
`uid` INT(10) UNSIGNED NOT NULL,
`field_name` VARCHAR(32) NOT NULL,
`entity_type` VARCHAR(32) NOT NULL,
`entity_id` INT(10) UNSIGNED NOT NULL,
`entity_revision` INT(10) UNSIGNED,
`incident_type` VARCHAR(32) NOT NULL,
`incident_id` INT(10) UNSIGNED NOT NULL,
`citation` TINYINT(4) NOT NULL DEFAULT 0,
`status` TINYINT(4) NOT NULL DEFAULT 1,
`created` INT(11) NOT NULL DEFAULT 0,
`changed` INT(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`kid`),
KEY `entity` (`entity_type`, `entity_id`),
KEY `incident` (`incident_type`, `incident_id`),
KEY `user` (`uid`),
KEY `created` (`created`)
);
knowledge_entity_statistics
Statistics aggregation table.
CREATE TABLE `knowledge_entity_statistics` (
`entity_id` INT(10) UNSIGNED NOT NULL,
`entity_type` VARCHAR(32) NOT NULL,
`field_name` VARCHAR(32) NOT NULL,
`total_count` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
`short_count` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
`medium_count` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
`long_count` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
`total_citation` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
`short_citation` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
`medium_citation` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
`long_citation` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
`last_knowledge_timestamp` INT(11),
`last_knowledge_uid` INT(10) UNSIGNED,
PRIMARY KEY (`entity_id`, `entity_type`, `field_name`)
);
Custom Schema Definitions
/**
* Implements hook_schema().
*/
function mymodule_schema() {
$schema['mymodule_knowledge_tracking'] = [
'description' => 'Tracks custom knowledge metrics',
'fields' => [
'id' => [
'type' => 'serial',
'not null' => TRUE,
],
'kid' => [
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
],
'metric_name' => [
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
],
'metric_value' => [
'type' => 'float',
'not null' => TRUE,
],
'timestamp' => [
'type' => 'int',
'not null' => TRUE,
'default' => 0,
],
],
'primary key' => ['id'],
'indexes' => [
'kid' => ['kid'],
'timestamp' => ['timestamp'],
],
'foreign keys' => [
'knowledge' => [
'table' => 'knowledge',
'columns' => ['kid' => 'kid'],
],
],
];
return $schema;
}
Migration Support
Migrating from Legacy System
Example migration from legacy knowledge system:
id: legacy_knowledge
label: Legacy Knowledge Migration
migration_group: knowledge
source:
plugin: legacy_knowledge_source
# Custom source plugin
process:
# Map knowledge type
knowledge_type:
plugin: static_map
source: type
map:
1: 'support_to_article'
2: 'issue_to_solution'
default_value: 'support_to_article'
# Map user
uid:
plugin: migration_lookup
migration: users
source: user_id
no_stub: true
# Map entities
entity_type:
plugin: static_map
source: target_type
map:
article: 'node'
page: 'node'
entity_id:
plugin: migration_lookup
migration: content
source: target_id
no_stub: true
incident_type:
plugin: default_value
default_value: 'node'
incident_id:
plugin: migration_lookup
migration: tickets
source: ticket_id
no_stub: true
field_name:
plugin: default_value
default_value: 'field_knowledge'
# Map status
status:
plugin: static_map
source: published
map:
0: 0
1: 1
default_value: 1
# Map timestamps
created: created_date
changed: modified_date
destination:
plugin: entity:knowledge
migration_dependencies:
required:
- users
- content
- tickets
Custom Source Plugin
namespace Drupal\mymodule\Plugin\migrate\source;
use Drupal\migrate\Plugin\migrate\source\SqlBase;
/**
* Source plugin for legacy knowledge data.
*
* @MigrateSource(
* id = "legacy_knowledge_source"
* )
*/
class LegacyKnowledgeSource extends SqlBase {
/**
* {@inheritdoc}
*/
public function query() {
$query = $this->select('legacy_knowledge', 'lk')
->fields('lk', [
'id',
'type',
'user_id',
'target_type',
'target_id',
'ticket_id',
'published',
'created_date',
'modified_date',
]);
return $query;
}
/**
* {@inheritdoc}
*/
public function fields() {
return [
'id' => $this->t('Knowledge ID'),
'type' => $this->t('Knowledge Type'),
'user_id' => $this->t('User ID'),
'target_type' => $this->t('Target Type'),
'target_id' => $this->t('Target ID'),
'ticket_id' => $this->t('Ticket ID'),
'published' => $this->t('Published Status'),
'created_date' => $this->t('Created Date'),
'modified_date' => $this->t('Modified Date'),
];
}
/**
* {@inheritdoc}
*/
public function getIds() {
return [
'id' => [
'type' => 'integer',
'alias' => 'lk',
],
];
}
}
Testing
Kernel Tests
namespace Drupal\Tests\knowledge\Kernel;
use Drupal\KernelTests\KernelTestBase;
use Drupal\knowledge\Entity\Knowledge;
use Drupal\knowledge\Entity\KnowledgeType;
/**
* Tests Knowledge entity functionality.
*
* @group knowledge
*/
class KnowledgeEntityTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'knowledge',
'knowledge_field',
'node',
'field',
'text',
'user',
'system',
];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installEntitySchema('user');
$this->installEntitySchema('node');
$this->installEntitySchema('knowledge');
$this->installEntitySchema('knowledge_type');
$this->installConfig(['knowledge', 'node']);
}
/**
* Tests knowledge entity creation.
*/
public function testKnowledgeCreation() {
// Create knowledge type
$knowledge_type = KnowledgeType::create([
'id' => 'test_type',
'label' => 'Test Type',
'target_entity_type_id' => 'node',
'incident_entity_type_id' => 'node',
]);
$knowledge_type->save();
// Create knowledge
$knowledge = Knowledge::create([
'knowledge_type' => 'test_type',
'uid' => 1,
'entity_type' => 'node',
'entity_id' => 1,
'incident_type' => 'node',
'incident_id' => 2,
'field_name' => 'field_knowledge',
]);
$knowledge->save();
// Assert
$this->assertNotNull($knowledge->id());
$this->assertEquals('test_type', $knowledge->bundle());
$this->assertEquals('node', $knowledge->getKnowledgeedEntityTypeId());
}
}
Functional Tests
namespace Drupal\Tests\knowledge\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Tests knowledge UI functionality.
*
* @group knowledge
*/
class KnowledgeUiTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['knowledge', 'node'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* Tests knowledge type creation through UI.
*/
public function testKnowledgeTypeCreation() {
// Create admin user
$admin = $this->drupalCreateUser([
'administer knowledge types',
]);
$this->drupalLogin($admin);
// Visit knowledge type creation page
$this->drupalGet('admin/structure/knowledge/link/types/add');
$this->assertSession()->statusCodeEquals(200);
// Fill in form
$edit = [
'label' => 'Test Knowledge Type',
'id' => 'test_knowledge_type',
'description' => 'Test description',
'target_entity_type_id' => 'node',
'incident_entity_type_id' => 'node',
];
$this->submitForm($edit, 'Save');
// Assert success
$this->assertSession()->pageTextContains('Saved the Test Knowledge Type knowledge type');
}
}
Extending the Module
Creating a Custom Knowledge Statistics Calculator
namespace Drupal\mymodule\Service;
use Drupal\knowledge\KnowledgeInterface;
use Drupal\knowledge\KnowledgeStatistics;
/**
* Custom knowledge statistics calculator.
*/
class CustomKnowledgeStatistics extends KnowledgeStatistics {
/**
* {@inheritdoc}
*/
public function update(KnowledgeInterface $knowledge) {
// Call parent implementation
parent::update($knowledge);
// Add custom statistics tracking
$this->updateCustomMetrics($knowledge);
}
/**
* Update custom metrics.
*/
protected function updateCustomMetrics(KnowledgeInterface $knowledge) {
// Track custom metrics
// e.g., response time, quality score, etc.
$metrics = $this->calculateMetrics($knowledge);
// Store in custom table
$this->database->merge('mymodule_knowledge_metrics')
->key(['kid' => $knowledge->id()])
->fields($metrics)
->execute();
}
}
Creating an Event Subscriber
namespace Drupal\mymodule\EventSubscriber;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\knowledge\Event\KnowledgeEvents;
use Drupal\knowledge\Event\KnowledgeEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Knowledge event subscriber.
*/
class KnowledgeEventSubscriber implements EventSubscriberInterface {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Constructs a new KnowledgeEventSubscriber.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[KnowledgeEvents::KNOWLEDGE_INSERT][] = ['onKnowledgeInsert'];
$events[KnowledgeEvents::KNOWLEDGE_UPDATE][] = ['onKnowledgeUpdate'];
return $events;
}
/**
* Responds to knowledge insert event.
*/
public function onKnowledgeInsert(KnowledgeEvent $event) {
$knowledge = $event->getKnowledge();
// Perform custom action
\Drupal::logger('mymodule')->notice('New knowledge created: @id', [
'@id' => $knowledge->id(),
]);
// Trigger custom workflow
$this->notifyStakeholders($knowledge);
}
/**
* Responds to knowledge update event.
*/
public function onKnowledgeUpdate(KnowledgeEvent $event) {
$knowledge = $event->getKnowledge();
// Check for significant changes
if ($knowledge->hasField('field_important') &&
$knowledge->get('field_important')->value) {
$this->escalate($knowledge);
}
}
}
Creating a REST Resource
namespace Drupal\mymodule\Plugin\rest\resource;
use Drupal\knowledge\Entity\Knowledge;
use Drupal\rest\Plugin\ResourceBase;
use Drupal\rest\ResourceResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Provides a Knowledge REST Resource.
*
* @RestResource(
* id = "knowledge_resource",
* label = @Translation("Knowledge Resource"),
* uri_paths = {
* "canonical" = "/api/knowledge/{kid}",
* "create" = "/api/knowledge"
* }
* )
*/
class KnowledgeResource extends ResourceBase {
/**
* Responds to GET requests.
*/
public function get($kid) {
$knowledge = Knowledge::load($kid);
if (!$knowledge) {
throw new NotFoundHttpException();
}
$response = [
'id' => $knowledge->id(),
'type' => $knowledge->bundle(),
'incident_id' => $knowledge->getIncidentEntityId(),
'entity_id' => $knowledge->getKnowledgeedEntityId(),
'author' => $knowledge->getAuthorName(),
'created' => $knowledge->getCreatedTime(),
];
return new ResourceResponse($response);
}
/**
* Responds to POST requests.
*/
public function post(array $data) {
// Validate and create knowledge entity
$knowledge = Knowledge::create($data);
$knowledge->save();
return new ResourceResponse(['id' => $knowledge->id()], 201);
}
}
Additional Resources
Code References
- Entity definitions:
src/Entity/ - Services:
src/Service/ - Forms:
src/Form/ - Plugins:
src/Plugin/ - Tests:
tests/src/
Further Reading
- Drupal Entity API Documentation
- Drupal Plugin API
- Drupal Services and Dependency Injection
- Drupal Migrate API
Contributing
To contribute to the Knowledge module:
- Review the issue queue: https://www.drupal.org/project/issues/knowledge
- Follow Drupal coding standards
- Include tests with patches
- Update documentation as needed
For questions or support, create an issue in the project's issue queue.