Search by

parisek / drupal-kit

parisek

Shared Drupal infrastructure for sites built on the PORTA component pattern: services, base classes, Twig extensions, image resizer.

Package info

github.com/parisek/drupal-kit

Type:drupal-module

pkg:composer/parisek/drupal-kit

Statistics

Installs: 68

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v3.0.0 2026-09-23 08:24 UTC

README

Packagist Version Packagist Downloads CI Drupal PHPStan Coverage License: GPL-2.0-or-later

parisek/drupal-kit — base library module for Drupal sites built on the PORTA component pattern. Provides shared infrastructure (services, base classes, Twig extensions, image resizer) reused across projects. The Drupal counterpart of parisek/timber-kit (WordPress).

Requires PHP 8.3+ and Drupal 10 or 11.

Installation

Published on Packagist:

composer require parisek/drupal-kit
drush en drupal_kit

What this module provides

Services

  • drupal_kit.entity_helper — high-level entity loading and rendering helpers consumed by display plugins and Twig templates. Facade over the three builders below.
  • drupal_kit.media_array_builder — builds the documented array shapes for Media and File entities (image, SVG, video, remote video, document, Lottie).
  • drupal_kit.menu_tree_builder — renders a menu into the documented item shape (active trail, field_* enrichment, subtree scoping via params['root']).
  • drupal_kit.taxonomy_tree_builder — builds nested taxonomy term trees.
  • Drupal\drupal_kit\Services\FeatureFlags (drupal_kit.feature_flags) — reads the opt-in flags in the drupal_kit.feature_flags config object, the shape core uses for system.feature_flags. enabled('<flag>') is FALSE unless the module declares the flag and the project set it, which is how hook-level behaviour ships opt-in.
  • Drupal\drupal_kit\Services\Resizer — static utility: image style + focal point + responsive variant generator. Call Resizer::resizer($images, $variants) directly. $variants is a list of positional tuples, or an orientation map (landscape / portrait / square) that picks its tuples from the image's own aspect ratio — see Orientation-aware variants.
  • drupal_kit.menu_active_trail_resolver — resolves the active menu trail accounting for entity references and aliases.
  • drupal_kit.twig_extension — registers Twig functions used by component templates, including the typography-aware translation helpers _xt / __t / _nt / _nxt (translate, then pipe through |typography).
  • drupal_kit.typography_twig_extension — provides the |typography Twig filter; delegates to parisek/twig-typography and resolves typography config from {active_theme}/static/typography.yml.
  • drupal_kit.route_subscriber — alters routes for entity access edge cases.

Base classes

  • Drupal\drupal_kit\ComponentBase — base for component block plugins.
  • Drupal\drupal_kit\DisplayBase — base for extra_field display plugins that render components.

Filters

FilterImage, FilterLinks, FilterTable, FilterTypography, FilterYoutubetext format filters that normalize editor output into PORTA's component shape.

Orientation-aware variants

|resizer takes either positional tuples or one orientation map. The map is recognised by its keys, so a template that never writes one never changes:

{# tuples — the historical shape #}
{{ image|resizer(['960', '720', '1280', 'crop'], ['480', '360', '', 'crop']) }}

{# orientation map — the image's own aspect ratio picks the bucket #}
{{ image|resizer({
  landscape: [['960', '720', '1280', 'crop'], ['480', '360', '', 'crop']],
  portrait:  [['720', '960', '1280', 'crop'], ['360', '480', '', 'crop']],
  square:    [['800', '800', '1280', 'crop'], ['400', '400', '', 'crop']],
}) }}

An image counts as square while its sides differ by no more than 10 %, inclusive at both edges; otherwise the longer side decides. A bucket that is absent or empty falls through to landscape, so a map carries only the orientations that actually differ.

An image with no width or height is classified as landscape. That is the ordinary case rather than an edge case: MediaArrayBuilder fills the dimensions only when the file exists and is a valid image, so a remote file, a missing file or a broken one arrives with neither.

The same call shape works in parisek/timber-kit and in the styleguide preview, so one template renders on every stack.

Required modules

Pulled automatically by Composer when you install:

Optional integrations

The following modules are optional. When present, EntityHelper automatically exposes additional fields and renderers; when absent, those code paths gracefully no-op.

Contrib (install via Composer):

Drupal core (enable via drush en …):

  • comment — comment entity support (comment_body field on Comment entities).

Drupal core patches (apply via cweagans/composer-patches):

  • drupal.org#2466553 — adds menu.language_tree_manipulator to Drupal core. When applied, EntityHelper::getMenu() filters menu links by the current content language. When absent, the filter step is silently skipped and menu items for all languages appear — on multilingual sites the status report (/admin/reports/status) shows a warning so the gap is visible.

Local development

Local environment is DDEV — pinned to PHP 8.3 in .ddev/config.yaml so it matches the production deploy target and CI. The database container is omitted; kernel tests use sqlite in-memory.

ddev start
ddev composer install
ddev exec scripts/dev-link-module.sh   # symlink module into web/modules/contrib + bridge web/autoload.php
ddev exec vendor/bin/phpunit

scripts/dev-link-module.sh resolves paths relative to where it runs, so it must be invoked inside the container — otherwise the symlinks point to host paths the container can't see.

Tests

Tests are self-contained — Composer scaffolds Drupal via installer-paths; PHPUnit bootstraps from web/core/tests/bootstrap.php.

ddev exec vendor/bin/phpunit --testsuite unit
ddev exec vendor/bin/phpunit --testsuite kernel

Coverage

ddev coverage is a custom command (defined in .ddev/commands/web/coverage) that runs PHPUnit with xdebug.mode=coverage and emits both clover XML and a textual summary:

ddev coverage
ddev coverage --filter ResizerTest   # any phpunit args pass through

CI uses the same flags so local + CI numbers stay aligned.

Step-debugging

ddev xdebug on    # loads xdebug in debug mode; listen on host port 9003
ddev xdebug off   # debug mode is a heavy perf hit; keep it off by default

See CONTRIBUTING.md for how to add tests and the unit-vs-kernel decision tree.

Without DDEV

DDEV is the canonical local environment, but the repo doesn't hard-depend on it — CI runs vanilla composer install + vendor/bin/phpunit against PHP 8.3 from the shivammathur/setup-php GitHub Action. If you prefer host-PHP, ensure you're on PHP 8.3 (matching CI / production) to avoid composer.lock drift.

Releasing

Tag-driven; published on Packagist, which syncs tags automatically via the GitHub webhook. Version bumps follow Conventional Commits, the public-API surface and deprecation lifecycle are defined in RELEASING.md — read it before tagging.

Distribution scope: composer require ships only the module files, src/, templates/, composer.json, LICENSE and README.md — everything development-only is export-ignored in .gitattributes.

Related projects

Part of the PORTA ecosystem:

  • parisek/timber-kit — the WordPress counterpart: shared Timber/ACF infrastructure for PORTA themes.
  • parisek/twig-typography — framework-agnostic typography Twig extension that powers our |typography filter.

License

GPL-2.0-or-later. See LICENSE.