Skip to content

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

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_proficiency (Content Entity)
├── Tracks user proficiency across roles
├── Revisionable with editorial workflow
└── No bundles

knowledge_alignment (Content Entity)
├── Process Alignment samples of linking practices
├── Not revisionable
└── No bundles

knowledge_quality (Content Entity)
├── Content Standard Checklist samples of knowledge articles
├── Revisionable with editorial workflow
└── No bundles

Service Architecture

knowledge.manager
├── Core Knowledge-Centered Success functionality
├── Entity creation and manipulation
└── Statistics aggregation

knowledge.statistics
├── Statistical calculations
├── Frequency tracking (short/medium/long)
└── Citation vs. direct knowledge tracking

knowledge.proficiency
├── Proficiency 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\Entity\LinkInterface

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_link')
  ->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\Entity\LinkTypeInterface

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

Entity Type ID: knowledge_proficiency
Class: Drupal\knowledge\Entity\Proficiency
Interface: Drupal\knowledge\Entity\ProficiencyInterface

Base Fields

$fields['id']         // Proficiency ID
$fields['user_id']    // Learner user ID
$fields['roles']      // Multi-value proficiency role field
$fields['correct']    // Total correct proficiency items
$fields['total']      // Total proficiency items 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 Proficiency

use Drupal\knowledge\Entity\Proficiency;

// Load user's proficiency
$proficiency = \Drupal::entityTypeManager()
  ->getStorage('knowledge_proficiency')
  ->loadByProperties(['user_id' => $uid]);

if (!empty($proficiency)) {
  $proficiency = reset($proficiency);

  // Get overall progress
  $correct = $proficiency->get('correct')->value;
  $total = $proficiency->get('total')->value;
  $percentage = ($total > 0) ? ($correct / $total) * 100 : 0;

  // Check for pending proposals
  $pending_role = $proficiency->isPending();
  if ($pending_role) {
    // User has pending proposal for advancement
  }

  // Check completion
  if ($proficiency->get('completed')->value) {
    $completed_time = $proficiency->get('completed')->value;
    // All proficiency items achieved
  }
}

Process Alignment Entity

Entity Type ID: knowledge_alignment
Class: Drupal\knowledge\Entity\Alignment

Base Fields

$fields['id']            // Process Alignment 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

Content Standard Checklist Entity

Entity Type ID: knowledge_quality
Class: Drupal\knowledge\Entity\Quality

Base Fields

$fields['id']         // Content Standard Checklist ID
$fields['user_id']    // Worker (article author)
$fields['coach']      // Coach who recorded the sample
$fields['entity_type'] // Reviewed entity type
$fields['entity_id']   // Reviewed entity ID
$fields['entity_vid']  // Reviewed entity revision
$fields['correct']     // Checks marked met
$fields['total']       // Total checks
$fields['status']      // Published status
$fields['created']     // Review timestamp
$fields['changed']     // Modification timestamp

Key Methods

$quality->getTotal();    // Returns total check count
$quality->getCorrect();  // Returns checks marked met
$quality->getScore();    // Coaching-sample index, not a grade

Coaching-sample index

use Drupal\knowledge\Entity\Quality;

// Formula: round(100 * ((correct - (total - correct)) / total))
// Returns 0 when total is 0. The value can be negative.
// This is not a pass/fail grade. UI should show checks met
// (getCorrect() of getTotal()), not getScore().
$score = $quality->getScore();

Services

knowledge.manager

Class: Drupal\knowledge\Service\Manager
Service ID: knowledge.manager

Main service for knowledge 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\Service\Statistics
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.proficiency

Class: Drupal\knowledge\Service\ProficiencyService
Service ID: knowledge.proficiency

Manages proficiency calculations and workflows.

$proficiency_service = \Drupal::service('knowledge.proficiency');

Methods

// Calculate proficiency items for a user
$proficiency_service->calculate($user);

// Propose user for role advancement
$proficiency_service->propose($proficiency, $role);

// Approve role advancement
$proficiency_service->approve($proficiency, $role);

// Pre-save operations
$proficiency_service->doPreSave($roles, $original_roles);

// Post-save operations
$proficiency_service->doPostSave($user, $roles, $original_roles);

knowledge.leader

Class: Drupal\knowledge\Service\LeaderService
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

Alter knowledge link render arrays before display.

/**
 * Implements hook_knowledge_links_alter().
 */
function mymodule_knowledge_links_alter(array &$links, LinkInterface $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\Item

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

Plugin ID: knowledge_proficiency_role
Class: Drupal\knowledge_field\Plugin\Field\FieldType\ProficiencyRoleItem

Multi-value field tracking proficiency per role.

Properties

[
  'role' => 'knowledge_contributor',  // Role machine name
  'correct' => 5,                     // Correct proficiency items
  'total' => 10,                      // Total proficiency items
  'proposed' => 1234567890,           // Proposed timestamp
  'approved' => 1234567891,           // Approved timestamp
  'proposer' => 3,                    // Proposer user ID
  'approver' => 2,                    // Approver user ID
]

Migrate Destination Plugins

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

EntityLinkType

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 CustomLinkFormatter 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_link');
    $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\Entity\LinkInterface;
use Drupal\knowledge\Service\Statistics;

/**
 * Custom knowledge statistics calculator.
 */
class CustomKnowledgeStatistics extends Statistics {

  /**
   * {@inheritdoc}
   */
  public function update(LinkInterface $knowledge) {
    // Call parent implementation
    parent::update($knowledge);

    // Add custom statistics tracking
    $this->updateCustomMetrics($knowledge);
  }

  /**
   * Update custom metrics.
   */
  protected function updateCustomMetrics(LinkInterface $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

Contributing

To contribute to the Knowledge module:

  1. Review the issue queue: https://www.drupal.org/project/issues/knowledge
  2. Follow Drupal coding standards
  3. Include tests with patches
  4. Update documentation as needed

For questions or support, create an issue in the project's issue queue.