Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "Save entity" derivative #419

Open
wants to merge 13 commits into
base: 8.x-3.x
Choose a base branch
from
64 changes: 49 additions & 15 deletions src/Plugin/RulesAction/EntitySave.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,36 @@
namespace Drupal\rules\Plugin\RulesAction;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\rules\Core\RulesActionBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Entity\EntityStorageInterface;

/**
* Provides a 'Save entity' action.
*
* @RulesAction(
* id = "rules_entity_save",
* label = @Translation("Save entity"),
* category = @Translation("Entity"),
* context = {
* "entity" = @ContextDefinition("entity",
* label = @Translation("Entity"),
* description = @Translation("Specifies the entity, which should be saved permanently.")
* ),
* "immediate" = @ContextDefinition("boolean",
* label = @Translation("Force saving immediately"),
* description = @Translation("Usually saving is postponed till the end of the evaluation, so that multiple saves can be fold into one. If this set, saving is forced to happen immediately."),
* default_value = FALSE,
* required = FALSE
* )
* }
* deriver = "Drupal\rules\Plugin\RulesAction\EntitySaveDeriver",
* )
*
* @todo: Add access callback information from Drupal 7.
*/
class EntitySave extends RulesActionBase {
class EntitySave extends RulesActionBase implements ContainerFactoryPluginInterface {

/**
* The entity storage service.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $storage;

/**
* The entity type id.
*
* @var string
*/
protected $entityTypeId;

/**
* Flag that indicates if the entity should be auto-saved later.
Expand All @@ -42,6 +46,36 @@ class EntitySave extends RulesActionBase {
*/
protected $saveLater = TRUE;

/**
* Constructs an EntitySave object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
* The entity storage service.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityStorageInterface $storage) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->storage = $storage;
$this->entityTypeId = $plugin_definition['entity_type_id'];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about this __construct()

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems ok?

}

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager')->getStorage($plugin_definition['entity_type_id'])
);
}

/**
* Saves the Entity.
*
Expand Down
96 changes: 96 additions & 0 deletions src/Plugin/RulesAction/EntitySaveDeriver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

/**
* @file
* Contains \Drupal\rules\Plugin\RulesAction\EntitySaveDeriver.
*/

namespace Drupal\rules\Plugin\RulesAction;

use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\rules\Context\ContextDefinition;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
* Derives entity save plugin definitions based on content entity types.
*
* @see \Drupal\rules\Plugin\RulesAction\EntitySave
*/
class EntitySaveDeriver extends DeriverBase implements ContainerDeriverInterface {
use StringTranslationTrait;

/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing NL before this line.

* The entity field manager.
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface;
*/
protected $entityFieldManager;
/**
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing NL before this line.

* Saves a new EntitySaveDeriver object.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
* The string translation service.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, TranslationInterface $string_translation) {
$this->entityTypeManager = $entity_type_manager;
$this->entityFieldManager = $entity_field_manager;
$this->stringTranslation = $string_translation;
}

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $base_plugin_id) {
return new static(
$container->get('entity_type.manager'),
$container->get('entity_field.manager'),
$container->get('string_translation')
);
}

/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
// Only allow content entities and ignore configuration entities.
if (!$entity_type instanceof ContentEntityTypeInterface) {
continue;
}

$this->derivatives[$entity_type_id] = [
'label' => $this->t('Save @entity_type', ['@entity_type' => $entity_type->getLowercaseLabel()]),
'category' => $entity_type->getLabel(),
'entity_type_id' => $entity_type_id,
'context' => [
'entity' => ContextDefinition::create("entity:$entity_type_id")
->setLabel($entity_type->getLabel())
->setDescription($this->t('Specifies the entity, which should be saved permanently.'))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specifies the entity, which should be saved permanently - should be improved to

Specifies the @entity-type-label that should be saved permanently.

->setRequired(TRUE),
'immediate' => ContextDefinition::create('boolean')
->setLabel($this->t('Force saving immediately'))
->setDescription($this->t('Usually saving is postponed till the end of the evaluation, so that multiple saves can be fold into one. If this set, saving is forced to happen immediately.'))
->setDefaultValue(FALSE)
->setRequired(FALSE),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated, but this should be actually required. NULL is invalid, only TRUE or FALSE is fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I change it to TRUE?

It was "required = FALSE" check the line 29 above in EntitySave.php.

],
] + $base_plugin_definition;
}

return $this->derivatives;
}

}
16 changes: 8 additions & 8 deletions tests/src/Integration/Action/EntityCreateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,31 +60,31 @@ public function setUp() {
$bundle_field_definition->getItemDefinition()
->willReturn($item_definition->reveal());
$bundle_field_definition->getCardinality()->willReturn(1)
->shouldBeCalledTimes(1);
->shouldBeCalled();
$bundle_field_definition->getType()->willReturn('string');
$bundle_field_definition->getLabel()->willReturn('Bundle')
->shouldBeCalledTimes(1);
->shouldBeCalled();
$bundle_field_definition->getDescription()
->willReturn('Bundle mock description')
->shouldBeCalledTimes(1);
->shouldBeCalled();

$bundle_field_definition_required->getItemDefinition()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are all these mocks really needed? Looks like left-overs from EntityCreateTest for me?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the EntityCreateTest file.

the $bundle_field_definition_required and $bundle_field_definition_optional are used in willReturn()

->willReturn($item_definition->reveal());
$bundle_field_definition_required->getCardinality()->willReturn(1)
->shouldBeCalledTimes(1);
->shouldBeCalled();
$bundle_field_definition_required->getType()->willReturn('string');
$bundle_field_definition_required->getLabel()->willReturn('Required field')
->shouldBeCalledTimes(1);
->shouldBeCalled();
$bundle_field_definition_required->getDescription()
->willReturn('Required field mock description')
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is that used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. The test does not work without these lines. All the comments in EntityCreateTest.php are unrelated. I only changed shouldBeCalledTimes(1) to shouldBeCalled() as you suggested during discussion.

->shouldBeCalledTimes(1);
->shouldBeCalled();
$bundle_field_definition_required->isRequired()
->willReturn(TRUE)
->shouldBeCalledTimes(1);
->shouldBeCalled();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if those are called 0 times, remove them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

those are called 1 time


$bundle_field_definition_optional->isRequired()
->willReturn(FALSE)
->shouldBeCalledTimes(1);
->shouldBeCalled();

// Prepare mocked entity storage.
$entity_type_storage = $this->prophesize(EntityStorageBase::class);
Expand Down
25 changes: 24 additions & 1 deletion tests/src/Integration/Action/EntitySaveTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace Drupal\Tests\rules\Integration\Action;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageBase;
use Drupal\Tests\rules\Integration\RulesEntityIntegrationTestBase;

/**
Expand Down Expand Up @@ -38,7 +39,15 @@ public function setUp() {

$this->entity = $this->prophesizeEntity(EntityInterface::class);

$this->action = $this->actionManager->createInstance('rules_entity_save');
// Prepare mocked entity storage.
$entity_type_storage = $this->prophesize(EntityStorageBase::class);

// Return the mocked storage controller.
$this->entityTypeManager->getStorage('test')
->willReturn($entity_type_storage->reveal());

// Instantiate the action we are testing.
$this->action = $this->actionManager->createInstance('rules_entity_save:test');
}

/**
Expand All @@ -50,6 +59,20 @@ public function testSummary() {
$this->assertEquals('Save entity', $this->action->summary());
}

/**
* Tests the action execution.
*
* @covers ::execute
*/
public function testActionExecution() {
$this->entity->save()->shouldBeCalledTimes(1);

$this->action->setContextValue('entity', $this->entity->reveal())
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gives exception " The entity context is not a valid context."

->setContextValue('immediate', TRUE);

$this->action->execute();
}

/**
* Tests the action execution when saving immediately.
*
Expand Down