Problem/Motivation

When I create an EVA display for a view, and add exposed filters, and then try to view a node with the EVA on it, I get the following errors:

Warning: Illegal string offset 'options' in modules/contrib/eva/src/Plugin/views/display/Eva.php on line 278
Error: Cannot use string offset as an array in Drupal\eva\Plugin\views\display\Eva->getPath() (line 278 of modules/contrib/eva/src/Plugin/views/display/Eva.php).

The full stack trace is:

Drupal\eva\Plugin\views\display\Eva->getPath() (Line: 2020)
Drupal\views\ViewExecutable->getPath() (Line: 1943)
Drupal\views\ViewExecutable->getUrl() (Line: 114)
Drupal\views\Form\ViewsExposedForm->buildForm(Array, Object)
call_user_func_array(Array, Array) (Line: 514)
Drupal\Core\Form\FormBuilder->retrieveForm('views_exposed_form', Object) (Line: 271)
Drupal\Core\Form\FormBuilder->buildForm('views_exposed_form', Object) (Line: 135)
Drupal\views\Plugin\views\exposed_form\ExposedFormPluginBase->renderExposedForm(1) (Line: 115)
eva_entity_view(Array, Object, Object, 'full')
call_user_func_array('eva_entity_view', Array) (Line: 402)
Drupal\Core\Extension\ModuleHandler->invokeAll('entity_view', Array) (Line: 270)
Drupal\Core\Entity\EntityViewBuilder->buildMultiple(Array) (Line: 220)
Drupal\Core\Entity\EntityViewBuilder->build(Array)
call_user_func(Array, Array) (Line: 376)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 195)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 226)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 574)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 227)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 117)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object) (Line: 111)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch('kernel.view', Object) (Line: 149)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 64)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 99)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 78)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 50)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 656)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)

The code in getPath() reads as follows:

public function getPath() {
  if (isset($this->view->current_entity)) {
    $uri = $this->view->current_entity->url();
    if ($uri) {
      $uri['options']['absolute'] = TRUE;
      return url($uri['path'], $uri['options']);
    }
  }
  return parent::getPath();
}

The issue is that $this->view->current_entity->url() returns a string (in my case, the string "/node/2"), but other statements in that function ($uri['options']['absolute'], $uri['path'], $uri['options']) assume it is some kind of array structure. I think this code might not have been ported from D7 properly (potentially relevant change entries: Many methods for generating URLs and links deprecated; EntityInterface::toUrl() and EntityInterface::toLink() added).

Also worth noting: the url() function no longer exists.

Steps to reproduce

Environment:

  • Apache/2.2.31 (Unix) mod_wsgi/3.5 Python/2.7.13 PHP/7.0.15 mod_ssl/2.2.31 OpenSSL/1.0.2j DAV/2 mod_fastcgi/2.4.6 mod_perl/2.0.9 Perl/v5.24.0
  • PHP: 7.0.15, Memory limit: -1 (Unlimited)
  • MySQL 5.6.35

Instructions:

  1. Clone Drupal core on branch 8.3.x. I am on commit 5d6ea2c5ec from 2017-05-01.
  2. Clone EVA on branch 8.x-1.x. I am on commit 66a6412 from 2017-03-16.
  3. Install Drupal using the Standard install profile.
  4. Log in as /user/1.
  5. Go to /admin/structure/views/add. Enter the following data:
    • View basic information:
      • View name: Test
      • Machine name: test
      • Leave Description unchecked.
    • View settings:
      • Show Content of type Article tagged with: (leave blank) sorted by: Newest first
    • Page settings
      • Leave Create a page unchecked.
    • Block settings
      • Leave Create a block unchecked.
    • Click Save and edit
  6. You are taken to a view edit page. Under Displays, next to Master, click Add, and click EVA.
  7. Under Entity content settings set:
    • Entity type: Content.
    • Bundles: Basic page.
    • Arguments: Use the ID of the entity the view is attached to.
  8. Next to Filter criteria, click Add. From the Content category, check Title, then click Add and configure filter criteria. Set:
    • Check Expose this filter to visitors, to allow them to change it.
    • Filter type to expose: Single filter
    • Ensure Required is unchecked.
    • Label: Title.
    • Description: (leave empty).
    • Operator: Is equal to.
    • Ensure Expose operator is unchecked.
    • Value: (leave empty).
    • Ensure Remember the last selection is unchecked.
    • Administrative title: (leave empty)
    • Click Apply.
  9. Under Displays, next to EVA, click Add, and click Page.
  10. Under Page settings, set Path = test.
  11. Under Displays, click EVA to go back to that display.
  12. Under Pager, click More link and set:
    • Set For = This entity_view (override).
    • Check Create more link.
    • Leave Always display the more link checked.
    • Leave More link text = more.
    • Click Apply (this display).
  13. Click Save on the main view.
  14. Go to /admin/structure/types/manage/page/display. Ensure the EVA is displayed on the Default and/or Full content display modes.
  15. Go to /node/add/article. Create an Article. Lorem ipsum text is fine.
  16. Go to /node/add/page. Create a Basic page. Lorem ipsum text is fine.
  17. You should end up on the basic page node view page (if you do not, go to /admin/content and click the basic page's title to view it). You see the fatal errors mentioned in this issue.

Proposed resolution

To be determined.

Remaining tasks

  1. Add steps to reproduce on on 8.x-1.x HEAD
  2. Propose a resolution
  3. Write a patch
  4. Review and feedback
  5. RTBC and feedback
  6. Commit

User interface changes

Likely none.

API changes

Likely none.

Data model changes

Likely none.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

mparker17 created an issue. See original summary.

mparker17’s picture

Category: Task » Bug report

Whoops, this is a bug report.

ahebrank’s picture

The deprecated functions aren't really relevant since this is all in the Views namespace, but it's possible that the ViewExecutable::$current_entity (or whatever type of object $this->view is) and its url() method have changed without us noticing. Which D8 is this?

mparker17’s picture

@ahebrank this is on Drupal 8.3.1.

ahebrank’s picture

Sorry, my mistake; you're absolutely right that this function didn't get touched after 8.0. Something like this might work? I'm not sure exactly what form the URL out of getPath is supposed to take.

  public function getPath() {
    if (isset($this->view->current_entity)) {
      $url = $this->view->current_entity->toUrl();
      if ($url) {
        $url->setOption('absolute', TRUE); // is this neccessary?
        return $url->toString(); // getInternalPath()?
      }
    }
    return parent::getPath();
  }
mparker17’s picture

Issue summary: View changes

Added steps to reproduce to the issue summary.

mparker17’s picture

Version: 8.x-1.1 » 8.x-1.x-dev
Assigned: mparker17 » Unassigned
Status: Active » Needs review
FileSize
1.27 KB

Oops, forgot to update version to 8.x-1.x-dev after adding steps to reproduce it on that version.


@ahebrank, getPath() is defined by \Drupal\views\Plugin\views\display\DisplayPluginInterface::getPath(), which doesn't specify the return value. However all the implementations I can see in Drupal core return strings, so let's go with that :)


Attaching a patch which fixes it both in my test environment (8.3.x / eva-8.x-1.x-dev) and my project environment (8.3.1 / eva-8.x-1.1, where I initially discovered the error).

Feedback welcome!

ahebrank’s picture

Thanks for this. I haven't yet been able to reproduce the original issue but as soon as I can do that I'll merge and then release a new 1.2 as a hotfix since this is clearly buggy code.

This probably qualifies as a different issue, but when reading through the display code I was wondering whether this approach is still a good way to get the base View path, especially given that entities-within-entities is now a common approach for sitebuilding modularity. For instance, if I attached an Eva to a Paragraph, which was then referenced within a node, I think the Eva would attempt get the (non-existent) canonical Paragraph path when it should be (?) using the parent node path. Would it be better for the hook_entity_view to inject the current path from the routing system (or is that info already in the View executable)?

mparker17’s picture

@ahebrank if the steps to reproduce in the issue summary aren't working, I could provide either the whole test site configuration, or just the configuration for the view (I prefer steps to reproduce, as they unequivocally prove that there isn't a site-specific configuration issue, but a CMI export can be useful in a pinch).


This probably qualifies as a different issue, but when reading through the display code I was wondering whether this approach is still a good way to get the base View path, especially given that entities-within-entities is now a common approach for sitebuilding modularity. For instance, if I attached an Eva to a Paragraph, which was then referenced within a node, I think the Eva would attempt get the (non-existent) canonical Paragraph path when it should be (?) using the parent node path. Would it be better for the hook_entity_view to inject the current path from the routing system (or is that info already in the View executable)?

I think this problem does qualify as a different issue; but I think you're right: the code in the Eva::getPath() function looks as if it would return the path to the Paragraph in that case. It might be better to get the path from the routing system.

  • ahebrank committed 20c026e on issue-2874662 authored by mparker17
    Issue #2874662 by mparker17: Error: Cannot use string offset as an array...

  • ahebrank authored fc08acb on 8.x-1.x
    Issue #2874662 by mparker17: Error: Cannot use string offset as an array...
ahebrank’s picture

Status: Needs review » Fixed

Replicated with instructions above and merged to dev; see PR diff at https://github.com/ahebrank/eva/pull/15/files.

mparker17’s picture

Awesome! Thanks! :D

Status: Fixed » Closed (fixed)

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