You are here

class ParagraphsPreviewController in Paragraphs Previewer 8

Previewer for paragraphs.

Hierarchy

Expanded class hierarchy of ParagraphsPreviewController

File

src/Controller/ParagraphsPreviewController.php, line 17

Namespace

Drupal\paragraphs_previewer\Controller
View source
class ParagraphsPreviewController extends ControllerBase {

  /**
   * Render a preview while on a form.
   *
   * This callback is mapped to the path
   * 'paragraphs-previewer/form/{form_build_id}.
   *
   * Usage:
   * 'paragraphs-previewer/form/abcd1234?p[0]=field_name&p[1]=delta'.
   *
   * @param string $form_build_id
   *   The form build id.
   * @param string|array $element_parents
   *   The item parents argument from the field to the item delta.
   *
   * @return array
   *   The render array.
   *
   * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
   *   If the parameters are invalid.
   */
  public function onForm($form_build_id, $element_parents) {

    // Parameter check.
    if (empty($form_build_id) || empty($element_parents)) {
      throw new AccessDeniedHttpException();
    }

    // Initialize render array.
    $output_render = [];

    // Expand element parents.
    $element_parents_array = is_array($element_parents) ? $element_parents : explode(':', $element_parents);
    if (!empty($element_parents_array) && count($element_parents_array) >= 2) {
      $form_state = new FormState();
      $form = \Drupal::formBuilder()
        ->getCache($form_build_id, $form_state);
      if ($form && ($form_entity = $form_state
        ->getFormObject()
        ->getEntity())) {
        $field_parents = $element_parents_array;
        $field_delta = array_pop($field_parents);
        $field_name = array_pop($field_parents);
        $widget_state = WidgetBase::getWidgetState($field_parents, $field_name, $form_state);
        if (!empty($widget_state['paragraphs'][$field_delta]['entity'])) {
          $paragraph = $widget_state['paragraphs'][$field_delta]['entity'];
          $parent_entity = $this
            ->findParentEntity($paragraph, $field_parents, $form_state, $form_entity);
          if ($parent_entity) {
            $field_render = $this
              ->paragraphsPreviewRenderParentField($paragraph, $field_name, $parent_entity);
            if ($field_render) {
              $output_render['preview'] = $field_render;
            }
          }
        }
      }
    }

    // Set empty message if nothing is rendered.
    if (empty($output_render)) {
      $output_render['empty'] = [
        '#markup' => $this
          ->t('No preview available.'),
      ];
    }

    // Add styles to cleanup display.
    $output_render['#attached']['library'][] = 'paragraphs_previewer/preview-page';
    return $output_render;
  }

  /**
   * Find the parent entity of the paragraph.
   *
   * Finds any parent paragraphs else defaults to the form entity provided.
   * Note: only paragraphs are supported as parent or intermediate entities.
   *
   * @param \Drupal\paragraphs\Entity\Paragraph $paragraph
   *   The paragraph entity.
   * @param array $field_parents
   *   The field parents of the paragraph.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param \Drupal\Core\Entity\EntityInterface $form_entity
   *   The form entity.
   *
   * @return \Drupal\Core\Entity\EntityInterface
   *   The parent entity if found else the form entity.
   */
  public function findParentEntity(Paragraph $paragraph, array $field_parents, FormStateInterface $form_state, EntityInterface $form_entity = NULL) {
    if (in_array('subform', $field_parents, TRUE)) {

      // Traverse up to find the parent.
      foreach (array_reverse($field_parents, TRUE) as $i => $element_key) {
        if ($element_key === 'subform') {

          // Slice one level above 'subform'.
          $parent_field_parents = array_slice($field_parents, 0, $i);
          if ($parent_field_parents) {
            $parent_field_delta = array_pop($parent_field_parents);
            $parent_field_name = array_pop($parent_field_parents);
            $widget_state = WidgetBase::getWidgetState($parent_field_parents, $parent_field_name, $form_state);
            if (!empty($widget_state['paragraphs'][$parent_field_delta]['entity'])) {

              // Return first found.
              return $widget_state['paragraphs'][$parent_field_delta]['entity'];
            }
          }

          // Break on first found.
          break;
        }
      }
    }
    return $form_entity;
  }

  /**
   * Render a single field on the parent entity for the given paragraph.
   *
   * @param Paragraph $paragraph
   *   The paragraph entity.
   * @param string $parent_field_name
   *   The field name of the paragraph reference field on the parent entity.
   * @param ContentEntityBase $parent_entity
   *   Optional. The parent entity. This is used when on a form to allow
   *   rendering with un-saved parents.
   *
   * @return array|null
   *   A render array for the field.
   */
  public function paragraphsPreviewRenderParentField(Paragraph $paragraph, $parent_field_name, ContentEntityBase $parent_entity = NULL) {
    if (!isset($parent_entity)) {
      $parent_entity = $paragraph
        ->getParentEntity();
    }
    if ($parent_entity && $parent_entity instanceof ContentEntityBase) {
      $parent_class = get_class($parent_entity);
      $parent_entity_type = $parent_entity
        ->getEntityTypeId();
      if ($parent_entity
        ->hasField($parent_field_name)) {
        $parent_view_mode = \Drupal::config('paragraphs_previewer.settings')
          ->get('previewer_view_mode');
        $parent_view_mode = $parent_view_mode ? $parent_view_mode : 'full';

        // Create a new paragraph with no id.
        $paragraph_clone = $paragraph
          ->createDuplicate();

        // Clone the entity since we are going to modify field values.
        $parent_clone = clone $parent_entity;

        // Create field item values.
        $parent_field_item_value = [
          'entity' => $paragraph_clone,
        ];

        // Based on \Drupal\Core\Entity\EntityViewBuilder to allow arbitrary
        // field data to be rendered.
        // See https://www.drupal.org/node/2274169
        // Push the item as the single value for the field, and defer to
        // FieldItemBase::view() to build the render array.
        $parent_clone->{$parent_field_name}
          ->setValue([
          $parent_field_item_value,
        ]);

        // TODO: This clones the parent again and uses
        // EntityViewBuilder::viewFieldItem().
        $elements = $parent_clone->{$parent_field_name}
          ->view($parent_view_mode);

        // Extract the part of the render array we need.
        $output = isset($elements[0]) ? $elements[0] : [];
        if (isset($elements['#access'])) {
          $output['#access'] = $elements['#access'];
        }
        return $output;
      }
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ControllerBase::$configFactory protected property The configuration factory.
ControllerBase::$currentUser protected property The current user service. 1
ControllerBase::$entityFormBuilder protected property The entity form builder.
ControllerBase::$entityManager protected property The entity manager.
ControllerBase::$entityTypeManager protected property The entity type manager.
ControllerBase::$formBuilder protected property The form builder. 2
ControllerBase::$keyValue protected property The key-value storage. 1
ControllerBase::$languageManager protected property The language manager. 1
ControllerBase::$moduleHandler protected property The module handler. 2
ControllerBase::$stateService protected property The state service.
ControllerBase::cache protected function Returns the requested cache bin.
ControllerBase::config protected function Retrieves a configuration object.
ControllerBase::container private function Returns the service container.
ControllerBase::create public static function Instantiates a new instance of this class. Overrides ContainerInjectionInterface::create 40
ControllerBase::currentUser protected function Returns the current user. 1
ControllerBase::entityFormBuilder protected function Retrieves the entity form builder.
ControllerBase::entityManager Deprecated protected function Retrieves the entity manager service.
ControllerBase::entityTypeManager protected function Retrieves the entity type manager.
ControllerBase::formBuilder protected function Returns the form builder service. 2
ControllerBase::keyValue protected function Returns a key/value storage collection. 1
ControllerBase::languageManager protected function Returns the language manager service. 1
ControllerBase::moduleHandler protected function Returns the module handler. 2
ControllerBase::redirect protected function Returns a redirect response object for the specified route. Overrides UrlGeneratorTrait::redirect
ControllerBase::state protected function Returns the state storage service.
LinkGeneratorTrait::$linkGenerator protected property The link generator. 1
LinkGeneratorTrait::getLinkGenerator Deprecated protected function Returns the link generator.
LinkGeneratorTrait::l Deprecated protected function Renders a link to a route given a route name and its parameters.
LinkGeneratorTrait::setLinkGenerator Deprecated public function Sets the link generator service.
LoggerChannelTrait::$loggerFactory protected property The logger channel factory service.
LoggerChannelTrait::getLogger protected function Gets the logger for a specific channel.
LoggerChannelTrait::setLoggerFactory public function Injects the logger channel factory.
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
ParagraphsPreviewController::findParentEntity public function Find the parent entity of the paragraph.
ParagraphsPreviewController::onForm public function Render a preview while on a form.
ParagraphsPreviewController::paragraphsPreviewRenderParentField public function Render a single field on the parent entity for the given paragraph.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 1
RedirectDestinationTrait::getDestinationArray protected function Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
UrlGeneratorTrait::$urlGenerator protected property The url generator.
UrlGeneratorTrait::getUrlGenerator Deprecated protected function Returns the URL generator service.
UrlGeneratorTrait::setUrlGenerator Deprecated public function Sets the URL generator service.
UrlGeneratorTrait::url Deprecated protected function Generates a URL or path for a specific route based on the given parameters.