drupal

Wim Leers: XB week 20: 0.1.0-alpha during DrupalCon!

DrupalCon week! On Monday, we landed the last issue to achieve the 0.1 milestone: The XB annotations and labels should not change size when zooming — thanks Utkarsh “utkarsh_33”, Atul “soaratul” Dubey and Bálint “balintbrews” Kléri for guiding it across the finish line!

That was the only noteworthy commit of the week, because of Acquia’s team working full-time on Experience Builder (XB), Ben “bnjmnm” Mullins, Jesse “jessebaker” Baker, Lauri “lauriii” Timmanee and Bálint (he helped us achieve 0.1 on Monday and traveled on Tuesday!) were attending DrupalCon. Also at DrupalCon was Dave “longwave” Long, who we’re sponsoring part time.

So it was with a lot of satisfaction that I tagged the 0.1.0-alpha1 release on the morning of the DriesNote :)

Not at DrupalCon: research mode

With roughly half of the team at DrupalCon this week, and with 0.1.0 done, the rest of us pivoted to preparing for the next milestone: 0.2.0. Many technical details need to be figured out for the next batch of product requirements that Lauri prioritized (together with Alex “effulgentsia” Bronstein).

We started research on:

XB data model meeting — 50/50 remote & in-person

During DrupalCon, Lauri, Ted, Alex, I met with with core committers Alex Pott, catch and Dave met to discuss XB’s JSON-based data storage model that XB currently implements. We’re not yet fully aligned (catch pointed out the search index aspect is important to support — the question is how to support that without compromising the UX Lauri envisions), but the discussion is much clearer today than it was in June, because there’s now concrete code to point to. That removed a lot of confusion on both “sides” (we’re all on the same side: we want the brightest future for Drupal!).

The meeting we had during DrupalCon led to:

  1. Alex Bronstein identifying a possible alternative implementation that would meet both the original goals, and address most concerns: #3477428: Refactor the XB field type to be multi-valued, to de-jsonify the tree, and to reference the field_union type of the prop values.
  2. Me unpostponing the #3467870: Support {type: array, …} prop shapes issue and pushing it forward. First making this work would help prevent #3477428 (see prior point) going in a direction that would make it impossible to support type: array Single Directory Component (SDC) props, which should be represented by multi-value fields (fields configured for multiple cardinality). I made the back-end pieces work during DrupalCon, but to make it work end-to-end additional infrastructure on the client side is needed first. For that: see the last “research” bullet above.

Missed a prior week? See all posts tagged Experience Builder.

Goal: make it possible to follow high-level progress by reading ~5 minutes/week. I hope this empowers more people to contribute when their unique skills can best be put to use!

For more detail, join the #experience-builder Slack channel. Check out the pinned items at the top!

Presentations at DrupalCon

Of course, Dries included and demonstrated Experience Builder 0.1.0 during the DriesNote:

The XB section of the DriesNote starts at 50:44.

Lauri talked about what’s been happening with XB and what will happen next:

Many of the things Lauri shared with all of you had only been seen by Lauri, not by anybody else! :D

After his session, Lauri had many hallway conversations that increased our conviction that we’re on the right track with XB! :)

And in my humble opinion the most inspiring — Ben’s session about how XB uses parts of the JSX theme engine and Redux:

Ben walks you through how XB leverages React and Redux to achieve the UX we need, while using existing Drupal field widgets. This will become even more important once we integrate the content entity form, with field widgets for base and bundle fields.

You have to watch the 20 seconds starting at 1:37 — pure genius: not the predictable AI-generated images to illustrate his talk, instead … his son’s drawings! :D

I hope to follow in his footsteps at a future DrupalCon, because I too am becoming a dad, very soon! :D I’ll be working at a very reduced rate during my paternity leave, but will be keeping these weekly blog posts going — it’s my way of keeping myself in the loop as well as all of you. That is also why I’ve shifted attention to meta things, to ensure the right expertise is present in areas that need to keep moving during my upcoming paternity leave :)

Week 20 was September 23–29, 2024.

  1. We’re asking Dave to weigh in on a number of areas, to point his critical, independent core committer eye to key decisions early on. ↩︎

Talking Drupal: Talking Drupal #470 - Creating Recipes

Today we are talking about Creating Recipes, What Recipes already exist, and helpful tips and tricks with guest Jim Birch. We’ll also cover Features as our module of the week.

For show notes visit: https://www.talkingDrupal.com/470

Topics
  • What are recipes
  • How do you recommend someone get started writing recipes
  • Where can people find recipes
  • Can you include sub recipes
  • How should you test recipes
  • Any tools that make writing recipes easier
  • What recipes are needed that do not exist
  • How can people move recipes forward
Resources Guests

Jim Birch - linkedin.com/in/jimbirch thejimbirch

Hosts

Nic Laflin - nLighteneddevelopment.com nicxvan John Picozzi - epam.com johnpicozzi Aubrey Sambor - star-shaped.org starshaped

MOTW Correspondent

Martin Anderson-Clutz - mandclu.com mandclu

  • Brief description:
    • Have you ever wanted an admin UI to manage sets of configuration, to version and share across Drupal sites? There’s a module for that.
  • Module name/project name:
  • Brief history
    • How old: created in Mar 2009 by yhahn, though recent releases are by Dave Reid
    • Versions available: 7.x-2.15 and 8.x-3.14, the latter of which works with Drupal 9.4 and 10
  • Maintainership
    • Minimally maintained
    • Security coverage
    • Test coverage
    • Documentation: Has a documentation guide and probably hundreds if not thousands of of tutorials available
    • Number of open issues: 610 open issues, 54 of which are bugs against the 8.x branch
  • Usage stats:
    • Almost 117,000 sites, though the majority are using the D7 version
  • Module features and usage
    • Many listeners will remember Features as the de facto solution for configuration management in Drupal 7 and earlier
    • As the name implies, it was really intended to share common capabilities across different Drupal sites
    • Unlike recipes, Features can have version numbers, because there is a path to sync configuration updates across sites using a Feature, though this is where a lot of teams found Features could be complex to use
    • We did previously cover Features as MOTW all the way back in episode #147, but I thought it was relevant to today’s discussion because of the way it provides a UI for organizing and exporting specific sets of configuration
    • There is an open issue for Features to directly export recipes, because it already does a lot of the time-consuming work of collecting together necessary config files, including dependencies
    • Even its current state, it could be a time saver for anyone wanting to start creating their own recipes

Twin Cities Drupal Camp: After Camp: Stay connected with our Mid-Day Meetup

After Camp: Stay connected with our Mid-Day Meetup Published Date Monday, October 7th, 2024 - 12:52 pm cosmicdreams Mon, 10/07/2024 - 12:52 Image removed.

We're restarting our Mid-Day Meetup:  a remote-only meetup we have over Zoom.  You can RSVP to attend.

https://groups.drupal.org/node/537103

We'll be talking about the knowledge shared at the recent Twin Cities Drupal Camp and Drupalcon Barcelona.  We're also trying to expand the reach of our Mid-Day meetup beyond our regional meetup and welcome everybody into our remote hour of Drupal chatter.

Agenda

  • 15m - Meet and Greet
  • 30m - Discuss new stuff going on in Drupal / Drupalcon Barcelona's "Mother of all Demos"
  • 15m - cosmicdreams is hoping to do a demo of adapting an existing component library system to Single Directory Components (SDC).

Help us spread the word of these Mid-Day meetups by sharing this with your coworkers.

 

 

Posted In Drupal Planet

LN Webworks: LN Webworks Amazing Experience at DrupalCon Barcelona 2024

Image removed.

As a Top-rated Drupal Development Company, attending DrupalCon Barcelona for the first time exceeded all of our expectations. The energy of the event was incredible, and it gave us the opportunity to connect with so many people in person. One of the standout moments was the inspiring StarShot initiative, whose marketing strategy makes a compelling case for businesses to consider Drupal as a solution.

Starshot / Drupal CMS Product Strategy

NO CODE website building, built on top of Drupal core itself. So, it will be easily able to beat the other no-code solutions like WIX, SQUARESPACE, and Shopify while still being able to maintain its open-source nature where you still will be able to have full control to customize and override things on your own.

Promet Source: DUSWDS: Your Agency's USWDS-Aligned CMS Solution

Takeaway: Federal agencies face significant challenges in creating modern, accessible, and user-friendly websites, falling short of accessibility standards and user expectations. DUSWDS is a solution to these challenges. It is a USWDS-aligned Drupal distribution specifically designed for federal agencies to meet accessibility requirements, improve user experience, and save time and resources on development and maintenance.

Golems GABB: Drupal integrations with Popular Cloud Services: AWS vs MS Azure vs GCP

Drupal integrations with Popular Cloud Services: AWS vs MS Azure vs GCP Editor Fri, 10/04/2024 - 15:39

Welcome to the world of cloud integration, where popular cloud services such as AWS, Azure, and GCP are the keys to a Drupal site's success.
Imagine that you've finished your Drupal website. It turned out fantastic, but the basic options are not enough for you. So you can't wait to unleash its full potential. This is where cloud services come into play. They are your site's superhero assistants.
Today, our Drupal team plans to look at the benefits of these cloud services and how AWS, Azure, and GCP can take your Drupal website to a new performance, scalability, and security. Get ready to revolutionize your online presence and, of course, leave your competitors behind.

PreviousNext: Entity theming with Pinto

Learn how to make entity theming a breeze using the Pinto module. If you haven’t already, check out the first part of this series for an introduction to all things Pinto.

by adam.bramley / 3 October 2024

In our last post, we discussed Pinto concepts and how to use Theme objects to encapsulate theming logic in a central place for a component. Next, we’ll apply that knowledge to theming an entity. This will demonstrate the power of Pinto and how it will dramatically improve the velocity of delivering new components. 

One of the hardest things about theming Drupal is outputting markup that matches your design system. 

For example:

  • Removing the “div soup” of Drupal fields
  • Adding custom classes or attributes to field output
  • Wrapping fields in custom tags (e.g. an h2)

While there are plenty of modules to alleviate this, it can often mean you have a mix of YAML configuration for markup, preprocess hooks, overridden templates, etc., to pull everything together. Pinto allows you to easily render an entity while reusing your frontender’s perfect template!

We need to cover a few more concepts and set things up to pull this all together. Once set up, new bundles or entity types can be added with ease.

We'll continue our Card component example from the previous post and cover:

  1. Setting up a bundle class. In this example, we will implement it as a Block Content bundle
  2. Using a custom entity view builder
  3. Theming a Card block using Pinto

Bundle classes

In case you’re not aware, Drupal introduced the concept of Bundle classes almost three years ago. They essentially allow business logic for each bundle to be encapsulated in its own PHP class and benefit from regular PHP concepts such as code sharing via Traits, Interfaces, etc.

At PreviousNext, our go-to for implementing bundle classes is the BCA module, which allows you to define a class as a custom Bundle class via an attribute, removing the need for hook_entity_bundle_info_alter.

Our standard setup on projects is:

  • An Interface per entity type (e.g MyProjectBlockContentInterface)
  • An abstract base class per entity type (e.g. MyProjectBlockContentBase)
  • A Bundle class per bundle
  • Traits and interfaces for any shared fields/logic (e.g. BodyTrait for all bundles that have a Body field)

My preferred approach is to have a directory structure that matches the entity type located inside the project’s profile (e.g. src/Entity/BlockContent/Card.php. Feel free to set this up however you like. For example, some people may prefer to separate entity types into different modules.

Let’s set up our Card bundle class:

namespace Drupal\my_project_profile\Entity\BlockContent; use Drupal\bca\Attribute\Bundle; use Drupal\my_project_profile\Traits\DescriptionTrait; use Drupal\my_project_profile\Traits\ImageTrait; use Drupal\my_project_profile\Traits\TitleTrait; #[Bundle(entityType: self::ENTITY_TYPE_ID, bundle: self::BUNDLE)] final class Card extends MyProjectBlockContentBase { use TitleTrait; use DescriptionTrait; use ImageTrait; public const string BUNDLE = 'card'; }

Here we use the Bundle attribute provided by the BCA module to automatically register this class as the bundle class for the card bundle. We’re using constants here to make it easy to reference this machine name anywhere in our codebase. The ENTITY_TYPE_ID constant comes from the parent interface.

NOTE: I won’t go into too much detail about how the interfaces, base classes, and traits are set up. There are plenty of examples of how you might write these. Check out the change record for some basic examples! 

In our case, each trait is a getter/setter pair for each of our fields required to build our Card component: 

  • Title - a plain text field
  • Description - another plain text field
  • Image - a Media reference field.

Custom entity view builder

EntityViewBuilders are PHP classes that contain logic on how to build (or render) an entity. Entity types can have custom EntityViewBuilders; for example BlockContent has its own defined in core. These are defined in the view_builder handler in an entity type's annotation and can also be overridden by using hook_entity_type_alter.

By default, the view builder class takes all of your configuration in an entity view display (i.e. field formatter settings, view modes, etc.) and renders it. We are using a custom view builder class to bypass all of that and simply return a render array via a Pinto object.

The function that drives this is getBuildDefaults so that’s all we need to override.

For this example, a custom view builder for the block content entity type can be as simple as:

namespace Drupal\my_project_profile\Handler; use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Entity\EntityInterface; use Drupal\block_content\BlockContentViewBuilder; use Drupal\my_project_profile\Entity\Interface\BuildableEntityInterface; class MyProjectBlockContentViewBuilder extends BlockContentViewBuilder { /**  * {@inheritdoc}  */ public function getBuildDefaults(EntityInterface $entity, $view_mode) {   $build = parent::getBuildDefaults($entity, $view_mode);   if (!$entity instanceof BuildableEntityInterface || !$entity->shouldBuild($view_mode)) {     return $build;   }   $cache = CacheableMetadata::createFromRenderArray($build);   $build = $entity->build($view_mode);   $cache->merge(CacheableMetadata::createFromRenderArray($build))     ->applyTo($build);   return $build; } }

Here, we check for a custom BuildableEntityInterface and call a shouldBuild method. If either of those are FALSE then we fall back to Drupal’s default behaviour. Otherwise, we gather cacheable metadata from both the default build and the result of calling the build method, and then return the output. We will cover these in more detail shortly.

Now we just need an alter hook to wire things up:

use Drupal\my_project_profile\Handler\MyProjectBlockContentViewBuilder; /** * Implements hook_entity_type_alter(). */ function my_project_profile_entity_type_alter(array &$entity_types): void {  /** @var \Drupal\Core\Entity\ContentEntityType $blockContentDefinition */  $blockContentDefinition = $entity_types['block_content'];  // Override view builder class.  $blockContentDefinition->setViewBuilderClass(MyProjectBlockContentViewBuilder::class); }

Pro tip: Use the Hux module to do this in a Hooks class.

Now, any BlockContent bundle class that implements BuildableEntityInterface and returns TRUE from its shouldBuild method will completely bypass Drupal’s standard entity rendering and instead just return whatever we want from its build method.

BuildableEntityInterface

namespace Drupal\my_project_profile\Entity\Interface; /** * Interface for entities which override the view builder. */ interface BuildableEntityInterface { /**  * Default method to build an entity.  */ public function build(string $viewMode): array; /**  * Determine if the entity should be built for the given view mode.  */ public function shouldBuild(string $viewMode): bool; }

This interface can be added to the Bundle class itself or the custom entity type interface we discussed earlier to keep all bundles consistent. This doesn’t just apply to the Block content entity type; you can use this for Paragraphs, Media, or your custom entity types. You’ll just need to override the view builder for each. 

It is generally not recommended to use this for Node since you’re more likely to get value out of something like Layout Builder for rendering nodes. Those layouts would then have block content added to them, which in turn will be rendered via this method.

Back to our Card example. It was extending a custom base class MyProjectBlockContentBase. That class may look something like this:

namespace Drupal\my_project_profile\Entity\BlockContent; use Drupal\block_content\BlockContentTypeInterface; use Drupal\block_content\Entity\BlockContent; abstract class MyProjectBlockContentBase extends BlockContent implements MyProjectBlockContentInterface { /**  * {@inheritdoc}  */ public function shouldBuild(string $viewMode): bool {   return TRUE; } }

Our base class extends core’s BlockContent class and implements our custom interface.

That custom interface can then extend BuildableEntityInterface.

The shouldBuild method is an optional implementation detail, but it is nice if you have multiple view modes for a bundle, which need to have differing logic. For example, you might have a media_library view mode that you want to continue to use Drupal’s standard rendering.

Now, all we need to do is implement the build method on our BlockContent bundle classes.

Let’s look at the Card example:

use Drupal\my_project_ds\ThemeObject\Card as PintoCard; final class Card extends MyProjectBlockContentBase {    // Trimmed for easy reading.  /**   * {@inheritdoc}   */  public function build(string $viewMode): array {    return PintoCard::createFromCardBlock($this)();  } }

Here, we’re simply returning the render array that results from invoking our Card Pinto object (aliased as PintoCard via the use statement).

We have also introduced a factory method createFromCardBlock on the Pinto theme object, which takes the entity and injects its data into the object.

This is what the fully implemented Pinto object would look like

namespace Drupal\my_project_ds\ThemeObject; use Drupal\Core\Cache\CacheableDependencyInterface; use Drupal\my_project_profile\Entity\BlockContent\Card as CardBlock; use Drupal\my_project_ds\MyProjectDs\MyProjectObjectTrait; use Pinto\Attribute\ThemeDefinition; #[ThemeDefinition([ 'variables' => [   'title' => '',   'description' => '',   'image' => '', ], ])] final class Card implements CacheableDependencyInterface { use MyProjectObjectTrait; private function __construct(   private readonly string $title,   private readonly array $image,   private readonly ?string $description, ) {} public static function createFromCardBlock(CardBlock $card): static {   return new static(     $card->getTitle(),     $card->getImage(),     $card->getDescription(),   ); } protected function build(mixed $build): mixed {   return $build + [     '#title' => $this->title,     '#description' => $this->description,     '#image' => $this->image,   ]; } }

The build and constructor methods were covered in our previous Pinto post. All that’s new here is the createFromCardBlock method, where we use the getters from the bundle class traits to inject the entity’s data into the constructor.

We also briefly mentioned cacheable metadata in our last post. Since our Pinto object implements CacheableDependencyInterface, we can add that metadata directly to the theme object. For example, you should enhance the bundle class’ build method to add the Image media entity as a cacheable dependency. That way if the media entity is updated, the Card output is invalidated.

/** * {@inheritdoc} */ public function build(string $viewMode): array { $build = PintoCard::createFromCardBlock($this); $image = $this->image->entity; if ($image) {    $build->addCacheableDependency($image);  } return $build(); }

Now, we have end-to-end rendering of a Drupal entity using Pinto Theme objects to render templates defined in a Storybook design system.

New bundles are simple to implement. All that’s needed is to click together the fields in the UI to build the content model, add the new Theme object, and wire that together with a bundle class.

I can’t overstate how much this has sped up our backend development. My latest project utilised Pinto from the very beginning, and it has made theming the entire site extremely fast and even… fun! 😀