CerPresetHandler.inc 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. <?php
  2. /**
  3. * @file
  4. * Contains CerPresetHandler.
  5. */
  6. /**
  7. * @class
  8. * Contains the logic for performing CER operations on a single entity,
  9. * using a single preset.
  10. */
  11. class CerPresetHandler {
  12. /**
  13. * @var CerFieldChain
  14. */
  15. protected $left;
  16. /**
  17. * @var CerFieldChain
  18. */
  19. protected $right;
  20. /**
  21. * @var EntityDrupalWrapper
  22. */
  23. protected $entity;
  24. /**
  25. * @var array
  26. */
  27. protected $refIDs;
  28. public function __construct(CerPreset $preset, EntityDrupalWrapper $entity) {
  29. $this->left = $preset->wrapper->cer_left->chain->value();
  30. $this->right = $preset->wrapper->cer_right->chain->value();
  31. $this->entity = $entity;
  32. // Store the current set of reference IDs so that we only need to instantiate
  33. // the left handler once.
  34. $this->refIDs = $this->left->getHandler( $entity )->getIDs();
  35. }
  36. /**
  37. * Process an entity insert. This loops through the referenced entity $IDs and
  38. * adds a reference to this entity if the reference doesn't already have one.
  39. */
  40. public function insert(array $IDs = array()) {
  41. // If no IDs were passed in, use the current reference set.
  42. $IDs = ($IDs ? $IDs : $this->refIDs);
  43. // Get this entity's ID right now, so we don't have to keep calling
  44. // $this->entity->cer->owner->getIdentifier(). Hooray for micro-optimization!
  45. $myID = $this->entity->cer->owner->getIdentifier();
  46. foreach ($this->load( $IDs ) as $ref) {
  47. $handler = $this->right->getHandler( $ref );
  48. // Only create the backreference if the reference doesn't already reference
  49. // this entity (which it might, if there is more than one preset that references
  50. // a single field instance).
  51. if (! in_array($myID, $handler->getIDs())) {
  52. $handler->add( $this->entity->cer->owner );
  53. }
  54. }
  55. }
  56. /**
  57. * Process an entity update. This could be either a normal update done by a user,
  58. * or a bulk update.
  59. */
  60. public function update() {
  61. // Get the previous set of reference IDs. $entity->cer->original will return either
  62. // $entity->original, if it exists, or the current entity. So, if this is a bulk
  63. // update, $originalIDs will be identical to $this->refIDs.
  64. $originalIDs = $this->left->getHandler( $this->entity->cer->original )->getIDs();
  65. // If there are any references that were in the previous set but not the current
  66. // set, delete those backreferences. Under normal circumstances, there will be
  67. // nothing to delete during a bulk update, since the previous set and current
  68. // set should be identical.
  69. $deleted = array_diff($originalIDs, $this->refIDs);
  70. if ($deleted) {
  71. $this->delete($deleted);
  72. }
  73. // If the previous set is identical to the current set, we'll be processing
  74. // all existing references (see the first line of $this->insert()).
  75. $added = array_diff($this->refIDs, $originalIDs);
  76. $this->insert($added);
  77. }
  78. /**
  79. * Process an entity delete. Loops through the referenced entity IDs and clears
  80. * their references to this entity.
  81. */
  82. public function delete(array $IDs = array()) {
  83. // As with $this->insert(), we can process a specific set of references or
  84. // everything in the current set.
  85. $IDs = ($IDs ? $IDs : $this->refIDs);
  86. foreach ($this->load( $IDs ) as $ref) {
  87. $this->right->getHandler( $ref )->delete( $this->entity->cer->owner );
  88. }
  89. }
  90. /**
  91. * Loads referenced entities. This might seem like a convenience method, but it
  92. * is a critical part CER's core logic.
  93. *
  94. * @param array $IDs
  95. * Array of entity IDs to load.
  96. *
  97. * @return array
  98. * The requested entities, wrapped by EntityDrupalWrapper. If nothing could be
  99. * loaded, an empty array is returned.
  100. */
  101. protected function load(array $IDs) {
  102. if (empty($IDs)) {
  103. return array();
  104. }
  105. $this->right->rewind();
  106. $right = $this->right->current();
  107. $entity_type = $right->entityType;
  108. $query = new EntityFieldQuery();
  109. $query->entityCondition('entity_type', $entity_type);
  110. $query->entityCondition('entity_id', $IDs);
  111. // If the right entity type has bundles, we need to filter by that too. If we don't,
  112. // we could run into a bug where, if the left field can reference multiple bundles,
  113. // we might try to modify the wrong entity. Essentially, the loading of referenced
  114. // entities should be as targeted as possible to prevent ambiguities and buggery.
  115. if ($right->isBundleable) {
  116. $query->entityCondition('bundle', $right->bundle);
  117. }
  118. $result = $query->execute();
  119. if (isset($result[$entity_type])) {
  120. $result[$entity_type] = entity_load($entity_type, array_keys($result[$entity_type]));
  121. foreach ($result[$entity_type] as $id => $entity) {
  122. $result[$entity_type][$id] = new EntityDrupalWrapper($entity_type, $entity);
  123. }
  124. return $result[$entity_type];
  125. }
  126. else {
  127. return array();
  128. }
  129. }
  130. }