원클릭으로
drupal-lazy-builders
Drupal lazy builders and placeholder implementation. Use when asked about
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Drupal lazy builders and placeholder implementation. Use when asked about
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | drupal-lazy-builders |
| description | Drupal lazy builders and placeholder implementation. Use when asked about |
Lazy builders defer rendering of dynamic/personalized content, enabling page caching while preserving dynamic elements.
session or user cache contextPage renders → Placeholder inserted → Page cached →
Request → Cached page served → Placeholder replaced with fresh content
Only scalar types allowed: string, int, float, bool, NULL
Not allowed: Objects, arrays, closures
Input: "I want to show 'Hello [username]' without breaking page cache"
Output:
// In your render array:
$build['greeting'] = [
'#lazy_builder' => [
'my_module.lazy_builders:userGreeting',
[\Drupal::currentUser()->id()], // Pass user ID, not user object!
],
'#create_placeholder' => TRUE,
];
# my_module.services.yml
services:
my_module.lazy_builders:
class: Drupal\my_module\LazyBuilders
arguments: ['@entity_type.manager']
<?php
// src/LazyBuilders.php
namespace Drupal\my_module;
use Drupal\Core\Security\TrustedCallbackInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
class LazyBuilders implements TrustedCallbackInterface {
protected EntityTypeManagerInterface $entityTypeManager;
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
public static function trustedCallbacks() {
return ['userGreeting']; // MUST list all lazy builder methods
}
public function userGreeting(int $uid): array {
$user = $this->entityTypeManager->getStorage('user')->load($uid);
return [
'#markup' => t('Hello @name!', ['@name' => $user->getDisplayName()]),
'#cache' => [
'contexts' => ['user'],
'tags' => ['user:' . $uid],
],
];
}
}
Input: "Show cart item count in header without breaking cache"
Output:
$build['cart'] = [
'#lazy_builder' => [
'my_module.lazy_builders:cartCount',
[session_id()], // Session-based, will be placeholdered
],
'#create_placeholder' => TRUE,
];
public static function trustedCallbacks() {
return ['cartCount'];
}
public function cartCount(string $session_id): array {
$count = $this->cartService->getItemCount($session_id);
return [
'#markup' => t('@count items', ['@count' => $count]),
'#cache' => [
'contexts' => ['session'],
'max-age' => 0, // OK here - isolated to placeholder only
],
];
}
Input: "My page is UNCACHEABLE because of a timestamp, how do I fix it?"
Output:
// BEFORE - breaks page cache:
$build['timestamp'] = [
'#markup' => 'Generated: ' . date('Y-m-d H:i:s'),
'#cache' => ['max-age' => 0], // Makes entire page UNCACHEABLE!
];
// AFTER - isolate with lazy builder:
$build['timestamp'] = [
'#lazy_builder' => ['my_module.lazy_builders:timestamp', []],
'#create_placeholder' => TRUE,
];
public function timestamp(): array {
return [
'#markup' => 'Generated: ' . date('Y-m-d H:i:s'),
'#cache' => ['max-age' => 0], // Now only affects this placeholder
];
}
Input: "I need to pass node ID and view mode to my lazy builder"
Output:
$build['related'] = [
'#lazy_builder' => [
'my_module.lazy_builders:relatedContent',
[$node->id(), 'teaser', $limit], // All must be scalar!
],
'#create_placeholder' => TRUE,
];
public function relatedContent(int $nid, string $view_mode, int $limit): array {
$node = $this->entityTypeManager->getStorage('node')->load($nid);
// ... render related content
return [
'#theme' => 'item_list',
'#items' => $items,
'#cache' => [
'tags' => ['node:' . $nid, 'node_list'],
],
];
}
Input: "I need to pass an array of IDs to my lazy builder"
Output:
// WRONG - arrays not allowed:
['my_service:method', [[1, 2, 3]]] // ❌ Will fail
// CORRECT - serialize to string:
$ids = [1, 2, 3];
$build['items'] = [
'#lazy_builder' => [
'my_module.lazy_builders:multipleItems',
[implode(',', $ids)], // Pass as comma-separated string
],
'#create_placeholder' => TRUE,
];
public function multipleItems(string $ids_string): array {
$ids = array_filter(explode(',', $ids_string));
$nodes = $this->entityTypeManager->getStorage('node')->loadMultiple($ids);
// ... render items
}
Input: "My block shows user-specific content, how do I make it cacheable?"
Output:
class UserNotificationsBlock extends BlockBase {
public function build() {
// Don't render user content directly in build()
return [
'#lazy_builder' => [
'my_module.lazy_builders:notifications',
[\Drupal::currentUser()->id()],
],
'#create_placeholder' => TRUE,
];
}
// Block itself is cacheable
public function getCacheMaxAge() {
return Cache::PERMANENT;
}
public function getCacheContexts() {
// No user context needed here - lazy builder handles it
return parent::getCacheContexts();
}
}
Input: "My lazy builder isn't being called, how do I debug?"
Output:
// Common issues:
// 1. Missing TrustedCallbackInterface
class LazyBuilders { // ❌ Missing interface
}
class LazyBuilders implements TrustedCallbackInterface { // ✓ Correct
public static function trustedCallbacks() {
return ['myMethod']; // Must list the method!
}
}
// 2. Wrong service name
'wrong_service:method' // Check services.yml spelling
// 3. Object argument
['service:method', [$node]] // ❌ Pass $node->id() instead
// 4. Check placeholder in HTML source:
// Look for: data-big-pipe-placeholder-id="callback=my_module..."
Input: "Can I use lazy builder directly in Twig template?"
Output:
// In preprocess or controller, add to variables:
$variables['user_greeting'] = [
'#lazy_builder' => ['my_module.lazy_builders:greeting', [$uid]],
'#create_placeholder' => TRUE,
];
{# In template #}
<div class="greeting">
{{ user_greeting }}
</div>
Lazy builder elements can only contain:
$build['lazy'] = [
'#lazy_builder' => [...], // Required
'#create_placeholder' => TRUE, // Optional
'#cache' => [...], // Optional
'#weight' => 10, // Optional
];
// NOT allowed alongside #lazy_builder:
// '#markup', '#theme', '#type', '#prefix', '#children', etc.
| Mistake | Error | Fix |
|---|---|---|
| Object argument | Serialization error | Pass ID, load in callback |
| Array argument | Runtime error | Use implode() |
Missing trustedCallbacks() | Security exception | Implement interface method |
Method not in trustedCallbacks() | Security exception | Add method to array |
Other properties with #lazy_builder | Render error | Remove extra properties |
# Check if BigPipe is processing placeholders
# Look in HTML source for:
# <div data-big-pipe-placeholder-id="callback=...">
# Disable BigPipe temporarily to test
drush pm:uninstall big_pipe
# Check Drupal logs for lazy builder errors
drush watchdog:show --type=php
Invoke whenever the user is working with GitLab. Trigger on any of these signals: a URL containing "gitlab" (gitlab.com or any self-hosted instance like gitlab.sparkfabrik.com), a git remote pointing to GitLab (git@gitlab.com:... or https://gitlab...), the !N merge-request notation (!15, !42), or words like "merge request", "MR", "glab", or "gitlab". Handles issues, merge requests, CI/CD pipelines, releases, and reading files from GitLab repos. Always use glab—not WebFetch or curl—for any GitLab URL because GitLab requires authentication. Do not invoke for GitHub tasks (use the gh skill instead).
Invoke whenever the user is working with GitHub. Trigger on any of these signals: a URL containing "github.com" or a self-hosted GitHub Enterprise host, a git remote pointing to GitHub (git@github.com:... or https://github.com/...), the #N issue or PR notation (#15, #42), or words like "PR", "pull request", "gh", "GitHub issue", "GitHub Actions", "workflow run", "release", "gist", "review this PR", "merge this PR", "CI status", "checks", "fork", or "clone". Handles issues, pull requests, PR reviews and comments, GitHub Actions and CI runs, releases, gists, search, and reading files from GitHub repos. Always use gh, not WebFetch or curl, for any GitHub URL because gh handles authentication and returns structured data. This skill is the GitHub equivalent of glab for GitLab; if the project is on GitHub use this skill, and do not invoke it for GitLab tasks (use the glab skill instead).
Enforce SparkFabrik commit message and branch naming conventions including conventional commits, legacy format detection, issue references, branch prefixes, and the mandatory Assisted-by trailer. MUST be loaded before EVERY git commit, commit message preparation, MR/PR title creation, branch creation, or any commit-related operation. Use whenever the agent is about to run git commit, git checkout -b, git branch, write a commit message, create a branch, or prepare a merge request or pull request title. Also use when the user mentions "commit", "git commit", "conventional commit", "commit message", "refs #", "assisted-by", "commit convention", "branch name", "new branch", or "feature branch". Never create a commit or branch without consulting this skill first.
Create and manage Google Cloud Monitoring dashboards with Terraform (google_monitoring_dashboard) without perpetual plan drift. Use this skill whenever the user works with GCP monitoring dashboards in Terraform or any IaC context - creating a new dashboard, exporting a console dashboard to code, importing an existing dashboard into state, or debugging a terraform plan that keeps showing an in-place update on dashboard_json even right after apply. Trigger on mentions of google_monitoring_dashboard, dashboard_json, Cloud Monitoring / Stackdriver dashboards, dashboard drift, perma-diff, permadiff, "plan is never clean", mosaicLayout, or gcloud monitoring dashboards.
Browser automation with the playwright-cli tool. Use when the user needs to automate browser interactions, test web apps, take screenshots, fill forms, or extract data from pages. Trigger on: "playwright-cli", "playwright", "browser automation", "headless browser", "web scraping", "screenshot", "browser testing", "e2e test".
Configure, run, and troubleshoot Spark HTTP Proxy, the Traefik-based local development reverse proxy that gives Docker containers clean domain names (for example myapp.spark.loc) over HTTP and HTTPS. Use this skill whenever the user wants to expose a local container under a domain, edit a docker-compose service to add VIRTUAL_HOST/VIRTUAL_PORT or traefik.* labels, generate trusted local certificates with mkcert, set up .loc/.dev domain resolution, or debug why a container is not reachable through the proxy. Trigger on signals like VIRTUAL_HOST, VIRTUAL_PORT, spark-http-proxy, "http-proxy", *.spark.loc / *.loc / *.dev / .local dev domains, "my app is not routing locally", "expose this container", "localhost port chaos", mkcert / trusted local HTTPS, configure-dns, or a docker-compose.yml in a local project that should be reachable by name. This is for LOCAL DEVELOPMENT only; it is not for configuring a production Traefik deployment.