Yaffs site version 1.1
[yaffs-website] / vendor / gabordemooij / redbean / testing / RedUNIT / Base / Relations.php
1 <?php
2
3 namespace RedUNIT\Base;
4
5 use RedUNIT\Base as Base;
6 use RedBeanPHP\Facade as R;
7 use RedBeanPHP\RedException as RedException;
8 use RedBeanPHP\OODBBean as OODBBean;
9
10 /**
11  * Relations
12  *
13  * Tests whether RedBeanPHP handles relational data properly.
14  * This test suite is quite large because it tests various types
15  * of relations as well as simple and complex usage scenarios.
16  *
17  * @file    RedUNIT/Base/Relations.php
18  * @desc    Tests N:1 relations, nested beans.
19  * @author  Gabor de Mooij and the RedBeanPHP Community
20  * @license New BSD/GPLv2
21  *
22  * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
23  * This source file is subject to the New BSD/GPLv2 License that is bundled
24  * with this source code in the file license.txt.
25  */
26 class Relations extends Base
27 {
28         /**
29          * Test whether untainted parent bean dont
30          * get saved.
31          *
32          * @return void
33          */
34         public function testDontSaveParentIfNotTainted()
35         {
36                 R::nuke();
37                 $author = R::dispense( 'author' );
38                 R::store( $author );
39                 $book = R::dispense( 'book' );
40                 $book->author = $author;
41                 $book->author->name = 'x';
42                 $book->author->setMeta( 'tainted', false );
43                 R::store( $book );
44                 $author = $author->fresh();
45                 asrt( isset( $author->name ), false );
46         }
47
48         /**
49          * Tests whether via() applies camelcase-to-snakecase
50          * conversion. Although most of the time you do not need
51          * this since via() is meant to remap typical link tables
52          * to bean - but alas.
53          *
54          * @return void
55          */
56         public function testCamelCasingVia()
57         {
58                 R::nuke();
59                 $book = R::dispense('book');
60                 $book->sharedPage[] = R::dispense('page');
61                 R::store( $book );
62                 $book = $book->fresh();
63                 asrt( count( $book->via('bookPage')->sharedPage ), 1 );
64                 $book = $book->fresh();
65                 asrt( count( $book->via('book_page')->sharedPage ), 1 );
66         }
67
68         /**
69          * Test whether we can't add more than one FK.
70          */
71         public function testDuplicateFK()
72         {
73                 R::nuke();
74                 list( $book, $page ) = R::dispenseAll( 'book,page' );
75                 $book->sharedPage[] = $page;
76                 R::store( $page );
77                 R::store( $book );
78                 $writer = R::getWriter();
79                 $added1 = $writer->addFK( 'book_page', 'book', 'book_id', 'id', TRUE );
80                 $added2 = $writer->addFK( 'book_page', 'page', 'page_id', 'id', TRUE );
81                 $added = ( $added1 && $added2 );
82                 asrt( $added, FALSE );
83         }
84
85         /**
86          * Test whether ->all() reloads a list.
87          *
88          * @return void
89          */
90         public function testAllPrefix()
91         {
92                 R::nuke();
93                 $book = R::dispense( 'book' );
94                 $book->ownPage = R::dispense( 'page', 10 );
95                 $book->sharedTag = R::dispense( 'tag', 2 );
96                 $i = 0;
97                 foreach( $book->ownPage as $page ) {
98                         $page->pageno = $i++;
99                 }
100                 R::store( $book );
101                 $book = $book->fresh();
102                 asrt( count( $book->ownPage ), 10 );
103                 asrt( count( $book->withCondition(' pageno < 5 ')->ownPage ), 5 );
104                 asrt( count( $book->ownPage ), 5 );
105                 asrt( count( $book->all()->ownPage ), 10 );
106                 asrt( count( $book->with(' LIMIT 3 ')->ownPage ), 3 );
107                 asrt( count( $book->ownPage ), 3 );
108                 asrt( count( $book->all()->ownPage ), 10 );
109                 asrt( count( $book->sharedTag ), 2 );
110                 asrt( count( $book->with( ' LIMIT 1 ' )->sharedTag ), 1 );
111                 asrt( count( $book->sharedTag ), 1 );
112                 asrt( count( $book->all()->sharedTag ), 2 );
113         }
114
115         /**
116          * Test Relations and conditions.
117          *
118          * @return void
119          */
120         public function testRelationsAndConditions()
121         {
122                 list( $book1, $book2 ) = R::dispense( 'book', 2 );
123                 list( $page1, $page2, $page3, $page4 ) = R::dispense( 'page', 4 );
124                 list( $author1, $author2 ) = R::dispense( 'author', 2 );
125                 $book1->title = 'a';
126                 $book2->title = 'b';
127                 $page1->thename = '1';
128                 $page2->thename = '2';
129                 $page3->thename = '3';
130                 $page3->thename = '4';
131                 $book1->ownPage = array( $page1, $page2 );
132                 $book2->ownPage = array( $page3, $page4 );
133                 $author1->sharedBook = array( $book1, $book2 );
134                 $author2->sharedBook = array( $book2 );
135                 R::storeAll( array( $author1, $author2 ) );
136                 asrt( count( $author1->sharedBook ), 2 );
137                 asrt( count( $author1->withCondition( ' title = ? ', array( 'a' ) )->sharedBook ), 1 );
138                 R::store( $author1 );
139                 asrt( count( $author1->sharedBook ), 2 );
140                 asrt( count( $author1->withCondition( ' xtitle = ? ', array( 'a' ) )->sharedBook ), 0 );
141                 R::store( $author1 );
142                 asrt( count( $author1->sharedBook ), 2 );
143                 $book1 = R::load( 'book', $book1->id );
144                 $book2 = $book2->fresh();
145                 asrt( count( $book1->ownPage ), 2 );
146                 asrt( count( $book1->with( ' LIMIT 1 ' )->ownPage ), 1 );
147                 $book1 = $book1->fresh();
148                 asrt( count( $book1->ownPage ), 2 );
149                 asrt( count( $book1->withCondition( ' thename = ? ', array( '1' ) )->ownPage ), 1 );
150         }
151
152         /**
153          * Test filtering relations on links (using columns in the link table).
154          *
155          * @return void
156          */
157         public function testSharedLinkCond()
158         {
159                 testpack( 'Test new shared relations with link conditions' );
160                 $w = R::getWriter();
161                 list( $b1, $b2 ) = R::dispense( 'book', 2 );
162                 $b1->name = 'book1';
163                 $b2->name = 'book2';
164                 list( $p1, $p2, $p3 ) = R::dispense( 'page', 3 );
165                 $p1->text   = 'page1';
166                 $p1->number = 3;
167                 $p2->text   = 'page2';
168                 $p3->text   = 'page3';
169                 $b1->link( 'book_page', array( 'order' => 1 ) )->page = $p1;
170                 $b1->link( 'bookPage', array( 'order' => 2 ) )->page = $p2;
171                 $b2->link( 'book_page', array( 'order' => 1 ) )->page = $p3;
172                 $b2->link( 'bookPage', array( 'order' => 2 ) )->page = $p2;
173                 $b2->link( 'book_page', array( 'order' => 3 ) )->page = $p1;
174                 R::storeAll( array( $b1, $b2 ) );
175                 $b1 = R::load( 'book', $b1->id );
176                 $b2 = R::load( 'book', $b2->id );
177                 $pages = $b1->withCondition( ' book_page.' . $w->esc( 'order' ) . ' = 2 ' )->sharedPage;
178                 $page = reset( $pages );
179                 asrt( $page->text, 'page2' );
180                 $pages = $b2->withCondition( ' ' . $w->esc( 'order' ) . ' = 3 ' )->sharedPage;
181                 $page = reset( $pages );
182                 asrt( $page->text, 'page1' );
183                 $b1 = R::load( 'book', $b1->id );
184                 $b2 = R::load( 'book', $b2->id );
185                 $pages = $b1->withCondition( ' book_page.' . $w->esc( 'order' ) . ' < 3  AND page.number = 3' )->sharedPage;
186                 $page = reset( $pages );
187                 asrt( $page->text, 'page1' );
188                 $pages = $b2->withCondition( ' ' . $w->esc( 'order' ) . ' > 1  ORDER BY book_page.' . $w->esc( 'order' ) . ' ASC ' )->sharedPage;
189                 $page = array_shift( $pages );
190                 asrt( $page->text, 'page2' );
191                 $page = array_shift( $pages );
192                 asrt( $page->text, 'page1' );
193                 testpack( 'Test new shared relations and cache' );
194
195                 /**
196                  * why does this not destroy cache in psql?
197                  * ah: An error occurred: SQLSTATE[42703]: Undefined column: 7
198                  * ERROR:  column "page" of relation "page" does not exist
199                  */
200                 R::exec( 'UPDATE page SET ' . $w->esc( 'number' ) . ' = 1 ' );
201                 R::getWriter()->setUseCache( TRUE );
202                 $p1 = R::load( 'page', (int) $p1->id );
203                 // Someone else changes the records. Cache remains.
204                 R::exec( ' UPDATE page SET ' . $w->esc( 'number' ) . ' = 9 -- keep-cache' );
205                 $b1 = R::load( 'book', $b1->id );
206                 $p1 = R::load( 'page', (int) $p1->id );
207                 // Yupz a stale cache, phantom read!
208                 asrt( (int) $p1->number, 1 );
209                 $pages = $b1->withCondition( ' book_page.' . $w->esc( 'order' ) . ' = 1 ' )->sharedPage;
210                 $page = reset( $pages );
211                 // Inconsistent, sad but TRUE, different query -> cache key is different
212                 asrt( (int) $page->number, 9 );
213                 // However, cache must have been invalidated by this query
214                 $p1 = R::load( 'page', (int) $p1->id );
215                 // Yes! we're consistent again! -- as if the change just happened later!
216                 asrt( (int) $page->number, 9 );
217                 // By doing this we keep getting 9 instead of 8
218                 $b1->fresh()->withCondition( ' book_page.' . $w->esc( 'order' ) . ' = 1 ' )->sharedPage;
219                 // Someone else is busy again...
220                 R::exec( ' UPDATE page SET ' . $w->esc( 'number' ) . ' = 8 -- keep-cache' );
221                 $b1 = R::load( 'book', $b1->id );
222                 $pages = $b1->withCondition( ' book_page.' . $w->esc( 'order' ) . ' = 1 ' )->sharedPage;
223                 $page = reset( $pages );
224
225                 /**
226                  * yes! we get 9 instead of 8, why because the cache key has not changed,
227                  * our last query was PAGE-BOOK-RELATION and now we ask for
228                  * PAGE-BOOK-RELATION again. if we would have used just a load page
229                  * query we would have gotten the new value (8).... let's test that!
230                  */
231                 asrt( (int) $page->number, 9 );
232                 R::exec( ' UPDATE page SET ' . $w->esc( 'number' ) . ' = 9' );
233                 $p1 = R::load( 'page', (int) $p1->id );
234                 asrt( (int) $page->number, 9 );
235                 // Someone else is busy again...
236                 R::exec( ' UPDATE page SET ' . $w->esc( 'number' ) . ' = 8 -- keep-cache' );
237                 $b1 = R::load( 'book', $b1->id );
238                 $pages = $b1->withCondition( ' book_page.' . $w->esc( 'order' ) . ' = 1 ' )->sharedPage;
239                 $page = reset( $pages );
240                 // Yes, keep-cache wont help, cache key changed!
241                 asrt( (int) $page->number, 8 );
242                 R::getWriter()->setUseCache( FALSE );
243         }
244
245         /**
246          * Test related count using via().
247          *
248          * @return void
249          */
250         public function testRelatedCountVia()
251         {
252                 testpack( 'Test relatedCount with via()' );
253                 $shop              = R::dispense( 'shop' );
254                 $shop->ownRelation = R::dispense( 'relation', 13 );
255                 foreach ( $shop->ownRelation as $relation ) {
256                         $relation->shop     = $shop;
257                         $relation->customer = R::dispense( 'customer' );
258                 }
259                 R::store( $shop );
260                 $shop = $shop->fresh();
261                 asrt( $shop->via( 'relation' )->countShared( 'customer' ), 13 );
262         }
263
264         /**
265          * Test counting and aliasing.
266          *
267          * @return void
268          */
269         public function testCountingAndAliasing()
270         {
271                 $book = R::dispense( 'book' );
272                 $book->ownPage = R::dispense( 'page', 10 );
273                 $book2 = R::dispense( 'book' );
274                 $book2->ownPage = R::dispense( 'page', 4 );
275                 list( $Bill, $James, $Andy ) = R::dispense( 'person', 3 );
276                 $book->author   = $Bill;
277                 $book->coAuthor = $James;
278                 $book2->author   = $Bill;
279                 $book2->coAuthor = $Andy;
280                 $book->price  = 25;
281                 $book2->price = 50;
282                 $notes = R::dispense( 'note', 10 );
283                 $book->sharedNote  = array( $notes[0], $notes[1], $notes[2] );
284                 $book2->sharedNote = array( $notes[3], $notes[4], $notes[1], $notes[0] );
285                 $books = R::dispense( 'book', 5 );
286                 $books[2]->title = 'boe';
287                 $book->sharedBook = array( $books[0], $books[1] );
288                 $book2->sharedBook = array( $books[0], $books[2], $books[4] );
289                 R::storeAll( array( $book, $book2 ) );
290                 asrt( $book->countOwn( 'page' ), 10 );
291                 asrt( $book->withCondition( ' id < 5 ' )->countOwn( 'page' ), 4 );
292                 asrt( $Bill->alias( 'author' )->countOwn( 'book' ), 2 );
293                 asrt( $Andy->alias( 'coAuthor' )->countOwn( 'book' ), 1 );
294                 asrt( $James->alias( 'coAuthor' )->countOwn( 'book' ), 1 );
295                 asrt( $Bill->alias( 'author' )->countOwn( 'book' ), 2 );
296                 asrt( $book->countShared( 'note' ), 3 );
297                 asrt( $book2->countShared( 'note' ), 4 );
298                 asrt( $book2->countShared( 'book' ), 3 );
299                 $book2 = $book2->fresh();
300                 asrt( $book2->withCondition( ' title = ? ', array( 'boe' ) )->countShared( 'book' ), 1 );
301         }
302
303         /**
304          * Test via.
305          *
306          * @return void
307          */
308         public function testVia()
309         {
310                 testpack( 'Test via()' );
311                 $d = R::dispense( 'doctor' )->setAttr( 'name', 'd1' );
312                 $p = R::dispense( 'patient' )->setAttr( 'name', 'p1' );
313                 $d->via( 'consult' )->sharedPatient[] = $p;
314                 R::store( $d );
315                 $d = R::load( 'doctor', $d->id );
316                 asrt( count( $d->sharedPatient ), 1 );
317                 asrt( in_array( 'consult', R::getWriter()->getTables() ), TRUE );
318         }
319
320         /**
321          * Issue #348 via() should reload shared list
322          *
323          * @return void
324          */
325         public function testIssue348()
326         {
327                 $product = R::dispense( 'product' );
328                 $product->name = 'test';
329                 $color = R::dispense( 'color' );
330                 $color->name = 'cname';
331                 $color->code = 'ccode';
332                 R::store( $product );
333                 R::store( $color );
334                 $product->link( 'productColor', array(
335                          'stock' => 1,
336                          'position' => 0
337                 ) )->color = $color;
338                 R::store( $product );
339                 asrt( count( $product->sharedColor ), 0 );
340                 asrt( count( $product->via( 'product_color' )->sharedColor ), 1 );
341                 asrt( count( $product->sharedColor ), 1 );
342                 R::renameAssociation( 'color_product', NULL );
343         }
344
345         /**
346          * Test creation of link table.
347          *
348          * @return void
349          */
350         public function testCreationOfLinkTable()
351         {
352                 asrt( in_array( 'consult', R::getWriter()->getTables() ), FALSE );
353                 $d = R::dispense( 'doctor' )->setAttr( 'name', 'd1' );
354                 $p = R::dispense( 'patient' )->setAttr( 'name', 'p1' );
355                 $d->sharedPatient[] = $p;
356                 R::store($d);
357                 asrt( in_array( 'consult', R::getWriter()->getTables() ), TRUE );
358         }
359
360         /**
361          * Fast track link block code should not affect self-referential N-M relations.
362          *
363          * @return void
364          */
365         public function testFastTrackRelations()
366         {
367                 testpack( 'Test fast-track linkBlock exceptions' );
368                 list( $donald, $mickey, $goofy, $pluto ) = R::dispense( 'friend', 4 );
369                 $donald->name = 'D';
370                 $mickey->name = 'M';
371                 $goofy->name  = 'G';
372                 $pluto->name  = 'P';
373                 $donald->sharedFriend = array( $mickey, $goofy );
374                 $mickey->sharedFriend = array( $pluto, $goofy );
375                 $mickey->sharedBook   = array( R::dispense( 'book' ) );
376                 R::storeAll( array( $mickey, $donald, $goofy, $pluto ) );
377                 $donald = R::load( 'friend', $donald->id );
378                 $mickey = R::load( 'friend', $mickey->id );
379                 $goofy  = R::load( 'friend', $goofy->id );
380                 $pluto  = R::load( 'friend', $pluto->id );
381                 $names = implode( ',', R::gatherLabels( $donald->sharedFriend ) );
382                 asrt( $names, 'G,M' );
383                 $names = implode( ',', R::gatherLabels( $goofy->sharedFriend ) );
384                 asrt( $names, 'D,M' );
385                 $names = implode( ',', R::gatherLabels( $mickey->sharedFriend ) );
386                 asrt( $names, 'D,G,P' );
387                 $names = implode( ',', R::gatherLabels( $pluto->sharedFriend ) );
388                 asrt( $names, 'M' );
389                 // Now in combination with with() conditions...
390                 $donald = R::load( 'friend', $donald->id );
391                 $names = implode( ',', R::gatherLabels( $donald->withCondition( ' name = ? ', array( 'M' ) )->sharedFriend ) );
392                 asrt( $names, 'M' );
393                 // Now in combination with with() conditions...
394                 $donald = R::load( 'friend', $donald->id );
395                 $names = implode( ',', R::gatherLabels( $donald->with( ' ORDER BY name ' )->sharedFriend ) );
396                 asrt( $names, 'G,M' );
397                 // Now counting
398                 $goofy = R::load( 'friend', $goofy->id );
399                 asrt( (int) $goofy->countShared( 'friend' ), 2 );
400                 asrt( (int) $donald->countShared( 'friend' ), 2 );
401                 asrt( (int) $mickey->countShared( 'friend' ), 3 );
402                 asrt( (int) $pluto->countShared( 'friend' ), 1 );
403         }
404
405         /**
406          * Test list beautifications.
407          *
408          * @return void
409          */
410         public function testListBeautifications()
411         {
412                 testpack( 'Test list beautifications' );
413                 $book = R::dispense( 'book' );
414                 $page = R::dispense( 'page' )->setAttr( 'name', 'a' );
415                 $book->sharedPage[] = $page;
416                 $id = R::store( $book );
417                 $book = R::load( 'book', $id );
418                 $p = reset( $book->ownBookPage );
419                 asrt( $p->page->name, 'a' );
420                 $bean = R::dispense( 'bean' );
421                 $bean->sharedAclRole[] = R::dispense( 'role' )->setAttr( 'name', 'x' );
422                 R::store( $bean );
423                 asrt( R::count( 'role' ), 1 );
424                 $aclrole = R::getRedBean()->dispense( 'acl_role' );
425                 $aclrole->name = 'role';
426                 $bean = R::dispense( 'bean' );
427                 $bean->sharedAclRole[] = $aclrole;
428                 R::store( $bean );
429                 asrt( count( $bean->sharedAclRole ), 1 );
430         }
431
432         /**
433          * Test list add and delete.
434          *
435          * @return void
436          */
437         public function testListAddDelete()
438         {
439                 testpack( 'Test list add/delete scenarios.' );
440                 R::nuke();
441                 $b = R::dispense( 'book' );
442                 $p = R::dispense( 'page' );
443                 $b->title = 'a';
444                 $p->name  = 'b';
445                 $b->xownPage[] = $p;
446                 R::store( $b );
447                 $b->xownPage = array();
448                 R::store( $b );
449                 asrt( R::count( 'page' ), 0 );
450                 $p = R::dispense( 'page' );
451                 $z = R::dispense( 'paper' );
452                 $z->xownPage[] = $p;
453                 R::store( $z );
454                 asrt( R::count( 'page' ), 1 );
455                 $z->xownPage = array();
456                 R::store( $z );
457                 asrt( R::count( 'page' ), 0 );
458                 $i = R::dispense( 'magazine' );
459                 $i->ownPage[] = R::dispense( 'page' );
460                 R::store( $i );
461                 asrt( R::count( 'page' ), 1 );
462                 $i->ownPage = array();
463                 R::store( $i );
464                 asrt( R::count( 'page' ), 1 );
465         }
466
467         /**
468          * Test basic and complex common usage scenarios for
469          * relations and associations.
470          *
471          * @return void
472          */
473         public function testScenarios()
474         {
475                 list( $q1, $q2 ) = R::dispense( 'quote', 2 );
476                 list( $pic1, $pic2 ) = R::dispense( 'picture', 2 );
477                 list( $book, $book2, $book3 ) = R::dispense( 'book', 4 );
478                 list( $topic1, $topic2, $topic3, $topic4, $topic5 ) = R::dispense( 'topic', 5 );
479                 list( $page1, $page2, $page3, $page4, $page5, $page6, $page7 ) = R::dispense( 'page', 7 );
480                 $q1->text = 'lorem';
481                 $q2->text = 'ipsum';
482                 $book->title  = 'abc';
483                 $book2->title = 'def';
484                 $book3->title = 'ghi';
485                 $page1->title = 'pagina1';
486                 $page2->title = 'pagina2';
487                 $page3->title = 'pagina3';
488                 $page4->title = 'pagina4';
489                 $page5->title = 'pagina5';
490                 $page6->title = 'cover1';
491                 $page7->title = 'cover2';
492                 $topic1->name = 'holiday';
493                 $topic2->name = 'cooking';
494                 $topic3->name = 'gardening';
495                 $topic4->name = 'computing';
496                 $topic5->name = 'christmas';
497                 // Add one page to the book
498                 $book->ownPage[] = $page1;
499                 $id = R::store( $book );
500                 asrt( count( $book->ownPage ), 1 );
501                 asrt( reset( $book->ownPage )->getMeta( 'type' ), 'page' );
502                 $book = R::load( 'book', $id );
503                 asrt( count( $book->ownPage ), 1 );
504                 asrt( reset( $book->ownPage )->getMeta( 'type' ), 'page' );
505                 // Performing an own addition
506                 $book->ownPage[] = $page2;
507                 $id   = R::store( $book );
508                 $book = R::load( 'book', $id );
509                 asrt( count( $book->ownPage ), 2 );
510                 // Performing a deletion
511                 $book = R::load( 'book', $id );
512                 unset( $book->ownPage[1] );
513                 $id = R::store( $book );
514                 $book = R::load( 'book', $id );
515                 asrt( count( $book->ownPage ), 1 );
516                 asrt( reset( $book->ownPage )->getMeta( 'type' ), 'page' );
517                 asrt( R::count( 'page' ), 2 ); //still exists
518                 asrt( reset( $book->ownPage )->id, '2' );
519                 // Doing a change in one of the owned items
520                 $book->ownPage[2]->title = 'page II';
521                 $id   = R::store( $book );
522                 $book = R::load( 'book', $id );
523                 asrt( reset( $book->ownPage )->title, 'page II' );
524                 // Change by reference now... don't copy!
525                 $refToPage2 = $book->ownPage[2];
526                 $refToPage2->title = 'page II b';
527                 $id   = R::store( $book );
528                 $book = R::load( 'book', $id );
529                 asrt( reset( $book->ownPage )->title, 'page II b' );
530                 // Doing all actions combined
531                 $book->ownPage[] = $page3;
532                 R::store( $book );
533                 $book = R::load( 'book', $id );
534                 unset( $book->ownPage[2] );
535                 // And test custom key
536                 $book->ownPage['customkey'] = $page4;
537                 $book->ownPage[3]->title    = "THIRD";
538                 R::store( $book );
539                 $book = R::load( 'book', $id );
540                 asrt( count( $book->ownPage ), 2 );
541                 $p4 = $book->ownPage[4];
542                 $p3 = $book->ownPage[3];
543                 asrt( $p4->title, 'pagina4' );
544                 asrt( $p3->title, 'THIRD' );
545                 // Test replacing an element
546                 $book = R::load( 'book', $id );
547                 $book->ownPage[4] = $page5;
548                 R::store( $book );
549                 $book = R::load( 'book', $id );
550                 asrt( count( $book->ownPage ), 2 );
551                 $p5 = $book->ownPage[5];
552                 asrt( $p5->title, 'pagina5' );
553                 // Other way around - single bean
554                 asrt( $p5->book->title, 'abc' );
555                 asrt( R::load( 'page', 5 )->book->title, 'abc' );
556                 asrt( R::load( 'page', 3 )->book->title, 'abc' );
557                 // Add the other way around - single bean
558                 $page1->id   = 0;
559                 $page1->book = $book2;
560                 $page1 = R::load( 'page', R::store( $page1 ) );
561                 asrt( $page1->book->title, 'def' );
562                 $b2 = R::load( 'book', $id );
563                 asrt( count( $b2->ownPage ), 2 );
564                 // Remove the other way around - single bean
565                 unset( $page1->book );
566                 R::store( $page1 );
567                 $b2 = R::load( 'book', $book2->id );
568                 asrt( count( $b2->ownPage ), 1 ); //does not work
569                 $page1->book = NULL;
570                 R::store( $page1 );
571                 $b2 = R::load( 'book', $book2->id );
572                 asrt( count( $b2->ownPage ), 0 ); //works
573                 // Re-add the page
574                 $b2->ownPage[] = $page1;
575                 R::store( $b2 );
576                 $b2 = R::load( 'book', $book2->id );
577                 asrt( count( $b2->ownPage ), 1 );
578                 // Different, less elegant way to remove
579                 $page1 = reset( $b2->ownPage );
580                 $page1->book_id = NULL;
581                 R::store( $page1 );
582                 $b2 = R::load( 'book', $book2->id );
583                 asrt( count( $b2->ownPage ), 0 );
584                 // Re-add the page
585                 $b2->ownPage[] = $page1;
586                 R::store( $b2 );
587                 $b2 = R::load( 'book', $book2->id );
588                 asrt( count( $b2->ownPage ), 1 );
589                 // Another less elegant way to remove
590                 $page1->book = NULL;
591                 R::store( $page1 );
592                 $cols = R::getColumns( 'page' );
593                 asrt( isset( $cols['book'] ), FALSE );
594                 $b2 = R::load( 'book', $book2->id );
595                 asrt( count( $b2->ownPage ), 0 );
596                 // Re-add the page
597                 $b2->ownPage[] = $page1;
598                 R::store( $b2 );
599                 $b2 = R::load( 'book', $book2->id );
600                 asrt( count( $b2->ownPage ), 1 );
601                 // Another less elegant... just plain ugly... way to remove
602                 $page1->book = FALSE;
603                 R::store( $page1 );
604                 $cols = R::getColumns( 'page' );
605                 asrt( isset( $cols['book'] ), FALSE );
606                 $b2 = R::load( 'book', $book2->id );
607                 asrt( count( $b2->ownPage ), 0 );
608                 // Re-add the page
609                 $b2->ownPage[] = $page1;
610                 R::store( $b2 );
611                 $b2 = R::load( 'book', $book2->id );
612                 asrt( count( $b2->ownPage ), 1 );
613                 // You are not allowed to re-use the field for something else
614                 foreach (
615                         array(
616                                 1, -2.1, array(),
617                                 TRUE, 'NULL', new \stdClass,
618                                 'just a string', array( 'a' => 1 ), 0
619                         ) as $value
620                 ) {
621                         try {
622                                 $page1->book = $value;
623                                 fail();
624                         } catch ( RedException $e ) {
625                                 pass();
626                         }
627                 }
628                 // Test fk, not allowed to set to 0
629                 $page1 = reset( $b2->ownPage );
630                 $page1->book_id = 0;
631                 // Even uglier way, but still needs to work
632                 $page1 = reset( $b2->ownPage );
633                 $page1->book_id = NULL;
634                 R::store( $b2 );
635                 $b2 = R::load( 'book', $book2->id );
636                 asrt( count( $b2->ownPage ), 0 );
637                 // Test shared items
638                 $book = R::load( 'book', $id );
639                 $book->sharedTopic[] = $topic1;
640                 $id = R::store( $book );
641                 // Add an item
642                 asrt( count( $book->sharedTopic ), 1 );
643                 asrt( reset( $book->sharedTopic )->name, 'holiday' );
644                 $book = R::load( 'book', $id );
645                 asrt( count( $book->sharedTopic ), 1 );
646                 asrt( reset( $book->sharedTopic )->name, 'holiday' );
647                 // Add another item
648                 $book->sharedTopic[] = $topic2;
649                 $id   = R::store( $book );
650                 $tidx = R::store( R::dispense( 'topic' ) );
651                 $book = R::load( 'book', $id );
652                 asrt( count( $book->sharedTopic ), 2 );
653                 $t1 = $book->sharedTopic[1];
654                 $t2 = $book->sharedTopic[2];
655                 asrt( $t1->name, 'holiday' );
656                 asrt( $t2->name, 'cooking' );
657                 // Remove an item
658                 unset( $book->sharedTopic[2] );
659                 asrt( count( $book->sharedTopic ), 1 );
660                 $id = R::store( $book );
661                 $book = R::load( 'book', $id );
662                 asrt( count( $book->sharedTopic ), 1 );
663                 asrt( reset( $book->sharedTopic )->name, 'holiday' );
664                 // Add and change
665                 $book->sharedTopic[]        = $topic3;
666                 $book->sharedTopic[1]->name = 'tropics';
667                 $id = R::store( $book );
668                 $book = R::load( 'book', $id );
669                 asrt( count( $book->sharedTopic ), 2 );
670                 asrt( $book->sharedTopic[1]->name, 'tropics' );
671                 testids( $book->sharedTopic );
672                 R::trash( R::load( 'topic', $tidx ) );
673                 $id = R::store( $book );
674                 $book = R::load( 'book', $id );
675                 // Delete without save
676                 unset( $book->sharedTopic[1] );
677                 $book = R::load( 'book', $id );
678                 asrt( count( $book->sharedTopic ), 2 );
679                 $book = R::load( 'book', $id );
680                 // Delete without init
681                 asrt( ( R::count( 'topic' ) ), 3 );
682                 unset( $book->sharedTopic[1] );
683                 $id = R::store( $book );
684                 asrt( ( R::count( 'topic' ) ), 3 );
685                 asrt( count( $book->sharedTopic ), 1 );
686                 asrt( count( $book2->sharedTopic ), 0 );
687                 // Add same topic to other book
688                 $book2->sharedTopic[] = $topic3;
689                 asrt( count( $book2->sharedTopic ), 1 );
690                 $id2 = R::store( $book2 );
691                 asrt( count( $book2->sharedTopic ), 1 );
692                 $book2 = R::load( 'book', $id2 );
693                 asrt( count( $book2->sharedTopic ), 1 );
694                 // Get books for topic
695                 asrt( $topic3->countShared('book'), 2 );
696                 $t3 = R::load( 'topic', $topic3->id );
697                 asrt( count( $t3->sharedBook ), 2 );
698                 // Nuke an own-array, replace entire array at once without getting first
699                 $page2->id    = 0;
700                 $page2->title = 'yet another page 2';
701                 $page4->id    = 0;
702                 $page4->title = 'yet another page 4';
703                 $book = R::load( 'book', $id );
704                 $book->ownPage = array( $page2, $page4 );
705                 R::store( $book );
706                 $book = R::load( 'book', $id );
707                 asrt( count( $book->ownPage ), 2 );
708                 asrt( reset( $book->ownPage )->title, 'yet another page 2' );
709                 asrt( end( $book->ownPage )->title, 'yet another page 4' );
710                 testids( $book->ownPage );
711                 // Test with alias format
712                 $book3->cover = $page6;
713                 $idb3 = R::store( $book3 );
714                 $book3 = R::load( 'book', $idb3 );
715                 $justACover = $book3->fetchAs( 'page' )->cover;
716                 asrt( ( $book3->cover instanceof OODBBean ), TRUE );
717                 asrt( $justACover->title, 'cover1' );
718                 // No page property
719                 asrt( isset( $book3->page ), FALSE );
720                 // Test doubling and other side effects ... should not occur..
721                 $book3->sharedTopic = array( $topic1, $topic2 );
722                 $book3 = R::load( 'book', R::store( $book3 ) );
723                 $book3->sharedTopic = array();
724                 $book3 = R::load( 'book', R::store( $book3 ) );
725                 asrt( count( $book3->sharedTopic ), 0 );
726                 $book3->sharedTopic[] = $topic1;
727                 $book3 = R::load( 'book', R::store( $book3 ) );
728                 // Added only one, not more?
729                 asrt( count( $book3->sharedTopic ), 1 );
730                 asrt( intval( R::getCell( "select count(*) from book_topic where book_id = $idb3" ) ), 1 );
731                 // Add the same
732                 $book3->sharedTopic[] = $topic1;
733                 $book3 = R::load( 'book', R::store( $book3 ) );
734                 asrt( count( $book3->sharedTopic ), 1 );
735                 asrt( intval( R::getCell( "select count(*) from book_topic where book_id = $idb3" ) ), 1 );
736                 $book3->sharedTopic['differentkey'] = $topic1;
737                 $book3 = R::load( 'book', R::store( $book3 ) );
738                 asrt( count( $book3->sharedTopic ), 1 );
739                 asrt( intval( R::getCell( "select count(*) from book_topic where book_id = $idb3" ) ), 1 );
740                 // Ugly assign, auto array generation
741                 $book3->ownPage[] = $page1;
742                 $book3 = R::load( 'book', R::store( $book3 ) );
743                 asrt( count( $book3->ownPage ), 1 );
744                 asrt( intval( R::getCell( "select count(*) from page where book_id = $idb3 " ) ), 1 );
745                 $book3 = R::load( 'book', $idb3 );
746                 $book3->ownPage = array();
747                 // No change until saved
748                 asrt( intval( R::getCell( "select count(*) from page where book_id = $idb3 " ) ), 1 );
749                 $book3 = R::load( 'book', R::store( $book3 ) );
750                 asrt( intval( R::getCell( "select count(*) from page where book_id = $idb3 " ) ), 0 );
751                 asrt( count( $book3->ownPage ), 0 );
752                 $book3 = R::load( 'book', $idb3 );
753                 /**
754                  * Why do I need to do this ---> why does trash() not set id -> 0?
755                  * Because you unset() so trash is done on origin not bean
756                  */
757                 $page1->id = 0;
758                 $page2->id = 0;
759                 $page3->id = 0;
760                 $book3->ownPage[] = $page1;
761                 $book3->ownPage[] = $page2;
762                 $book3->ownPage[] = $page3;
763                 $book3 = R::load( 'book', R::store( $book3 ) );
764                 asrt( intval( R::getCell( "select count(*) from page where book_id = $idb3 " ) ), 3 );
765                 asrt( count( $book3->ownPage ), 3 );
766                 unset( $book3->ownPage[$page2->id] );
767                 $book3->ownPage[]                  = $page3;
768                 $book3->ownPage['try_to_trick_ya'] = $page3;
769                 $book3 = R::load( 'book', R::store( $book3 ) );
770                 asrt( intval( R::getCell( "select count(*) from page where book_id = $idb3 " ) ), 2 );
771                 asrt( count( $book3->ownPage ), 2 );
772                 // Delete and re-add
773                 $book3 = R::load( 'book', $idb3 );
774                 unset( $book3->ownPage[10] );
775                 $book3->ownPage[] = $page1;
776                 $book3 = R::load( 'book', R::store( $book3 ) );
777                 asrt( count( $book3->ownPage ), 2 );
778                 $book3 = R::load( 'book', $idb3 );
779                 unset( $book3->sharedTopic[1] );
780                 $book3->sharedTopic[] = $topic1;
781                 $book3 = R::load( 'book', R::store( $book3 ) );
782                 asrt( count( $book3->sharedTopic ), 1 );
783                 // Test performance
784                 $logger = R::debug( true, 1 );
785                 $book = R::load( 'book', 1 );
786                 $book->sharedTopic = array();
787                 R::store( $book );
788                 // No more than 1 update
789                 asrt( count( $logger->grep( 'UPDATE' ) ), 1 );
790                 $book = R::load( 'book', 1 );
791                 $logger->clear();
792                 print_r( $book->sharedTopic, 1 );
793                 // No more than 1 select
794                 asrt( count( $logger->grep( 'SELECT' ) ), 1 );
795                 $logger->clear();
796                 $book->sharedTopic[] = $topic1;
797                 $book->sharedTopic[] = $topic2;
798                 asrt( count( $logger->grep( 'SELECT' ) ), 0 );
799                 R::store( $book );
800                 $book->sharedTopic[] = $topic3;
801                 // Now do NOT clear all and then add one, just add the one
802                 $logger->clear();
803                 R::store( $book );
804                 $book = R::load( 'book', 1 );
805                 asrt( count( $book->sharedTopic ), 3 );
806                 // No deletes
807                 asrt( count( $logger->grep( "DELETE FROM" ) ), 0 );
808                 $book->sharedTopic['a'] = $topic3;
809                 unset( $book->sharedTopic['a'] );
810                 R::store( $book );
811                 $book = R::load( 'book', 1 );
812                 asrt( count( $book->sharedTopic ), 3 );
813                 // No deletes
814                 asrt( count( $logger->grep( "DELETE FROM" ) ), 0 );
815                 $book->ownPage = array();
816                 R::store( $book );
817                 asrt( count( $book->ownPage ), 0 );
818                 $book->ownPage[]    = $page1;
819                 $book->ownPage['a'] = $page2;
820                 asrt( count( $book->ownPage ), 2 );
821                 R::store( $book );
822                 unset( $book->ownPage['a'] );
823                 asrt( count( $book->ownPage ), 2 );
824                 unset( $book->ownPage[11] );
825                 R::store( $book );
826                 $book = R::load( 'book', 1 );
827                 asrt( count( $book->ownPage ), 1 );
828                 $aPage = $book->ownPage[10];
829                 unset( $book->ownPage[10] );
830                 $aPage->title .= ' changed ';
831                 $book->ownPage['anotherPage'] = $aPage;
832                 $logger->clear();
833                 R::store( $book );
834                 $book = R::load( 'book', 1 );
835                 asrt( count( $book->ownPage ), 1 );
836                 $ap = reset( $book->ownPage );
837                 asrt( $ap->title, "pagina1 changed " );
838                 // Fix udiff instead of diff
839                 $book3->ownPage = array( $page3, $page1 );
840                 $i = R::store( $book3 );
841                 $book3 = R::load( 'book', $i );
842                 asrt( intval( R::getCell( "select count(*) from page where book_id = $idb3 " ) ), 2 );
843                 asrt( count( $book3->ownPage ), 2 );
844                 $pic1->name = 'aaa';
845                 $pic2->name = 'bbb';
846                 R::store( $pic1 );
847                 R::store( $q1 );
848                 $book3->ownPicture[] = $pic1;
849                 $book3->ownQuote[]   = $q1;
850                 $book3 = R::load( 'book', R::store( $book3 ) );
851                 // two own-arrays -->forgot array_merge
852                 asrt( count( $book3->ownPicture ), 1 );
853                 asrt( count( $book3->ownQuote ), 1 );
854                 asrt( count( $book3->ownPage ), 2 );
855                 $book3 = R::load( 'book', R::store( $book3 ) );
856                 unset( $book3->ownPicture[1] );
857                 $book3 = R::load( 'book', R::store( $book3 ) );
858                 asrt( count( $book3->ownPicture ), 0 );
859                 asrt( count( $book3->ownQuote ), 1 );
860                 asrt( count( $book3->ownPage ), 2 );
861                 $book3 = R::load( 'book', R::store( $book3 ) );
862                 $NOTE = 0;
863                 $quotes = R::dispense( 'quote', 10 );
864                 foreach ( $quotes as &$justSomeQuote ) {
865                         $justSomeQuote->note = 'note' . ( ++$NOTE );
866                 }
867                 $pictures = R::dispense( 'picture', 10 );
868                 foreach ( $pictures as &$justSomePic ) {
869                         $justSomePic->note = 'note' . ( ++$NOTE );
870                 }
871                 $topics = R::dispense( 'topic', 10 );
872                 foreach ( $topics as &$justSomeTopic ) {
873                         $justSomeTopic->note = 'note' . ( ++$NOTE );
874                 }
875                 for ( $j = 0; $j < 10; $j++ ) {
876                         // Do several mutations
877                         for ( $x = 0; $x < rand( 1, 20 ); $x++ ) {
878                                 modgr( $book3, $quotes, $pictures, $topics );
879                         }
880                         $qbefore = count( $book3->ownQuote );
881                         $pbefore = count( $book3->ownPicture );
882                         $tbefore = count( $book3->sharedTopic );
883                         $qjson = json_encode( $book->ownQuote );
884                         $pjson = json_encode( $book->ownPicture );
885                         $tjson = json_encode( $book->sharedTopic );
886                         $book3 = R::load( 'book', R::store( $book3 ) );
887                         asrt( count( $book3->ownQuote ), $qbefore );
888                         asrt( count( $book3->ownPicture ), $pbefore );
889                         asrt( count( $book3->sharedTopic ), $tbefore );
890                         asrt( json_encode( $book->ownQuote ), $qjson );
891                         asrt( json_encode( $book->ownPicture ), $pjson );
892                         asrt( json_encode( $book->sharedTopic ), $tjson );
893                         testids( $book->ownQuote );
894                         testids( $book->ownPicture );
895                         testids( $book->sharedTopic );
896                 }
897         }
898
899         /**
900          * Test parent bean relations.
901          *
902          * @return void
903          */
904         public function testParentBean()
905         {
906                 $village = R::dispense( 'village' );
907                 $village->name = 'village';
908                 $home = R::dispense( 'building' );
909                 $home->village = $village;
910                 $id = R::store( $home );
911                 $home = R::load( 'building', $id );
912                 asrt( $home->village->name, 'village' );
913                 asrt( R::count( 'village' ), 1 );
914                 asrt( R::count( 'building' ), 1 );
915                 R::trash( $home );
916                 pass();
917                 asrt( R::count( 'village' ), 1 );
918                 asrt( R::count( 'building' ), 0 );
919         }
920
921         /**
922          * test N-M relations through intermediate beans
923          *
924          * @return void
925          */
926         public function testNMRelationsIntermediate()
927         {
928                 list( $mrA, $mrB, $mrC ) = R::dispense( 'person', 3 );
929                 list( $projA, $projB, $projC ) = R::dispense( 'project', 3 );
930                 $projA->title = 'A';
931                 $projB->title = 'B';
932                 $projC->title = 'C';
933                 $participant = R::dispense( 'participant' );
934                 $projA->link( 'participant', array( 'role' => 'manager' ) )->person                  = $mrA;
935                 $projA->link( $participant->setAttr( 'role', 'developer' ) )->person                 = $mrB;
936                 $projB->link( R::dispense( 'participant' )->setAttr( 'role', 'developer' ) )->person = $mrB;
937                 $projB->link( 'participant', '{"role":"helpdesk"}' )->person                         = $mrC;
938                 $projC->link( 'participant', '{"role":"sales"}' )->person                            = $mrC;
939                 R::storeAll( array( $projA, $projB, $projC ) );
940                 $a = R::findOne( 'project', ' title = ? ', array( 'A' ) );
941                 $b = R::findOne( 'project', ' title = ? ', array( 'B' ) );
942                 $c = R::findOne( 'project', ' title = ? ', array( 'C' ) );
943                 asrt( count( $a->ownParticipant ), 2 );
944                 asrt( count( $b->ownParticipant ), 2 );
945                 asrt( count( $c->ownParticipant ), 1 );
946                 $managers = $developers = 0;
947                 foreach ( $a->ownParticipant as $p ) {
948                         if ( $p->role === 'manager' ) {
949                                 $managers++;
950                         }
951                         if ( $p->role === 'developer' ) {
952                                 $developers++;
953                         }
954                 }
955                 $p = reset( $a->ownParticipant );
956                 asrt( $p->person->getMeta( 'type' ), 'person' );
957                 asrt( ( $p->person->id > 0 ), TRUE );
958                 asrt( $managers, 1 );
959                 asrt( $developers, 1 );
960                 asrt( (int) R::count( 'participant' ), 5 );
961                 asrt( (int) R::count( 'person' ), 3 );
962         }
963
964         /**
965          * test emulation of sharedList through intermediate beans
966          *
967          * @return void
968          */
969         public function testSharedListIntermediate()
970         {
971                 list( $v1, $v2, $v3 ) = R::dispense( 'village', 3 );
972                 list( $a1, $a2, $a3 ) = R::dispense( 'army', 3 );
973                 $a1->name = 'one';
974                 $a2->name = 'two';
975                 $a3->name = 'three';
976                 $v1->name = 'Ville 1';
977                 $v2->name = 'Ville 2';
978                 $v3->name = 'Ville 3';
979                 $v1->link( 'armyVillage' )->army    = $a3;
980                 $v2->link( 'army_village' )->army    = $a2;
981                 $v3->link( 'armyVillage' )->army    = $a1;
982                 $a2->link( 'army_village' )->village = $v1;
983                 $id1 = R::store( $v1 );
984                 $id2 = R::store( $v2 );
985                 $id3 = R::store( $v3 );
986                 $village1 = R::load( 'village', $id1 );
987                 $village2 = R::load( 'village', $id2 );
988                 $village3 = R::load( 'village', $id3 );
989                 asrt( count( $village1->sharedArmy ), 2 );
990                 asrt( count( $village2->sharedArmy ), 1 );
991                 asrt( count( $village3->sharedArmy ), 1 );
992         }
993
994         /**
995          * test emulation via association renaming
996          *
997          * @return void
998          */
999         public function testAssociationRenaming()
1000         {
1001                 list( $p1, $p2, $p3 ) = R::dispense( 'painting', 3 );
1002                 list( $m1, $m2, $m3 ) = R::dispense( 'museum', 3 );
1003                 $p1->name = 'painting1';
1004                 $p2->name = 'painting2';
1005                 $p3->name = 'painting3';
1006                 $m1->thename = 'a';
1007                 $m2->thename = 'b';
1008                 $m3->thename = 'c';
1009                 R::renameAssociation( 'museum_painting', 'exhibited' );
1010                 // Also test array syntax
1011                 R::renameAssociation( array( 'museum_museum' => 'center' ) );
1012                 $m1->link( 'center', array( 'name' => 'History Center' ) )->museum2            = $m2;
1013                 $m1->link( 'exhibited', '{"from":"2014-02-01","til":"2014-07-02"}' )->painting = $p3;
1014                 $m2->link( 'exhibited', '{"from":"2014-07-03","til":"2014-10-02"}' )->painting = $p3;
1015                 $m3->link( 'exhibited', '{"from":"2014-02-01","til":"2014-07-02"}' )->painting = $p1;
1016                 $m2->link( 'exhibited', '{"from":"2014-02-01","til":"2014-07-02"}' )->painting = $p2;
1017                 R::storeAll( array( $m1, $m2, $m3 ) );
1018                 list( $m1, $m2, $m3 ) = array_values( R::findAll( 'museum', ' ORDER BY thename ASC' ) );
1019                 asrt( count( $m1->sharedMuseum ), 1 );
1020                 asrt( count( $m1->sharedPainting ), 1 );
1021                 asrt( count( $m2->sharedPainting ), 2 );
1022                 asrt( count( $m3->sharedPainting ), 1 );
1023                 $p3 = reset( $m1->sharedPainting );
1024                 asrt( count( $p3->ownExhibited ), 2 );
1025                 asrt( count( $m2->ownExhibited ), 2 );
1026                 R::storeAll( array( $m1, $m2, $m3 ) );
1027                 list( $m1, $m2, $m3 ) = array_values( R::findAll( 'museum', ' ORDER BY thename ASC' ) );
1028                 asrt( count( $m1->sharedPainting ), 1 );
1029                 asrt( count( $m2->sharedPainting ), 2 );
1030                 asrt( count( $m3->sharedPainting ), 1 );
1031                 $p3 = reset( $m1->sharedPainting );
1032                 asrt( count( $p3->ownExhibited ), 2 );
1033                 $paintings = $m2->sharedPainting;
1034                 foreach ( $paintings as $painting ) {
1035                         if ( $painting->name === 'painting2' ) {
1036                                 pass();
1037                                 $paintingX = $painting;
1038                         }
1039                 }
1040                 unset( $m2->sharedPainting[$paintingX->id] );
1041                 R::store( $m2 );
1042                 $m2 = R::load( 'museum', $m2->id );
1043                 asrt( count( $m2->sharedPainting ), 1 );
1044                 $left = reset( $m2->sharedPainting );
1045                 asrt( $left->name, 'painting3' );
1046                 asrt( count( $m2->ownExhibited ), 1 );
1047                 $exhibition = reset( $m2->ownExhibited );
1048                 asrt( $exhibition->from, '2014-07-03' );
1049                 asrt( $exhibition->til, '2014-10-02' );
1050         }
1051
1052         /**
1053          * Test don't try to store other things in shared list.
1054          *
1055          * @return void
1056          */
1057         public function testDontTryToStoreOtherThingsInSharedList() {
1058
1059                 $book = R::dispense( 'book' );
1060                 $book->sharedPage[] = 'nonsense';
1061                 try {
1062                         R::store( $book );
1063                         fail();
1064                 } catch( RedException $exception) {
1065                         pass();
1066                 }
1067                 $book->sharedPageList = R::dispense( 'page', 2 );
1068                 R::store( $book );
1069                 $book->sharedPageList;
1070                 R::trash( $book );
1071                 asrt( R::count('page'), 2 );
1072         }
1073
1074         /**
1075          * Test whether magic array interface functions like isset() and
1076          * unset work correctly with the x-own-list and the List-suffix.
1077          *
1078          * Array functions do not reveal x-own-lists and list-alias because
1079          * you dont want duplicate entries in foreach-loops.
1080          * Also offers a slight performance improvement for array access.
1081          *
1082          * @return void
1083          */
1084         public function testWhetherIssetWorksWithXList()
1085         {
1086                 R::nuke();
1087                 $book = R::dispense( 'book' );
1088                 $page = R::dispense( 'page' );
1089                 asrt( isset( $book->xownPageList ), FALSE );
1090                 asrt( isset( $book->ownPageList ), FALSE );
1091                 asrt( isset( $book->xownPage ), FALSE );
1092                 asrt( isset( $book->ownPage ), FALSE );
1093                 $book->xownPageList[] = $page;
1094                 asrt( isset( $book->xownPageList ), TRUE );
1095                 asrt( isset( $book->ownPageList ), TRUE );
1096                 asrt( isset( $book->xownPage ), TRUE );
1097                 asrt( isset( $book->ownPage ), TRUE );
1098                 //Test array access
1099                 asrt( isset( $book['xownPageList'] ), TRUE );
1100                 asrt( isset( $book['ownPageList'] ), TRUE );
1101                 asrt( isset( $book['xownPage'] ), TRUE );
1102                 asrt( isset( $book['ownPage'] ), TRUE );
1103                 R::store( $book );
1104                 $book = $book->fresh();
1105                 asrt( isset( $book->xownPageList ), FALSE );
1106                 asrt( isset( $book->ownPageList ), FALSE );
1107                 asrt( isset( $book->xownPage ), FALSE );
1108                 asrt( isset( $book->ownPage ), FALSE );
1109                 asrt( isset( $book['xownPageList'] ), FALSE );
1110                 asrt( isset( $book['ownPageList'] ), FALSE );
1111                 asrt( isset( $book['xownPage'] ), FALSE );
1112                 asrt( isset( $book['ownPage'] ), FALSE );
1113                 $book->xownPageList;
1114                 asrt( isset( $book->xownPageList ), TRUE );
1115                 asrt( isset( $book->ownPageList ), TRUE );
1116                 asrt( isset( $book->xownPage ), TRUE );
1117                 asrt( isset( $book->ownPage ), TRUE );
1118                 asrt( isset( $book['xownPageList'] ), TRUE );
1119                 asrt( isset( $book['ownPageList'] ), TRUE );
1120                 asrt( isset( $book['xownPage'] ), TRUE );
1121                 asrt( isset( $book['ownPage'] ), TRUE );
1122                 $book = $book->fresh();
1123                 asrt( isset( $book->xownPageList ), FALSE );
1124                 asrt( isset( $book->ownPageList ), FALSE );
1125                 asrt( isset( $book->xownPage ), FALSE );
1126                 asrt( isset( $book->ownPage ), FALSE );
1127                 asrt( isset( $book['xownPageList'] ), FALSE );
1128                 asrt( isset( $book['ownPageList'] ), FALSE );
1129                 asrt( isset( $book['xownPage'] ), FALSE );
1130                 asrt( isset( $book['ownPage'] ), FALSE );
1131                 $book->noLoad()->xownPageList;
1132                 asrt( isset( $book->xownPageList ), TRUE );
1133                 asrt( isset( $book->ownPageList ), TRUE );
1134                 //but empty
1135                 asrt( count( $book->ownPageList ), 0 );
1136                 asrt( count( $book->xownPageList ), 0 );
1137                 asrt( count( $book->ownPage ), 0 );
1138                 asrt( count( $book->xownPage ), 0 );
1139                 $book->xownPageList[] = $page;
1140                 asrt( isset( $book->xownPageList ), TRUE );
1141                 asrt( isset( $book->ownPageList ), TRUE );
1142                 asrt( isset( $book->xownPage ), TRUE );
1143                 asrt( isset( $book->ownPage ), TRUE );
1144                 asrt( isset( $book['xownPageList'] ), TRUE );
1145                 asrt( isset( $book['ownPageList'] ), TRUE );
1146                 asrt( isset( $book['xownPage'] ), TRUE );
1147                 asrt( isset( $book['ownPage'] ), TRUE );
1148                 unset( $book->xownPageList );
1149                 asrt( isset( $book->xownPageList ), FALSE );
1150                 asrt( isset( $book->ownPageList ), FALSE );
1151                 asrt( isset( $book->xownPage ), FALSE );
1152                 asrt( isset( $book->ownPage ), FALSE );
1153                 asrt( isset( $book['xownPageList'] ), FALSE );
1154                 asrt( isset( $book['ownPageList'] ), FALSE );
1155                 asrt( isset( $book['xownPage'] ), FALSE );
1156                 asrt( isset( $book['ownPage'] ), FALSE );
1157                 $book->xownPageList[] = $page;
1158                 asrt( isset( $book->xownPageList ), TRUE );
1159                 asrt( isset( $book->ownPageList ), TRUE );
1160                 asrt( isset( $book->xownPage ), TRUE );
1161                 asrt( isset( $book->ownPage ), TRUE );
1162                 asrt( isset( $book['xownPageList'] ), TRUE );
1163                 asrt( isset( $book['ownPageList'] ), TRUE );
1164                 asrt( isset( $book['xownPage'] ), TRUE );
1165                 asrt( isset( $book['ownPage'] ), TRUE );
1166                 unset( $book->xownPage );
1167                 asrt( isset( $book->xownPageList ), FALSE );
1168                 asrt( isset( $book->ownPageList ), FALSE );
1169                 asrt( isset( $book->xownPage ), FALSE );
1170                 asrt( isset( $book->ownPage ), FALSE );
1171                 asrt( isset( $book['xownPageList'] ), FALSE );
1172                 asrt( isset( $book['ownPageList'] ), FALSE );
1173                 asrt( isset( $book['xownPage'] ), FALSE );
1174                 asrt( isset( $book['ownPage'] ), FALSE );
1175                 $book = $book->fresh();
1176                 asrt( isset( $book->xownPageList ), FALSE );
1177                 asrt( isset( $book->ownPageList ), FALSE );
1178                 asrt( isset( $book->xownPage ), FALSE );
1179                 asrt( isset( $book->ownPage ), FALSE );
1180                 asrt( isset( $book['xownPageList'] ), FALSE );
1181                 asrt( isset( $book['ownPageList'] ), FALSE );
1182                 asrt( isset( $book['xownPage'] ), FALSE );
1183                 asrt( isset( $book['ownPage'] ), FALSE );
1184                 $book->ownPageList;
1185                 asrt( isset( $book->xownPageList ), TRUE );
1186                 asrt( isset( $book->ownPageList ), TRUE );
1187                 asrt( isset( $book->xownPage ), TRUE );
1188                 asrt( isset( $book->ownPage ), TRUE );
1189                 asrt( isset( $book['xownPageList'] ), TRUE );
1190                 asrt( isset( $book['ownPageList'] ), TRUE );
1191                 asrt( isset( $book['xownPage'] ), TRUE );
1192                 asrt( isset( $book['ownPage'] ), TRUE );
1193         }
1194
1195         /**
1196          * Test whether you can still set items starting with 'xown' or
1197          * 'own' not followed by an uppercase character.
1198          *
1199          * @return void
1200          */
1201         public function testConfusionWithXOwnList()
1202         {
1203                 $book = R::dispense( 'book' );
1204                 $book->xownitem = 1;
1205                 asrt( isset( $book->xownitem ), TRUE );
1206                 asrt( (int) $book->xownitem, 1 );
1207                 asrt( isset( $book->xownItem ), FALSE );
1208                 asrt( isset( $book->xownItemList ), FALSE );
1209                 $book->ownitem = 1;
1210                 asrt( isset( $book->ownitem ), TRUE );
1211                 asrt( (int) $book->ownitem, 1 );
1212                 asrt( isset( $book->ownItemList ), FALSE );
1213                 R::store( $book );
1214                 $book = $book->fresh();
1215                 asrt( isset( $book->xownitem ), TRUE );
1216                 asrt( (int) $book->xownitem, 1 );
1217                 asrt( isset( $book->xownItem ), FALSE );
1218                 asrt( isset( $book->xownItemList ), FALSE );
1219                 asrt( isset( $book->ownitem ), TRUE );
1220                 asrt( (int) $book->ownitem, 1 );
1221                 asrt( isset( $book->ownItemList ), FALSE );
1222         }
1223
1224         /**
1225          * Test whether we can determine the mode of a list.
1226          *
1227          * @return void
1228          */
1229         public function testModeCheckerOfLists()
1230         {
1231                 foreach( array( 'ownPage', 'xownPage', 'ownPageList', 'xownPageList' ) as $listName ) {
1232                         $book = R::dispense( 'book' );
1233                         asrt( $book->isListInExclusiveMode( $listName ), FALSE );
1234                         $book->ownPageList[] = R::dispense( 'page' );
1235                         asrt( $book->isListInExclusiveMode( $listName ), FALSE );
1236                         $book = R::dispense( 'book' );
1237                         asrt( $book->isListInExclusiveMode( $listName ), FALSE );
1238                         $book->xownPageList[] = R::dispense( 'page' );
1239                         asrt( $book->isListInExclusiveMode( $listName ), TRUE );
1240                         $book = R::dispense( 'book' );
1241                         asrt( $book->isListInExclusiveMode( $listName ), FALSE );
1242                         $book->ownPage[] = R::dispense( 'page' );
1243                         asrt( $book->isListInExclusiveMode( $listName ), FALSE );
1244                         $book = R::dispense( 'book' );
1245                         asrt( $book->isListInExclusiveMode( $listName ), FALSE );
1246                         $book->xownPage[] = R::dispense( 'page' );
1247                         asrt( $book->isListInExclusiveMode( $listName ), TRUE );
1248                 }
1249         }
1250 }