Row.php 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. <?php
  2. namespace Drupal\migrate;
  3. use Drupal\Component\Utility\NestedArray;
  4. use Drupal\migrate\Plugin\MigrateIdMapInterface;
  5. /**
  6. * Stores a row.
  7. */
  8. class Row {
  9. /**
  10. * The actual values of the source row.
  11. *
  12. * @var array
  13. */
  14. protected $source = [];
  15. /**
  16. * The source identifiers.
  17. *
  18. * @var array
  19. */
  20. protected $sourceIds = [];
  21. /**
  22. * The destination values.
  23. *
  24. * @var array
  25. */
  26. protected $destination = [];
  27. /**
  28. * Level separator of destination and source properties.
  29. */
  30. const PROPERTY_SEPARATOR = '/';
  31. /**
  32. * The mapping between source and destination identifiers.
  33. *
  34. * @var array
  35. */
  36. protected $idMap = [
  37. 'original_hash' => '',
  38. 'hash' => '',
  39. 'source_row_status' => MigrateIdMapInterface::STATUS_NEEDS_UPDATE,
  40. ];
  41. /**
  42. * Whether the source has been frozen already.
  43. *
  44. * Once frozen the source can not be changed any more.
  45. *
  46. * @var bool
  47. */
  48. protected $frozen = FALSE;
  49. /**
  50. * The raw destination properties.
  51. *
  52. * Unlike $destination which is set by using
  53. * \Drupal\Component\Utility\NestedArray::setValue() this array contains
  54. * the destination as setDestinationProperty was called.
  55. *
  56. * @var array
  57. * The raw destination.
  58. *
  59. * @see getRawDestination()
  60. */
  61. protected $rawDestination = [];
  62. /**
  63. * TRUE when this row is a stub.
  64. *
  65. * @var bool
  66. */
  67. protected $isStub = FALSE;
  68. /**
  69. * The empty destination properties.
  70. *
  71. * @var array
  72. */
  73. protected $emptyDestinationProperties = [];
  74. /**
  75. * Constructs a \Drupal\Migrate\Row object.
  76. *
  77. * @param array $values
  78. * An array of values to add as properties on the object.
  79. * @param array $source_ids
  80. * An array containing the IDs of the source using the keys as the field
  81. * names.
  82. * @param bool $is_stub
  83. * TRUE if the row being created is a stub.
  84. *
  85. * @throws \InvalidArgumentException
  86. * Thrown when a source ID property does not exist.
  87. */
  88. public function __construct(array $values = [], array $source_ids = [], $is_stub = FALSE) {
  89. $this->source = $values;
  90. $this->sourceIds = $source_ids;
  91. $this->isStub = $is_stub;
  92. foreach (array_keys($source_ids) as $id) {
  93. if (!$this->hasSourceProperty($id)) {
  94. throw new \InvalidArgumentException("$id is defined as a source ID but has no value.");
  95. }
  96. }
  97. }
  98. /**
  99. * Retrieves the values of the source identifiers.
  100. *
  101. * @return array
  102. * An array containing the values of the source identifiers. Returns values
  103. * in the same order as defined in $this->sourceIds.
  104. */
  105. public function getSourceIdValues() {
  106. return array_merge($this->sourceIds, array_intersect_key($this->source, $this->sourceIds));
  107. }
  108. /**
  109. * Determines whether a source has a property.
  110. *
  111. * @param string $property
  112. * A property on the source.
  113. *
  114. * @return bool
  115. * TRUE if the source has property; FALSE otherwise.
  116. */
  117. public function hasSourceProperty($property) {
  118. return NestedArray::keyExists($this->source, explode(static::PROPERTY_SEPARATOR, $property));
  119. }
  120. /**
  121. * Retrieves a source property.
  122. *
  123. * @param string $property
  124. * A property on the source.
  125. *
  126. * @return mixed|null
  127. * The found returned property or NULL if not found.
  128. */
  129. public function getSourceProperty($property) {
  130. $return = NestedArray::getValue($this->source, explode(static::PROPERTY_SEPARATOR, $property), $key_exists);
  131. if ($key_exists) {
  132. return $return;
  133. }
  134. }
  135. /**
  136. * Returns the whole source array.
  137. *
  138. * @return array
  139. * An array of source plugins.
  140. */
  141. public function getSource() {
  142. return $this->source;
  143. }
  144. /**
  145. * Sets a source property.
  146. *
  147. * This can only be called from the source plugin.
  148. *
  149. * @param string $property
  150. * A property on the source.
  151. * @param mixed $data
  152. * The property value to set on the source.
  153. *
  154. * @throws \Exception
  155. */
  156. public function setSourceProperty($property, $data) {
  157. if ($this->frozen) {
  158. throw new \Exception("The source is frozen and can't be changed any more");
  159. }
  160. else {
  161. NestedArray::setValue($this->source, explode(static::PROPERTY_SEPARATOR, $property), $data, TRUE);
  162. }
  163. }
  164. /**
  165. * Freezes the source.
  166. *
  167. * @return $this
  168. */
  169. public function freezeSource() {
  170. $this->frozen = TRUE;
  171. return $this;
  172. }
  173. /**
  174. * Clones the row with an empty set of destination values.
  175. *
  176. * @return static
  177. */
  178. public function cloneWithoutDestination() {
  179. return (new static($this->getSource(), $this->sourceIds, $this->isStub()))->freezeSource();
  180. }
  181. /**
  182. * Tests if destination property exists.
  183. *
  184. * @param array|string $property
  185. * An array of properties on the destination.
  186. *
  187. * @return bool
  188. * TRUE if the destination property exists.
  189. */
  190. public function hasDestinationProperty($property) {
  191. return NestedArray::keyExists($this->destination, explode(static::PROPERTY_SEPARATOR, $property));
  192. }
  193. /**
  194. * Sets destination properties.
  195. *
  196. * @param string $property
  197. * The name of the destination property.
  198. * @param mixed $value
  199. * The property value to set on the destination.
  200. */
  201. public function setDestinationProperty($property, $value) {
  202. $this->rawDestination[$property] = $value;
  203. NestedArray::setValue($this->destination, explode(static::PROPERTY_SEPARATOR, $property), $value, TRUE);
  204. }
  205. /**
  206. * Removes destination property.
  207. *
  208. * @param string $property
  209. * The name of the destination property.
  210. */
  211. public function removeDestinationProperty($property) {
  212. unset($this->rawDestination[$property]);
  213. NestedArray::unsetValue($this->destination, explode(static::PROPERTY_SEPARATOR, $property));
  214. }
  215. /**
  216. * Sets a destination to be empty.
  217. *
  218. * @param string $property
  219. * The destination property.
  220. */
  221. public function setEmptyDestinationProperty($property) {
  222. $this->emptyDestinationProperties[] = $property;
  223. }
  224. /**
  225. * Gets the empty destination properties.
  226. *
  227. * @return array
  228. * An array of destination properties.
  229. */
  230. public function getEmptyDestinationProperties() {
  231. return $this->emptyDestinationProperties;
  232. }
  233. /**
  234. * Returns the whole destination array.
  235. *
  236. * @return array
  237. * An array of destination values.
  238. */
  239. public function getDestination() {
  240. return $this->destination;
  241. }
  242. /**
  243. * Returns the raw destination. Rarely necessary.
  244. *
  245. * For example calling setDestination('foo/bar', 'baz') results in
  246. * @code
  247. * $this->destination['foo']['bar'] = 'baz';
  248. * $this->rawDestination['foo/bar'] = 'baz';
  249. * @endcode
  250. *
  251. * @return array
  252. * The raw destination values.
  253. */
  254. public function getRawDestination() {
  255. return $this->rawDestination;
  256. }
  257. /**
  258. * Returns the value of a destination property.
  259. *
  260. * @param string $property
  261. * The name of a property on the destination.
  262. *
  263. * @return mixed
  264. * The destination value.
  265. */
  266. public function getDestinationProperty($property) {
  267. return NestedArray::getValue($this->destination, explode(static::PROPERTY_SEPARATOR, $property));
  268. }
  269. /**
  270. * Sets the Migrate ID mappings.
  271. *
  272. * @param array $id_map
  273. * An array of mappings between source ID and destination ID.
  274. */
  275. public function setIdMap(array $id_map) {
  276. $this->idMap = $id_map;
  277. }
  278. /**
  279. * Retrieves the Migrate ID mappings.
  280. *
  281. * @return array
  282. * An array of mapping between source and destination identifiers.
  283. */
  284. public function getIdMap() {
  285. return $this->idMap;
  286. }
  287. /**
  288. * Recalculates the hash for the row.
  289. */
  290. public function rehash() {
  291. $this->idMap['original_hash'] = $this->idMap['hash'];
  292. $this->idMap['hash'] = hash('sha256', serialize($this->source));
  293. }
  294. /**
  295. * Checks whether the row has changed compared to the original ID map.
  296. *
  297. * @return bool
  298. * TRUE if the row has changed, FALSE otherwise. If setIdMap() was not
  299. * called, this always returns FALSE.
  300. */
  301. public function changed() {
  302. return $this->idMap['original_hash'] != $this->idMap['hash'];
  303. }
  304. /**
  305. * Returns if this row needs an update.
  306. *
  307. * @return bool
  308. * TRUE if the row needs updating, FALSE otherwise.
  309. */
  310. public function needsUpdate() {
  311. return $this->idMap['source_row_status'] == MigrateIdMapInterface::STATUS_NEEDS_UPDATE;
  312. }
  313. /**
  314. * Returns the hash for the source values..
  315. *
  316. * @return mixed
  317. * The hash of the source values.
  318. */
  319. public function getHash() {
  320. return $this->idMap['hash'];
  321. }
  322. /**
  323. * Reports whether this row is a stub.
  324. *
  325. * @return bool
  326. * The current stub value.
  327. */
  328. public function isStub() {
  329. return $this->isStub;
  330. }
  331. }