2

I created a custom entity called foo. This entity has a unique name field as well as the standard id key. I would like to build a page where the user can see the entity rendered similarly to how it would be on the admin page, but instead of referencing it by the id, I would like to use the name.

I have the following in my controller:

public function content($name) {
  $output = [];
  //Add a couple things to the $output array
  $output['first thing'] = [/*stuff*/];

  $sql = "SELECT id FROM foo WHERE name=:name";
  $sql_params = [':name' => $name];
  $results = db_query($sql, $sql_params)->fetchAll(PDO::FETCH_ASSOC);
  $render_array = [];
  if (count($results) > 0) {
    $id = $results[0]['id'];

    $entity_type = 'foo';
    $view_builder = \Drupal::entityTypeManager()->getViewBuilder($entity_type);
    $controller = \Drupal::entityManager()->getStorage($entity_type);
    $entity = $controller->load($id);
    $build = $view_builder->view($entity);
    $render_array = render($build);
    $output['entity'] = $render_array;
  }
  return $output;
}

However, I receive an error: User error: "entity" is an invalid render array key in Drupal\Core\Render\Element::children() (line 97 of core\lib\Drupal\Core\Render\Element.php).

What do I need to do differently to make my entity render to the page?

4

Controllers should return a render array (or a custom response), you almost never need to render anything yourself outside a template.

Just replace this:

$build = $view_builder->view($entity);
$render_array = render($build);
$output['entity'] = $render_array;

with:

$output = $view_builder->view($entity);

to return a render array and let Twig handle the rendering.

But going further, you don't need to (and shouldn't) go into the database directly for this; the entity type manager works for all entity types, including your own, and entity storage classes have methods to help with this.

(As an aside, that service should also be injected rather than loaded from the static Drupal class method, your controller class is container aware).

Once that's available the code would be simplified to something like this:

public function content($name) {
  $build = [];
  $entity_type = 'foo';

  $storage = $this->entityTypeManager->getStorage($entity_type);
  $entities = $storage->loadByProperties(['name' => $name]);

  if ($entity = reset($entities)) {
    $build = $this->entityTypeManager->getViewBuilder($entity_type)->view($entity);
  }

  return $build;
}

To go one step further, you can add a loadByName() method to your entity class with a slight variation of the above, might save you some time and repetition further down the line.

| improve this answer | |
  • In my example, I have "other stuff" in the $output array. is there still a way to have these elements on the page with the entity? – Kevin Nowaczyk Dec 5 '17 at 20:58
  • Sure, render arrays are very flexible. $build['entity'] = $this->entityTypeManager->getViewBuilder... would be fine, for example. The main thing to take away is not to render any part of the build yourself, let it fall through to Twig which will handle it. BTW I rejected that edit because entityTypeManager in that example would be an injected object, not a function. docs.acquia.com/article/lesson-83-dependency-injection is a good reference if you're not familiar – Clive Dec 5 '17 at 21:00
  • I had to add in the parenthesis to get the snippet to work. These’s a missing semicolon too that I had in my edit. – Kevin Nowaczyk Dec 5 '17 at 21:28
  • Oh yeah, good spot, I fixed the semicolon. If entityTypeManager is a function you don't have DI set up quite right – Clive Dec 5 '17 at 21:35
  • 1
    @Clive, you're right about proper DI, but in case Kevin doesn't need testing it's OK to extend ControllerBase and use $this->entityTypeManager(). – 4k4 Dec 5 '17 at 22:16

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.