Bundle classes have arrived! In my opinion a major change to drupal and the way we will do theming. Introduced in branch 9.3.x.

Below are some examples of how they can be used.

Examples

For example, a custom module could declare a bundle class for a node type like this:

use Drupal\mymodule\Entity\BasicPage;

function mymodule_entity_bundle_info_alter(array &$bundles): void {
  if (isset($bundles['node']['page'])) {
    $bundles['node']['page']['class'] = BasicPage::class;
  }
}

Interfaces and traits

You can define an interface for specific custom logic, for example, support for a body field:

namespace Drupal\mymodule\Entity;

interface BodyInterface {

  /**
   * Returns the body.
   *
   * @return string
   */
  public function getBody(): string;

}

An interface can then be created for the bundle in a custom module, extending both NodeInterface and the custom BodyInterface:

namespace Drupal\mymodule\Entity;

use Drupal\node\Entity\NodeInterface;

interface BasicPageInterface extends NodeInterface, BodyInterface {

}

The bundle classes themselves extend the entity class, but implement any additional required methods from the other interfaces it provides.

namespace Drupal\mymodule\Entity;

use Drupal\node\Entity\Node;

class BasicPage extends Node implements BasicPageInterface {

  // Implement whatever business logic specific to basic pages.
  public function getBody(): string {
    return $this->get('body')->value;
  }

}

Alternatively, the getBody() implementation could live in a BodyTrait that was shared across multiple bundle subclasses.

Using an abstract base class

You can start to introduce common functionality for all of your node types with an abstract class. This approach requires defining a bundle subclass for every node type on the site (or every bundle of whatever entity type you're using this for: media, etc).

Introduce a common interface:

namespace Drupal\myproject\Entity;

use Drupal\node\NodeInterface;

class MyProjectNodeInterface extends NodeInterface {

  public function getTags(): array;

  public function hasTags(): bool;

}

And then introduce an abstract base class:

namespace Drupal\myproject\Entity;

use Drupal\node\Entity\Node;

abstract class MyProjectNodeBase extends Node implements MyProjectNodeInterface {
  public function getTags: array {
    return [];
  }
  public function hasTags: bool {
    return FALSE;
  }
}

And then even some traits:


namespace Drupal\myproject\Entity;

trait NodeWithTagsTrait {

  public function getTags(): array {
   // Put a real implementation here.
    return $this->get('tags')->getValue();
  }

  public function hasTags(): bool {
    return TRUE;
  }

}

Then you can rewrite your basic page as follows:

namespace Drupal\mymodule\Entity;

use Drupal\node\Entity\Node;
use Drupal\myproject\Entity\NodeWithTagsTrait;
use Drupal\myproject\Entity\MyProjectNodeBase;

class BasicPage extends MyProjectNodeBase implements BasicPageInterface {

  use NodeWithTagsTrait

  public function getBody(): string {
    return $this->get('body')->value;
  }

}

More about bundle classes via this link: Introducing bundle classes