In the world of content management systems, a major anxiety for editorial staff is whether their site is going to allow them to easily build complex pages. With today's demand for editorial workflows and internationalization, this problem gets even more complex. Add Drupal's Lego-like architecture to the mix and there can be a huge array of options for a site builder or architect to consider. At the recent New England Drupal Camp, aka NEDCamp, I presented an overview of four competing options.
Drupal's internal content architecture is based upon a system called "entities" over which "fields" are added. In layperson's terms, the entities are analogous to tables in a database, and the fields are columns in those tables. In practice this is an imperfect analogy, but it's at least a starting point to help understand what this article is talking about.
For the new pages we're going to focus on the following fairly common set of requirements:
- The ability to add different types of structured data to the page, which we'll dub "page components". Specific examples include:
- Text with floating photos.
- A full-width photo, with optional caption.
- A full-width video, with optional caption.
- A photo carousel.
- Embedded tweets.
- Embedded Instagram photos.
- The ability to relatively easily add further structures for other types of content as needs change over time.
- Support for editorial workflows, so that drafts may be created and reviewed prior to being published.
- The ability to present different translations of the page for different languages.
Many of our clients have requirements like the above, they look to add photos of varying sizes, videos, and lots of other types of data in their pages.
Aspects to review
For any site looking to add functionality defined above, several aspects should be taken into consideration:
- Does the solution meet the requirements?
- How usable is the solution? Does it fit with other UIs that the editorial team would have been using for other content in the site? If it's a different structure, how complicated is it, is it straight forward enough that staff might be able to quickly learn?
- How stable is the solution? Is there a stable release available for the module(s) involved? Are there major or critical outstanding bugs in its/their issue queues(s)?
- Can the data be queried using Views?
- Is there support for the Migrate system, used for upgrading or importing content from another system?
Bonus points may be awarded for the following:
- How does the data architecture work?
- How actively maintained is the solution?
- If the solution involves contributed modules, are there any projects underway to add them to core?
Please note that I did not get into the front-end aspects of the architectures or user permissions, this article is focused on the architectural aspects of the functionality.
Option 1: Out-of-the-box functionality
Drupal 8 includes several pieces that provide some portion of the requirements, but in general it falls very short.
The closest approximation to our requirements would entail creating multiple block types, one for each page component that was needed.
- Open "Manage" -> "Structure" -> "Block layout" -> "Custom block library" -> "Block types" (i.e. /admin/structure/block/block-content/types).
- Click the "Add custom block type" button.
- Fill in a "Label", which is the name of this page component, e.g. "Text with floating image", and make sure a suitable "Machine name" is assigned. It may also be useful to provide a "Description" that explains what the intended use is for this block type. Click "Save" when finished to return to the main "Block types" page.
- Click the "Manage fields" button for the new block type.
- Add all of the fields that are required for this block type, e.g. a text field, an image field, etc.
- Go to the "Manage form display" tab to customize and rearrange the fields as desired.
- Go to the "Manage display" tab to customize how the fields will appear when the block is displayed on a page.
- Repeat the steps for each page component.
Once the page component structures are ready they need to be added to a content type.
- Open Structure -> Content types.
- Click the "Add content type" button.
- Provide a suitable "Name" for this content type, e.g. "Landing page" and make sure a suitable "Machine name" is assigned. It may also be useful to provide a "Description" that explains what the intended use is for this content type. Click "Save and manage fields" when finished.
- The content system in Drupal core automatically adds a default "Body" field, so let's remove that. Click the "Operations" dropdown button to see the additional options and click "Delete". Click "Delete" on the confirmation page to remove this unnecessary field.
- Click the "Add field" button.
- On the "Add a new field" selector, look for the "Reference" section of the list and select "Other" (i.e. not a "Content", "File", etc item). Fill in a "Label" for this new field, e.g. "Page components", and make sure a suitable "Machine name" was generated. Click "Save and continue" to continue.
- On the "Field settings" page set the "Type of item to reference" option to "Custom block", and change the "Allowed number of values" to "Unlimited"; click "Save field settings" to continue.
- Provide some "Help text" to explain to the site's content team on how to use the field.
- Lastly, under the "Reference type" section of the "Edit" form, in the "Bundles" option select each of the block types which were created earlier; click "Save settings" when finished.
- Add other fields as necessary, e.g. a Metatag field for overriding the page's meta tags using the Metatag module, etc.
- Load the "Manage display" tab.
- For the new "Page components" field, set the label to be "- Hidden -" and change the format to be "Rendered entity"; click "Save" when finished.
To then create a custom page the following steps would need to be taken.
- Open up the node/add form for the content type.
- Fill in the title and other meta information about the page.
- Open up a new browser tab and go to the "Custom block library" section (/admin/structure/block/block-content).
- Click the "Add custom block" button and then proceed to create blocks for each of the sections of content that are needed on the page; make sure to give each a clear name as they will be needed later.
- Once each of the blocks is created, return to the node creation page in the other tab.
- For each of the blocks that was created, use the reference field's autocomplete text field to add each one in turn.
- Should positioning of the blocks need to be changed a simple drag ‘n drop of the item in the field will accomplish the goal.
- Once all of the blocks are added to the page, click "Save and publish" to save the new node.
Should changes be needed, it would be necessary to use the "Custom block library" to find and then edit the required blocks. Note that once changes are made to a block they would be instantly visible to visitors, even if the Content Moderation functionality of Drupal 8.2 was being used to create a draft.
The data of the sub components, i.e. the blocks created above, is stored in regular field data stored in the block's tables, and those blocks are referenced by ID directly in the fields on the node's tables. This results in data structures that are very straightforward and very easy to query using Views or custom query logic.
Because this option uses core data structures there is (at least some) existing support for Migrate.
- An obvious benefit of this solution is that all (or nearly all, see below) it uses modules already available in core, so they are stable and well supported.
- It uses (mostly) known data editorial processes and UIs, so adoption should be straightforward.
As can be guessed from the above, there are a number of problems with this plan.
- The blocks have to be created first and then added to the reference field, there's no way out-of-the-box to create blocks at the same time as creating the node without e.g. using a separate browser tab.
- There's no way to edit the blocks from the node form, the editor has to go to the Block admin section and manage them there; staff may find this difficult or cumbersome.
- The block UI becomes cluttered with blocks and block types that are designed for embedding in nodes, not displayed in page regions.
- While individual blocks may have new revisions created of them, there is no support for tying a block's revision state to the parent node's editorial state. In practical terms, this means that changes are always "live".
- It is hard to move field-level pieces of content from one referenced block to another, e.g. to move a chunk of text from one area to another. Accomplishing this usually involves copying and pasting, but depending on the type of field it can get more cumbersome, e.g. moving a file or image may require uploading it again.
- Embedding a View is difficult to do.
- Use the Inline Entity Form module to allow for creating and editing custom blocks right on the node form. This would resolve the obvious problem of having to go to two separate locations - the node edit form and the custom block library - to edit all parts of a single page.
- Use the ECK module to create custom entity types for use in the reference field instead of custom block types. This would resolve the problem of cluttering up the block UI with blocks that were not going to be used as miscellaneous items to be displayed in page layouts.
Option 2: Paragraphs
The Paragraphs solution requires two modules - Paragraphs itself, and its sole dependency Entity Reference Revisions. Built to provide a better solution for adding groups of fields, and different groups of fields for e.g. different portions of a page, the module has become a very popular choice for building complex pages.
The process for creating the required editorial system for Paragraphs is similar to that of Option #1. While Option #1 used the blocks system and so created custom "Block types", so with Paragraphs it is necessary to create custom "Paragraph types". This system uses the exact same fields available to blocks, content types, etc, so it's "the same but different."
Similar to Block types, Paragraph type definitions are reusable, so they can be used with multiple content types (or other situations where they're needed). For example, a "Text with floating image" paragraph type might be used on a "Blog post" content type and a "Product" content type, while e.g. a "Product" content type might have a "Carousel" Paragraph type that's used to demo different poses of the product.
The general steps are:
- Use the Paragraphs admin section (Structure -> Paragraph types) to create different block types, one for each type of "component". Add the fields that are necessary for each type.
- Add a content type, as before.
- Add a new field to the content type, this time using a "Reference revision -> Paragraphs" field as opposed to a regular "Reference" field. On its field settings page ensure that the "Type of item to reference" is set to "Paragraphs" and it is set to allow unlimited items. Lastly, on the "Reference type" section of the field edit form select the Paragraph types created earlier.
Once the data structures are created, creating content follows a normal enough editorial processes.
- Load the node/add form for the content type.
- A field will be available on the edit form that starts off as one of Drupal 8's "add" buttons with a dropdown that lists each available Paragraph Type. To add a new piece of content, open up the dropdown menu and click the appropriate "Add" button for the desired piece of content.
- Fill in the fields as appropriately.
- Continue adding more pieces of content as needed.
- Click "Save and publish" when finished.
Editing existing content follows the same process as always, just go to the node's "edit" form and edit away.
The data structure is almost identical to the core Entity Reference + Block Types solution (option #1), with the added note that the Entity Reference Revisions field that controls the system points to the individual *revision ID* of the referenced paragraph item, not its primary ID.
While there is no support for the Migrate system yet, at the time of writing that is actively being worked on and should be available soon.
- The modules involved, Paragraphs and Entity Reference Revisions, are very stable at this point on Drupal 8.
- The solution works great with Content Moderation workflows, it's kindof the module's raison d'être.
- Multiple Paragraph Types can be used in different reference fields, there are no restrictions that limit which fields they are used with, greatly improving their reuse across different scenarios.
There are some limitations or problems with the Paragraphs solution, though they're honestly smaller problems than some of the other options:
- There is no support for the Quick Edit system, though that is being worked on.
- Similar to using core reference fields and block types (option #1), it can be hard to move individual fields around, e.g. to move a portion of text from one area to another.
- Migrate support is not ready yet, but is also being worked on.
- Embedding a View is a little difficult to do.
Some have said they do not like the editorial UX, but given it uses the core field system's UX it is at least a familiar process. Some discussions have started up about presenting an alternative UI, to make it more drag-n-droppy, and the maintainers of Field Collection have bounced around the idea of turning that module into an alternative UI. Personally, I think a grid-style UI could be rather good for some types of data, but we'll see what becomes of the initiatives. Incidentally, the maintainers are seeking collaborators to help improve the module.
Option 3: Entity Embed
The scenarios covered above all focused on directly connecting one type of entity to another using a type of field that refers one entity to the other. With Entity Embed the connection is indirect - the connection from the parent node to the children page components is managed using code embedded inside the WYSIWYG editor; it's reminiscent to the 1980's where we had to store special printer control codes in a word processing document to format the output, only this time we have a GUI.
Because this architecture uses the text formats rather than custom fields, it will be usable site-wide on any text field on any entity. This means that once the module is configured properly it can be used on any text field on the site with no additional setup, so taxonomy term descriptions, blog posts, user profile biography fields, etc, etc can take advantage of it.
Thanks to Drupal 8 having a WYSIWYG editor in core, this module only has one dependency - the Embed module.
The Entity Embed system builds upon the existing WYSIWYG functionality in Drupal core, and then allows different types of things to be inserted through that mechanism. Along with core's Editor and Filter modules, Entity Embed also requires the Embed module.
Setting up this system will, as with others, require creating either content types or block types for each type of page component that is needed, so again the fields system gives lots of flexibility for building the requirements as needed; see Option #1 for details on building out the page components.
Once the page component structures are built, further steps are needed to make them available within the WYSIWYG editor.
- Load the Manage -> Configuration section of the site.
- Open the "Text editor embed buttons" page (/admin/config/content/embed).
- Click the "Add embed button" button.
- Fill in a label for this component, e.g. "Carousel", and make sure a machine name has been assigned. This label will be used as the label in the popup, and the hover text on the WYSIWYG toolbar.
- Select the type of thing being embedded; for our use case we're just focused on entities, so select "Entity".
- After the form updates, select the type of entity being embedded, i.e. if content types are being used for the components pick "Content", or if block types are being used select "Custom block".
- Select one of the content types or block types (aka the entity bundle) that was created earlier.
- Presuming that the intention is to only use the default display of the selected page component structures, select either "Full content" or "Full" as the "Allowed Entity Embed Display plugins".
- If some icons are available it may be worth adding one. Otherwise, a default button with the character "E" will be used.
- Click "Save" to finish.
- Use the breadcrumb trail to the "Content authoring" page, and then click "Text formats and editors".
- Find the text format that the embed button is to be added to, one of the ones with a text editor defined, and click the "Configure" button across from it.
- In the "Toolbar configuration" section of the page, find the new entity embed button that was just created in the "Available buttons" list and drag it to the appropriate group in the "Active toolbar".
- Click "Save configuration" to finish.
These steps will need to be repeated for each page component that needs to be added to the page.
One useful aspect of this option is that, as mentioned, everything is handled through the WYSIWYG editor on the relevant node-edit form. As a result, it becomes somewhat easier for editorial staff to manage their content.
To create a new landing page, create a new node as normal. The node will have the usual body field along with a WYSIWYG editor. What will be new to the editorial team are the new WYSIWYG editor toolbar buttons, which will allow the team to insert the page components as needed. Clicking on one of the buttons will open a popup allowing content to be linked. Besides that, the editorial experience is the same as with core's normal processes.
The Entity Embed system works off the normal input filters and uses custom control strings embedded via the WYSIWYG to do its work. This means that the data is stored in regular text fields, but it also means those strings have to be parsed via regular expression in order to extract the information. This makes working with these data structures cumbersome, and there's little chance of ever seeing Views support.
There is no Migrate support for this yet, and I would be surprised if anything could be made general enough to contribute, given how custom the data structures would be.
There are several reasons why this may be a reasonable way of handling the requirements.
- The WYSIWYG-based workflow may be easier for some people to grasp.
- Being based on the text formats system, the page components can be added to any content type, taxonomy term description, user profile biography field, etc as is needed.
- Embed buttons, therefore page components, can be reused across multiple text formats - it isn't necessary to create duplicates of these for different places they will be used.
There are some definite problems with this structure.
- Neither the Entity Embed nor Embed modules have stable releases yet, though they appear to be getting close and they have been used in many production sites already.
- Setup is a little cumbersome - the page component structure needs to be built, the embed button needs to be created (preferably with a new icon), the WYSIWYG toolbar needs to be updated... I had difficulty getting it to all work correctly when I tried.
- The solution does not work well with Content Moderation workflows because the embedded components are referenced by the primary ID, not by revision ID; as a result, any changes to the page components would be immediately visible to anonymous visitors.
- Not all types of content will benefit from a WYSIWYG-focused editorial experience.
- Properly handling the HTML and CSS output of this structure can be tricky, if not downright difficult, depending on how the components are placed within the flow of text in the WYSIWYG editor.
- It isn't possible to control which users (i.e. user roles) will have access to add page components via the WYSIWYG toolbar, without also limiting their access to that text format. This may lead to needing multiple text formats for different access levels.
- Using the text format system also means that the list of embed buttons cannot be controlled on a per-use case basis, i.e. the toolbar for editing an Article node will be the same for editing a taxonomy term's description, so again it may be necessary to create multiple text formats to fine tune which components are available in which structures
- The data structure is almost impossible to work with from a Views or a normal entity query, it would require possibly heavy custom logic (PHP or stored procedures, whichever) to make sense of it.
- Just using core's entities, the content types or block types that are used will clutter the management of those systems.
- Embedding a View is difficult to do.
- The module allows any type of entity to be added as a new embed button, so it may be worth using the ECK module to create custom entity types instead of using block or content types.
- The problem of needing to create the referenced content before linking it can be streamlined by adding the Entity Browser and Inline Entity Form modules, though that is making the solution even more complicated.
Option 4: Panelizer
A staple for many Drupal 7 sites I built over the past number of years, Panelizer provides a different interface for embedding different page components, leveraging the Panels system instead of core's standard entity templating system. When paired with the In-Place Editor (IPE) from Panels it allows site editorial teams to drag ‘n drop page components around the page with little effort. For Drupal 8 the user interface and setup has changed a bit from Drupal 7, though once set up it appears to work fairly well. That said, there are some gotchas that will be covered shortly.
Drupal allows different displays of the same content for different situations, e.g. a teaser is displayed differently to a full page display. These display mechanisms are called "view modes". Panelizer goes further by allowing each view mode to be customized individually so that the teaser can be displayed one way while the full display (aka "Full content") is different.
The Panelizer module requires Panels, Page Manager and CTools modules, so they need to be installed too. Block types will need to be created for each page component, as with Option #1, and a new content type will also need to be added.
Note: there are two versions of Panelizer and Panels for use with Drupal 8. If using Drupal 8.2 then the 8.x-3.x versions of both Panels and Panelizer should be used, and the separate Layout Plugin module must also be installed. If using Drupal 8.3 then use the 8.x-4.x versions of Panels and Panelizer, and Layout Plugin is not needed as it was merged into core.
In order to allow individual nodes to be managed by Panelizer, a little bit of setup is needed (correct as of 8.x-4.0-beta1).
- On the content type, go to the "Manage display" settings page.
- Enable the "Panelize this view mode" option. This will use Panelizer
- In order to allow each individual node to be customized, check the "Allow each content item to have its display customized" option.
- Click the "Save" button.
The default display will list each field and several meta elements (author, created timestamp, etc) in a single column, so it may be worthwhile to adjust the default display. Thankfully the default can be changed by following a few steps:
- Load the content type's "Manage display" settings page again.
- In the list of "Panelized Displays", find the "Default" item and click the "Edit" operation button that corresponds with it.
- Click the "Content" option to load a page where the fields can be adjusted.
- Click the "Add new block" button to get a list of the blocks that are available to be added.
- In the dialog that shows, fill in the title, select which region it should go in and adjust the other options as needed. Click "Add block" when finished to add the new block to the page.
- Drag ‘n drop the blocks into the different regions as needed, change their options via their "Edit" buttons, etc.
- Click "Update and save" when finished.
Day-to-day usage of Panelizer may take a little getting used to.
- Create a node as usual; it may be worthwhile to leave the node as unpublished until the content is filled in as needed.
- By default the node will use the "Default" display customized above.
- To edit the page's display, look for the new toolbar that appears at the bottom of the page and click the "Edit" button. This will load the "Panels In-Place Editor" which allows the page's content and layout to be modified. The interface allows existing blocks to be sorted and moved between page regions, and a gear icon to the right side will show all of the settings currently available for that pane.
- The interface also provides all of the available blocks via the "Manage content" display, which groups the blocks by category; clicking on a category will show the blocks available in that category. To add a new block just find the one that is to be used, drag it up to the content area and drop it into the region that it is intended to go in, making sure to order it as needed.
- To change the node's layout, e.g. from one column to two, click the "Change layout" button in the toolbar, select the number of columns the display should have, and then click the specific layout that is desired.
- Once the desired changes are finished, click "Save" and then "Save as custom" to save the display for this node. Be careful, clicking the "Save as default" option will force all other nodes (which haven't already been customized) to use the new display, not just the one being edited.
Panelizer's per-entity display customizations are stored in a field, so that level of the data structure is at least partly manageable. The data in the field's record is stored in an old fashioned serialized array, which contains identifiers for the types of items being positioned in the various layout regions, their per-instance settings, etc. In short, it's rather messy to work with and, as could be guessed, there isn't any Views support yet.
There is no Migrate support yet, though I believe the data structure is ironed out enough to where it could be done.
- This solution opens up a greater amount of flexibility than would be available otherwise. It becomes super easy to position Views output or other random blocks alongside content fields.
- Multiple predetermined displays can be created ahead of time and then simply switched via the node's regular "edit" form; this can be really useful for many sites where full control of the display is not necessarily needed, but an ability to set up e.g. a "promotional display" version of a node is a regular requirement.
There are several problems with this solution.
- Stable releases of Panelizer, Page Manager and CTools aren't available yet. That said, they are expected by DrupalCon Baltimore in April and they are already being in used on many production sites by way of the Lightning distribution.
- Right now Panelizer ties changes in the display from one revision to another, which is a good step in the right direction, it just doesn't yet support tracking changes in the page components (blocks) themselves. The functionality to tie changes to the blocks in a Panelizer display to the node's editorial state is not finished yet, but it's intended be ready in time for DrupalCon Baltimore.
- The editorial process and UI for modifying the display is different from core's standard editorial UI.
- The Spider-Man Mantra comes into play - wIth great power (and flexibility) comes great responsibility. Use of Panelizer can lead to a greater need for training editorial staff, to spending time limiting options available to the staff by hiding items they would not need to use.
There's definitely overlap between the IPE interface available on an individual Panelizer-enabled node and the Quick Edit and Outside-In systems in core; work is in progress to streamline this workflow and functionality for Drupal 8.4.
|Edit process||Fields||Fields||WYSIWYG||Separate UI|
|Dynamically add pieces||Yes||Yes||Yes||Yes|
|Dynamically add different pieces||Yes||Yes||Yes||Yes|
|Combine blocks with content||Kinda||Kinda||Yes||Yes|
|Combine Views with content||Hard||Hard||Hard||Yes|
|Editorial workflow||No||Yes||Not really||WIP|
|Migrate integration||Yes||WIP||Custom||Not yet|
The not-so-obvious answer to the question on everyone's mind, which option is best, is that it depends upon the project's requirements. Some questions to help narrow down the choices would be:
- Does the site require a WYSIWYG-driven editorial process? If so, Entity Embed may the best way to go.
- Does the site need to launch quickly? If so, Paragraphs is stable now, whereas Panelizer is not quite finished yet
- Is a clear upgrade path needed immediately, e.g. to import content from another CMS? If so, core's functionality is the only one with a supported upgrade/migrate path right now.
- Are there developers available to help finish off missing features or fix bugs in the chosen option? If so, then it doesn't really matter which option is selected.
My personal recommend is:
- Use Paragraphs for most use cases.
- Use Panelizer for either complicated layouts, where getting into nested Paragraphs to achieve different layout styles makes it really messy, or where there's a desire to not use the normal entity edit interface.
Appendix: It's about the revisions!
A common failing with reference-based systems is an inability to support revisions of the parent entity. For example, when editing an "article" node, anything that is in the node should go through the same editorial workflow as the node itself. For many years the "nested" data structures available to Drupal - Panelizer, Entity Reference, Fieldable Panels Panes, Field Collection, etc, did not support revisions. Some of us tried to add revision tracking to EntityReference but the discussion ultimately lead to a refrain of "this module will follow Drupal 8 core", only to see people dismiss the idea of adding the functionality to core because it hadn't been proven in the contrib world yet - a nasty catch 22.
(On an aside - yes, Field Collection did add support for revisions in 2012 but it had other limitations to make it an unsuitable solution for many sites, like still being in beta after six years)
The problem with not supporting revisions stems from sites wanting to have an editorial workflow around their content. Many sites want to have their content changes be reviewed by others – whether it's new content or changes to existing content – before it gets published to the world. Some sites will also want to publish items at a certain time, e.g. when an official announcement is made at a conference there might need to be changes scheduled to become visible for visitors. Other sites might have legal requirements that content be vetted by a lawyer before it is published and to then be able to track the text changes that the lawyer requests. If the items added to the page do not have their revision tied to the parent node's revision, these changes would be immediately visible to all visitors. For some businesses, this might be inconvenient, but for others it could lead to major legal ramifications.
With the advent of Paragraphs and Entity Reference Revision, and to a degree some recent improvements to Panelizer and Fieldable Panels Panes (and forthcoming improvements in Panelizer on D8), there is finally a consideration for the revision state of the node that these objects are attached to. Changes to a node can now be made with the full understanding that everything editable from the node's edit form will go through the same editorial review process, and that there won't be unexpected surprises when a portion of the page is published before the rest. This is how site editors have expected, and site builders hoped, their site to work for years, it is only now that the Drupal community has the stable tools to support this simple expectation.
Appendix: Options that were not considered
The above does not cover every available option. As is always the case with Drupal, there were many other options that could have been considered but were excluded from the list for various reasons.
A module with a long history, Field Group provides a way of grouping multiple fields onto an entity's edit form. This doesn't really cover any of the use cases we needed - it's more of a cosmetic UX improvement rather than architecturally going beyond what core already provides. Many sites will use Field Group to group fields together into fieldsets or even new vertical tab groups, but it really doesn't do anything to help our use cases. In fact, the Field Group module is so ubiquitous that hopefully it will be added to core at some point.
This module started life as a spin-off of the work "multigroup" functionality of the ill-fated CCK v3 for Drupal 6 and provides a way of repeating a grouping aka "collection" of fields. The older Drupal's architecture left much to be desired for cleanly implementing this system, but Drupal 7's entity system finally provided a clean base to build from. As a result, the Drupal 7 release of Multigroup became a reference field with a custom entity type and was the defacto standard for several years for building such data structures. Over time as people realized they wanted editorial control over their content and so the necessary revisioning support was added; a similar need lead to support for translations using the Entity Translations module. All told, at this point is has a very similar functionality set to Paragraphs.
On the Drupal 8 front, in my opinion it suffered from a lack of momentum and in 2016 was on its third port attempt with no stable releases yet available, as compared to Paragraphs which hit its 1.0 release in July of that year. Seeing this situation unfold, and knowing that duplicate efforts can be a waste of effort and of the community's limited time, I suggested that the site-building world would be a lot simpler were Field Collections deprecated and efforts were instead put into improving Paragraphs. Thankfully, after some discussion, the maintainers saw the benefit and have started collaborating!
A late addition to the race, the Stacks module, seems like it may have some interesting potential. Currently, only a development snapshot is available for Drupal 8, with no details currently available on the maintainers' plans to get it to a stable release. However, what has been disclosed so far via some screencasts listed on the project page and promotional information on the maintainers' website it looks like there are lots of interesting ideas wrapped up in the module. So far it seems like a combination of Entity Reference, Inline Entity Form, a custom entity type, the Template Picker module and some UI customizations for good measure. For people new to it, the Template Picker module provides a method for having different displays available for each type of component based upon the available Twig files.
The key problem with the Stacks module is that, like Entity Reference in core (see option #1 above), it does not support tracking content changes by its revision so changes are published immediately. Given this is a key requirement for many sites, I don't recommend its use and instead would suggest people put their efforts into Paragraphs or one of the other solutions.
I just discovered one more "nested entities" system that had slipped past my radar - Bricks. This system has actually been around for a few years and is currently at v4.5 for Drupal 7. Again, this lets you nest other entities inside of a parent entity, like Option 1. Again, like Stacks, this module provides an alternative UI for managing the nested entities, with some definitely interesting ideas. However, again, like Stacks, it fails to properly support revisions so right now I cannot recommend it.
Appendix: What about Outside-In?
The new UX paradigm for Drupal core, "outside in", presents an interesting shift in Drupal's editorial workflow. Because this was only added in Drupal 8.2, which was only released a few months ago, the contrib world hasn't caught up yet to supporting or taking advantage of the new facilities. What I'm personally hoping for is that some of the data structure tools (Paragraphs, etc) will start to support the new processes and ultimately build a bridge so editorial teams can take advantage of the usability improvements.