0

After reading the assumably-outdated Drupal docs page* on Database API extenders, I have the following code in /var/www/mysite/web/modules/custom/my_module/src/QueryExtender.php:

<?php

namespace Drupal\my_module\QueryExtender;

use \Drupal\Core\Database\Query\PagerSelectExtender;

class QueryExtender extends PagerSelectExtender {
  /**
   * Escapes an alias name string.
   *
   * Force all alias names to be strictly alphanumeric-plus-underscore-or-space. In
   * contrast to DatabaseConnection::escapeField() / DatabaseConnection::escapeTable(),
   * this doesn't allow the period (".") because that isn't allowed in aliases.
   *
   * @param string $field
   *   An unsanitized alias name.
   *
   * @return string
   *   The sanitized alias name.
   */
  public function escapeAlias($field) {
    $escapedAlias = preg_replace('/[^A-Za-z0-9_ ]+/', '', $field);
    if (strpos($escapedAlias, ' ')) {
      $escapedAlias = "'$escapedAlias'";
    }
    return $escapedAlias;
  }

  /**
   * {@inheritdoc}
   * 
   * This is an extension of PagerSelectExtender that overrides the __toString
   * method to allow spaces in expression aliases. It took a LOT of digging to
   * find out how to do this, just to change a single $#@!ing character, and it
   * doesn't even work!
   */
  public function __toString() {
    // Lines 796 - 824 of core/lib/Drupal/Core/Database/Query/Select.php go here unmodified

    foreach ($this->expressions as $expression) {
      // This is where we override the default Connection::escapeAlias behaviour.
      // For reference, the original code is: $fields[] = $expression['expression']
      // . ' AS ' . $this->connection->escapeAlias($expression['alias']);
      $fields[] = $expression['expression'] . ' AS ' .
        $this->escapeAlias($expression['alias']);
    }

    // Rest of the normal __toString method goes here
  }
};

And this code in /var/www/mysite/web/modules/custom/my_module/src/Controller/MyController.php:

$results = \Drupal::database()
  ->select('table_name', 't')
  ->groupBy('t.id')
  ->orderBy($order, $direction)
  ->extend('Drupal\my_module\QueryExtender')
  ->limit($pageSize)
  ->execute()
  ->fetchAll(\PDO::FETCH_ASSOC);

But now I'm getting an unhandled exception when hitting the page: Error: Class 'Drupal\my_module\QueryExtender' not found in Drupal\Core\Database\Query\Select->extend() (line 325 of core/lib/Drupal/Core/Database/Query/Select.php).

But wait, there's more! I went to the effort of installing and configuring XDebug just to narrow down the problem, and got as far as...wait for it...the return true; statement of loadClass! The exception is thrown right after that function returns true, so it appears as though Drupal is having no trouble at all finding my class, yet the devs don't seem to want to admit it!

What the heck is going on here?

Drupal 8 docs: https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Database%21Query%21Select.php/function/Select%3A%3Aextend/8.9.x

  • 1
    Get the QueryExtender classname out of your namespace, clear cache. The namespace doesn't match the class location. – Kevin Mar 21 at 20:58
  • Thanks @Kevin, I figured that might be the problem after writing this question but thought the code execution is still making it to loadClass and returning true, so it must be OK. Could you explain this to me please? – Kenny83 Mar 22 at 1:07
  • And BTW, your comment doesn't solve the problem! – Kenny83 Mar 22 at 1:25
  • Just a step in the debugging process, it jumped out. – Kevin Mar 22 at 3:41
  • 3
    As Kevin mentioned, the namespace in your question is wrong, you have the class name appended to it when the rest of the code makes it clear you didn’t mean to - to be 100% clear, are you saying you’ve corrected that, but the class still isn’t found? That you have a file named module/src/QueryExtender.php, containing a namespace declaration of namespace Drupal\my_module, and a class named QueryExtender, and you’re still getting this error: Class 'Drupal\my_module\QueryExtender' not found? – Clive Mar 22 at 9:06

Browse other questions tagged or ask your own question.