Problem/Motivation

Currently, a variants configuration is part of the Page config entities configuration.

This makes life a little difficult for a couple reasons:

  • Variants have two part IDs: the page id, then the variant id or uuid. This means all the UIs need to pass around the page id, which, if the UI comes from the display variant plugin - shouldn't need to know the page id! See #2511570: Remove Drupal\page_manager\Plugin\PageAwareVariantInterface
  • Currently, page manager works around this by making the variant aware of Page, which then couples the variants to Page Manager. But we might want to use the same display variant plugin (for example, the Panels varaint) outside of Page Manager, for example, on a Mini Panel or in Panelizer.
  • We can't provide a single variant as part of a Feature

Proposed resolution

Make variants into their own independent config entity!

Remaining tasks

  1. Extract the variant config entity stuff from the wizard patch at #2550879: Use CTools Wizard API to add/edit Pages (and move plugin UI using PluginWizardInterface)
  2. Get current tests all passing
  3. Add new test coverage for new code
  4. Review
  5. Commit

User interface changes

None.

API changes

Lots.

Data model changes

Yes.

CommentFileSizeAuthor
#88 2551633-pm-88.patch166.43 KBtim.plunkett
#88 interdiff.txt11.85 KBtim.plunkett
#87 2551633-pm-87.patch165.97 KBtim.plunkett
#87 interdiff.txt1.62 KBtim.plunkett
#83 interdiff.txt30.65 KBtim.plunkett
#83 2551633-pm-83.patch165.14 KBtim.plunkett
#82 2551633-pm-81.patch148.66 KBtim.plunkett
#82 interdiff.txt20.1 KBtim.plunkett
#80 interdiff.txt3.58 KBtim.plunkett
#80 2551633-pm-80.patch148.51 KBtim.plunkett
#78 2551633-pm-78.patch147.28 KBtim.plunkett
#78 interdiff.txt15.41 KBtim.plunkett
#77 2551633-pm-77.patch149.08 KBtim.plunkett
#77 interdiff.txt2.51 KBtim.plunkett
#72 interdiff.txt959 bytestim.plunkett
#72 2551633-pm-72.patch147.93 KBtim.plunkett
#70 2551633-pm-70.patch147.26 KBtim.plunkett
#70 interdiff.txt1.66 KBtim.plunkett
#67 interdiff.txt9.04 KBdsnopek
#67 page_manager-variant-entity-2551633-67.patch155.88 KBdsnopek
#65 interdiff.txt44.8 KBdsnopek
#65 page_manager-variant-entity-2551633-65.patch150.79 KBdsnopek
#64 interdiff.txt63.59 KBdsnopek
#64 page_manager-variant-entity-2551633-64.patch151.16 KBdsnopek
#63 interdiff.txt6.74 KBdsnopek
#63 page_manager-variant-entity-2551633-63.patch123.05 KBdsnopek
#62 2551633-pm-62.patch113.23 KBtim.plunkett
#62 interdiff.txt3.39 KBtim.plunkett
#60 interdiff.txt2.76 KBdsnopek
#60 page_manager-variant-entity-2551633-60.patch122.93 KBdsnopek
#54 interdiff.txt15.96 KBtim.plunkett
#54 2551633-pm-54.patch111.61 KBtim.plunkett
#49 page_manager-variant-entity-2551633-48.patch123.56 KBdsnopek
#47 page_manager-variant-entity-2551633-47.patch122.65 KBdsnopek
#46 page_manager-variant-entity-2551633-46.patch134.34 KBdsnopek
#45 interdiff.txt514 bytesdsnopek
#45 page_manager-variant-entity-2551633-45.patch134.3 KBdsnopek
#44 interdiff.txt1.54 KBmpotter
#44 page_manager-variant-entity-2551633-44.patch104.88 KBmpotter
#37 interdiff.txt6.45 KBdsnopek
#37 page_manager-variant-entity-2551633-37.patch134.1 KBdsnopek
#34 interdiff.txt32.69 KBdsnopek
#34 page_manager-variant-entity-2551633-34.patch132.05 KBdsnopek
#33 interdiff.txt50.35 KBdsnopek
#33 page_manager-variant-entity-2551633-33.patch111.61 KBdsnopek
#32 interdiff.txt16.52 KBdsnopek
#32 page_manager-variant-entity-2551633-31.patch77.57 KBdsnopek
#30 interdiff.txt4.17 KBdsnopek
#30 page_manager-variant-entity-2551633-30.patch76.09 KBdsnopek
#22 interdiff.txt2.9 KBdsnopek
#22 page_manager-variant-entity-2551633-22.patch76.9 KBdsnopek
#21 interdiff.txt439 bytesdsnopek
#21 page_manager-variant-entity-2551633-21.patch74.15 KBdsnopek
#20 interdiff.txt5.52 KBdsnopek
#20 page_manager-variant-entity-2551633-20.patch74.3 KBdsnopek
#17 interdiff.txt65.22 KBdsnopek
#17 page_manager-variant-entity-2551633-17.patch75.33 KBdsnopek
#15 page_manager-variant-entity-2551633-15.patch79.1 KBdsnopek
#3 page_manager-variant-entity-2551633-3.patch66.5 KBdsnopek
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

dsnopek created an issue. See original summary.

dsnopek’s picture

Assigned: Unassigned » dsnopek

I was working on this most of yesterday and it's turning out to be a bit trickier than I thought. But I hope to have a patch up today! Assigning to myself so no one else starts working on it. :-)

dsnopek’s picture

Status: Active » Needs review
FileSize
66.5 KB

Here's my first pass at this patch. I extracted the page variant entity stuff from the wizard patch, then attempted to get it working with the existing page_manager UI (so that wizards could just be about wizards).

However... this was super hard. It's kinda working for adding and editing variants, but not much else.

I think the key to making this into an easier patch might be to remove even more stuff, and marshall stuff in/out of the page variant entities. We could then make some of the bigger refactors along with the wizard changes, or as another incremental step.

Berdir’s picture

I'm not sure I understand the point of this.

As long as variants still have a hard dependency on the page (and I don't see how they could not, with static and route-based contexts in blocks, just to name one example), I'm not sure what we gain by making it separate.

I do see a number of downsides however, it makes dependencies more complex as you have a cross-dependency between the two, that's something the config system can't really handle. If a variant depends on the page and the page on the variant, which one should a config import going to import first?

dsnopek’s picture

My main goal is to decouple display variant plugins from the Page entity. In this new model, the page variant entity would be coupled to the Page entity (and the UIs would apply to that) and the display variant could hopefully be made blissfully unaware of the Page. :-) This will be important if we want to use the same display variant plugins for non-page things, like Mini Panels and Panelizer (the view modes, not the full page override).

However, there are some other benefits that EclipseGC is going for! The Features one mentioned in the issue summary is one. I'm pretty sure there are others, but I don't recall them at the moment - hopefully, EclipseGC can jump in on that. :-)

dsnopek’s picture

I tried to make the issue summary a little clearer.

Berdir’s picture

Yes but again, I don't see how the display *can* be unaware of the page. Haven't look at the patch yet, but i don't see how you intend to deal with the context problem? And when it's not, then the single-variant feature is kinda pointless since it won't work ;)

We created a module called widget which is basically mini-panels as a block and we are still using an old branch of the layout project for that, one of the things that it has there is https://github.com/md-systems/layout/blob/old/src/LayoutRendererBlockAnd..., which in turn uses https://github.com/md-systems/layout/blob/old/src/Plugin/Layout/LayoutBl...), a very simple and generic interface that abstracts the concept of something that provides blocks, regions and context that is then rendered into a template.

Might be considerable easier to bring such a concept into ctools and then just use that in BlockDisplayVariant? See https://github.com/md-systems/widget/blob/master/src/Plugin/Block/Widget... how our usage of that looks like.

Berdir’s picture

To be fair, I'm also worried about the fact that we are using page manager in production and such a massive change would result in considerable effort for me to write an upgrade path for our configuration.

But if everyone else thinks this direction makes sense and actually *works* (having variants that do not depend on the page in any way in the end) then don't let me hold you back. I'd just like to avoid a situation where we do all this refactoring and then in the end don't actually gain anything from it.

dsnopek’s picture

That project sounds really cool! I'll definitely check it out when I have a chance. :-)

Well, we did it in Drupal 7 using a similar architecture. In D7, we had the 'page_manager_handlers' table which was essentially the "page variant entity" which was coupled to to 'page_manager_pages' (basically, the Page entity). Then the 'panels_display' table held the configuration for the Panels variant (so, the display plugin configuration).

This patch doesn't do any of the actual decoupling yet - just trying to make the Page entity and Page variant entity separation.

I think we'll need to finish #2511570: Remove Drupal\page_manager\Plugin\PageAwareVariantInterface and #2511568: Create "context stack" service where available contexts can be registered before we can really decouple things.

dsnopek’s picture

Assigned: dsnopek » Unassigned
Status: Needs review » Needs work

Unassigning - I don't think I'll be able to get back to this today

EclipseGc’s picture

Getting involved to help push this forward ideologically.

The primary benefit is actually #3 in the issue summary. Node routes (as an example) have a long history of having variants introduced by feature plugins. When you take over node/{node}, and then you require a single module to own all variants, you're limiting your ability to introduce new node types and corresponding variants for them, which will make site-building an absolute pain, much less more generic site-building attempts.

Likewise, while I'm not certain that variants will be useful for mini-panels, they definitely share a lot of architectural overlap with panelizer (and a little overlap with mini-panels), so moving to this more abstracted feature element will likely result in a "DRY"er solution.

Finally, @berdir specifically, I feel your pain about upgrade path. While I am uncomfortable with so many people using the current page_manager implementation in production D8 sites, I recognize it's happening. Regardless of that, we can't let that fact retard progress on the D8 branch of page_manager, and its current UI and arch was Tim's effort to get SOMETHING, but I don't think anyone wants to maintain (or deal with) the current code in its current form for the next however many years. However, we should probably make a concession to reality here and commit to providing an upgrade path for this change. I've been debating that for a while, and I'm leaning toward including one.

Eclipse

Berdir’s picture

Thanks for responding.

Don't worry too much about my upgrade path worries, I'm aware of the risk of using page manager in the current state and will deal with it. The upgrade path doesn't need to happen here, it could be added later once someone actually needs to do and test that (It won't work anyway otherwise ;)).

My main point is that I'm not sure that we're fully aware if the implications of this and we should test scenarios like config deployments, define who depends on whom and how things like static and route based contexts are supposed to work if a block in a variant uses it, as they are provided by the page at the moment (static could move I guess, route based not so much...)

EclipseGc’s picture

Right, so I can describe how that should work, and as we refactor, if we find that our current reality doesn't match what I describe, we need to change it.

In the case of context injection into plugins (since that's the lion share of your argument/question) it should be irrelevant where the context originates. If a page has defined routes (or takes over a route) with arguments we're upcasting into contexts, those contexts should be injected into the variant entity when executing it. As a practical example:

say we take over the node of type "article" display on /node/{node}. We'll get a node from the route, and the page knows about this. The page then loops over the variant entities it possesses. As it does so, it injects its contexts (from url parameters) into the variants. When we arrive at our "article" variant, the specific contexts relevant to this variant are loaded, our selection criteria are evaluated, and then our page is rendered. If we have a panel that renders something dependent upon the node from the url, this is not horribly relevant to the variant, because internally it maps it to a key by a name ('node' probably since it's {node}). In Drupal 7, we can take a variant from one page, and put it on another, so long as the parameters upcast from the url remain the same. Even if the key of the context were to change, Page Manager will try to guess at the correct context to use, which it does by matching the first context of the necessary type. This logic doesn't exist in the 8.x version yet, but long term should be on our road map. It's also useful for remapping plugin contexts when you remove a context from the variant's scope that had plugins mapped to use it (or when you change the url upcasting on a custom landing page).

TL:DR;
Contexts should be injected through out our process, we should never manually reach backward to the page specifically to get contexts. The plugin mapping that is saved into a variants configuration should be completely ignorant of WHERE its contexts came from.

dsnopek’s picture

FileSize
79.1 KB

This is a re-roll but I had to comment out some stuff and just put a @todo by it, so even less stuff works now. :-/ I'm really struggling with making this patch work! I'm tempted to just start over and copy in pieces of the original patch bit by bit as necessary, because there's just too much half working stuff in here that it's super hard to wrangle.

dsnopek’s picture

Assigned: Unassigned » dsnopek

I started attempting to re-build this patch piece-by-piece. Hopefully, this'll lead to something more working!

dsnopek’s picture

Assigned: dsnopek » Unassigned
FileSize
75.33 KB
65.22 KB

Ok! While this patch still has loads of problems and lots of things don't work, it does work more than any previous patch. :-) I also removed a bunch of forms and stuff that are really part of the wizard changes. This should be a much better base for work going forward!

tim.plunkett’s picture

  1. +++ b/page_manager.routing.yml
    @@ -121,58 +121,44 @@ page_manager.display_variant_select:
       path: '/admin/structure/page_manager/manage/{page}/add/{display_variant_id}'
    
    @@ -207,3 +193,29 @@ page_manager.selection_condition_delete:
    +  path: '/admin/config/system/page_variant'
    
    +++ b/src/Entity/PageVariantEntity.php
    @@ -0,0 +1,243 @@
    + *     "collection" = "/admin/config/system/page_variant"
    

    Why the different path?

  2. +++ b/src/Controller/PageVariantListBuilder.php
    @@ -0,0 +1,36 @@
    +class PageVariantListBuilder extends ConfigEntityListBuilder {
    

    Why do we need to list page variants in isolation from their pages?

  3. +++ b/src/Entity/Page.php
    @@ -79,11 +77,11 @@ class Page extends ConfigEntityBase implements PageInterface {
    -  protected $display_variants = [];
    +  protected $displayVariants = [];
    

    Out of scope

dsnopek’s picture

#18.1: Good question! It was like that in the wizard patch. I see no compelling reason to have a new path - we should be able to move that back.

#18.2: Dunno! It was like that in the wizard patch. :-) I'd be fine with removing that.

#18.3: Actually, I think this is in scope and represents what changed. In HEAD, $page->display_variants is an array of arrays that is saved to the Page config entity. In the patch, $page->displayVariants is an array of PageVariantEntity objects which isn't saved to the Page config entity - it's just used at runtime to store the entities.

So, I can make an updated patch that fixes #18.1 and #18.2 - but I think that #18.3 should stay as it is in the patch.

dsnopek’s picture

FileSize
74.3 KB
5.52 KB

Alright, here's a patch that addresses #18.1 and #18.2.

dsnopek’s picture

FileSize
74.15 KB
439 bytes

Oops! Forgot the action link.

dsnopek’s picture

FileSize
76.9 KB
2.9 KB

This gets the phpunit tests passing. However, the simpletest tests are still failing quite catastrophically. :-)

Also, I moved some methods from VariantAwareInterface directly on to PageInterface now that those methods are coupled directly to PageVariantEntity's - there really isn't much point to that interface as it is. (I do have some ideas for reviving something like it later to share between page_manager and panels_everywhere - but we can address that in CTools when the time comes).

dsnopek’s picture

Issue summary: View changes

Updated the "Remaining tasks" a little bit. I'm going to try really hard to not work on this one any more today. My brain needs to recover. :-)

Berdir’s picture

First real look at this...

  1. +++ b/config/schema/page_manager.schema.yml
    @@ -18,17 +18,17 @@ page_manager.page.*:
           sequence:
    -        - type: display_variant.plugin.[id]
    -          label: 'Display variant'
    -    access_logic:
    -      type: string
    -      label: 'Access logic'
    +        - type: mapping
    +          label: 'Legacy display variant configuration.'
    

    Haven't seen anything else yet but this looks weird...

  2. +++ b/config/schema/page_manager.schema.yml
    @@ -46,6 +46,42 @@ page_manager.page.*:
    +    page:
    +      type: page_manager.page.[id]
    +      label: 'Parent page'
    

    Again, only just started. But why is the reference from the display to the page and not the page that has a list of displays?

    Config queries by default are slow, but if we do this, we should at least the optimized keys that it offers, see lookup_keys in Block entity.

  3. +++ b/page_manager.links.action.yml
    @@ -2,4 +2,9 @@ entity.page.add_form:
       title: 'Add page'
       appears_on:
    -    - 'page_manager.page_list'
    +    - 'entity.page.collection'
    

    the rename makes sense but doesn't seem like something that needs to be in this issue?

  4. +++ b/page_manager.routing.yml
    @@ -48,6 +48,60 @@ entity.page.disable:
    +entity.page.condition.add:
    +  path: '/admin/structure/page_manager/access/add/{machine_name}/{condition}'
    +  defaults:
    +    _form: 'Drupal\page_manager\Form\AccessConfigure'
    +    tempstore_id: page_manager.page
    +    _title: 'Add Condition'
    +  requirements:
    

    what are those routes exactly? and is the tempstore stuff really related to this?

  5. +++ b/page_manager.routing.yml
    @@ -121,18 +175,20 @@ page_manager.display_variant_select:
    +    #_entity_access: page_variant.add
    

    You're looking for _entity_create_access: page_variant I think.

  6. +++ b/src/Entity/Page.php
    @@ -45,8 +48,9 @@ use Drupal\page_manager\Plugin\VariantAwareTrait;
      *     "add-form" = "/admin/structure/page_manager/add",
    - *     "edit-form" = "/admin/structure/page_manager/manage/{page}",
    + *     "edit-form" = "/admin/structure/page_manager/manage/{machine_name}",
      *     "delete-form" = "/admin/structure/page_manager/manage/{page}/delete",
    

    That doesn't sound right.

  7. +++ b/src/Entity/Page.php
    @@ -191,6 +193,7 @@ class Page extends ConfigEntityBase implements PageInterface {
         parent::postCreate($storage);
         // Ensure there is at least one display variant.
    +    /*
         if (!count($this->getVariants())) {
           $this->addVariant([
             'id' => 'http_status_code',
    

    Interesting problem, should a new page auto-create a display, or should it do this at runtime. or just display an errror?

  8. +++ b/src/Entity/Page.php
    @@ -213,6 +217,12 @@ class Page extends ConfigEntityBase implements PageInterface {
         parent::postDelete($storage, $entities);
    +    /** @var $entity \Drupal\page_manager\Entity\Page */
    +    foreach ($entities as $entity) {
    +      foreach ($entity->getVariants() as $variant) {
    +        $variant->delete();
    +      }
    +    }
    

    Define config dependencies (make the variant depend on the page) and drupal will do that automatically for you.

  9. +++ b/src/Entity/Page.php
    @@ -342,6 +352,71 @@ class Page extends ConfigEntityBase implements PageInterface {
    +  public function save() {
    +    $variants = $this->getVariants();
    +    $return = parent::save();
    +    if ($return) {
    +      foreach ($variants as $variant) {
    +        $variant->save();
    +      }
    +    }
    +    return $return;
    +  }
    

    is it even valid to change a loaded variant as part of a page?

    If yes, then save() is the wrong place for this, needs to be in post/preSave(). Consider save() a final method, since you could just call $storage->save($page)

  10. +++ b/src/Entity/Page.php
    @@ -342,6 +352,71 @@ class Page extends ConfigEntityBase implements PageInterface {
    +    foreach ($entities as $entity) {
    +      $variants = [];
    +      foreach (\Drupal::entityManager()->getStorage('page_variant')->loadByProperties(['page' => $entity->id()]) as $variant) {
    +        $variants[$variant->uuid()] = $variant;
    +      }
    +      $entity->set('display_variants', $variants);
    

    Yeah. even if you cache it, this still has overhead, while a stored list of display ID's would just be a loadMultiple(). And wasn't one point of this to be able to re-use a display? that's not possible if it has a single, forced reference on a page?

  11. +++ b/src/Entity/Page.php
    @@ -363,8 +437,19 @@ class Page extends ConfigEntityBase implements PageInterface {
     
    +  public function __wakeup() {
    +    parent::__wakeup();
    +    foreach ($this->display_variants as $key => $variant) {
    +      $this->display_variants[$key] = unserialize($variant);
    +    }
    +  }
    

    This is very tricky because you don't know if the variant changed.. you might still have the page cached and it might use an old version of the variant. Probably have to load them again. But you might want to consider loading them only on-demand anyway.

  12. +++ b/src/Entity/PageVariantEntity.php
    @@ -0,0 +1,234 @@
    + *   config_prefix = "page_variant",
    

    You don't need a config prefix if it's identical to the ID. only if you e.g. want to shorten the config name to just page_manager.variant instead of page_variant.

  13. +++ b/src/Entity/PageVariantEntity.php
    @@ -0,0 +1,234 @@
    +   * {@inheritdoc}
    +   */
    +  public function toArray() {
    +    $properties = parent::toArray();
    +    $names = [
    +      'id',
    +      'label',
    +      'uuid',
    +      'variant',
    +      'variant_settings',
    +      'page',
    

    You can define them as a config_export property in the annotation.

  14. +++ b/src/Entity/PageVariantEntity.php
    @@ -0,0 +1,234 @@
    +  /**
    +   * @todo Document!
    +   */
    +  public function getAccessLogic() {
    +    return $this->selection_logic;
    +  }
    

    a bit strange name mismatch...

dsnopek’s picture

@Berdir: Thanks for the review! However... I think you're looking at an older version of the patch. :-/ Your points 1, 2, 4, 6, 11 and 13 are all about things that were removed in my updated patches from this morning. Anyway, I'll look into the rest of the points when I come back to this and try to address them!

Berdir’s picture

Grml, yeah, apparently. sorry :)

rlmumford’s picture

How is this related to #2561963: Display Config Entity. It seems like there could be a lot of duplication of effort.

dsnopek’s picture

@rlmumford: Well, they both involve making new config entities, but they aren't duplicates!

#2561963: Display Config Entity is an internal implementation detail of the Panels module, designed to make it easier for other Panels-dependent modules to reuse it's display variant (ie. panels_everwhere, panelizer, mini-panels, etc). It's basically an improvement on the Panels API.

Effectively, this means that the variant configuration that gets stored on the page variant entity (which we're creating in this issue) for Panels display variants will just be a reference to the Panels display config entity that's created on #2561963. But that's Panels' business - Page Manager doesn't care. :-)

Unfortunately, for Page Manager users who use Panels display variants, lots of config entities will be created, which is the main downside of this approach. We discussed this at last SCOTCH meeting, and we came away feeling that this was less than optimal, but necessary to preserve important features we had in D7.

On the upside, the number of config entity types matches the number of tables used in D7, and the number of config entities matches the number of table rows in D7. The Panels display config entity corresponds to the 'panels_display' table in D7 (see comment #9 for the rest of the mapping). So, we're basically recreating the same overall architecture as in D7...

rlmumford’s picture

Thanks for the explanation! I've been trying to keep up with the Port Panels8 meetings but obviously missed that conversation.

dsnopek’s picture

FileSize
76.09 KB
4.17 KB

Re review from #23:

1. Removed in earlier patch.

2. Removed in earlier patch.

3. Yeah, if you think it'd be better to split that into it's own issue, I can do that! This issue makes the inconsistency of that route name even more apparent, because it adds some new "entity.*" route names. Leaving it in this patch for now, though.

4. Removed in earlier patch.

5. Ah, thanks! Fixed.

6. Removed in earlier patch.

7. In D7, you have to select the initial variant type in the creation wizard. Personally, I think this should be up to the UI and not done at the entity level. Any UI that wants to allow users to create a Page without the default "HTTP status code" variant would actually have to remove the variant after entity creation, which seems messy. I'm going to just remove this altogether - but we can continue to discuss this if anyone disagrees!

8. Cool, thanks! Fixed.

9. No, I think you're right - this doesn't make sense. Removed.

10. Hm. This'll need more thought. First of all, no, I don't think we were talking about having multiple pages reuse a variant. The page variants need an upward reference to the page so that we can do the 3rd point in the issue summary, which is to have several Features provide page variants for the same page. But I agree that we probably shouldn't do this in postLoad() - it'd be better to load them on demand in getVariants().

11. Removed in earlier patch.

12. Cool, thanks! Removed.

13. Ah, cool! I added the 'config_export'. However, Page also implements a similar toArray(). Is that really not necessary there either?

14. Yeah.. I'm not sure what that's about! I think it might be trying to use the same method name but change the config key. I'll dig into this a bit deeper and see if we can't make them match.

Thanks again for the review!

dsnopek’s picture

Assigned: Unassigned » dsnopek

Picking this back up again...

dsnopek’s picture

FileSize
77.57 KB
16.52 KB

This fully addresses the points that weren't totally addressed in #30. It also documents all the methods on the PageVariantEntityInterface, renames some stuff for clarity, and does other little clean-up!

Still TODO:

  1. Get the selection critieria saving again
  2. Get changing weight of variants working again
  3. Get all the simpletests passing
dsnopek’s picture

FileSize
111.61 KB
50.35 KB

Alright, this gets selection criteria and weight working! I also renamed some of the selection criteria methods to match the access condition methods (they were easier to reason about with a normal plural form). Some of the phpunit tests are failing, but the big test push is the next step anyway.

This also removes some now unused classes, interfaces and traits.

dsnopek’s picture

Status: Needs work » Needs review
FileSize
132.05 KB
32.69 KB

Alright! This passes almost all of the tests. Still more to do, but this is probably ready for some more in-depth reviews!

dsnopek’s picture

Status: Needs work » Needs review
FileSize
134.1 KB
6.45 KB

This one should actually pass tests. For real. :-)

japerry’s picture

Sounds like we're postponing this one dependent on the page-manager -> ctools move.

I'd suggest re-rolling this patch against patch on #2511566: Add an abstract version of Drupal\page_manager\Plugin\DisplayVariant\BlockDisplayVariant from Page Manager comment 10.

Also for what its worth EclipseGC hates the ConditionVariantInterface so we want to remove it from ctools pretty quickly.

But we also don't want to block the panels folks from using the newly moved code.

dsnopek’s picture

This patch removes the ConditionVariantInterface. :-) Which is one of the reasons I think we should postpone #2511566: Add an abstract version of Drupal\page_manager\Plugin\DisplayVariant\BlockDisplayVariant from Page Manager on this one.

japerry’s picture

Status: Needs review » Postponed

this will take a while, there are arguments around whether to do this or not. We're moving items to ctools first.

mpotter’s picture

Also, from the Features perspective, in D7 we already do this using Features Override and we'll likely just need something similar for D8. I can see the case that the architecture of variants within pages is analogous to displays in a view. Having a config entity have a list of other related config entities is beyond the scope of just panel variants I think. So I agree with postponed.

dsnopek’s picture

From IRC conversation:

<mpotter1> dsnopek: yeah, we'll talk about 2551633 at the meeting next week.  I talked with timplunkett since all that was really left was the "would be nice for Features" bit and I can see his points.  If we want to handle config entities containing config entities it's a bigger issue than just page manager
8:50 AM <dsnopek> mpotter1: Hm.. Since we have config dependencies, I'm not sure I understand what the issue is
8:50 AM <dsnopek> mpotter1: But we can jam on it at the meeting!
8:50 AM <mpotter1> dsnopek: how does the page know which variant entities it uses?  
8:51 AM <mpotter1> with dependency, the variant would need to be dependent on the specific page?
8:51 AM <dsnopek> mpotter1: Yeah, so the page variant has a dependency on the page (that's already in the patch)
8:51 AM <mpotter1> but the point is the architecture is similar to displays within a view.  so seems like a more general architecture
8:52 AM <mpotter1> would we make displays into config entities and then make then dependent on the view?
8:52 AM <dsnopek> mpotter1: That's what I would say!
8:52 AM <dsnopek> mpotter1: But for Views this isn't a regression
8:52 AM <dsnopek> mpotter1: For Panels it is
8:52 AM <dsnopek> mpotter1: And without it, providing a node/% page with variants from several Features would be impossible
8:53 AM <dsnopek> mpotter1: You'd basically need a single Feature that knows about every content type, and can provide variants for them all
8:53 AM <dsnopek> mpotter1: Rather than having each Feature which knows about each content type provide the variant for it's content type
8:53 AM <dsnopek> mpotter1: Anyway, to answer a previous question: the page knows it's variants by loading them based on an upward reference in the variant entity
8:54 AM <dsnopek> mpotter1: You can see the code in the current patch:
8:54 AM <dsnopek> https://www.irccloud.com/pastebin/8CkGMhcR/
8:54 AM <dsnopek> mpotter1: ^^

But we can jam on this more on Tuesday at the SCOTCH meeting!

mpotter’s picture

oh wait, I was confusing "pages" with "panelizer". In D7 page variants *are* separate exportables. Sorry. So yeah, we are still debating ;)

mpotter’s picture

OK, here is a version of the patch that refactors the entityManager->getStorage('page_variant') into protected methods.

dsnopek’s picture

Discussed this with Tim on IRC a bit. One of his main issues was the performance associated with loading the variant entities which are associated with the page. Here is a patch that should address this by making 'page' a lookup key on the variant entity.

dsnopek’s picture

FileSize
134.34 KB

So, this patch desperately needs to be re-rolled for all the recent changes, but I just noticed that the patch I uploaded in #45 is incorrect - it doesn't have the changes in the interdiff. So, just for completeness, here is the real patch!

dsnopek’s picture

Status: Postponed » Needs review
FileSize
122.65 KB

This is a straight re-roll of the previous patch. Unfortunately, some of the code it changed is now in CTools so there we'll need to patch it as well to get everything right (although, tests pass and it basically works without the CTools changes): #2580469: Clean-up after Page Manager starts using page variant entities

dsnopek’s picture

Status: Needs work » Needs review
FileSize
123.56 KB

Oops! Lost some changes intended for PageBlockDisplayVariant that were lost in the re-roll.

dsnopek’s picture

Hrm. All tests are passing for me locally. Not sure what's going wrong with the testbot? :-/

dsnopek’s picture

Blergh. I'm able to reproduce the test failures with the latest Drupal HEAD and page_manager even without this patch. Here's a new issue for that: #2580493: Tests failing on \Drupal::service('cache_contexts_manager')->assertValidTokens($cache_contexts)

dsnopek’s picture

Status: Needs work » Needs review

Alright, the patch is green again!

tim.plunkett’s picture

FileSize
111.61 KB
15.96 KB

Just nitpick fixes for now, added a couple @todos that we need to clean up.

Also rebased it on other nitpick changes I made to 8.x-1.x

Wim Leers’s picture

High-level review. Looking at the entity types and cacheability aspects.

  1. +++ b/src/Entity/PageVariantEntity.php
    @@ -0,0 +1,259 @@
    + *   id = "page_variant",
    ...
    +class PageVariantEntity extends ConfigEntityBase implements PageVariantEntityInterface {
    

    Shouldn't the class be named PageVariant? And the interface PageVariantInterface?

    That'd then match the ID. And the form classes.

    And every entity type in core.

  2. +++ b/src/Entity/PageViewBuilder.php
    @@ -20,15 +21,24 @@ class PageViewBuilder extends EntityViewBuilder {
    +        // We need to add all variants because they all affect the selection of
    +        // the variant (ie. a variant's weight can change and bump ahead of the
    +        // others).
    +        foreach ($entity->getVariants() as $other_variant) {
    +          $display_variant->addCacheableDependency($other_variant);
    +        }
    

    I'd expect this to be taken care of in the Page and PageVariant config entities' logic. It should be encapsulated there, otherwise everything that deals with Pages and Page Variants will have to repeat this sort of logic.

    Look at e.g. \Drupal\aggregator\Entity\Item::postSave() for inspiration — we have a few of these entity types with very strong coupling amongst each other.

dsnopek’s picture

Thanks for the review, @Wim!

1. That name was to avoid confusing the entity with the plugin (we have a plugin and an entity that are "variants") and \Drupal\Core\Display\PageVariantInterface which again, refers to the plugin and not the entity. If possible, I'd prefer to keep this name as it avoids confusion (I was personally confused by this the first time I reviewed EclipseGC's wizard patch which used the names you're proposing).

2. I think you're getting confused between the plugin and the entity. :-) That code is adding the cacheable dependencies to the plugin, which is only just created, not the entity where we could save the dependencies on the config object. I could see an argument, though, for adding the cacheable dependency information to the plugin in PageExecutable::selectVariant() so that it wouldn't need to be repeated by some other code that wanted to render a Page.

Overall, we need to agree on a naming scheme to differentiate between the plugin and entity for variants. I have the following proposal:

  • Keep PageVariantEntity and PageVariantEntityInterface
  • Banish (ie. stop using) "display variant" or $display_variant any where in code or comments which is sometimes used to refer to the plugin - it's too ambiguous
  • Use "variant" (as opposed to "page variant" or "display variant") in any UI strings, since users don't care which it is - it's all the same to them
  • Use $variant_entity, $page_variant or "page variant entity" to refer to the entity
  • Use $variant_plugin, getVariantPlugin(), selectVariantPlugin() and "variant plugin" to refer to the plugin

My current patch doesn't totally do all of the above, so if this was acceptable, it'd need to be updated. What does everyone think?

Wim Leers’s picture

  1. With that reasoning, it should be PageVariantPlugin and PageVariantEntity?
  2. Ah, I see! Indeed :)

    But my overall point still stands I think: a page entity actually depends on multiple page variant entities: when the page is rendered, it selects a page variant to use. So doesn't it then make more sense to let the page variant entities use the same cache tags as the page entity they are associated with? (Just like e.g. feed item entities use the cache tag of the feed they belong to.)

Keep PageVariantEntity and PageVariantEntityInterface

For minimum consistency, we should then at least rename Page to PageEntity. And per my first point, actually also PagePlugin and PageVariantPlugin. Or… just go with Page and PageVariant everywhere, and rely on namespaces — that's what they're here for. You can still always call the instances' variable names $page_plugin and $page_entity.

Banish (ie. stop using) "display variant" or $display_variant any where in code or comments which is sometimes used to refer to the plugin - it's too ambiguous

If it's the same as "page variant", then yes, please consolidate all that. But I think that doesn't need to happen here per se; could be a follow-up.

dsnopek’s picture

1. With that reasoning, it should be PageVariantPlugin and PageVariantEntity?

Well, there's no PageVariantPlugin - the interface for those is from core, which is \Drupal\Core\Display\VariantInterface and optionally \Drupal\Core\Display\PageVariantInterface. I suppose whereever we "use" those interfaces we could do "as VariantPluginInterface" and "as PageVariantPluginInterface" to make it clear - I'd be for that!

Or… just go with Page and PageVariant everywhere, and rely on namespaces — that's what they're here for.

I'm curious what Tim thinks! Relying only on namespaces was confusing to me - it's my interation on @EclipseGC's code that changed the class name. In the end, I'm happy to go with whatever Tim wants to do.

2. Ah, I see! Indeed :)

But my overall point still stands I think: a page entity actually depends on multiple page variant entities: when the page is rendered, it selects a page variant to use. So doesn't it then make more sense to let the page variant entities use the same cache tags as the page entity they are associated with? (Just like e.g. feed item entities use the cache tag of the feed they belong to.)

Ok, so you're saying that rather than adding cache tags to the variant plugin for the individual variant config entities, we add a PageVariantEntity::postSave() which invalidates the cache tag for the page entity? Does the postSave() will when importing a .yaml via CMI? If so, that makes sense! It means adding fewer cache tags to the render array.

Wim Leers’s picture

Does the

postSave()

will when importing a .yaml via CMI?

Yes, it's called then too. Otherwise it'd be a poor abstraction :) (But given that such edge cases were commonplace in D7, I totally understand your skepticism :D)

It means adding fewer cache tags to the render array.

Yes, but more importantly: a system that is easier to reason about!

dsnopek’s picture

FileSize
122.93 KB
2.76 KB

Ok, here's a new patch that attempts to address #55.2! My first thought was that overriding ::getCacheTagsToInvalidate() to return the page's cache tags would be enough, but it turns out that ConfigEntityBase overrides ::invalidateTagsOnSave() and ::invalidateTagsOnDelete() such that the cache tags for the individual entities aren't overridden, only those for the entity type. So, I had to override those too and invalidate the entity's cache tags as well.

This doesn't make any changes to naming because I want to find out what Tim thinks first.

tim.plunkett’s picture

  1. +++ b/src/Entity/PageVariantEntity.php
    @@ -0,0 +1,297 @@
    +  public function getPluginCollections() {
    +    return [
    +      'selection_criteria' => $this->getSelectionConditions(),
    +    ];
    +  }
    ...
    +  public function getVariantPlugin() {
    +    if (!$this->variantPlugin) {
    +      // @todo Inject this service!
    +      $this->variantPlugin = \Drupal::service('plugin.manager.display_variant')->createInstance($this->variant, $this->variant_settings);
    +    }
    +    return $this->variantPlugin;
    +  }
    

    Why doesn't getPluginCollections include the variant plugin? It can use a single plugin collection like the block->block_plugin relationship.

  2. +++ b/src/Entity/PageVariantEntity.php
    @@ -0,0 +1,297 @@
    +    return Page::load($this->page);
    

    Do we want to store this? Or just rely on entity static caching?
    Also, ewwwwwwwww. Really wish this wasn't necessary.

  3. +++ b/src/Entity/PageViewBuilder.php
    @@ -20,15 +21,17 @@ class PageViewBuilder extends EntityViewBuilder {
    +    if ($page_variant = $entity->getExecutable()->selectVariant()) {
    ...
    +      return $display_variant->build();
    

    Would we want to have a PageVariant view builder instead?

  4. +++ b/src/PageExecutable.php
    @@ -58,19 +62,49 @@ class PageExecutable implements PageExecutableInterface {
    +   * @return boolean
    

    bool

  5. +++ b/src/PageExecutable.php
    @@ -58,19 +62,49 @@ class PageExecutable implements PageExecutableInterface {
    +  public function canSelectPageVariant(PageVariantEntityInterface $page_variant) {
    

    This method seems a little out of place. Should it be on the page variant entity?

tim.plunkett’s picture

FileSize
3.39 KB
113.23 KB
dsnopek’s picture

FileSize
123.05 KB
6.74 KB

Discussed #61.3 with @tim.plunkett and we're going to punt on that until after this patch and #2305199: Create a route for every variant and use a "route filter" to apply selection rules.

This patch fixes the remaining points from #61 that weren't fixed by Tim's patch in #62.

dsnopek’s picture

FileSize
151.16 KB
63.59 KB

Here's all the renaming that I proposed in #56, which Tim said he's cool with.

dsnopek’s picture

FileSize
150.79 KB
44.8 KB

And this renaming PageVariantEntity back to PageVariant per Tim's request.

phenaproxima’s picture

Status: Needs review » Needs work
  1. +++ b/src/Controller/PageManagerController.php
    @@ -326,4 +314,22 @@ class PageManagerController extends ControllerBase {
    +    $entity = $this->entityManager()->getStorage('page_variant')->create(['page' => $page->id(), 'variant' => $display_variant_id]);
    

    Nit: The stuff in create() should be broken up into several lines for readability.

  2. +++ b/src/Entity/Page.php
    @@ -346,13 +342,84 @@ class Page extends ConfigEntityBase implements PageInterface {
    +  public function addVariant(array $configuration) {
    +    if (empty($configuration['id'])) {
    +      throw new \Exception("Must pass an 'id' for the new page variant.");
    +    }
    

    One suggestion -- if the variant ID is required, maybe it should be an explicit argument to the method.

  3. +++ b/src/Entity/Page.php
    @@ -346,13 +342,84 @@ class Page extends ConfigEntityBase implements PageInterface {
    +  public function addVariantEntity(PageVariantEntityInterface $variant) {
    +    $this->displayVariants[$variant->id()] = $variant;
    +    return $variant->id();
    

    I see that variants are sorted by UUID in getVariants(). Why are they keyed by ID here instead of UUID? Should $this->displayVariants be re-sorted after adding the variant?

  4. +++ b/src/Entity/Page.php
    @@ -346,13 +342,84 @@ class Page extends ConfigEntityBase implements PageInterface {
    +    if (!isset($variants[$variant_id])) {
    +      throw new \Exception('The requested display variant does not exist or is not associated with this page.');
    +    }
    

    Nit: Maybe this should be \InvalidArgumentException or something otherwise more specific than \Exception.

  5. +++ b/src/Entity/Page.php
    @@ -346,13 +342,84 @@ class Page extends ConfigEntityBase implements PageInterface {
    +    if (!isset($this->displayVariants)) {
    +      $this->displayVariants = [];
    

    Why bother with an isset() check here? Is there some reason $this->displayVariants can't be defined with an empty array as its default value?

  6. +++ b/src/Entity/Page.php
    @@ -346,13 +342,84 @@ class Page extends ConfigEntityBase implements PageInterface {
    +      foreach ($this->entityStorage()->loadByProperties(['page' => $this->id()]) as $variant) {
    

    This line is strange. $this->entityStorage() is getting the *variant* entity storage, yet this is on the page entity class. entityStorage() should probably be renamed to getVariantStorage() or similar.

  7. +++ b/src/Entity/Page.php
    @@ -346,13 +342,84 @@ class Page extends ConfigEntityBase implements PageInterface {
    +  public function variantSortHelper($a, $b) {
    

    $a and $b should probably be type hinted.

  8. +++ b/src/Entity/PageVariantEntity.php
    @@ -0,0 +1,334 @@
    +  /**
    +   * The ID of the display variant plugin.
    +   *
    +   * @var string
    +   */
    +  protected $variant;
    

    I kind of wish this was named pluginId or similar, rather than the more ambiguous "variant".

  9. +++ b/src/Entity/PageVariantEntity.php
    @@ -0,0 +1,334 @@
    +  /**
    +   * @var array
    +   */
    +  protected $contexts = [];
    

    This one needs an explanation :)

  10. +++ b/src/Entity/PageVariantEntity.php
    @@ -0,0 +1,334 @@
    +  public function setWeight($weight) {
    +    $this->weight = $weight;
    +    return $this;
    

    $weight isn't type hinted so maybe this should be $this->set('weight')?

  11. +++ b/src/Entity/PageVariantEntity.php
    @@ -0,0 +1,334 @@
    +  public function getSelectionLogic() {
    +    return $this->selection_logic;
    +  }
    

    For consistency, should this be $this->get('selection_logic')?

  12. +++ b/src/Entity/PageVariantEntity.php
    @@ -0,0 +1,334 @@
    +    foreach ($conditions as $condition) {
    +      if ($condition instanceof ContextAwarePluginInterface) {
    +        /** @var \Drupal\Core\Plugin\Context\ContextHandlerInterface $context_handler */
    +        $context_handler = \Drupal::service('context.handler');
    +        $context_handler->applyContextMapping($condition, $contexts);
    +      }
    +    }
    

    Micro-optimization -- the call to \Drupal::service() can go outside the foreach loop.

  13. +++ b/src/Form/DisplayVariantDeleteBlockForm.php
    @@ -83,11 +74,20 @@ class DisplayVariantDeleteBlockForm extends ConfirmFormBase {
    +  /**
    +   * @todo.
    +   *
    +   * @return \Drupal\ctools\Plugin\BlockVariantInterface
    +   */
    +  protected function getVariantPlugin() {
    

    @todo? :)

  14. +++ b/src/Form/PageVariantAddForm.php
    @@ -0,0 +1,35 @@
    +/**
    + * Class PageVariantForm.
    + *
    + * @package Drupal\page_manager\Form
    + */
    +class PageVariantAddForm extends PageVariantFormBase {
    

    This docblock isn't very helpful.

  15. +++ b/src/Form/PageVariantConfigureForm.php
    @@ -0,0 +1,67 @@
    +  /**
    +   * Returns a unique string identifying the form.
    +   *
    +   * @return string
    +   *   The unique string identifying the form.
    +   */
    +  public function getFormId() {
    

    Should be @inheritdoc.

  16. +++ b/src/Form/PageVariantConfigureForm.php
    @@ -0,0 +1,67 @@
    +  /**
    +   * Form constructor.
    +   *
    +   * @param array $form
    +   *   An associative array containing the structure of the form.
    +   * @param \Drupal\Core\Form\FormStateInterface $form_state
    +   *   The current state of the form.
    +   *
    +   * @return array
    +   *   The form structure.
    +   */
    +  public function buildForm(array $form, FormStateInterface $form_state) {
    

    Ditto.

  17. +++ b/src/Form/PageVariantConfigureForm.php
    @@ -0,0 +1,67 @@
    +  /**
    +   * Form submission handler.
    +   *
    +   * @param array $form
    +   *   An associative array containing the structure of the form.
    +   * @param \Drupal\Core\Form\FormStateInterface $form_state
    +   *   The current state of the form.
    +   */
    +  public function submitForm(array &$form, FormStateInterface $form_state) {
    

    Ditto.

  18. +++ b/src/Form/PageVariantDeleteForm.php
    @@ -0,0 +1,62 @@
    +  /**
    +   * The entity being used by this form.
    +   *
    +   * @var \Drupal\page_manager\PageVariantEntityInterface
    +   */
    +  protected $entity;
    

    Already defined in EntityForm, which this form extends.

  19. +++ b/src/Form/PageVariantEditForm.php
    @@ -0,0 +1,304 @@
    +        '#attached' => [
    +          'library' => [
    +            'core/drupal.ajax',
    +          ],
    +        ],
    

    I don't see how this is being used -- is there any reason to attach drupal.ajax here?

  20. +++ b/src/Form/PageVariantEditForm.php
    @@ -0,0 +1,304 @@
    +      '#attached' => [
    +        'library' => [
    +          'core/drupal.ajax',
    +        ],
    +      ],
    

    Again, I don't see how drupal.ajax is being used here, so does it need to be attached?

  21. +++ b/src/Form/PageVariantEditForm.php
    @@ -0,0 +1,304 @@
    +  public function save(array $form, FormStateInterface $form_state) {
    +    // @todo This feels very wrong.
    +    $variant_plugin = $this->getVariantPlugin();
    

    Missing doc comment.

  22. +++ b/src/Form/PageVariantFormBase.php
    @@ -0,0 +1,187 @@
    +/**
    + * Class PageVariantForm.
    + *
    + * @package Drupal\page_manager\Form
    + */
    +abstract class PageVariantFormBase extends EntityForm {
    

    This doc comment is not informative.

  23. +++ b/src/Form/PageVariantFormBase.php
    @@ -0,0 +1,187 @@
    +   * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
    +   *   The entity query factory.
    +   */
    +  public function __construct(QueryFactory $entity_query) {
    

    Shouldn't this be an interface instead of a concrete implementation?

  24. +++ b/src/PageInterface.php
    @@ -48,6 +47,55 @@ interface PageInterface extends ConfigEntityInterface, EntityWithPluginCollectio
    +  /**
    +   * @todo.
    +   *
    +   * @param \Drupal\page_manager\PageVariantEntityInterface $variant
    +   *
    +   * @return string
    +   */
    +  public function addVariantEntity(PageVariantEntityInterface $variant);
    

    s/@todo/actual info

  25. +++ b/src/PageVariantEntityInterface.php
    @@ -0,0 +1,112 @@
    +  /**
    +   * Gets the plugin id of the display variant plugin.
    +   *
    +   * @return string
    +   */
    +  public function getVariantPluginId();
    

    Nit: s/plugin id/plugin ID

  26. +++ b/src/PageVariantEntityInterface.php
    @@ -0,0 +1,112 @@
    +  /**
    +   * Gets selection criteria by condition id.
    +   *
    +   * @param string $condition_id
    +   *   The id of the condition.
    +   *
    +   * @return \Drupal\Core\Condition\ConditionInterface
    +   */
    +  public function getSelectionCondition($condition_id);
    

    Nit: s/id/ID

  27. +++ b/src/PageVariantEntityInterface.php
    @@ -0,0 +1,112 @@
    +  /**
    +   * Removes selection criteria by condition id.
    +   *
    +   * @param string $condition_id
    +   *   The id of the condition.
    +   *
    +   * @return $this
    +   */
    +  public function removeSelectionCondition($condition_id);
    

    Ditto.

dsnopek’s picture

Status: Needs work » Needs review
FileSize
155.88 KB
9.04 KB

This doesn't address the review in #66 yet. It just re-rolls the patch for the latest changes to page_manager. But it doesn't pass tests yet!

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
1.66 KB
147.26 KB

Small fixes. Too lazy to run all the tests locally.

tim.plunkett’s picture

FileSize
147.93 KB
959 bytes

Saving variants also needs to rebuild the router. We have this code on the page, is there any better way to do this @dsnopek?

tim.plunkett’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 72: 2551633-pm-72.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
2.51 KB
149.08 KB

Okay my @todo from before was the last bug in PageManagerAdminTest. What else is left?

tim.plunkett’s picture

Assigned: dsnopek » tim.plunkett
FileSize
15.41 KB
147.28 KB

#66

1) Fixed
2) Removed this method
3) Should always sort by ID
4) Yep, fixed
5) We do that to help prevent loading them all each time
6) Renamed.
7) We usually don't do this, not sure why. Undone for now
8) Not sure how I feel, unchanged for now.
9) Unchanged
10) Nah, setters are good. We want to discourage usage of the generic set/get
11) Redundant.
12) Unchanged, but yes. Also it should be in a protected method (the service call)
13) Unchanged
14) Unchanged
15/16/17) Fixed
18) This is for typehinting purposes.
19/20) This is needed for the AjaxFormTrait calls to work
21) Fixed
22) Unchanged
23) This has no interface :( But it's a factory, not as bad as the actual class
24) Unchanged
25) Fixed
26/27) Fixed

I also removed the variant ID munging. This will likely break many tests. More to do!

---

So #66 points 9, 12, 13, 14, 22, 24 need addressing still.

Status: Needs review » Needs work

The last submitted patch, 78: 2551633-pm-78.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
148.51 KB
3.58 KB

Fixed an issue with saving. The way variants are saved is similar to plugin collections, so we fix it in copyFormValuesToEntity.

dsnopek’s picture

Green again! :-)

tim.plunkett’s picture

FileSize
20.1 KB
148.66 KB

This fixes access and the rest of #66. I still have some work to do, just posting progress.

tim.plunkett’s picture

FileSize
165.14 KB
30.65 KB

I spent some time on the plane home looking into PageVariantViewBuilder, and it SO VASTLY simplified things, I want to include it here.

So! The final blocker here....
The breadcrumbs are completely broken now.

First we have the page edit form:
/admin/structure/page_manager/manage/{page}
Then we have the variant edit form:
/admin/structure/page_manager/variant/{page_variant}

Either we should make it
/admin/structure/page_manager/manage/{page}/variant/{page_variant}
or we have to write a custom breadcrumb class.

But there is no way to get from a variant back to it's page, and it is infuriating.

Wim Leers’s picture

I spent some time on the plane home looking into PageVariantViewBuilder, and it SO VASTLY simplified things, I want to include it here.

Nice :)

Reviewed just that bit:

  1. +++ b/src/Entity/PageVariantViewBuilder.php
    @@ -0,0 +1,46 @@
    +class PageVariantViewBuilder extends EntityViewBuilder {
    

    Hrm… it seems misleading for this to extend EntityViewBuilder, because…

  2. +++ b/src/Entity/PageVariantViewBuilder.php
    @@ -0,0 +1,46 @@
    +    return $variant_plugin->build();
    

    … that is where all the building happens!

    It seems more honest/clear to make this implement the interface directly and make several of the methods no-ops.

tim.plunkett’s picture

I do that because EntityViewBuilder is a shitty interface. It should be called FieldableEntityViewBuilderInterface...

Also, PageViewBuilder (which we're replacing), does the same thing.

Wim Leers’s picture

But that means that e.g. \Drupal\Core\Entity\EntityViewBuilderInterface::viewFieldItem() is implemented, which AFAICT makes no sense for page variant entities?

I just gave it a try… and proved myself more wrong than right :)

This is basically what you want:

class PageVariantViewBuilder implements EntityViewBuilderInterface {
  public function buildComponents(array &$build, array $entities, array $displays, $view_mode) {
    throw \LogicException;
  }

  public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) {
    // c/p from patch
  }

  public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL) {
    // c/p from patch
  }

  public function resetCache(array $entities = NULL) {
    // Duplicate from EntityViewBuilder.
  }

  public function viewField(FieldItemListInterface $items, $display_options = array()) {
    throw \LogicException;
  }


  public function viewFieldItem(FieldItemInterface $item, $display_options = array()) {
    throw \LogicException;
  }

  public function getCacheTags();
    // Duplicate from EntityViewBuilder.
  }

}

But, note two bits are duplicated from EntityViewBuilder. Well… except they shouldn't be, because ::view() doesn't actually add the cache tags from getCacheTags(), which means resetCache() is pointless to implement.

So, either:

  • extend EntityViewBuilder like your patch does, update it to use EntityViewBuilder::getCacheTags() in view(), and override the nonsensical methods to throw a logic exception
  • implement the interface directly, and also throw logic exceptions from getCacheTags() and resetCache()
tim.plunkett’s picture

FileSize
1.62 KB
165.97 KB

Gahh I hate EntityViewBuilderInterface so much.
But you're right.

We can't safely throw exceptions in restCache(). But WhyTF is buildComponents public?!

tim.plunkett’s picture

FileSize
11.85 KB
166.43 KB

This fixes the path structure. I think I'm happy with this now?!

EclipseGc’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me! Let's get this in.

Eclipse

The last submitted patch, 67: page_manager-variant-entity-2551633-67.patch, failed testing.

The last submitted patch, 70: 2551633-pm-70.patch, failed testing.

The last submitted patch, 78: 2551633-pm-78.patch, failed testing.

The last submitted patch, 72: 2551633-pm-72.patch, failed testing.

tim.plunkett’s picture

Status: Reviewed & tested by the community » Fixed

Thanks everyone!

  • tim.plunkett committed 77b10de on 8.x-1.x
    Issue #2551633 by dsnopek, tim.plunkett, EclipseGc, mpotter, Berdir, Wim...
dsnopek’s picture

Woohoo! This is the happiest news of the morning. :-)

Status: Fixed » Closed (fixed)

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