3 namespace Drupal\KernelTests\Core\Entity;
5 use Drupal\comment\Entity\Comment;
6 use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
7 use Drupal\comment\Tests\CommentTestTrait;
8 use Drupal\Core\Database\Database;
9 use Drupal\Core\Language\LanguageInterface;
10 use Drupal\block\Entity\Block;
11 use Drupal\entity_test\Entity\EntityTest;
12 use Drupal\node\Entity\NodeType;
13 use Drupal\taxonomy\Entity\Term;
14 use Drupal\node\Entity\Node;
15 use Drupal\taxonomy\Entity\Vocabulary;
16 use Drupal\user\Entity\User;
17 use Drupal\file\Entity\File;
20 * Tests the invocation of hooks when creating, inserting, loading, updating or
24 * - hook_entity_insert() and hook_ENTITY_TYPE_insert()
25 * - hook_entity_load() and hook_ENTITY_TYPE_load()
26 * - hook_entity_update() and hook_ENTITY_TYPE_update()
27 * - hook_entity_predelete() and hook_ENTITY_TYPE_predelete()
28 * - hook_entity_delete() and hook_ENTITY_TYPE_delete()
30 * These hooks are each tested for several entity types.
34 class EntityCrudHookTest extends EntityKernelTestBase {
43 public static $modules = ['block', 'block_test', 'entity_crud_hook_test', 'file', 'taxonomy', 'node', 'comment'];
47 protected function setUp() {
50 $this->installSchema('user', ['users_data']);
51 $this->installSchema('file', ['file_usage']);
52 $this->installSchema('node', ['node_access']);
53 $this->installSchema('comment', ['comment_entity_statistics']);
54 $this->installConfig(['node', 'comment']);
58 * Checks the order of CRUD hook execution messages.
60 * entity_crud_hook_test.module implements all core entity CRUD hooks and
61 * stores a message for each in $GLOBALS['entity_crud_hook_test'].
64 * An array of plain-text messages in the order they should appear.
66 protected function assertHookMessageOrder($messages) {
68 foreach ($messages as $message) {
69 // Verify that each message is found and record its position.
70 $position = array_search($message, $GLOBALS['entity_crud_hook_test']);
71 if ($this->assertTrue($position !== FALSE, $message)) {
72 $positions[] = $position;
76 // Sort the positions and ensure they remain in the same order.
79 $this->assertTrue($sorted == $positions, 'The hook messages appear in the correct order.');
83 * Tests hook invocations for CRUD operations on blocks.
85 public function testBlockHooks() {
86 $entity = Block::create([
87 'id' => 'stark_test_html',
88 'plugin' => 'test_html',
92 $this->assertHookMessageOrder([
93 'entity_crud_hook_test_block_create called',
94 'entity_crud_hook_test_entity_create called for type block',
97 $GLOBALS['entity_crud_hook_test'] = [];
100 $this->assertHookMessageOrder([
101 'entity_crud_hook_test_block_presave called',
102 'entity_crud_hook_test_entity_presave called for type block',
103 'entity_crud_hook_test_block_insert called',
104 'entity_crud_hook_test_entity_insert called for type block',
107 $GLOBALS['entity_crud_hook_test'] = [];
108 $entity = Block::load($entity->id());
110 $this->assertHookMessageOrder([
111 'entity_crud_hook_test_entity_load called for type block',
112 'entity_crud_hook_test_block_load called',
115 $GLOBALS['entity_crud_hook_test'] = [];
116 $entity->label = 'New label';
119 $this->assertHookMessageOrder([
120 'entity_crud_hook_test_block_presave called',
121 'entity_crud_hook_test_entity_presave called for type block',
122 'entity_crud_hook_test_block_update called',
123 'entity_crud_hook_test_entity_update called for type block',
126 $GLOBALS['entity_crud_hook_test'] = [];
129 $this->assertHookMessageOrder([
130 'entity_crud_hook_test_block_predelete called',
131 'entity_crud_hook_test_entity_predelete called for type block',
132 'entity_crud_hook_test_block_delete called',
133 'entity_crud_hook_test_entity_delete called for type block',
138 * Tests hook invocations for CRUD operations on comments.
140 public function testCommentHooks() {
141 $account = $this->createUser();
146 $this->addDefaultCommentField('node', 'article', 'comment', CommentItemInterface::OPEN);
148 $node = Node::create([
149 'uid' => $account->id(),
151 'title' => 'Test node',
155 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
156 'created' => REQUEST_TIME,
157 'changed' => REQUEST_TIME,
161 $GLOBALS['entity_crud_hook_test'] = [];
163 $comment = Comment::create([
167 'entity_type' => 'node',
168 'field_name' => 'comment',
169 'uid' => $account->id(),
170 'subject' => 'Test comment',
171 'created' => REQUEST_TIME,
172 'changed' => REQUEST_TIME,
174 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
177 $this->assertHookMessageOrder([
178 'entity_crud_hook_test_comment_create called',
179 'entity_crud_hook_test_entity_create called for type comment',
182 $GLOBALS['entity_crud_hook_test'] = [];
185 $this->assertHookMessageOrder([
186 'entity_crud_hook_test_comment_presave called',
187 'entity_crud_hook_test_entity_presave called for type comment',
188 'entity_crud_hook_test_comment_insert called',
189 'entity_crud_hook_test_entity_insert called for type comment',
192 $GLOBALS['entity_crud_hook_test'] = [];
193 $comment = Comment::load($comment->id());
195 $this->assertHookMessageOrder([
196 'entity_crud_hook_test_entity_load called for type comment',
197 'entity_crud_hook_test_comment_load called',
200 $GLOBALS['entity_crud_hook_test'] = [];
201 $comment->setSubject('New subject');
204 $this->assertHookMessageOrder([
205 'entity_crud_hook_test_comment_presave called',
206 'entity_crud_hook_test_entity_presave called for type comment',
207 'entity_crud_hook_test_comment_update called',
208 'entity_crud_hook_test_entity_update called for type comment',
211 $GLOBALS['entity_crud_hook_test'] = [];
214 $this->assertHookMessageOrder([
215 'entity_crud_hook_test_comment_predelete called',
216 'entity_crud_hook_test_entity_predelete called for type comment',
217 'entity_crud_hook_test_comment_delete called',
218 'entity_crud_hook_test_entity_delete called for type comment',
223 * Tests hook invocations for CRUD operations on files.
225 public function testFileHooks() {
226 $this->installEntitySchema('file');
228 $url = 'public://entity_crud_hook_test.file';
229 file_put_contents($url, 'Test test test');
230 $file = File::create([
233 'filename' => 'entity_crud_hook_test.file',
235 'filemime' => 'text/plain',
236 'filesize' => filesize($url),
238 'created' => REQUEST_TIME,
239 'changed' => REQUEST_TIME,
242 $this->assertHookMessageOrder([
243 'entity_crud_hook_test_file_create called',
244 'entity_crud_hook_test_entity_create called for type file',
247 $GLOBALS['entity_crud_hook_test'] = [];
250 $this->assertHookMessageOrder([
251 'entity_crud_hook_test_file_presave called',
252 'entity_crud_hook_test_entity_presave called for type file',
253 'entity_crud_hook_test_file_insert called',
254 'entity_crud_hook_test_entity_insert called for type file',
257 $GLOBALS['entity_crud_hook_test'] = [];
258 $file = File::load($file->id());
260 $this->assertHookMessageOrder([
261 'entity_crud_hook_test_entity_load called for type file',
262 'entity_crud_hook_test_file_load called',
265 $GLOBALS['entity_crud_hook_test'] = [];
266 $file->setFilename('new.entity_crud_hook_test.file');
269 $this->assertHookMessageOrder([
270 'entity_crud_hook_test_file_presave called',
271 'entity_crud_hook_test_entity_presave called for type file',
272 'entity_crud_hook_test_file_update called',
273 'entity_crud_hook_test_entity_update called for type file',
276 $GLOBALS['entity_crud_hook_test'] = [];
279 $this->assertHookMessageOrder([
280 'entity_crud_hook_test_file_predelete called',
281 'entity_crud_hook_test_entity_predelete called for type file',
282 'entity_crud_hook_test_file_delete called',
283 'entity_crud_hook_test_entity_delete called for type file',
288 * Tests hook invocations for CRUD operations on nodes.
290 public function testNodeHooks() {
291 $account = $this->createUser();
293 $node = Node::create([
294 'uid' => $account->id(),
296 'title' => 'Test node',
300 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
301 'created' => REQUEST_TIME,
302 'changed' => REQUEST_TIME,
305 $this->assertHookMessageOrder([
306 'entity_crud_hook_test_node_create called',
307 'entity_crud_hook_test_entity_create called for type node',
310 $GLOBALS['entity_crud_hook_test'] = [];
313 $this->assertHookMessageOrder([
314 'entity_crud_hook_test_node_presave called',
315 'entity_crud_hook_test_entity_presave called for type node',
316 'entity_crud_hook_test_node_insert called',
317 'entity_crud_hook_test_entity_insert called for type node',
320 $GLOBALS['entity_crud_hook_test'] = [];
321 $node = Node::load($node->id());
323 $this->assertHookMessageOrder([
324 'entity_crud_hook_test_entity_load called for type node',
325 'entity_crud_hook_test_node_load called',
328 $GLOBALS['entity_crud_hook_test'] = [];
329 $node->title = 'New title';
332 $this->assertHookMessageOrder([
333 'entity_crud_hook_test_node_presave called',
334 'entity_crud_hook_test_entity_presave called for type node',
335 'entity_crud_hook_test_node_update called',
336 'entity_crud_hook_test_entity_update called for type node',
339 $GLOBALS['entity_crud_hook_test'] = [];
342 $this->assertHookMessageOrder([
343 'entity_crud_hook_test_node_predelete called',
344 'entity_crud_hook_test_entity_predelete called for type node',
345 'entity_crud_hook_test_node_delete called',
346 'entity_crud_hook_test_entity_delete called for type node',
351 * Tests hook invocations for CRUD operations on taxonomy terms.
353 public function testTaxonomyTermHooks() {
354 $this->installEntitySchema('taxonomy_term');
356 $vocabulary = Vocabulary::create([
357 'name' => 'Test vocabulary',
359 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
360 'description' => NULL,
361 'module' => 'entity_crud_hook_test',
364 $GLOBALS['entity_crud_hook_test'] = [];
366 $term = Term::create([
367 'vid' => $vocabulary->id(),
368 'name' => 'Test term',
369 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
370 'description' => NULL,
374 $this->assertHookMessageOrder([
375 'entity_crud_hook_test_taxonomy_term_create called',
376 'entity_crud_hook_test_entity_create called for type taxonomy_term',
379 $GLOBALS['entity_crud_hook_test'] = [];
382 $this->assertHookMessageOrder([
383 'entity_crud_hook_test_taxonomy_term_presave called',
384 'entity_crud_hook_test_entity_presave called for type taxonomy_term',
385 'entity_crud_hook_test_taxonomy_term_insert called',
386 'entity_crud_hook_test_entity_insert called for type taxonomy_term',
389 $GLOBALS['entity_crud_hook_test'] = [];
390 $term = Term::load($term->id());
392 $this->assertHookMessageOrder([
393 'entity_crud_hook_test_entity_load called for type taxonomy_term',
394 'entity_crud_hook_test_taxonomy_term_load called',
397 $GLOBALS['entity_crud_hook_test'] = [];
398 $term->setName('New name');
401 $this->assertHookMessageOrder([
402 'entity_crud_hook_test_taxonomy_term_presave called',
403 'entity_crud_hook_test_entity_presave called for type taxonomy_term',
404 'entity_crud_hook_test_taxonomy_term_update called',
405 'entity_crud_hook_test_entity_update called for type taxonomy_term',
408 $GLOBALS['entity_crud_hook_test'] = [];
411 $this->assertHookMessageOrder([
412 'entity_crud_hook_test_taxonomy_term_predelete called',
413 'entity_crud_hook_test_entity_predelete called for type taxonomy_term',
414 'entity_crud_hook_test_taxonomy_term_delete called',
415 'entity_crud_hook_test_entity_delete called for type taxonomy_term',
420 * Tests hook invocations for CRUD operations on taxonomy vocabularies.
422 public function testTaxonomyVocabularyHooks() {
423 $this->installEntitySchema('taxonomy_term');
425 $vocabulary = Vocabulary::create([
426 'name' => 'Test vocabulary',
428 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
429 'description' => NULL,
430 'module' => 'entity_crud_hook_test',
433 $this->assertHookMessageOrder([
434 'entity_crud_hook_test_taxonomy_vocabulary_create called',
435 'entity_crud_hook_test_entity_create called for type taxonomy_vocabulary',
438 $GLOBALS['entity_crud_hook_test'] = [];
441 $this->assertHookMessageOrder([
442 'entity_crud_hook_test_taxonomy_vocabulary_presave called',
443 'entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary',
444 'entity_crud_hook_test_taxonomy_vocabulary_insert called',
445 'entity_crud_hook_test_entity_insert called for type taxonomy_vocabulary',
448 $GLOBALS['entity_crud_hook_test'] = [];
449 $vocabulary = Vocabulary::load($vocabulary->id());
451 $this->assertHookMessageOrder([
452 'entity_crud_hook_test_entity_load called for type taxonomy_vocabulary',
453 'entity_crud_hook_test_taxonomy_vocabulary_load called',
456 $GLOBALS['entity_crud_hook_test'] = [];
457 $vocabulary->set('name', 'New name');
460 $this->assertHookMessageOrder([
461 'entity_crud_hook_test_taxonomy_vocabulary_presave called',
462 'entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary',
463 'entity_crud_hook_test_taxonomy_vocabulary_update called',
464 'entity_crud_hook_test_entity_update called for type taxonomy_vocabulary',
467 $GLOBALS['entity_crud_hook_test'] = [];
468 $vocabulary->delete();
470 $this->assertHookMessageOrder([
471 'entity_crud_hook_test_taxonomy_vocabulary_predelete called',
472 'entity_crud_hook_test_entity_predelete called for type taxonomy_vocabulary',
473 'entity_crud_hook_test_taxonomy_vocabulary_delete called',
474 'entity_crud_hook_test_entity_delete called for type taxonomy_vocabulary',
479 * Tests hook invocations for CRUD operations on users.
481 public function testUserHooks() {
482 $account = User::create([
483 'name' => 'Test user',
484 'mail' => 'test@example.com',
485 'created' => REQUEST_TIME,
490 $this->assertHookMessageOrder([
491 'entity_crud_hook_test_user_create called',
492 'entity_crud_hook_test_entity_create called for type user',
495 $GLOBALS['entity_crud_hook_test'] = [];
498 $this->assertHookMessageOrder([
499 'entity_crud_hook_test_user_presave called',
500 'entity_crud_hook_test_entity_presave called for type user',
501 'entity_crud_hook_test_user_insert called',
502 'entity_crud_hook_test_entity_insert called for type user',
505 $GLOBALS['entity_crud_hook_test'] = [];
506 User::load($account->id());
508 $this->assertHookMessageOrder([
509 'entity_crud_hook_test_entity_load called for type user',
510 'entity_crud_hook_test_user_load called',
513 $GLOBALS['entity_crud_hook_test'] = [];
514 $account->name = 'New name';
517 $this->assertHookMessageOrder([
518 'entity_crud_hook_test_user_presave called',
519 'entity_crud_hook_test_entity_presave called for type user',
520 'entity_crud_hook_test_user_update called',
521 'entity_crud_hook_test_entity_update called for type user',
524 $GLOBALS['entity_crud_hook_test'] = [];
525 user_delete($account->id());
527 $this->assertHookMessageOrder([
528 'entity_crud_hook_test_user_predelete called',
529 'entity_crud_hook_test_entity_predelete called for type user',
530 'entity_crud_hook_test_user_delete called',
531 'entity_crud_hook_test_entity_delete called for type user',
536 * Tests rollback from failed entity save.
538 public function testEntityRollback() {
541 EntityTest::create(['name' => 'fail_insert'])->save();
542 $this->fail('Expected exception has not been thrown.');
544 catch (\Exception $e) {
545 $this->pass('Expected exception has been thrown.');
548 if (Database::getConnection()->supportsTransactions()) {
549 // Check that the block does not exist in the database.
550 $ids = \Drupal::entityQuery('entity_test')->condition('name', 'fail_insert')->execute();
551 $this->assertTrue(empty($ids), 'Transactions supported, and entity not found in database.');
554 // Check that the block exists in the database.
555 $ids = \Drupal::entityQuery('entity_test')->condition('name', 'fail_insert')->execute();
556 $this->assertFalse(empty($ids), 'Transactions not supported, and entity found in database.');