As part of the #2844054: Inline Revisionable Content Blocks project, I need to be able to react to a Panels display variant being saved by any given PanelsStorage implementation. Panels currently has no support for this, but it would be really simple and useful to add it, and it could be done without breaking backwards compatibility.

Comments

phenaproxima created an issue. See original summary.

phenaproxima’s picture

Status: Active » Needs review
Issue tags: +Needs tests
StatusFileSize
new4.25 KB

Here is a patch implementing one possible approach (PanelsStorageBase implements a save() method which fires the event). Needs tests, but I'd rather get buy-in from the maintainers before doing that.

Status: Needs review » Needs work

The last submitted patch, 2: 2877024-2.patch, failed testing.

phenaproxima’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
StatusFileSize
new10.76 KB
new6.46 KB

This should fix the broken tests. I also asserted that the event is dispatched, so maybe this doesn't need tests anymore. Optimistically removing the tag.

tim.plunkett’s picture

  1. +++ b/src/PanelsEvents.php
    @@ -0,0 +1,23 @@
    +final class PanelsEvents {
    
    +++ b/src/PanelsVariantEvent.php
    @@ -0,0 +1,40 @@
    +class PanelsVariantEvent extends Event {
    

    Looks good, follows the naming and docs conventions of core.

  2. +++ b/src/Plugin/PanelsStorage/PageManagerPanelsStorage.php
    @@ -67,6 +71,8 @@ class PageManagerPanelsStorage extends PanelsStorageBase implements ContainerFac
       public function save(PanelsDisplayVariant $panels_display) {
    +    parent::save($panels_display);
    +
         $id = $panels_display->getStorageId();
    

    This is the part that worries me. Like this code, Panelizer doesn't call parent::save.

    IMO, the actual event dispatching should happen in \Drupal\panels\Storage\PanelsStorageManager::save(). The way the code reads, that should be the only thing ever calling ::save() on PanelsStorage

  3. +++ b/src/Storage/PanelsStorageBase.php
    @@ -3,7 +3,50 @@
    +    // The event dispatcher is optional here in order to maintain backwards
    +    // compatibility.
    +    $this->eventDispatcher = $event_dispatcher ?: \Drupal::service('event_dispatcher');
    

    Good looking out, since without this Panelizer would break.

  4. +++ b/src/Storage/PanelsStorageBase.php
    @@ -3,7 +3,50 @@
    +    // Allow event subscribers to react to the variant being saved.
    +    $event = new PanelsVariantEvent($panels_display);
    +    $this->eventDispatcher->dispatch(PanelsEvents::VARIANT_SAVE, $event);
    

    Couldn't be more straightforward

  5. +++ b/tests/src/Unit/PanelsStorageTest.php
    @@ -82,7 +92,13 @@ class PanelsStorageTest extends UnitTestCase {
    -    $panels_storage = new PageManagerPanelsStorage([], '', [], $this->entityTypeManager->reveal());
    +    $panels_storage = new PageManagerPanelsStorage(
    +      [],
    +      '',
    +      [],
    +      $this->entityTypeManager->reveal(),
    +      $this->eventDispatcher->reveal()
    +    );
    

    *eyeroll for formatting change*

  6. +++ b/tests/src/Unit/PanelsStorageTest.php
    @@ -108,7 +124,18 @@ class PanelsStorageTest extends UnitTestCase {
    +    $this->eventDispatcher
    +      ->dispatch(PanelsEvents::VARIANT_SAVE, Argument::type(PanelsVariantEvent::class))
    +      ->shouldBeCalled();
    

    Yayyyy

phenaproxima’s picture

StatusFileSize
new11.11 KB

Replying to #5:

  1. Thanks!
  2. Fixed, with a dedicated kernel test.
  3. Cheers! It's gone now :)
  4. Yep. Gotta love it.
  5. *sheepish grin*
  6. Amen to that.

No interdiff this time because it is a rare case where the interdiff is longer than the patch, due to the number of things I ripped out :)

phenaproxima’s picture

Issue tags: +blocker

This is blocking inline block support for Panels: #2883154: Support inline blocks in Panels display variants.

tim.plunkett’s picture

  1. +++ b/src/Plugin/PanelsStorage/PageManagerPanelsStorage.php
    @@ -18,6 +18,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
     class PageManagerPanelsStorage extends PanelsStorageBase implements ContainerFactoryPluginInterface {
     
       /**
    +   * The entity type manager.
    +   *
        * @var \Drupal\Core\Entity\EntityTypeManagerInterface
        */
    
    +++ b/src/Storage/PanelsStorageBase.php
    +++ b/src/Storage/PanelsStorageBase.php
    @@ -4,6 +4,8 @@ namespace Drupal\panels\Storage;
    
    @@ -4,6 +4,8 @@ namespace Drupal\panels\Storage;
     
     use Drupal\Component\Plugin\PluginBase;
     
    +/**
    + * Base class for Panels storage plugins.
    + */
     abstract class PanelsStorageBase extends PluginBase implements PanelsStorageInterface {
    

    out of scope now

  2. +++ b/src/Storage/PanelsStorageManager.php
    @@ -84,6 +97,10 @@ class PanelsStorageManager extends DefaultPluginManager implements PanelsStorage
         $storage->save($panels_display);
    +
    +    // Allow event subscribers to react to the variant being saved.
    +    $event = new PanelsVariantEvent($panels_display);
    +    $this->eventDispatcher->dispatch(PanelsEvents::VARIANT_SAVE, $event);
    

    Is there any reason to move the event before the actual save call? Maybe if an event subscriber wanted to throw an exception or modify the thing before it is saved...

    Also this makes me think it should be named VARIANT_PRE_SAVE or VARIANT_POST_SAVE to indicate which is happening.

  3. +++ b/tests/src/Kernel/PanelsStorageManagerTest.php
    @@ -0,0 +1,70 @@
    +  public function testEvents() {
    

    This test does a lot of specific things to set up a valid $page and $variant, but the only assertion is on Argument::type(PanelsVariantEvent::class)

    To follow-up my earlier point of when the event is actually fired, I think a clearer test would be to do something like

    $this->container->get('event_dispatcher')->addListener(PanelsEvents::VARIANT_SAVE, function (PanelsVariantEvent $event) {
      $event->getVariant()->disable();
    }));
    

    And then check that the variant was disabled afterwards

  4. +++ b/tests/src/Unit/PanelsStorageTest.php
    @@ -82,7 +82,12 @@ class PanelsStorageTest extends UnitTestCase {
    -    $panels_storage = new PageManagerPanelsStorage([], '', [], $this->entityTypeManager->reveal());
    +    $panels_storage = new PageManagerPanelsStorage(
    +      [],
    +      '',
    +      [],
    +      $this->entityTypeManager->reveal()
    +    );
    
    @@ -108,7 +113,12 @@ class PanelsStorageTest extends UnitTestCase {
    -    $panels_storage = new PageManagerPanelsStorage([], '', [], $this->entityTypeManager->reveal());
    +    $panels_storage = new PageManagerPanelsStorage(
    +      [],
    +      '',
    +      [],
    +      $this->entityTypeManager->reveal()
    +    );
    

    Not gonna highlight them all, but also out of scope now

phenaproxima’s picture

StatusFileSize
new8.26 KB
new7.16 KB

All fixed! I added pre-save and post-save events, for convenience and consistency.

phenaproxima’s picture

StatusFileSize
new8.31 KB
new3.59 KB

Made additional changes requested by @tim.plunkett on Slack, and made the test a little more useful.

tim.plunkett’s picture

Status: Needs review » Reviewed & tested by the community

That test looks great! If it passes, this is good to go.

  • japerry committed c38767a on 8.x-4.x authored by phenaproxima
    Issue #2877024 by phenaproxima: Fire an event when a Panels display...
japerry’s picture

Status: Reviewed & tested by the community » Fixed

Looks good. Committed.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.