2525use Drupal \Core \Entity \EntityTypeManagerInterface ;
2626use Drupal \Core \Entity \Query \QueryBase ;
2727use Drupal \Core \Entity \Query \QueryInterface ;
28+ use Drupal \Core \Extension \ModuleHandlerInterface ;
29+ use Drupal \Core \Render \RendererInterface ;
30+ use Drupal \Core \Session \AccountInterface ;
31+ use Symfony \Component \HttpFoundation \RequestStack ;
2832
2933/**
3034 * Defines the entity query for Apigee Edge entities.
@@ -53,24 +57,85 @@ class Query extends QueryBase implements QueryInterface {
5357 *
5458 * @var \Drupal\Core\Entity\EntityTypeManagerInterface
5559 */
56- protected $ entityTypeManager ;
60+ protected EntityTypeManagerInterface $ entityTypeManager ;
61+
62+ /**
63+ * The current user.
64+ *
65+ * @var \Drupal\Core\Session\AccountInterface
66+ */
67+ protected AccountInterface $ currentUser ;
68+
69+ /**
70+ * The request stack.
71+ *
72+ * @var \Symfony\Component\HttpFoundation\RequestStack
73+ */
74+ protected RequestStack $ requestStack ;
75+
76+ /**
77+ * The renderer.
78+ *
79+ * @var \Drupal\Core\Render\RendererInterface
80+ */
81+ protected RendererInterface $ renderer ;
82+
83+ /**
84+ * The module handler.
85+ *
86+ * @var \Drupal\Core\Extension\ModuleHandlerInterface
87+ */
88+ protected ModuleHandlerInterface $ moduleHandler ;
5789
5890 /**
5991 * Constructs a Query object.
6092 *
6193 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
6294 * The entity type definition.
6395 * @param string $conjunction
64- * - AND: all of the conditions on the query need to match.
96+ * - AND: all the conditions on the query need to match.
6597 * - OR: at least one of the conditions on the query need to match.
6698 * @param array $namespaces
6799 * List of potential namespaces of the classes belonging to this query.
68100 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
69101 * The entity type manager.
102+ * @param \Drupal\Core\Session\AccountInterface|null $current_user
103+ * The current user.
104+ * @param \Symfony\Component\HttpFoundation\RequestStack|null $request_stack
105+ * The request stack.
106+ * @param \Drupal\Core\Render\RendererInterface|null $renderer
107+ * The renderer.
108+ * @param \Drupal\Core\Extension\ModuleHandlerInterface|null $module_handler
109+ * The module handler.
70110 */
71- public function __construct (EntityTypeInterface $ entity_type , string $ conjunction , array $ namespaces , EntityTypeManagerInterface $ entity_type_manager ) {
111+ public function __construct (EntityTypeInterface $ entity_type , string $ conjunction , array $ namespaces , EntityTypeManagerInterface $ entity_type_manager, AccountInterface $ current_user = NULL , RequestStack $ request_stack = NULL , RendererInterface $ renderer = NULL , ModuleHandlerInterface $ module_handler = NULL ) {
72112 parent ::__construct ($ entity_type , $ conjunction , $ namespaces );
113+
114+ if ($ current_user === NULL ) {
115+ $ current_user = \Drupal::currentUser ();
116+ @trigger_error ('Calling ' . __METHOD__ . ' without the $current_user is deprecated in apigee_edge:2.0.9 and is required before apigee_edge:3.0.0. See https://www.drupal.org/node/3338498 ' , E_USER_DEPRECATED );
117+ }
118+
119+ if ($ request_stack === NULL ) {
120+ $ request_stack = \Drupal::requestStack ();
121+ @trigger_error ('Calling ' . __METHOD__ . ' without the $request_stack is deprecated in apigee_edge:2.0.9 and is required before apigee_edge:3.0.0. See https://www.drupal.org/node/3338498 ' , E_USER_DEPRECATED );
122+ }
123+
124+ if ($ renderer === NULL ) {
125+ $ renderer = \Drupal::service ('renderer ' );
126+ @trigger_error ('Calling ' . __METHOD__ . ' without the $renderer is deprecated in apigee_edge:2.0.9 and is required before apigee_edge:3.0.0. See https://www.drupal.org/node/3338498 ' , E_USER_DEPRECATED );
127+ }
128+
129+ if ($ module_handler === NULL ) {
130+ $ module_handler = \Drupal::moduleHandler ();
131+ @trigger_error ('Calling ' . __METHOD__ . ' without the $module_handler is deprecated in apigee_edge:2.0.9 and is required before apigee_edge:3.0.0. See https://www.drupal.org/node/3338498 ' , E_USER_DEPRECATED );
132+ }
133+
73134 $ this ->entityTypeManager = $ entity_type_manager ;
135+ $ this ->currentUser = $ current_user ;
136+ $ this ->requestStack = $ request_stack ;
137+ $ this ->renderer = $ renderer ;
138+ $ this ->moduleHandler = $ module_handler ;
74139 }
75140
76141 /**
@@ -86,36 +151,60 @@ public function execute() {
86151 // result because this function gets called.
87152 $ all_records = $ this ->getFromStorage ();
88153
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.
92- // https://www.drupal.org/project/entity/issues/3332956
93- // (Having a fix for a similar Group module issue is a nice to have,
94- // https://www.drupal.org/project/group/issues/3332963.)
154+ // Be consistent with \Drupal\Core\Entity\Query\Sql\Query::prepare().
155+ // Add and fire special entity query tags.
156+ $ this ->addTag ('entity_query ' );
157+ $ this ->addTag ('entity_query_ ' . $ this ->entityTypeId );
158+
95159 if ($ this ->accessCheck ) {
160+ // We do not just add a tag but ensure that only those Apigee entities
161+ // are returned that the entity access API grants view access.
162+ // (Storage level filtering is not available or way too limited.)
163+ $ this ->addTag ($ this ->entityTypeId . '_access ' );
164+
96165 // Read meta-data from query, if provided.
97- if (! $ account = $ this ->getMetaData ('account ' )) {
98- // @todo DI dependency.
99- $ account = \Drupal:: currentUser () ;
166+ $ account = $ this ->getMetaData ('account ' );
167+ if ( $ account === NULL ) {
168+ $ account = $ this -> currentUser ;
100169 }
170+
101171 $ cacheability = CacheableMetadata::createFromRenderArray ([]);
102- $ all_records = array_filter ($ all_records , static function (EntityInterface $ entity ) use ($ cacheability , $ account ) {
172+ $ viewable_entity_ids = array_reduce ($ all_records , static function (array $ carry , EntityInterface $ entity ) use ($ cacheability , $ account ) {
103173 // Bubble up cacheability information even from a revoked access result.
104174 $ result = $ entity ->access ('view ' , $ account , TRUE );
105175 $ cacheability ->addCacheableDependency ($ result );
106- return $ result ->isAllowed ();
107- });
108- // @todo DI dependencies.
176+ if ($ result ->isAllowed ()) {
177+ $ carry [] = $ entity ->id ();
178+ }
179+ return $ carry ;
180+ }, []);
181+
182+ // We deliberately add conditions to the original entity query instead
183+ // of pre-filtering all records because query conditions are visible
184+ // in hook_query_TAG_alter() implementations for downstream developers.
185+ if (empty ($ viewable_entity_ids )) {
186+ // Add an always false condition. A persisted entity's primary id
187+ // cannot be null.
188+ $ this ->condition ->notExists ($ this ->entityType ->getKey ('id ' ));
189+ }
190+ else {
191+ $ this ->condition ->condition ($ this ->entityType ->getKey ('id ' ), $ viewable_entity_ids , 'IN ' );
192+ }
109193 /** @var \Symfony\Component\HttpFoundation\Request $request */
110- $ request = \Drupal::requestStack ()->getCurrentRequest ();
111- $ renderer = \Drupal::service ('renderer ' );
112- if ($ request ->isMethodCacheable () && $ renderer ->hasRenderContext ()) {
194+ $ request = $ this ->requestStack ->getCurrentRequest ();
195+ if ($ request ->isMethodCacheable () && $ this ->renderer ->hasRenderContext ()) {
113196 $ build = [];
114197 $ cacheability ->applyTo ($ build );
115- $ renderer ->render ($ build );
198+ $ this -> renderer ->render ($ build );
116199 }
117200 }
118201
202+ $ hooks = ['query ' ];
203+ foreach ($ this ->alterTags as $ tag => $ value ) {
204+ $ hooks [] = 'query_ ' . $ tag ;
205+ }
206+ $ this ->moduleHandler ->alter ($ hooks , $ this );
207+
119208 $ filter = $ this ->condition ->compile ($ this );
120209 $ result = array_filter ($ all_records , $ filter );
121210
@@ -124,7 +213,7 @@ public function execute() {
124213 }
125214
126215 if ($ this ->sort ) {
127- uasort ($ result , function (EntityInterface $ entity0 , EntityInterface $ entity1 ) : int {
216+ uasort ($ result , function (EntityInterface $ entity0 , EntityInterface $ entity1 ): int {
128217 foreach ($ this ->sort as $ sort ) {
129218 $ value0 = Condition::getProperty ($ entity0 , $ sort ['field ' ]);
130219 $ value1 = Condition::getProperty ($ entity1 , $ sort ['field ' ]);
@@ -150,7 +239,7 @@ public function execute() {
150239 $ result = array_slice ($ result , $ this ->range ['start ' ], $ this ->range ['length ' ]);
151240 }
152241
153- return array_map (function (EntityInterface $ entity ) : string {
242+ return array_map (static function (EntityInterface $ entity ): string {
154243 return (string ) $ entity ->id ();
155244 }, $ result );
156245 }
@@ -219,7 +308,7 @@ protected function getFromStorage(): array {
219308 else {
220309 $ ids = [$ id ];
221310 unset($ filtered_conditions [$ key ]);
222- // If we found an id field in the query do not look for an another
311+ // If we found an id field in the query do not look for another
223312 // because that would not make any sense to query one entity by
224313 // both id fields. (Where in theory both id field could refer to a
225314 // different entity.)
0 commit comments