PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/cases/data/source/mongo_db/ExporterTest.php

http://github.com/UnionOfRAD/lithium
PHP | 910 lines | 748 code | 123 blank | 39 comment | 3 complexity | c6909f9fc4ec4e242fa251daecd56652 MD5 | raw file
  1. <?php
  2. /**
  3. * li₃: the most RAD framework for PHP (http://li3.me)
  4. *
  5. * Copyright 2010, Union of RAD. All rights reserved. This source
  6. * code is distributed under the terms of the BSD 3-Clause License.
  7. * The full license text can be found in the LICENSE.txt file.
  8. */
  9. namespace lithium\tests\cases\data\source\mongo_db;
  10. use MongoId;
  11. use MongoDate;
  12. use lithium\data\Connections;
  13. use lithium\data\source\MongoDb;
  14. use lithium\data\entity\Document;
  15. use lithium\data\collection\DocumentSet;
  16. use lithium\data\source\mongo_db\Schema;
  17. use lithium\data\source\mongo_db\Exporter;
  18. use lithium\tests\mocks\data\source\MockResult;
  19. use lithium\tests\mocks\data\source\MockMongoPost;
  20. class ExporterTest extends \lithium\test\Unit {
  21. protected $_model = 'lithium\tests\mocks\data\source\MockMongoPost';
  22. protected $_schema = [
  23. '_id' => ['type' => 'id'],
  24. 'guid' => ['type' => 'id'],
  25. 'title' => ['type' => 'string'],
  26. 'tags' => ['type' => 'string', 'array' => true],
  27. 'comments' => ['type' => 'MongoId'],
  28. 'accounts' => ['type' => 'object', 'array' => true],
  29. 'accounts._id' => ['type' => 'id'],
  30. 'accounts.name' => ['type' => 'string'],
  31. 'accounts.created' => ['type' => 'date'],
  32. 'authors' => ['type' => 'MongoId', 'array' => true],
  33. 'created' => ['type' => 'MongoDate'],
  34. 'modified' => ['type' => 'datetime'],
  35. 'voters' => ['type' => 'id', 'array' => true],
  36. 'rank_count' => ['type' => 'integer', 'default' => 0],
  37. 'rank' => ['type' => 'float', 'default' => 0.0],
  38. 'notifications.foo' => ['type' => 'boolean'],
  39. 'notifications.bar' => ['type' => 'boolean'],
  40. 'notifications.baz' => ['type' => 'boolean']
  41. ];
  42. protected $_handlers = [];
  43. public function skip() {
  44. $this->skipIf(!MongoDb::enabled(), 'MongoDb is not enabled');
  45. }
  46. public function setUp() {
  47. $this->_handlers = [
  48. 'id' => function($v) {
  49. return is_string($v) && preg_match('/^[0-9a-f]{24}$/', $v) ? new MongoId($v) : $v;
  50. },
  51. 'date' => function($v) {
  52. $v = is_numeric($v) ? (integer) $v : strtotime($v);
  53. return !$v ? new MongoDate() : new MongoDate($v);
  54. },
  55. 'regex' => function($v) { return new MongoRegex($v); },
  56. 'integer' => function($v) { return (integer) $v; },
  57. 'float' => function($v) { return (float) $v; },
  58. 'boolean' => function($v) { return (boolean) $v; },
  59. 'code' => function($v) { return new MongoCode($v); },
  60. 'binary' => function($v) { return new MongoBinData($v); }
  61. ];
  62. $model = $this->_model;
  63. Connections::add('mockconn', ['object' => new MongoDb(['autoConnect' => false])]);
  64. $model::config(['meta' => ['connection' => 'mockconn']]);
  65. $model::schema(false);
  66. $model::schema($this->_schema);
  67. }
  68. public function tearDown() {
  69. Connections::remove('mockconn');
  70. MockMongoPost::reset();
  71. }
  72. public function testInvalid() {
  73. $this->assertNull(Exporter::get(null, null));
  74. }
  75. public function testCreateWithFixedData() {
  76. $time = time();
  77. $doc = new Document(['exists' => false, 'data' => [
  78. '_id' => new MongoId(),
  79. 'created' => new MongoDate($time),
  80. 'numbers' => new DocumentSet(['data' => [7, 8, 9]]),
  81. 'objects' => new DocumentSet(['data' => [
  82. new Document(['data' => ['foo' => 'bar']]),
  83. new Document(['data' => ['baz' => 'dib']])
  84. ]]),
  85. 'deeply' => new Document(['data' => ['nested' => 'object']])
  86. ]]);
  87. $this->assertEqual('object', $doc->deeply->nested);
  88. $this->assertTrue($doc->_id instanceof MongoId);
  89. $result = Exporter::get('create', $doc->export());
  90. $this->assertTrue($result['create']['_id'] instanceof MongoId);
  91. $this->assertTrue($result['create']['created'] instanceof MongoDate);
  92. $this->assertIdentical($time, $result['create']['created']->sec);
  93. $this->assertIdentical([7, 8, 9], $result['create']['numbers']);
  94. $expected = [['foo' => 'bar'], ['baz' => 'dib']];
  95. $this->assertIdentical($expected, $result['create']['objects']);
  96. $this->assertIdentical(['nested' => 'object'], $result['create']['deeply']);
  97. }
  98. public function testCreateWithChangedData() {
  99. $doc = new Document(['exists' => false, 'data' => [
  100. 'numbers' => new DocumentSet(['data' => [7, 8, 9]]),
  101. 'objects' => new DocumentSet(['data' => [
  102. new Document(['data' => ['foo' => 'bar']]),
  103. new Document(['data' => ['baz' => 'dib']])
  104. ]]),
  105. 'deeply' => new Document(['data' => ['nested' => 'object']])
  106. ]]);
  107. $doc->numbers[] = 10;
  108. $doc->deeply->nested2 = 'object2';
  109. $doc->objects[1]->dib = 'gir';
  110. $expected = [
  111. 'numbers' => [7, 8, 9, 10],
  112. 'objects' => [['foo' => 'bar'], ['baz' => 'dib', 'dib' => 'gir']],
  113. 'deeply' => ['nested' => 'object', 'nested2' => 'object2']
  114. ];
  115. $result = Exporter::get('create', $doc->export());
  116. $this->assertEqual(['create'], array_keys($result));
  117. $this->assertEqual($expected, $result['create']);
  118. }
  119. public function testUpdateWithNoChanges() {
  120. $doc = new Document(['exists' => true, 'data' => [
  121. 'numbers' => new DocumentSet(['exists' => true, 'data' => [7, 8, 9]]),
  122. 'objects' => new DocumentSet(['exists' => true, 'data' => [
  123. new Document(['exists' => true, 'data' => ['foo' => 'bar']]),
  124. new Document(['exists' => true, 'data' => ['baz' => 'dib']])
  125. ]]),
  126. 'deeply' => new Document(['exists' => true, 'data' => ['nested' => 'object']])
  127. ]]);
  128. $this->assertEmpty(Exporter::get('update', $doc->export()));
  129. }
  130. public function testUpdateFromResourceLoading() {
  131. $result = new MockResult([
  132. 'data' => [
  133. ['_id' => '4c8f86167675abfabdbf0300', 'title' => 'bar'],
  134. ['_id' => '5c8f86167675abfabdbf0301', 'title' => 'foo'],
  135. ['_id' => '6c8f86167675abfabdbf0302', 'title' => 'dib']
  136. ]
  137. ]);
  138. $doc = new DocumentSet(['model' => $this->_model, 'result' => $result]);
  139. $this->assertEmpty(Exporter::get('update', $doc->export()));
  140. $this->assertEqual('dib', $doc['6c8f86167675abfabdbf0302']->title);
  141. $doc['6c8f86167675abfabdbf0302']->title = 'bob';
  142. $this->assertEqual('6c8f86167675abfabdbf0302', $doc['6c8f86167675abfabdbf0302']->_id);
  143. $this->assertEqual('bob', $doc['6c8f86167675abfabdbf0302']->title);
  144. $doc['4c8f86167675abfabdbf0300']->title = 'bill';
  145. $this->assertEqual('4c8f86167675abfabdbf0300', $doc['4c8f86167675abfabdbf0300']->_id);
  146. $this->assertEqual('bill', $doc['4c8f86167675abfabdbf0300']->title);
  147. $expected = Exporter::get('update', $doc->export());
  148. $this->assertNotEmpty(Exporter::get('update', $doc->export()));
  149. $this->assertCount(2, $expected['update']);
  150. }
  151. public function testUpdateWithSubObjects() {
  152. $model = $this->_model;
  153. $exists = true;
  154. $model::config(['meta' => ['key' => '_id']]);
  155. $schema = new Schema(['fields' => [
  156. 'forceArray' => ['type' => 'string', 'array' => true],
  157. 'array' => ['type' => 'string', 'array' => true],
  158. 'dictionary' => ['type' => 'string', 'array' => true],
  159. 'numbers' => ['type' => 'integer', 'array' => true],
  160. 'objects' => ['type' => 'object', 'array' => true],
  161. 'deeply' => ['type' => 'object', 'array' => true],
  162. 'foo' => ['type' => 'string']
  163. ]]);
  164. $config = compact('model', 'schema', 'exists');
  165. $doc = new Document($config + ['data' => [
  166. 'numbers' => new DocumentSet($config + [
  167. 'data' => [7, 8, 9], 'pathKey' => 'numbers'
  168. ]),
  169. 'objects' => new DocumentSet($config + ['pathKey' => 'objects', 'data' => [
  170. new Document($config + ['data' => ['foo' => 'bar']]),
  171. new Document($config + ['data' => ['foo' => 'baz']])
  172. ]]),
  173. 'deeply' => new Document($config + ['pathKey' => 'deeply', 'data' => [
  174. 'nested' => 'object'
  175. ]]),
  176. 'foo' => 'bar'
  177. ]]);
  178. $doc->dictionary[] = 'A Word';
  179. $doc->forceArray = 'Word';
  180. $doc->array = ['one'];
  181. $doc->field = 'value';
  182. $doc->objects[1]->foo = 'dib';
  183. $doc->objects[] = ['foo' => 'diz'];
  184. $doc->deeply->nested = 'foo';
  185. $doc->deeply->nestedAgain = 'bar';
  186. $doc->array = ['one'];
  187. $doc->newObject = new Document([
  188. 'exists' => false, 'data' => ['subField' => 'subValue']
  189. ]);
  190. $doc->newObjects = [
  191. ['test' => 'one', 'another' => 'two'],
  192. ['three' => 'four']
  193. ];
  194. $this->assertEqual('foo', $doc->deeply->nested);
  195. $this->assertEqual('subValue', $doc->newObject->subField);
  196. $doc->numbers = [8, 9];
  197. $doc->numbers[] = 10;
  198. $doc->numbers->append(11);
  199. $export = $doc->export();
  200. $result = Exporter::get('update', $doc->export());
  201. $expected = [
  202. 'array' => ['one'],
  203. 'dictionary' => ['A Word'],
  204. 'forceArray' => ['Word'],
  205. 'numbers' => [8, 9, 10, 11],
  206. 'newObject' => ['subField' => 'subValue'],
  207. 'newObjects' => [
  208. ['test' => 'one', 'another' => 'two'],
  209. ['three' => 'four']
  210. ],
  211. 'field' => 'value',
  212. 'deeply.nested' => 'foo',
  213. 'deeply.nestedAgain' => 'bar',
  214. 'array' => ['one'],
  215. 'objects.1.foo' => 'dib',
  216. 'objects.2' => ['foo' => 'diz']
  217. ];
  218. $this->assertEqual($expected, $result['update']);
  219. $doc->objects[] = ['foo' => 'dob'];
  220. $exist = $doc->objects->find(
  221. function ($data) { return (strcmp($data->foo, 'dob') === 0); },
  222. ['collect' => false]
  223. );
  224. $this->assertTrue(!empty($exist));
  225. }
  226. public function testFieldRemoval() {
  227. $doc = new Document(['exists' => true, 'data' => [
  228. 'numbers' => new DocumentSet(['data' => [7, 8, 9]]),
  229. 'deeply' => new Document([
  230. 'pathKey' => 'deeply', 'exists' => true, 'data' => ['nested' => 'object']
  231. ]),
  232. 'foo' => 'bar'
  233. ]]);
  234. unset($doc->numbers);
  235. $result = Exporter::get('update', $doc->export());
  236. $this->assertEqual(['numbers' => true], $result['remove']);
  237. $doc->set(['flagged' => true, 'foo' => 'baz', 'bar' => 'dib']);
  238. unset($doc->foo, $doc->flagged, $doc->numbers, $doc->deeply->nested);
  239. $result = Exporter::get('update', $doc->export());
  240. $expected = ['foo' => true, 'deeply.nested' => true, 'numbers' => true];
  241. $this->assertEqual($expected, $result['remove']);
  242. $this->assertEqual(['bar' => 'dib'], $result['update']);
  243. }
  244. /**
  245. * Tests that when an existing object is attached as a value of another existing object, the
  246. * whole sub-object is re-written to the new value.
  247. */
  248. public function testAppendExistingObjects() {
  249. $doc = new Document(['exists' => true, 'data' => [
  250. 'deeply' => new Document([
  251. 'pathKey' => 'deeply', 'exists' => true, 'data' => ['nested' => 'object']
  252. ]),
  253. 'foo' => 'bar'
  254. ]]);
  255. $append = new Document(['exists' => true, 'data' => ['foo' => 'bar']]);
  256. $doc->deeply = $append;
  257. $result = Exporter::get('update', $doc->export());
  258. $expected = ['update' => ['deeply' => ['foo' => 'bar']]];
  259. $this->assertEqual($expected, $result);
  260. $expected = ['$set' => ['deeply' => ['foo' => 'bar']]];
  261. $this->assertEqual($expected, Exporter::toCommand($result));
  262. $doc->sync();
  263. $doc->append2 = new Document(['exists' => false, 'data' => ['foo' => 'bar']]);
  264. $expected = ['update' => ['append2' => ['foo' => 'bar']]];
  265. $this->assertEqual($expected, Exporter::get('update', $doc->export()));
  266. $doc->sync();
  267. $this->assertEmpty(Exporter::get('update', $doc->export()));
  268. $doc->append2->foo = 'baz';
  269. $doc->append2->bar = 'dib';
  270. $doc->deeply->nested = true;
  271. $expected = ['update' => [
  272. 'append2.foo' => 'baz', 'append2.bar' => 'dib', 'deeply.nested' => true
  273. ]];
  274. $this->assertEqual($expected, Exporter::get('update', $doc->export()));
  275. }
  276. public function testNestedObjectCasting() {
  277. $model = $this->_model;
  278. $data = ['notifications' => ['foo' => '', 'bar' => '1', 'baz' => 0, 'dib' => 42]];
  279. $result = $model::schema()->cast(null, null, $data, compact('model'));
  280. $this->assertIdentical(false, $result['notifications']->foo);
  281. $this->assertIdentical(true, $result['notifications']->bar);
  282. $this->assertIdentical(false, $result['notifications']->baz);
  283. $this->assertIdentical(42, $result['notifications']->dib);
  284. }
  285. /**
  286. * Tests handling type values based on specified schema settings.
  287. */
  288. public function testTypeCasting() {
  289. $time = time();
  290. $data = [
  291. '_id' => '4c8f86167675abfabd970300',
  292. 'title' => 'Foo',
  293. 'tags' => 'test',
  294. 'comments' => [
  295. "4c8f86167675abfabdbe0300", "4c8f86167675abfabdbf0300", "4c8f86167675abfabdc00300"
  296. ],
  297. 'empty_array' => [],
  298. 'authors' => '4c8f86167675abfabdb00300',
  299. 'created' => $time,
  300. 'modified' => date('Y-m-d H:i:s', $time),
  301. 'rank_count' => '45',
  302. 'rank' => '3.45688'
  303. ];
  304. $model = $this->_model;
  305. $handlers = $this->_handlers;
  306. $options = compact('model', 'handlers');
  307. $schema = new Schema(['fields' => $this->_schema]);
  308. $result = $schema->cast(null, null, $data, $options);
  309. $this->assertEqual(array_keys($data), array_keys($result->data()));
  310. $this->assertInstanceOf('MongoId', $result->_id);
  311. $this->assertEqual('4c8f86167675abfabd970300', (string) $result->_id);
  312. $this->assertInstanceOf('lithium\data\collection\DocumentSet', $result->comments);
  313. $this->assertCount(3, $result['comments']);
  314. $this->assertInstanceOf('MongoId', $result->comments[0]);
  315. $this->assertInstanceOf('MongoId', $result->comments[1]);
  316. $this->assertInstanceOf('MongoId', $result->comments[2]);
  317. $this->assertEqual('4c8f86167675abfabdbe0300', (string) $result->comments[0]);
  318. $this->assertEqual('4c8f86167675abfabdbf0300', (string) $result->comments[1]);
  319. $this->assertEqual('4c8f86167675abfabdc00300', (string) $result->comments[2]);
  320. $this->assertEqual($data['comments'], $result->comments->data());
  321. $this->assertEqual(['test'], $result->tags->data());
  322. $this->assertEqual(['4c8f86167675abfabdb00300'], $result->authors->data());
  323. $this->assertInstanceOf('MongoId', $result->authors[0]);
  324. $this->assertInstanceOf('MongoDate', $result->modified);
  325. $this->assertInstanceOf('MongoDate', $result->created);
  326. $this->assertGreaterThan($result->created->sec, 0);
  327. $this->assertInstanceOf('lithium\data\collection\DocumentSet', $result->empty_array);
  328. $this->assertEqual($time, $result->modified->sec);
  329. $this->assertEqual($time, $result->created->sec);
  330. $this->assertIdentical(45, $result->rank_count);
  331. $this->assertIdentical(3.45688, $result->rank);
  332. }
  333. /**
  334. * Tests handling type values of subdocument arrays based on specified schema settings.
  335. */
  336. public function testTypeCastingSubObjectArrays() {
  337. $time = time();
  338. $data = [
  339. '_id' => '4c8f86167675abfabd970300',
  340. 'accounts' => [
  341. [
  342. '_id' => "4fb6e2dd3e91581fe6e75736",
  343. 'name' => 'Foo',
  344. 'created' => $time
  345. ],
  346. [
  347. '_id' => "4fb6e2df3e91581fe6e75737",
  348. 'name' => 'Bar',
  349. 'created' => $time
  350. ]
  351. ]
  352. ];
  353. $model = $this->_model;
  354. $handlers = $this->_handlers;
  355. $options = compact('model', 'handlers');
  356. $schema = new Schema(['fields' => $this->_schema]);
  357. $result = $schema->cast(null, null, $data, $options);
  358. $this->assertEqual(array_keys($data), array_keys($result->data()));
  359. $this->assertInstanceOf('MongoId', $result->_id);
  360. $this->assertEqual('4c8f86167675abfabd970300', (string) $result->_id);
  361. $this->assertInstanceOf('lithium\data\collection\DocumentSet', $result->accounts);
  362. $this->assertCount(2, $result->accounts);
  363. $id1 = '4fb6e2dd3e91581fe6e75736';
  364. $id2 = '4fb6e2df3e91581fe6e75737';
  365. $this->assertInstanceOf('MongoId', $result->accounts[$id1]['_id']);
  366. $this->assertEqual($id1, (string) $result->accounts[$id1]['_id']);
  367. $this->assertInstanceOf('MongoId', $result->accounts[$id2]['_id']);
  368. $this->assertEqual($id2, (string) $result->accounts[$id2]['_id']);
  369. $this->assertInstanceOf('MongoDate', $result->accounts[$id1]['created']);
  370. $this->assertGreaterThan($result->accounts[$id1]['created']->sec, 0);
  371. $this->assertInstanceOf('MongoDate', $result->accounts[$id2]['created']);
  372. $this->assertGreaterThan($result->accounts[$id2]['created']->sec, 0);
  373. }
  374. public function testWithArraySchema() {
  375. $model = $this->_model;
  376. $model::schema([
  377. '_id' => ['type' => 'id'],
  378. 'list' => ['type' => 'string', 'array' => true],
  379. 'obj.foo' => ['type' => 'string'],
  380. 'obj.bar' => ['type' => 'string']
  381. ]);
  382. $doc = new Document(compact('model'));
  383. $doc->list[] = ['foo' => '!!', 'bar' => '??'];
  384. $data = ['list' => [['foo' => '!!', 'bar' => '??']]];
  385. $this->assertEqual($data, $doc->data());
  386. $result = Exporter::get('create', $doc->export());
  387. $this->assertEqual($data, $result['create']);
  388. $result = Exporter::get('update', $doc->export());
  389. $this->assertEqual($data, $result['update']);
  390. $doc = new Document(compact('model'));
  391. $doc->list = [];
  392. $doc->list[] = ['foo' => '!!', 'bar' => '??'];
  393. $data = ['list' => [['foo' => '!!', 'bar' => '??']]];
  394. $this->assertEqual($data, $doc->data());
  395. $result = Exporter::get('create', $doc->export());
  396. $this->assertEqual($result['create'], $data);
  397. $result = Exporter::get('update', $doc->export());
  398. $this->assertEqual($result['update'], $data);
  399. }
  400. public function testArrayConversion() {
  401. $time = time();
  402. $doc = new Document(['data' => [
  403. '_id' => new MongoId(),
  404. 'date' => new MongoDate($time)
  405. ]]);
  406. $result = $doc->data();
  407. $this->assertPattern('/^[a-f0-9]{24}$/', $result['_id']);
  408. $this->assertEqual($time, $result['date']);
  409. }
  410. /**
  411. * Allow basic type field to be replaced by a `Document` / `DocumentSet` type.
  412. */
  413. public function testArrayFieldChange() {
  414. $doc = new Document();
  415. $doc->someOtherField = 'someValue';
  416. $doc->list = 'test';
  417. $doc->sync();
  418. $doc->list = new DocumentSet();
  419. $doc->list['id'] = ['foo' => '!!', 'bar' => '??'];
  420. $data = ['list' => ['id' => ['foo' => '!!', 'bar' => '??']]];
  421. $result = Exporter::get('update', $doc->export());
  422. $this->assertEqual($data, $result['update']);
  423. $doc = new Document();
  424. $doc->someOtherField = 'someValue';
  425. $doc->list = new Document(['data' => ['foo' => '!!']]);
  426. $doc->sync();
  427. $doc->list = new DocumentSet();
  428. $doc->list['id'] = ['foo' => '!!', 'bar' => '??'];
  429. $result = Exporter::get('update', $doc->export());
  430. $this->assertEqual($data, $result['update']);
  431. }
  432. /**
  433. * Test that sub-objects are properly casted on creating a new `Document`.
  434. */
  435. public function testSubObjectCastingOnSave() {
  436. $model = $this->_model;
  437. $model::schema([
  438. '_id' => ['type' => 'id'],
  439. 'sub.foo' => ['type' => 'boolean'],
  440. 'bar' => ['type' => 'boolean']
  441. ]);
  442. $data = ['sub' => ['foo' => 0], 'bar' => 1];
  443. $doc = new Document(compact('data', 'model'));
  444. $this->assertIdentical(true, $doc->bar);
  445. $this->assertIdentical(false, $doc->sub->foo);
  446. $data = ['sub.foo' => '1', 'bar' => '0'];
  447. $doc = new Document(compact('data', 'model', 'schema'));
  448. $this->assertIdentical(false, $doc->bar);
  449. $this->assertIdentical(true, $doc->sub->foo);
  450. }
  451. /**
  452. * Tests that a nested key on a previously saved document gets updated properly.
  453. */
  454. public function testExistingNestedKeyOverwrite() {
  455. $doc = new Document(['model' => $this->_model]);
  456. $doc->{'this.that'} = 'value1';
  457. $this->assertEqual(['this' => ['that' => 'value1']], $doc->data());
  458. $result = Exporter::get('create', $doc->export());
  459. $this->assertEqual(['create' => ['this' => ['that' => 'value1']]], $result);
  460. $doc->sync();
  461. $doc->{'this.that'} = 'value2';
  462. $this->assertEqual(['this' => ['that' => 'value2']], $doc->data());
  463. $result = Exporter::get('update', $doc->export());
  464. $this->assertEqual(['update' => ['this.that' => 'value2']], $result);
  465. }
  466. public function testUpdatingArraysAndExporting() {
  467. $new = new Document(['data' => ['name' => 'Acme, Inc.', 'active' => true]]);
  468. $expected = ['name' => 'Acme, Inc.', 'active' => true];
  469. $result = $new->data();
  470. $this->assertEqual($expected, $result);
  471. $new->foo = new DocumentSet(['data' => ['bar']]);
  472. $expected = ['name' => 'Acme, Inc.', 'active' => true, 'foo' => ['bar']];
  473. $result = $new->data();
  474. $this->assertEqual($expected, $result);
  475. $expected = 'bar';
  476. $result = $new->foo[0];
  477. $this->assertEqual($expected, $result);
  478. $new->foo[1] = 'baz';
  479. $expected = 'baz';
  480. $result = $new->data();
  481. $this->assertEqual($expected, $result['foo'][1]);
  482. }
  483. /**
  484. * Tests that arrays of nested objects can be appended to and will be updated using the proper
  485. * MongoDB operators.
  486. */
  487. public function testAppendingNestedObjectArray() {
  488. $model = $this->_model;
  489. $model::schema(false);
  490. $model::schema([
  491. 'accounts' => ['type' => 'object', 'array' => true],
  492. 'accounts.name' => ['type' => 'string']
  493. ]);
  494. $doc = new Document(compact('model'));
  495. $this->assertEqual([], $doc->accounts->data());
  496. $doc->sync();
  497. $data = ['name' => 'New account'];
  498. $doc->accounts[] = new Document(compact('data'));
  499. $result = Exporter::get('update', $doc->export());
  500. $expected = ['update' => ['accounts.0' => $data]];
  501. $this->assertEqual($expected, $result);
  502. $result = Exporter::toCommand($result);
  503. $expected = ['$set' => ['accounts.0' => $data]];
  504. $this->assertEqual($expected, $result);
  505. }
  506. /**
  507. * @todo Implement me.
  508. */
  509. public function testCreateWithWhitelist() {
  510. }
  511. /**
  512. * Tests the casting of MongoIds in nested arrays.
  513. */
  514. public function testNestedArrayMongoIdCasting() {
  515. $articleOneId = new MongoId();
  516. $bookOneId = new MongoId();
  517. $bookTwoId = new MongoId();
  518. $data = [
  519. '_id' => '4c8f86167675abfabd970300',
  520. 'title' => 'Foo',
  521. 'similar_text' => [
  522. 'articles' => [
  523. $articleOneId
  524. ],
  525. 'books' => [
  526. $bookOneId,
  527. $bookTwoId
  528. ],
  529. 'magazines' => [
  530. "4fdfb4327a959c4f76000006",
  531. "4e95f6e098ef47722d000001"
  532. ]
  533. ]
  534. ];
  535. $model = $this->_model;
  536. $handlers = $this->_handlers;
  537. $options = compact('model', 'handlers');
  538. $schema = new Schema(['fields' => [
  539. '_id' => ['type' => 'MongoId'],
  540. 'title' => ['type' => 'text'],
  541. 'similar_text' => ['type' => 'array'],
  542. 'similar_text.articles' => ['type' => 'MongoId', 'array' => true],
  543. 'similar_text.books' => ['type' => 'MongoId', 'array' => true],
  544. 'similar_text.magazines' => ['type' => 'MongoId', 'array' => true]
  545. ]]);
  546. $result = $schema->cast(null, null, $data, $options);
  547. $this->assertInstanceOf('MongoId', $result['similar_text']['articles'][0]);
  548. $this->assertInstanceOf('MongoId', $result['similar_text']['books'][0]);
  549. $this->assertInstanceOf('MongoId', $result['similar_text']['books'][1]);
  550. $this->assertInstanceOf('MongoId', $result['similar_text']['magazines'][0]);
  551. $this->assertInstanceOf('MongoId', $result['similar_text']['magazines'][1]);
  552. }
  553. /**
  554. * Tests that updating arrays of `MongoId`s correctly preserves their type.
  555. */
  556. public function testUpdatingMongoIdArray() {
  557. $schema = new Schema(['fields' => [
  558. 'list' => ['type' => 'id', 'array' => true]
  559. ]]);
  560. $doc = new Document(['exists' => true, 'data' => [
  561. 'list' => [new MongoId(), new MongoId(), new MongoId()]
  562. ]]);
  563. $this->assertEqual([], Exporter::get('update', $doc->export()));
  564. $doc->list[] = new MongoId();
  565. $doc->list[] = new MongoId();
  566. $result = Exporter::get('update', $doc->export());
  567. $this->assertCount(1, $result);
  568. $this->assertCount(1, $result['update']);
  569. $this->assertCount(5, $result['update']['list']);
  570. for ($i = 0; $i < 5; $i++) {
  571. $this->assertInstanceOf('MongoId', $result['update']['list'][$i]);
  572. }
  573. $doc = new Document(['exists' => true, 'data' => [
  574. 'list' => [new MongoId(), new MongoId(), new MongoId()]
  575. ]]);
  576. $doc->list = [new MongoId(), new MongoId(), new MongoId()];
  577. $result = Exporter::get('update', $doc->export());
  578. $this->assertCount(1, $result);
  579. $this->assertCount(1, $result['update']);
  580. $this->assertCount(3, $result['update']['list']);
  581. for ($i = 0; $i < 3; $i++) {
  582. $this->assertInstanceOf('MongoId', $result['update']['list'][$i]);
  583. }
  584. }
  585. public function testToDataOnDocumentSet() {
  586. $data = [
  587. [
  588. '_id' => '4c8f86167675abfabd970300',
  589. 'accounts' => [
  590. [
  591. '_id' => "4fb6e2dd3e91581fe6e75736",
  592. 'name' => 'Foo1'
  593. ],
  594. [
  595. '_id' => "4fb6e2df3e91581fe6e75737",
  596. 'name' => 'Bar1'
  597. ]
  598. ]
  599. ],
  600. [
  601. '_id' => '4c8f86167675abfabd970301',
  602. 'accounts' => [
  603. [
  604. '_id' => "4fb6e2dd3e91581fe6e75738",
  605. 'name' => 'Foo2'
  606. ],
  607. [
  608. '_id' => "4fb6e2df3e91581fe6e75739",
  609. 'name' => 'Bar2'
  610. ]
  611. ]
  612. ]
  613. ];
  614. $model = $this->_model;
  615. $handlers = $this->_handlers;
  616. $options = compact('model', 'handlers');
  617. $schema = new Schema(['fields' => $this->_schema]);
  618. $set = $schema->cast(null, null, $data, $options);
  619. $result = $set->data();
  620. $accounts = $result['4c8f86167675abfabd970300']['accounts'];
  621. $this->assertEqual('Foo1', $accounts[0]['name']);
  622. $this->assertEqual('Bar1', $accounts[1]['name']);
  623. $accounts = $result['4c8f86167675abfabd970301']['accounts'];
  624. $this->assertEqual('Foo2', $accounts[0]['name']);
  625. $this->assertEqual('Bar2', $accounts[1]['name']);
  626. $result = $set->to('array', ['indexed' => false]);
  627. $accounts = $result[0]['accounts'];
  628. $this->assertEqual('Foo1', $accounts[0]['name']);
  629. $this->assertEqual('Bar1', $accounts[1]['name']);
  630. $accounts = $result[1]['accounts'];
  631. $this->assertEqual('Foo2', $accounts[0]['name']);
  632. $this->assertEqual('Bar2', $accounts[1]['name']);
  633. $result = $set->to('array', ['indexed' => true]);
  634. $accounts = $result['4c8f86167675abfabd970300']['accounts'];
  635. $this->assertEqual('Foo1', $accounts['4fb6e2dd3e91581fe6e75736']['name']);
  636. $this->assertEqual('Bar1', $accounts['4fb6e2df3e91581fe6e75737']['name']);
  637. $accounts = $result['4c8f86167675abfabd970301']['accounts'];
  638. $this->assertEqual('Foo2', $accounts['4fb6e2dd3e91581fe6e75738']['name']);
  639. $this->assertEqual('Bar2', $accounts['4fb6e2df3e91581fe6e75739']['name']);
  640. $result = $set->to('array');
  641. $accounts = $result['4c8f86167675abfabd970300']['accounts'];
  642. $this->assertEqual('Foo1', $accounts['4fb6e2dd3e91581fe6e75736']['name']);
  643. $this->assertEqual('Bar1', $accounts['4fb6e2df3e91581fe6e75737']['name']);
  644. $accounts = $result['4c8f86167675abfabd970301']['accounts'];
  645. $this->assertEqual('Foo2', $accounts['4fb6e2dd3e91581fe6e75738']['name']);
  646. $this->assertEqual('Bar2', $accounts['4fb6e2df3e91581fe6e75739']['name']);
  647. }
  648. public function testToDataOnDocument() {
  649. $data = [
  650. '_id' => '4c8f86167675abfabd970300',
  651. 'accounts' => [
  652. [
  653. '_id' => "4fb6e2dd3e91581fe6e75736",
  654. 'name' => 'Foo1'
  655. ],
  656. [
  657. '_id' => "4fb6e2df3e91581fe6e75737",
  658. 'name' => 'Bar1'
  659. ]
  660. ]
  661. ];
  662. $model = $this->_model;
  663. $handlers = $this->_handlers;
  664. $options = compact('model', 'handlers');
  665. $schema = new Schema(['fields' => $this->_schema]);
  666. $set = $schema->cast(null, null, $data, $options);
  667. $result = $set->data();
  668. $accounts = $result['accounts'];
  669. $this->assertEqual('Foo1', $accounts[0]['name']);
  670. $this->assertEqual('Bar1', $accounts[1]['name']);
  671. $result = $set->to('array', ['indexed' => false]);
  672. $accounts = $result['accounts'];
  673. $this->assertEqual('Foo1', $accounts[0]['name']);
  674. $this->assertEqual('Bar1', $accounts[1]['name']);
  675. $result = $set->to('array', ['indexed' => true]);
  676. $accounts = $result['accounts'];
  677. $this->assertEqual('Foo1', $accounts['4fb6e2dd3e91581fe6e75736']['name']);
  678. $this->assertEqual('Bar1', $accounts['4fb6e2df3e91581fe6e75737']['name']);
  679. }
  680. public function testIndexesOnExportingDocumentSet() {
  681. $schema = new Schema(['fields' => [
  682. '_id' => ['type' => 'id'],
  683. 'accounts' => ['type' => 'object', 'array' => true],
  684. 'accounts._id' => ['type' => 'id'],
  685. 'accounts.name' => ['type' => 'string']
  686. ]]);
  687. $data = [
  688. [
  689. '_id' => '4c8f86167675abfabd970300',
  690. 'accounts' => [
  691. [
  692. '_id' => "4fb6e2dd3e91581fe6e75736",
  693. 'name' => 'Foo1'
  694. ],
  695. [
  696. '_id' => "4fb6e2df3e91581fe6e75737",
  697. 'name' => 'Bar1'
  698. ]
  699. ]
  700. ],
  701. [
  702. '_id' => '4c8f86167675abfabd970301',
  703. 'accounts' => [
  704. [
  705. '_id' => "4fb6e2dd3e91581fe6e75738",
  706. 'name' => 'Foo2'
  707. ],
  708. [
  709. '_id' => "4fb6e2df3e91581fe6e75739",
  710. 'name' => 'Bar2'
  711. ]
  712. ]
  713. ]
  714. ];
  715. $model = $this->_model;
  716. $array = new DocumentSet(compact('model', 'schema', 'data'));
  717. $obj = $array['4c8f86167675abfabd970300']->accounts;
  718. $this->assertInstanceOf('lithium\data\collection\DocumentSet', $obj);
  719. $obj = $array['4c8f86167675abfabd970301']->accounts;
  720. $this->assertInstanceOf('lithium\data\collection\DocumentSet', $obj);
  721. $result = Exporter::get('create', $array->export());
  722. $this->assertTrue(isset($result['create'][0]));
  723. $this->assertTrue(isset($result['create'][1]));
  724. $this->assertFalse(isset($result['create']['4c8f86167675abfabd970300']));
  725. $this->assertFalse(isset($result['create']['4c8f86167675abfabd970301']));
  726. $this->assertTrue(isset($result['create'][0]['accounts'][0]));
  727. $this->assertTrue(isset($result['create'][0]['accounts'][1]));
  728. $this->assertTrue(isset($result['create'][1]['accounts'][0]));
  729. $this->assertTrue(isset($result['create'][1]['accounts'][1]));
  730. $result = Exporter::get('update', $array->export());
  731. $this->assertTrue(isset($result['update'][0]));
  732. $this->assertTrue(isset($result['update'][1]));
  733. $this->assertFalse(isset($result['update']['4c8f86167675abfabd970300']));
  734. $this->assertFalse(isset($result['update']['4c8f86167675abfabd970301']));
  735. $this->assertTrue(isset($result['update'][0]['accounts'][0]));
  736. $this->assertTrue(isset($result['update'][0]['accounts'][1]));
  737. $this->assertTrue(isset($result['update'][1]['accounts'][0]));
  738. $this->assertTrue(isset($result['update'][1]['accounts'][1]));
  739. }
  740. public function testIndexesOnExportingDocument() {
  741. $schema = new Schema(['fields' => [
  742. '_id' => ['type' => 'id'],
  743. 'accounts' => ['type' => 'object', 'array' => true],
  744. 'accounts._id' => ['type' => 'id'],
  745. 'accounts.name' => ['type' => 'string']
  746. ]]);
  747. $data = [
  748. '_id' => '4c8f86167675abfabd970300',
  749. 'accounts' => [
  750. [
  751. '_id' => "4fb6e2dd3e91581fe6e75736",
  752. 'name' => 'Foo1'
  753. ],
  754. [
  755. '_id' => "4fb6e2df3e91581fe6e75737",
  756. 'name' => 'Bar1'
  757. ]
  758. ]
  759. ];
  760. $model = $this->_model;
  761. $document = new Document(compact('model', 'schema', 'data'));
  762. $this->assertInstanceOf('lithium\data\collection\DocumentSet', $document->accounts);
  763. $export = $document->export();
  764. $result = Exporter::get('create', $document->export());
  765. $this->assertTrue(isset($result['create']['accounts'][0]));
  766. $this->assertTrue(isset($result['create']['accounts'][1]));
  767. $export['data'] = [];
  768. $result = Exporter::get('update', $export);
  769. $this->assertTrue(isset($result['update']['accounts'][0]));
  770. $this->assertTrue(isset($result['update']['accounts'][1]));
  771. }
  772. public function testEmptyArrayAsDocument() {
  773. $schema = new Schema(['fields' => [
  774. '_id' => ['type' => 'id'],
  775. 'accounts' => ['type' => 'object', 'array' => true],
  776. 'accounts.name' => ['type' => 'string']
  777. ]]);
  778. $data = [
  779. '_id' => '4c8f86167675abfabd970300',
  780. 'accounts' => [[]]
  781. ];
  782. $model = $this->_model;
  783. $document = new Document(compact('model', 'schema', 'data'));
  784. $this->assertInstanceOf('lithium\data\entity\Document', $document->accounts[0]);
  785. }
  786. }
  787. ?>