@@ -86,26 +86,51 @@ public function execute() {
8686 // result because this function gets called.
8787 $ all_records = $ this ->getFromStorage ();
8888
89- // @todo Proper entity query support that is aligned with the implementation
90- // in \Drupal\Core\Entity\Query\Sql\Query::prepare() can be only added
91- // if the following Entity API module issue is solved.
89+ // Be consistent with \Drupal\Core\Entity\Query\Sql\Query::prepare().
90+ // Add and fire special entity query tags.
91+ // @todo This fix can be only merged after the fix in the following issue
92+ // is available in a tagged release of entity.module.
9293 // https://www.drupal.org/project/entity/issues/3332956
94+ // The minimum required entity.module version also MUST be bumped as part
95+ // of this fix.
9396 // (Having a fix for a similar Group module issue is a nice to have,
9497 // https://www.drupal.org/project/group/issues/3332963.)
98+ $ this ->addTag ('entity_query ' );
99+ $ this ->addTag ('entity_query_ ' . $ this ->entityTypeId );
100+
95101 if ($ this ->accessCheck ) {
102+ // We do not just add a tag but ensure that only those Apigee entities
103+ // are returned that the entity access API grants view access.
104+ // (Storage level filtering is not available or way too limited.)
105+ $ this ->addTag ($ this ->entityTypeId . '_access ' );
106+
96107 // Read meta-data from query, if provided.
97108 if (!$ account = $ this ->getMetaData ('account ' )) {
98- // @todo DI dependency.
99109 $ account = \Drupal::currentUser ();
100110 }
111+
101112 $ cacheability = CacheableMetadata::createFromRenderArray ([]);
102- $ all_records = array_filter ($ all_records , static function (EntityInterface $ entity ) use ($ cacheability , $ account ) {
113+ $ viewable_entity_ids = array_reduce ($ all_records , static function (array $ carry , EntityInterface $ entity ) use ($ cacheability , $ account ) {
103114 // Bubble up cacheability information even from a revoked access result.
104115 $ result = $ entity ->access ('view ' , $ account , TRUE );
105116 $ cacheability ->addCacheableDependency ($ result );
106- return $ result ->isAllowed ();
107- });
108- // @todo DI dependencies.
117+ if ($ result ->isAllowed ()) {
118+ $ carry [] = $ entity ->id ();
119+ }
120+ return $ carry ;
121+ }, []);
122+
123+ // We deliberately add conditions to the original entity query instead
124+ // of pre-filtering all records because query conditions are visible
125+ // in hook_query_TAG_alter() implementations for downstream developers.
126+ if (empty ($ viewable_entity_ids )) {
127+ // Add an always false condition. A persisted entity's primary id
128+ // cannot be null.
129+ $ this ->condition ->notExists ($ this ->entityType ->getKey ('id ' ));
130+ }
131+ else {
132+ $ this ->condition ->condition ($ this ->entityType ->getKey ('id ' ), $ viewable_entity_ids , 'IN ' );
133+ }
109134 /** @var \Symfony\Component\HttpFoundation\Request $request */
110135 $ request = \Drupal::requestStack ()->getCurrentRequest ();
111136 $ renderer = \Drupal::service ('renderer ' );
@@ -116,6 +141,12 @@ public function execute() {
116141 }
117142 }
118143
144+ $ hooks = ['query ' ];
145+ foreach ($ this ->alterTags as $ tag => $ value ) {
146+ $ hooks [] = 'query_ ' . $ tag ;
147+ }
148+ \Drupal::moduleHandler ()->alter ($ hooks , $ this );
149+
119150 $ filter = $ this ->condition ->compile ($ this );
120151 $ result = array_filter ($ all_records , $ filter );
121152
0 commit comments