You are here

public function GeofieldGoogleMapViewStyle::render in Geofield Map 8.2

Same name and namespace in other branches
  1. 8 src/Plugin/views/style/GeofieldGoogleMapViewStyle.php \Drupal\geofield_map\Plugin\views\style\GeofieldGoogleMapViewStyle::render()

Renders the View.

Overrides StylePluginBase::render

File

src/Plugin/views/style/GeofieldGoogleMapViewStyle.php, line 802

Class

GeofieldGoogleMapViewStyle
Style plugin to render a View output as a Leaflet map.

Namespace

Drupal\geofield_map\Plugin\views\style

Code

public function render() {
  $map_settings = $this->options;
  $element = [];

  // Performs some preprocess on the maps settings before sending to js.
  $this
    ->preProcessMapSettings($map_settings);
  $js_settings = [
    'mapid' => Html::getUniqueId("geofield_map_view_" . $this->view
      ->id() . '_' . $this->view->current_display),
    'map_settings' => $map_settings,
    'data' => [],
  ];
  $data = [];

  // Collect bubbleable metadata when doing early rendering.
  $build_for_bubbleable_metadata = [];

  // Get the Geofield field.
  $geofield_name = $map_settings['data_source'];

  // If the Geofield field is null, output a warning
  // to the Geofield Map administrator.
  if (empty($geofield_name) && $this->currentUser
    ->hasPermission('configure geofield_map')) {
    $element = [
      '#markup' => '<div class="geofield-map-warning">' . $this
        ->t("The Geofield field hasn't not been correctly set for this View. <br>Add at least one Geofield to the View and set it as Data Source in the Geofield Google Map View Display Settings.") . "</div>",
      '#attached' => [
        'library' => [
          'geofield_map/geofield_map_general',
        ],
      ],
    ];
  }

  // It the Geofield field is not null, and there are results or a not null
  // empty behaviour has been set, render the results.
  if (!empty($geofield_name) && (!empty($this->view->result) || $map_settings['map_empty']['empty_behaviour'] == '1')) {
    $this
      ->renderFields($this->view->result);

    /* @var \Drupal\views\ResultRow  $result */
    foreach ($this->view->result as $id => $result) {

      // For proper processing make sure the geofield_value is created as an
      // array, also if single value.
      $geofield_value = (array) $this
        ->getFieldValue($id, $geofield_name);

      // In case the result is not null.
      if (!empty($geofield_value)) {
        if (empty($this->options['entity_source']) || $this->options['entity_source'] == '__base_table') {
          if (!empty($result->_entity)) {

            // Entity API provides a plain entity object.
            $entity = $result->_entity;
          }
          elseif (isset($result->_object)) {

            // Search API provides a TypedData EntityAdapter.
            $entity_adapter = $result->_object;
            if ($entity_adapter instanceof EntityAdapter) {
              $entity = $entity_adapter
                ->getValue();
            }
          }
        }
        else {
          if (!empty($result->_relationship_entities[$this->options['entity_source']])) {
            $entity = $result->_relationship_entities[$this->options['entity_source']];
          }
        }

        // We need to define this before.
        $description = [];

        // Render the entity with the selected view mode.

        /* @var \Drupal\core\Entity\FieldableEntityInterface $entity */
        if (isset($entity)) {

          // Get and set (if not set) the Geofield cardinality.

          /* @var \Drupal\Core\Field\FieldItemList $geofield_entity */
          if (!isset($js_settings['map_settings']['geofield_cardinality'])) {
            try {
              $geofield_entity = $entity
                ->get($geofield_name);
              $js_settings['map_settings']['geofield_cardinality'] = $geofield_entity
                ->getFieldDefinition()
                ->getFieldStorageDefinition()
                ->getCardinality();
            } catch (\Exception $e) {

              // In case of exception it means that $geofield_name field is
              // not directly related to the $entity and might be the case of
              // a geofield exposed through a relationship.
              // In this case it is too complicate to get the geofield related
              // entity, so apply a more general case of multiple/infinite
              // geofield_cardinality.
              // @see: https://www.drupal.org/project/leaflet/issues/3048089
              $js_settings['map_settings']['geofield_cardinality'] = -1;
            }
          }
          $entity_type = $entity
            ->getEntityTypeId();
          $entity_type_langcode_attribute = $entity_type . '_field_data_langcode';
          $view = $this->view;

          // Set the langcode to be used for rendering the entity.
          $rendering_language = $view->display_handler
            ->getOption('rendering_language');
          $dynamic_renderers = [
            '***LANGUAGE_entity_translation***' => 'TranslationLanguageRenderer',
            '***LANGUAGE_entity_default***' => 'DefaultLanguageRenderer',
          ];
          if (isset($dynamic_renderers[$rendering_language])) {

            /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
            $langcode = isset($result->{$entity_type_langcode_attribute}) ? $result->{$entity_type_langcode_attribute} : $entity
              ->language()
              ->getId();
          }
          else {
            if (strpos($rendering_language, '***LANGUAGE_') !== FALSE) {
              $langcode = PluginBase::queryLanguageSubstitutions()[$rendering_language];
            }
            else {

              // Specific langcode set.
              $langcode = $rendering_language;
            }
          }
          $description_field = isset($map_settings['map_marker_and_infowindow']['infowindow_field']) ? $map_settings['map_marker_and_infowindow']['infowindow_field'] : NULL;
          if (isset($description_field)) {

            /* @var \Drupal\Core\Field\FieldItemList $description_field_entity */
            $description_field_entity = $entity->{$description_field};

            // Assure the view_mode to eventually fallback into the
            // (initially defined) $this->options['view_mode'].
            $default_view_mode = !empty($this->options['view_mode']) ? $this->options['view_mode'] : (!empty($this->options['map_marker_and_infowindow']['view_mode']) ? $this->options['map_marker_and_infowindow']['view_mode'] : NULL);
            switch ($description_field) {
              case '#rendered_entity':
                $build = $this->entityManager
                  ->getViewBuilder($entity
                  ->getEntityTypeId())
                  ->view($entity, $default_view_mode, $langcode);
                $render_context = new RenderContext();
                $description[] = $this->renderer
                  ->executeInRenderContext($render_context, function () use (&$build) {
                  return $this->renderer
                    ->render($build, TRUE);
                });
                if (!$render_context
                  ->isEmpty()) {
                  $render_context
                    ->update($build_for_bubbleable_metadata);
                }
                break;
              case '#rendered_view_fields':

                // Normal rendering via view/row fields (with labels options,
                // formatters, classes, etc.).
                $renderedRow = [
                  $this->view->rowPlugin
                    ->render($result),
                ];
                $description[] = $this->renderer
                  ->renderPlain($renderedRow);
                break;
              case '#rendered_entity_ajax':
                $parameters = [
                  'entity_type' => $entity_type,
                  'entity' => $entity
                    ->id(),
                  'view_mode' => $default_view_mode,
                  'langcode' => $langcode,
                ];
                $url = Url::fromRoute('geofield_map.ajax_popup', $parameters);
                $description[] = sprintf('<div class="geofield-google-map-ajax-popup" data-geofield-google-map-ajax-popup="%s" %s></div>', $url
                  ->toString(), GeofieldMapAjaxPopupController::getPopupIdentifierAttribute($entity_type, $entity
                  ->id(), $default_view_mode, $langcode));
                $js_settings['map_settings']['ajaxPoup'] = TRUE;
                break;
              default:

                // Check if the entity has a $description_field field.
                if (isset($description_field_entity)) {
                  $description_field_cardinality = $description_field_entity
                    ->getFieldDefinition()
                    ->getFieldStorageDefinition()
                    ->getCardinality();
                  foreach ($description_field_entity
                    ->getValue() as $value) {
                    if ($description_field_cardinality == 1 || $map_settings['map_marker_and_infowindow']['multivalue_split'] == FALSE) {
                      $description[] = $this->rendered_fields[$id][$description_field];
                      break;
                    }
                    $description[] = isset($value['value']) ? $value['value'] : NULL;
                  }
                }
                elseif (isset($this->rendered_fields[$id][$description_field])) {
                  $description[] = $this->rendered_fields[$id][$description_field];
                }
            }
          }

          // Add Views fields to Json output as additional_data property.
          $view_data = [];
          foreach ($this->rendered_fields[$id] as $field_name => $rendered_field) {
            if (!empty($rendered_field) && !$this->view->field[$field_name]->options['exclude']) {

              /* @var \Drupal\Core\Render\Markup $rendered_field */
              $view_data[$field_name] = $rendered_field
                ->__toString();
            }
          }

          // Define a Tooltip for the Feature.
          $tooltip_field = isset($map_settings['map_marker_and_infowindow']['tooltip_field']) ? $map_settings['map_marker_and_infowindow']['tooltip_field'] : NULL;
          $tooltip = isset($entity) && !empty($tooltip_field) ? trim(html_entity_decode(strip_tags($this->rendered_fields[$id][$tooltip_field]), ENT_QUOTES)) : NULL;

          // Define possible tokens.
          $tokens = [];
          foreach ($this->rendered_fields[$result->index] as $field_name => $field_value) {
            $tokens[$field_name] = $field_value;
          }

          // Generate GeoJsonData.
          $geojson_data = $this
            ->getGeoJsonData($geofield_value, $entity
            ->id(), $description, $tooltip, $view_data);

          // Add Theming Icon based on the $theming plugin.
          $theming = NULL;
          if (isset($map_settings['map_marker_and_infowindow']['theming']) && $map_settings['map_marker_and_infowindow']['theming']['plugin_id'] != 'none') {
            $theming = $map_settings['map_marker_and_infowindow']['theming'];
            try {

              /* @var \Drupal\geofield_map\MapThemerInterface $map_themer */
              $map_themer = $this->mapThemerManager
                ->createInstance($theming['plugin_id'], [
                'geofieldMapView' => $this,
              ]);
              $map_theming = $theming[$map_themer
                ->getPluginId()]['values'];
              foreach ($geojson_data as $k => $datum) {
                if ($datum['geometry']->type === 'Point') {
                  $geojson_data[$k]['properties']['icon'] = $map_themer
                    ->getIcon($datum, $this, $entity, $map_theming);

                  // Flag the data with theming, for later rendering logic.
                  $geojson_data[$k]['properties']['theming'] = TRUE;
                }
              }
            } catch (PluginException $e) {
              watchdog_exception('geofield_map', $e);
            }
          }
          elseif ($map_settings['map_marker_and_infowindow']['icon_image_mode'] == 'icon_file' && strlen($map_settings['map_marker_and_infowindow']['icon_image_path']) > 0) {
            foreach ($geojson_data as $k => $datum) {
              if ($datum['geometry']->type === 'Point') {
                $geojson_data[$k]['properties']['icon'] = $this
                  ->viewsTokenReplace($this->options['map_marker_and_infowindow']['icon_image_path'], $tokens);
              }
            }
          }

          // Associate dynamic path properties (token based) to each feature,
          // in case of not point.
          foreach ($geojson_data as $k => $datum) {
            if ($datum['geometry']->type !== 'Point') {
              $geojson_data[$k]['properties']['path_options'] = str_replace([
                "\n",
                "\r",
              ], "", $this
                ->viewsTokenReplace($this->options['map_geometries_options'], $tokens));
            }
          }

          // Generate incremental GeoJsonData.
          $data = array_merge($data, $geojson_data);
        }
      }
    }
    $js_settings['data'] = [
      'type' => 'FeatureCollection',
      'features' => $data,
    ];

    // Allow other modules to add/alter the map js settings.
    $this->moduleHandler
      ->alter('geofield_map_googlemap_view_style', $js_settings, $this);
    $element = geofield_map_googlemap_render($js_settings);

    // Add the Core Drupal Ajax library for Ajax Popups.
    if (isset($js_settings['map_settings']['ajaxPoup']) && $js_settings['map_settings']['ajaxPoup'] == TRUE) {
      $build_for_bubbleable_metadata['#attached']['library'][] = 'core/drupal.ajax';
    }
    BubbleableMetadata::createFromRenderArray($element)
      ->merge(BubbleableMetadata::createFromRenderArray($build_for_bubbleable_metadata))
      ->applyTo($element);
  }
  return $element;
}