33namespace Drupal \patternkit \Element ;
44
55use Drupal \Core \Plugin \ContainerFactoryPluginInterface ;
6+ use Drupal \Core \Render \Attribute \RenderElement ;
67use Drupal \Core \Render \BubbleableMetadata ;
78use Drupal \Core \Render \Element ;
89use Drupal \Core \Render \Element \RenderElementBase ;
910use Drupal \patternkit \Entity \PatternInterface ;
10- use Drupal \patternkit \Exception \ PatternAssetException ;
11+ use Drupal \patternkit \Event \ PatternRenderFailureEvent ;
1112use Drupal \patternkit \Exception \UnknownPatternException ;
1213use Drupal \patternkit \PatternFieldProcessorPluginManager ;
14+ use Drupal \patternkit \PatternkitEvents ;
1315use Drupal \patternkit \PatternLibraryPluginInterface ;
1416use Drupal \patternkit \PatternLibraryPluginManager ;
1517use Drupal \patternkit \PatternRepository ;
1618use Symfony \Component \DependencyInjection \ContainerInterface ;
19+ use Symfony \Component \EventDispatcher \EventDispatcherInterface ;
1720
1821/**
1922 * Provides a render element to display a pattern.
3841 * ];
3942 * @endcode
4043 *
41- * @RenderElement("pattern")
44+ * @see \Drupal\patternkit\Element\PatternError
45+ * @see \Drupal\patternkit\PatternkitEvents::RENDER_FAILURE
46+ * @see \Drupal\patternkit\EventSubscriber\RenderFailureErrorDisplaySubscriber
4247 */
48+ #[RenderElement(
49+ id: "pattern " ,
50+ )]
4351class Pattern extends RenderElementBase implements ContainerFactoryPluginInterface {
4452
45- /**
46- * The pattern field processor plugin manager.
47- *
48- * @var \Drupal\patternkit\PatternFieldProcessorPluginManager
49- */
50- protected PatternFieldProcessorPluginManager $ fieldProcessorPluginManager ;
51-
52- /**
53- * The pattern library plugin manager.
54- *
55- * @var \Drupal\patternkit\PatternLibraryPluginManager
56- */
57- protected PatternLibraryPluginManager $ libraryPluginManager ;
58-
59- /**
60- * The pattern repository service for loading patterns.
61- *
62- * @var \Drupal\patternkit\PatternRepository
63- */
64- protected PatternRepository $ patternRepository ;
65-
6653 /**
6754 * {@inheritdoc}
6855 */
@@ -93,20 +80,19 @@ public function getInfo(): array {
9380 * The pattern library parser plugin manager service.
9481 * @param \Drupal\patternkit\PatternRepository $patternRepository
9582 * The pattern repository service.
83+ * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
84+ * The event dispatcher service.
9685 */
9786 public function __construct (
9887 array $ configuration ,
9988 $ plugin_id ,
10089 $ plugin_definition ,
101- PatternFieldProcessorPluginManager $ fieldProcessorPluginManager ,
102- PatternLibraryPluginManager $ libraryPluginManager ,
103- PatternRepository $ patternRepository ,
90+ protected PatternFieldProcessorPluginManager $ fieldProcessorPluginManager ,
91+ protected PatternLibraryPluginManager $ libraryPluginManager ,
92+ protected PatternRepository $ patternRepository ,
93+ protected EventDispatcherInterface $ eventDispatcher ,
10494 ) {
10595 parent ::__construct ($ configuration , $ plugin_id , $ plugin_definition );
106-
107- $ this ->fieldProcessorPluginManager = $ fieldProcessorPluginManager ;
108- $ this ->libraryPluginManager = $ libraryPluginManager ;
109- $ this ->patternRepository = $ patternRepository ;
11096 }
11197
11298 /**
@@ -120,6 +106,7 @@ public static function create(ContainerInterface $container, array $configuratio
120106 $ container ->get ('plugin.manager.pattern_field_processor ' ),
121107 $ container ->get ('plugin.manager.library.pattern ' ),
122108 $ container ->get ('patternkit.pattern.repository ' ),
109+ $ container ->get ('event_dispatcher ' ),
123110 );
124111 }
125112
@@ -133,26 +120,27 @@ public static function create(ContainerInterface $container, array $configuratio
133120 * The modified element.
134121 */
135122 public function loadPattern (array $ element ): array {
123+ // If the pattern is specified as a string, assume it is the pattern name
124+ // and attempt to load it.
136125 if (isset ($ element ['#pattern ' ]) && is_string ($ element ['#pattern ' ])) {
137126 $ pattern_id = $ element ['#pattern ' ];
138127
139128 try {
140129 $ element ['#pattern ' ] = $ this ->patternRepository ->getPattern ($ pattern_id );
141130 }
142- catch (UnknownPatternException $ exception ) {
143- // Replace the pattern output with an error element for more
144- // sophisticated output handling.
145- // @see \Drupal\patternkit\Element\PatternError
146- $ elements = [];
147- $ elements ['error ' ] = [
148- '#type ' => 'pattern_error ' ,
149- '#pattern ' => $ element ['#pattern ' ],
150- '#config ' => $ element ['#config ' ] ?? [],
151- '#context ' => $ element ['#context ' ] ?? [],
152- '#exception ' => $ exception ,
153- ];
131+ catch (\Throwable $ exception ) {
132+ // Dispatch an event for the render failure to allow subscribers to
133+ // react and handle it appropriately.
134+ $ event = $ this ->dispatchRenderFailureEvent ($ element , $ exception );
154135
155- return $ elements ;
136+ // If the event was handled, update the elements to be returned.
137+ if ($ event ->isHandled ()) {
138+ return $ event ->getElement ();
139+ }
140+ else {
141+ // Propagate the exception if an event dispatcher didn't handle it.
142+ throw $ exception ;
143+ }
156144 }
157145 }
158146
@@ -167,47 +155,46 @@ public function loadPattern(array $element): array {
167155 *
168156 * @return array
169157 * The modified element.
170- *
171- * @throws \Drupal\Component\Plugin\Exception\PluginException
172158 */
173159 public function preRenderPatternElement (array $ element ): array {
174- /** @var \Drupal\patternkit\Entity\PatternInterface|null $pattern */
175- $ pattern = $ element ['#pattern ' ];
176-
177- // Fail early if a pattern was unable to be loaded.
178- if ($ pattern === NULL ) {
179- return [
180- '#markup ' => $ this ->t ('Pattern unavailable. ' ),
181- ];
182- }
183-
184160 // Capture base bubbleable metadata.
185161 $ metadata = BubbleableMetadata::createFromRenderArray ($ element );
186162
187- // Ensure the metadata from the pattern is present.
188- $ metadata ->addCacheableDependency ($ pattern );
189-
190- $ pattern ->config = $ element ['#config ' ];
191- $ pattern ->context = $ element ['#context ' ];
163+ $ pattern = $ element ['#pattern ' ] ?? NULL ;
192164
193165 try {
166+ // Fail early if a pattern was unable to be loaded.
167+ if (!($ pattern instanceof PatternInterface)) {
168+ throw new UnknownPatternException ('No pattern entity available for rendering. ' );
169+ }
170+
171+ // Ensure the metadata from the pattern is present.
172+ $ metadata ->addCacheableDependency ($ pattern );
173+
174+ // Capture instance data for the pattern and apply preprocessing
175+ // throughout schema values.
176+ $ pattern ->config = $ element ['#config ' ];
177+ $ pattern ->context = $ element ['#context ' ];
194178 $ this ->fieldProcessorPluginManager ->processSchemaValues ($ pattern , $ pattern ->config , $ pattern ->context , $ metadata );
195179
180+ // Use the pattern's library plugin to produce rendered output.
196181 $ library_plugin = $ this ->getPatternLibraryPlugin ($ pattern );
197182 $ elements = $ library_plugin ->render ([$ pattern ]);
198183 }
199- catch (PatternAssetException $ exception ) {
200- // Replace the pattern output with an error element for more sophisticated
201- // output handling.
202- // @see \Drupal\patternkit\Element\PatternError
203- $ elements = [];
204- $ elements ['error ' ] = [
205- '#type ' => 'pattern_error ' ,
206- '#pattern ' => $ element ['#pattern ' ],
207- '#config ' => $ element ['#config ' ],
208- '#context ' => $ element ['#context ' ],
209- '#exception ' => $ exception ,
210- ];
184+ catch (\Throwable $ exception ) {
185+ // Dispatch an event for the render failure to allow subscribers to react
186+ // and handle it appropriately.
187+ $ event = $ this ->dispatchRenderFailureEvent ($ element , $ exception );
188+
189+ // If the event was handled, update the elements to be returned.
190+ if ($ event ->isHandled ()) {
191+ $ elements = $ event ->getElement ();
192+ }
193+ else {
194+ $ elements = [
195+ '#markup ' => $ this ->t ('This content is unavailable. ' ),
196+ ];
197+ }
211198 }
212199
213200 // Ensure all metadata persists to the result.
@@ -251,4 +238,20 @@ protected function getPatternLibraryPlugin(PatternInterface $pattern): PatternLi
251238 return $ plugin ;
252239 }
253240
241+ /**
242+ * Create a new render failure event instance for dispatching.
243+ *
244+ * @param array $element
245+ * The render element resulting in a failure.
246+ * @param \Throwable $exception
247+ * The exception encountered leading to the render failure.
248+ *
249+ * @return \Drupal\patternkit\Event\PatternRenderFailureEvent
250+ * The configured render failure event for dispatching.
251+ */
252+ protected function dispatchRenderFailureEvent (array $ element , \Throwable $ exception ): PatternRenderFailureEvent {
253+ $ event = new PatternRenderFailureEvent ($ element , $ exception );
254+ return $ this ->eventDispatcher ->dispatch ($ event , PatternkitEvents::RENDER_FAILURE );
255+ }
256+
254257}
0 commit comments