Thanks, I got it working.
Here's some proof-of-concept code for anyone interested - Block field support for Views and Webform blocks. (Not using content/custom blocks but understanding what's happening here, should be easy to implement.)
namespace Drupal\transform_api_block_field\Plugin\Transform\Field;
use Drupal\Core\Block\BlockManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\group\Context\GroupRouteContext;
use Drupal\transform_api\FieldTransformBase;
use Drupal\transform_api_views\Transform\ViewTransform;
use Drupal\views\Plugin\Block\ViewsBlock;
use Drupal\webform\Entity\Webform;
use Drupal\webform\Plugin\Block\WebformBlock;
use Drupal\webform\WebformTokenManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Transform field plugin for block field items.
*
* @FieldTransform(
* id = "block_field_transform",
* label = @Translation("Block field transform"),
* field_types = {
* "block_field"
* }
* )
*/
class BlockFieldTransform extends FieldTransformBase {
/**
* The block manager service.
*
* @var \Drupal\Core\Block\BlockManagerInterface
*/
protected $blockManager;
/**
* The group content storage service.
*
* @var \Drupal\group\Entity\GroupRouteContext
*/
protected $groupRouteContext;
/**
* The token manager service.
*
* @var \Drupal\webform\WebformTokenManagerInterface
*/
protected $tokenManager;
/**
* Constructs a BlockFieldTransform object.
*
* @param string $plugin_id
* The plugin_id for the transform.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The definition of the field to which the transform is associated.
* @param array $settings
* The transform settings.
* @param string $label
* The transform label display setting.
* @param string $transform_mode
* The transform mode.
* @param array $third_party_settings
* Any third party settings.
* @param \Drupal\Core\Block\BlockManagerInterface $block_manager
* The block manager service.
* @param \Drupal\group\Entity\GroupRouteContext $group_route_context
* The group route context to get the group entity.
* @param \Drupal\webform\WebformTokenManagerInterface $token_manager
* The token manager service.
*/
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $transform_mode, array $third_party_settings, BlockManagerInterface $block_manager, GroupRouteContext $group_route_context, WebformTokenManagerInterface $token_manager) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $transform_mode, $third_party_settings);
$this->blockManager = $block_manager;
$this->groupRouteContext = $group_route_context;
$this->tokenManager = $token_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$plugin_id,
$plugin_definition,
$configuration['field_definition'],
$configuration['settings'],
$configuration['label'],
$configuration['transform_mode'],
$configuration['third_party_settings'],
$container->get('plugin.manager.block'),
$container->get('group.group_route_context'),
$container->get('webform.token_manager')
);
}
/**
* {@inheritdoc}
*/
public function transformElements(FieldItemListInterface $items, $langcode) {
$elements = [];
if ($items->isEmpty()) {
return $elements;
}
foreach ($items as $item) {
// Assuming each $item contains the plugin_id and settings.
$plugin_id = $item->get('plugin_id')->getValue();
$settings = $item->get('settings')->getValue();
// Create block plugin instance.
$block_plugin = $this->blockManager->createInstance($plugin_id, $settings);
// We currently only handle Views blocks and Webform blocks.
// Views blocks leverage the ViewsTransform plugin, while Webform blocks
// get the form elements and transform them as a simple field list.
if ($block_plugin instanceof ViewsBlock) {
$args = [];
// If you need to pass contextual filter values, add them here.
// You can get any other fields from the parent by loading the entity
// with $items->getEntity(); - this is useful if you want to get a
// category, or some other setting the View needs.
//
// Get the View id and display id, this is one way.
$plugin_id_array = explode(':', $plugin_id);
$view_name_array = explode('-', $plugin_id_array[1]);
// Transform API Views submodule provides a ViewsTransform plugin.
$elements[] = new ViewTransform($view_name_array[0], $view_name_array[1], [], $args);
}
elseif ($block_plugin instanceof WebformBlock) {
// Get the Webform ID from the block settings.
$webform_id = $settings['webform_id'];
// Load the webform.
$webform = Webform::load($webform_id);
if ($webform) {
// Return only the form elements.
$webform_elements = $webform->getElementsInitialized();
$webform_elements = $this->tokenManager->replace($webform_elements, $webform);
$elements[] = [
'type' => 'webform',
'webform_id' => $webform_id,
'fields' => $this->processWebformFields($webform_elements),
];
}
}
}
return $elements;
}
/**
* Removes the hash from the keys of the given array for Transform API.
*
* Also turns the root level keys to numeric.
*/
protected function processWebformFields(array $array, $level = 0) {
$result = [];
$index = 0;
foreach ($array as $key => $value) {
// Remove the leading '#' from the key.
$newKey = ltrim($key, '#');
// If the value is an array, process it recursively.
if (is_array($value)) {
$value = $this->processWebformFields($value, $level + 1);
}
// Add the new key-value pair to the result array.
if ($level === 0) {
// For the first level, use numeric keys.
$result[$index] = $value;
$index++;
}
else {
// For deeper levels, retain associative keys.
$result[$newKey] = $value;
}
}
return $result;
}
}
Also field support for Views Transforms is definitely on the wishlist.
irowboat → created an issue.
But hopefully 1.1-beta5 resolves the issue.
The attached commit seems to fix the issue. Awesome work - this is crucial for decoupling a Paragraphs-driven site. Thank you!
@LupusGr3y
I went another route so can't confirm this at the moment, but good to hear.
On a sidenote, I still think this module has enormous potential.
I'm currently building a POC testing a setup where a lightweight JS (Sveltekit but that's irrelevant) app directs requests to Drupal and renders the Transform API-created JSON if the Drupal path exists. I'm not doing extra requests to Drupal for now, so it made sense to change the caching to user based (by adding user to cacheContexts in TransformationCache.php).
If this works, it's practically just replacing Drupal's theme layer with something less painful, sidestepping most headaches decoupling usually brings. I don't even need to worry about auth syncing with the frontend! Still to solve some issues like form submits and media paths though.
Also hoping the Views integration works well, since it definitely simplifies things.
irowboat → created an issue.
irowboat → changed the visibility of the branch 3398778- to active.
My goal was using Commerce, and noticed adding variation data to (json) product data wasn't configurable through the UI.
Configuration fields (such as Commerce's variations, their price fields etc.) apparently generally aren't visible to/configurable with Transform API (UI), as the basic entity config data is hardcoded. It would add a lot of flexibility (in some cases) if 'Manage display' and 'Manage transform' would show similar fields. Probably too much of a rewrite to ask, but at least worth mentioning.
As for the scenario mentioned, it seems that my options would (at least) be altering the product transform or creating a custom block to include variation data and appending that to products.
If you can elaborate on ways to go about it, it would definitely save some time.
Once again thanks!
irowboat → created an issue.