4

I've exported views into the custom module using Drupal Console and config:export:view command, so they exist as part of the module in modules/custom/my_module/config/install (YAML files).

After I've enabled the newly created custom module, the views are imported on install as expected.

However when I'm trying to to do config import (drush config-import), Configuration Manager wants to remove the views. Respectively, when I want to export (drush config-export), it asks to create them in ../config/sync (despite they're already existing in the module it-self).

What I'm looking is, the ability to hardcode the views into custom module, so they won't be part of ../config/sync anymore, so config import won't affect or remove them. In other words, I'm looking for locking down mechanism to avoid accidental removal during config import on the production (something similar to hook_views_default_views in D7), where views are hardcoded as part of the code.

2

I see 2 options you have:

  1. Use Features to take care of your view and its config together with Config Ignore or some similar approach to have this view's built-in config completely ignored from configuration management. With Features then you still have the ability to track and/or deploy any possible changes of the view.

  2. Build your own controller or block and have full control of the output, with your own templates, your own database queries, your own everything. Most if not all of this then will simply be invisible to configuration management.

Depending on the complexity of the view, I'd prefer option 2.

2

Yes that's right the config export will recreate them for its config sync handling. The /module/config/install is exactly that, just for install time. The config syncing would become a big mess of overlaps if every module could define different elements to handle syncing themselves. Is it such a problem that config sync has them? As that can be your master of that the view should contain. So think of it as: The module sets up the default upon install and Drupal's config sync will make a copy so that's now the master and won't get removed unless you remove it through the UI and then do a config export.

If people are paying attention it should hopefully be unlikely someone would delete a view from the UI, do a config export and do an import on live without realising they've messed up. But just in case have you thought about adding your config sync directory into GIT? So that if someone does say delete a view through the UI and do an export you'd be able to revert

  • 1
    Plus on the live instance you could also simply disable the Views UI sub-module. Maybe via config_split or some similar module/approach. – leymannx Jul 16 '18 at 19:36
2

To complete @leymannx answer, after exporting into Feature and adding the views into Config Ignore, the following hooks can be added for additional precaution to prevent any alteration of the views.

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\views\Entity\View;
use Symfony\Component\HttpFoundation\RedirectResponse;

/**
 * Implements hook_entity_predelete().
 */
function MYMODULE_entity_predelete(EntityInterface $entity) {
  if ($entity instanceof View) {
    // Protecting only non-local environments.
    if (strpos($entity->id(), 'MYVIEW_') === 0) {
      // Views are protected from deletion.
      drupal_set_message(t("This view can't be deleted as it is crucial for the system to work correctly."), 'error', FALSE);
      $response = new RedirectResponse('/admin/structure/views');
      $response->send();
      // Preventing any success messages from appearing.
      exit;
    }
  }
}

/**
 * Implements hook_entity_presave().
 */
function MYMODULE_entity_presave(EntityInterface $entity) {
  // Protecting only non-local environments.
  if ($entity instanceof View) {
    if (!$entity->isNew() && strpos($entity->id(), 'MYVIEW_') === 0) {
      // Views are protected from modification.
      drupal_set_message(t("This view can't be modified as it is crucial for the system to work correctly."), 'error', FALSE);
      $response = new RedirectResponse(\Drupal::request()->getRequestUri());
      $response->send();
      // Preventing any success messages from appearing.
      exit;
    }
  }
}

/**
 * Implements hook_form_alter().
 */
function MYMODULE_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  // Protecting only non-local environments.
  if ($form_id === 'view_edit_form' && empty($_POST)) {
    foreach ($form['#cache']['tags'] as $tag) {
      if (strpos($tag, 'config:views.view.MYVIEW_') === 0) {
        // Views are protected from modification.
        drupal_set_message(t("Note that changes to this view won't be visible across the page. The view, in its original state, is crucial for the system to work correctly."), 'warning', FALSE);
        return;
      }
    }
  }
}

Note: Extra conditions can be added as needed like if (empty($GLOBALS['APP_ISPROD'])) or similar.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.