Yaffs site version 1.1
[yaffs-website] / vendor / gabordemooij / redbean / testing / RedUNIT / Base / Finding.php
1 <?php
2
3 namespace RedUNIT\Base;
4
5 use RedUNIT\Base as Base;
6 use RedBeanPHP\Facade as R;
7 use RedBeanPHP\AssociationManager as AssociationManager;
8 use RedBeanPHP\OODB as OODB;
9 use RedBeanPHP\OODBBean as OODBBean;
10 use RedBeanPHP\RedException as RedException;
11 use RedBeanPHP\RedException\SQL as SQL;
12
13 /**
14  * Finding
15  *
16  * Tests whether we can find beans by passing SQL queries to the
17  * Facade or the Finder Service Class.
18  *
19  * @file    RedUNIT/Base/Finding.php
20  * @desc    Tests finding beans.
21  * @author  Gabor de Mooij and the RedBeanPHP Community
22  * @license New BSD/GPLv2
23  *
24  * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
25  * This source file is subject to the New BSD/GPLv2 License that is bundled
26  * with this source code in the file license.txt.
27  */
28 class Finding extends Base {
29
30         /**
31          * Helper for testing findLike.
32          *
33          * @param array   $flowers beans
34          * @param boolean $noSort  sorting?
35          *
36          * @return string
37          */
38         private function getColors( $flowers, $noSort = FALSE )
39         {
40                 $colors = array();
41                 foreach( $flowers as $flower ) $colors[] = $flower->color;
42                 if ( !$noSort) sort( $colors );
43                 return implode( ',', $colors );
44         }
45
46         /**
47          * Inserts data for findMulti-tests.
48          *
49          * @return void
50          */
51         private function insertBookData()
52         {
53                 list( $books, $pages, $texts, $categories ) = R::dispenseAll( 'book*5,page*25,text*60,category*3' );
54                 $texts[0]->content = 'C is a beautiful language.';
55                 $texts[1]->content = 'But also a bit dangerous.';
56                 $texts[2]->content = 'You need to know what you are doing.';
57                 $texts[3]->content = 'Javascript is very flexible.';
58                 $texts[4]->content = 'It can be anything you want...';
59                 $texts[5]->content = 'But it can lead to chaos.';
60                 $texts[6]->content = 'CSS was meant for documents';
61                 $texts[7]->content = 'Now we use it for applications...';
62                 $texts[8]->content = 'PHP is an easy language to learn,';
63                 $texts[9]->content = 'Maybe a bit too easy...';
64                 $texts[10]->content = 'SQL is much more powerful than you think.';
65                 $pages[0]->ownTextList = array( $texts[0], $texts[1] );
66                 $pages[1]->ownTextList = array( $texts[2] );
67                 $pages[2]->ownTextList = array( $texts[3] );
68                 $pages[3]->ownTextList = array( $texts[4] );
69                 $pages[4]->ownTextList = array( $texts[5] );
70                 $pages[5]->ownTextList = array( $texts[6], $texts[7] );
71                 $pages[6]->ownTextList = array( $texts[8] );
72                 $pages[7]->ownTextList = array( $texts[9] );
73                 $pages[8]->ownTextList = array( $texts[10] );
74                 $books[0]->ownPageList = array( $pages[0], $pages[1] );
75                 $books[1]->ownPageList = array( $pages[2], $pages[3], $pages[4] );
76                 $books[2]->ownPageList = array( $pages[5] );
77                 $books[3]->ownPageList = array( $pages[6], $pages[7] );
78                 $books[4]->ownPageList = array( $pages[8] );
79                 $books[0]->title = 'Diehard C';
80                 $books[1]->title = 'Adventures in JavaScript';
81                 $books[2]->title = 'CSS ala Picasso';
82                 $books[3]->title = 'PHP Tips and Tricks';
83                 $books[4]->title = 'Secrets of SQL';
84                 $categories[0]->name = 'Programming';
85                 $categories[1]->name = 'Design';
86                 $categories[2]->name = 'Web Development';
87                 $books[0]->sharedCategoryList = array( $categories[0] );
88                 $books[1]->sharedCategoryList = array( $categories[0], $categories[2] );
89                 $books[2]->sharedCategoryList = array( $categories[0], $categories[2], $categories[1] );
90                 $books[3]->sharedCategoryList = array( $categories[0], $categories[2] );
91                 $books[4]->sharedCategoryList = array( $categories[0], $categories[2] );
92                 R::storeAll( $books );
93         }
94
95         /**
96          * A custom record-to-bean mapping function for findMulti test.
97          *
98          * @param string $parentName name of the parent bean
99          * @param string $childName  name of the child bean
100          *
101          * @return array
102          */
103         private function map($parentName,$childName) {
104                 return array(
105                         'a' => $parentName,
106                         'b' => $childName,
107                         'matcher' => function( $parent, $child ) use ( $parentName ) {
108                                 $property = "{$parentName}ID";
109                                 return ( $child->$property == $parent->id );
110                         },
111                         'do' => function( $parent, $child ) use ( $childName ) {
112                                 $list = 'own'.ucfirst( $childName ).'List';
113                                 $parent->noLoad()->{$list}[] = $child;
114                         }
115                 );
116         }
117
118         /**
119          * FindMulti should not throw errors in case of
120          * a record-type mismatch.
121          *
122          * @return void
123          */
124         public function testFindMultiErrorHandling()
125         {
126                 $result = R::findMulti('a,b', array());
127                 asrt( is_array( $result ), TRUE );
128                 asrt( count( $result ), 2 );
129                 asrt( isset( $result['a'] ), TRUE );
130                 asrt( isset( $result['b'] ), TRUE );
131                 asrt( is_array( $result['a'] ), TRUE );
132                 asrt( is_array( $result['b'] ), TRUE );
133                 asrt( count( $result['a'] ), 0 );
134                 asrt( count( $result['b'] ), 0 );
135                 pass();
136                 $result = R::findMulti( 'book', array(
137                                 array( 'book__title' => 'The missing ID.' )
138                 ) );
139                 asrt( is_array( $result ), TRUE );
140                 asrt( count( $result ), 1 );
141                 asrt( isset( $result['book'] ), TRUE );
142                 asrt( is_array( $result['book'] ), TRUE );
143                 asrt( count( $result['book'] ), 0 );
144                 pass();
145         }
146
147         /**
148          * You can build your own mapping functions to remap records to bean.
149          * Just like the preloader once did. However now you can define the
150          * mapping yourself using closures. This test verifies that such a
151          * function would actually work.
152          *
153          * This method also tests whether empty records (resulting from LEFT JOINS for
154          * instance) do not produce unnecessary, empty beans.
155          *
156          * @return void
157          */
158         public function testFindMultiExtFunc()
159         {
160                 R::nuke();
161                 $shop = R::dispense( 'shop' );
162                 $shop2 = R::dispense( 'shop' );
163                 $products = R::dispense( 'product', 3 );
164                 $price = R::dispense( 'price' );
165                 $price->tag = 5;
166                 $products[0]->name = 'vase';
167                 $products[1]->name = 'candle';
168                 $products[2]->name = 'plate';
169                 $products[1]->ownPriceList[] = $price;
170                 $shop->ownProduct[] = $products[0];
171                 $shop->ownProduct[] = $products[1];
172                 $shop2->ownProduct[] = $products[2];
173                 R::storeAll( array( $shop, $shop2 ) );
174                 $collection = R::findMulti( 'shop,product,price', '
175                         SELECT shop.*, product.*, price.* FROM shop
176                         LEFT JOIN product ON product.shop_id = shop.id
177                         LEFT JOIN price ON price.product_id = product.id
178                 ', array(), array(
179                         '0' => $this->map( 'shop', 'product' ),
180                         '1' => $this->map( 'product', 'price' ),
181                 ));
182                 asrt( is_array( $collection ), TRUE );
183                 asrt( count( $collection ), 3 );
184                 asrt( count( $collection['shop'] ), 2 );
185                 asrt( count( $collection['product'] ), 3 );
186                 asrt( count( $collection['price'] ), 1 );
187                 $shop = reset( $collection['shop'] );
188                 asrt( count( $shop->ownProductList ), 2 );
189                 $shop2 = next( $collection['shop'] );
190                 asrt( count( $shop2->ownProductList ), 1 );
191                 $candle = NULL;
192                 foreach( $shop->ownProduct as $product ) {
193                                 if ( $product->name == 'candle' ) {
194                                         $candle = $product;
195                                 }
196                 }
197                 asrt( is_null( $candle ), FALSE );
198                 asrt( count( $candle->ownPrice ), 1 );
199                 asrt( $candle->name, 'candle' );
200                 $price = reset( $candle->ownPrice );
201                 asrt( (int) $price->tag, 5 );
202         }
203
204         /**
205          * Test findMuli with self-made arrays.
206          *
207          * @return void
208          */
209         public function testFindMultiDirectArray()
210         {
211                 R::nuke();
212                 $collection = R::findMulti( 'shop,product', array(
213                         array( 'shop__id' => 1, 'product__id' => 1, 'product__name' => 'vase', 'product__shop_id' => 1 ),
214                         array( 'shop__id' => 1, 'product__id' => 2, 'product__name' => 'candle', 'product__shop_id' => 1 ),
215                         array( 'shop__id' => 1, 'product__id' => 3, 'product__name' => 'plate', 'product__shop_id' => 1 ),
216                 ) );
217                 asrt( is_array( $collection ), TRUE );
218                 asrt( isset( $collection['shop'] ), TRUE );
219                 asrt( isset( $collection['product'] ), TRUE );
220                 asrt( (int) $collection['shop'][1]->id, 1 );
221                 asrt( (int) $collection['product'][1]->id, 1 );
222                 asrt( (int) $collection['product'][2]->id, 2 );
223                 asrt( (int) $collection['product'][3]->id, 3 );
224                 asrt( (int) $collection['product'][1]->shopID, 1 );
225                 asrt( (int) $collection['product'][2]->shopID, 1 );
226                 asrt( (int) $collection['product'][3]->shopID, 1 );
227                 asrt( $collection['product'][1]->name, 'vase' );
228                 asrt( $collection['product'][2]->name, 'candle' );
229                 asrt( $collection['product'][3]->name, 'plate' );
230                 R::nuke();
231                 $shop = R::dispense('shop');
232                 $shop2 = R::dispense('shop');
233                 $products = R::dispense('product', 3);
234                 $price = R::dispense('price');
235                 $price->tag = 5;
236                 $products[0]->name = 'vase';
237                 $products[1]->name = 'candle';
238                 $products[2]->name = 'plate';
239                 $products[1]->ownPriceList[] = $price;
240                 $shop->ownProduct = $products;
241                 R::store($shop);
242                 $collection = R::findMulti('shop,product,price', '
243                         SELECT shop.*, product.*, price.* FROM shop
244                         LEFT JOIN product ON product.shop_id = shop.id
245                         LEFT JOIN price ON price.product_id = product.id
246                 ', array(), array(
247                         '0' => $this->map('shop', 'product'),
248                         '1' => $this->map('product', 'price'),
249                 ));
250                 $collection = R::findMulti( 'shop,product', array(
251                         array( 'shop__id' => 1, 'product__id' => 1, 'product__name' => 'vase', 'product__shop_id' => 1 ),
252                         array( 'shop__id' => 1, 'product__id' => 2, 'product__name' => 'candle', 'product__shop_id' => 1 ),
253                         array( 'shop__id' => 1, 'product__id' => 3, 'product__name' => 'plate', 'product__shop_id' => 1 ),
254                         array( 'shop__id' => 1, 'product__id' => 2, 'product__name' => 'candle', 'product__shop_id' => 1)
255                 ), array(), array(
256                         array(
257                                 'a' => 'shop',
258                                 'b' => 'product',
259                                 'matcher' => function( $a, $b ) { return ( $b->shopID == $a->id ); },
260                                 'do'      => function( $a, $b ) { return $a->noLoad()->ownProductList[] = $b; }
261                         )
262                 ) );
263                 asrt( is_array( $collection ), TRUE );
264                 asrt( isset( $collection['shop'] ), TRUE );
265                 asrt( isset( $collection['product'] ), TRUE );
266                 asrt( (int) $collection['shop'][1]->id, 1 );
267                 asrt( (int) $collection['product'][1]->id, 1 );
268                 asrt( (int) $collection['product'][2]->id, 2 );
269                 asrt( (int) $collection['product'][3]->id, 3 );
270                 asrt( (int) $collection['product'][1]->shopID, 1 );
271                 asrt( (int) $collection['product'][2]->shopID, 1 );
272                 asrt( (int) $collection['product'][3]->shopID, 1 );
273                 asrt( $collection['product'][1]->name, 'vase' );
274                 asrt( $collection['product'][2]->name, 'candle' );
275                 asrt( $collection['product'][3]->name, 'plate' );
276                 asrt( isset( $collection['shop'][1]->ownProductList ), TRUE );
277                 asrt( is_array( $collection['shop'][1]->ownProductList ), TRUE );
278                 asrt( count( $collection['shop'][1]->ownProductList ), 3 );
279                 asrt( $collection['shop'][1]->ownProductList[0]->name, 'vase' );
280                 asrt( $collection['shop'][1]->ownProductList[1]->name, 'candle' );
281                 asrt( $collection['shop'][1]->ownProductList[2]->name, 'plate' );
282         }
283
284         /**
285          * Test findMulti() with manual crafted fields.
286          *
287          * @return void
288          */
289         public function testFindMultiDIY()
290         {
291                 R::nuke();
292                 $movie = R::dispense( 'movie' );
293                 $review = R::dispense( 'review' );
294                 $movie->ownReviewList[] = $review;
295                 $review->stars = 5;
296                 $movie->title = 'Gambit';
297                 R::store( $movie );
298                 $stuff = R::findMulti( 'movie,review', 'SELECT
299                         movie.id AS movie__id,
300                         movie.title AS movie__title,
301                         review.id AS review__id,
302                         review.stars AS review__stars,
303                         review.movie_id AS review__movie_id
304                         FROM movie
305                         LEFT JOIN review ON review.movie_id = movie.id
306                 ' );
307                 asrt( count( $stuff ), 2 );
308                 asrt( isset( $stuff['movie'] ), TRUE );
309                 asrt( isset( $stuff['review'] ), TRUE );
310                 asrt( is_array( $stuff['movie'] ), TRUE );
311                 asrt( is_array( $stuff['review'] ), TRUE );
312                 asrt( count( $stuff['movie'] ), 1 );
313                 asrt( count( $stuff['review'] ), 1 );
314                 $movie = reset( $stuff['movie'] );
315                 asrt( $movie->title, 'Gambit' );
316                 $review = reset( $stuff['review'] );
317                 asrt( (int) $review->stars, 5 );
318                 R::nuke();
319                 $movie = R::dispense( 'movie' );
320                 $review = R::dispense( 'review' );
321                 $movie->ownReviewList[] = $review;
322                 $review->stars = 5;
323                 $movie->title = 'Gambit';
324                 R::store( $movie );
325                 $stuff = R::findMulti( array( 'movie', 'review' ), 'SELECT
326                         movie.id AS movie__id,
327                         movie.title AS movie__title,
328                         review.id AS review__id,
329                         review.stars AS review__stars,
330                         review.movie_id AS review__movie_id
331                         FROM movie
332                         LEFT JOIN review ON review.movie_id = movie.id
333                 ' );
334                 asrt( count( $stuff ), 2 );
335                 asrt( isset( $stuff['movie'] ), TRUE );
336                 asrt( isset( $stuff['review'] ), TRUE );
337                 asrt( is_array( $stuff['movie'] ), TRUE );
338                 asrt( is_array( $stuff['review'] ), TRUE );
339                 asrt( count( $stuff['movie'] ), 1 );
340                 asrt( count( $stuff['review'] ), 1 );
341                 $movie = reset( $stuff['movie'] );
342                 asrt( $movie->title, 'Gambit' );
343                 $review = reset( $stuff['review'] );
344                 asrt( (int) $review->stars, 5 );
345         }
346
347         /**
348          * Test findMulti(). Basic version.
349          *
350          * @return void
351          */
352         public function testFindMulti()
353         {
354                 $book = R::dispense( 'book' );
355                 $book->title = 'My Book';
356                 $book->ownPageList = R::dispense( 'page', 3 );
357                 $no = 1;
358                 foreach( $book->ownPageList as $page ) {
359                         $page->num = $no++;
360                 }
361                 R::store( $book );
362                 $collection = R::findMulti( 'book,page', '
363                         SELECT book.*, page.* FROM book
364                         LEFT JOIN page ON page.book_id = book.id
365                 ' );
366                 asrt( count( $collection ), 2 );
367                 asrt( isset( $collection['book'] ), TRUE );
368                 asrt( isset( $collection['page'] ), TRUE );
369                 asrt( count( $collection['book'] ), 1 );
370                 asrt( count( $collection['page'] ), 3 );
371                 foreach( $collection['book'] as $bean ) asrt( ( $bean instanceof OODBBean ), TRUE );
372                 foreach( $collection['page'] as $bean ) asrt( ( $bean instanceof OODBBean ), TRUE );
373                 $book = reset( $collection['book'] );
374                 asrt( $book->title, 'My Book' );
375                 $no = 1;
376                 foreach( $collection['page'] as $page ) asrt( (int) $page->num, $no++ );
377                 R::nuke();
378                 $book->noLoad()->ownPageList = $collection['page'];
379                 asrt( count( $book->ownPageList ), 3 );
380         }
381
382         /**
383          * Tests the complex use case for findMulti().
384          *
385          * @return void
386          */
387         public function testMultiAdvanced()
388         {
389                 $this->insertBookData();
390                 $collection = R::findMulti( 'book,page,text,category', '
391                         SELECT book.*, page.*, text.*, category.*
392                         FROM book
393                         LEFT JOIN page ON page.book_id = book.id
394                         LEFT JOIN text ON text.page_id = page.id
395                         LEFT JOIN book_category ON book_category.book_id = book.id
396                         LEFT JOIN category ON book_category.category_id = category.id
397                 ' );
398                 asrt( count( $collection ), 4 );
399                 asrt( isset( $collection['book'] ), TRUE );
400                 asrt( isset( $collection['page'] ), TRUE );
401                 asrt( isset( $collection['text'] ), TRUE );
402                 asrt( isset( $collection['category'] ), TRUE );
403                 asrt( count( $collection['book'] ), 5 );
404                 asrt( count( $collection['page'] ), 9 );
405                 asrt( count( $collection['text'] ), 11 );
406                 asrt( count( $collection['category'] ), 3 );
407                 foreach( $collection['book'] as $bean ) asrt( ( $bean instanceof OODBBean ), TRUE );
408                 foreach( $collection['page'] as $bean ) asrt( ( $bean instanceof OODBBean ), TRUE );
409                 foreach( $collection['text'] as $bean ) asrt( ( $bean instanceof OODBBean ), TRUE );
410                 foreach( $collection['category'] as $bean ) asrt( ( $bean instanceof OODBBean ), TRUE );
411                 foreach( $collection['book']  as $book ) $titles[] = $book->title;
412                 asrt( in_array( 'Diehard C', $titles ), TRUE );
413                 asrt( in_array( 'Adventures in JavaScript', $titles ), TRUE );
414                 asrt( in_array( 'CSS ala Picasso', $titles ), TRUE );
415                 asrt( in_array( 'PHP Tips and Tricks', $titles ), TRUE );
416                 asrt( in_array( 'Secrets of SQL', $titles ), TRUE );
417                 $collection = R::findMulti( 'book,page,text,category,book_category', '
418                         SELECT book.*, page.*, text.*, category.*, book_category.*
419                         FROM book
420                         LEFT JOIN page ON page.book_id = book.id
421                         LEFT JOIN text ON text.page_id = page.id
422                         LEFT JOIN book_category ON book_category.book_id = book.id
423                         LEFT JOIN category ON book_category.category_id = category.id
424                         WHERE category_id > ?
425                         ORDER BY book.title ASC
426                 ', array( 0 ), array(
427                                 array(
428                                         'b'=>'page',
429                                         'a'=>'text',
430                                         'do' => function( $a, $b ) {
431                                                 $b->noLoad()->ownTextList[] = $a;
432                                                 $b->clearHistory();
433                                         },
434                                         'matcher' => function( $a, $b ){ return ($a->page_id == $b->id);  }
435                                         ),
436                                 array(
437                                         'b'=>'book',
438                                         'a'=>'page',
439                                         'do' => function( $a, $b ) {
440                                                 $b->noLoad()->ownPageList[] = $a;
441                                                 $b->clearHistory();
442                                         },
443                                         'matcher' => function( $a, $b ){ return ($a->book_id == $b->id);  }
444                                         ),
445                                 array(
446                                         'b' => 'category',
447                                         'a' => 'book',
448                                         'do'  => function($a, $b) {
449                                                 $a->noLoad()->sharedCategoryList[] = $b;
450                                                 $a->clearHistory();
451                                         },
452                                         'matcher' => function( $a, $b, $beans ) {
453                                                 foreach( $beans['book_category'] as $bean ) {
454                                                         if ( $bean->book_id == $a->id && $bean->category_id == $b->id ) return TRUE;
455                                                 }
456                                                 return FALSE;
457                                         }
458                                 ),
459                         )
460                 );
461                 $books = $collection['book'];
462                 $book = reset( $books );
463                 asrt( $book->title, 'Adventures in JavaScript' );
464                 R::nuke();
465                 asrt( count( $book->ownPageList ), 3 );
466                 $page = reset( $book->ownPageList );
467                 asrt( count( $page->ownTextList ), 1 );
468                 asrt( count( $book->sharedCategoryList ), 2);
469                 $categories = array();
470                 foreach( $book->sharedCategoryList as $category ) {
471                         $categories[] = $category->name;
472                 }
473                 sort( $categories );
474                 asrt( implode( ',', $categories ), 'Programming,Web Development' );
475                 $book = next( $books );
476                 asrt( $book->title, 'CSS ala Picasso' );
477                 asrt( count( $book->ownPage ), 1 );
478                 $page = reset( $book->ownPage );
479                 asrt( count( $page->ownTextList ), 2 );
480                 $texts = array();
481                 foreach( $page->ownTextList as $text ) $texts[] = $text->content;
482                 asrt( in_array( 'Now we use it for applications...', $texts ), TRUE );
483                 $categories = array();
484                 foreach( $book->sharedCategoryList as $category ) {
485                         $categories[] = $category->name;
486                 }
487                 sort( $categories );
488                 asrt( implode( ',', $categories ), 'Design,Programming,Web Development' );
489                 $book = next( $books );
490                 asrt( $book->title, 'Diehard C' );
491                 asrt( count( $book->ownPageList ), 2 );
492                 $page = reset( $book->ownPageList );
493                 asrt( count( $page->ownTextList ), 2 );
494                 $page = next( $book->ownPageList );
495                 asrt( count( $page->ownTextList ), 1 );
496                 $categories = array();
497                 foreach( $book->sharedCategoryList as $category ) {
498                         $categories[] = $category->name;
499                 }
500                 sort( $categories );
501                 asrt( implode( ',', $categories ), 'Programming' );
502                 //should have no effect, nothing should have changed
503                 R::storeAll($books);
504                 asrt( R::count('book'), 0 );
505                 asrt( R::count('page'), 0 );
506                 asrt( R::count('text'), 0 );
507         }
508
509         /**
510          * Test forming IN-clause using genSlots and flat.
511          *
512          * @return void
513          */
514         public function testINClause()
515         {
516                 list( $flowers, $shop ) = R::dispenseAll( 'flower*4,shop' );
517                 $flowers[0]->color = 'red';
518                 $flowers[1]->color = 'yellow';
519                 $flowers[2]->color = 'blue';
520                 $flowers[3]->color = 'purple';
521                 $flowers[0]->price = 10;
522                 $flowers[1]->price = 15;
523                 $flowers[2]->price = 20;
524                 $flowers[3]->price = 25;
525                 $shop->xownFlowerList = $flowers;
526                 R::store( $shop );
527                 $colors = array( 'red', 'yellow' );
528                 $result = $this->getColors( R::find( 'flower', ' color IN ('.R::genSlots( $colors ).' ) AND price < ?' , R::flat( array( $colors, 100 ) ) ) );
529                 asrt( $result, 'red,yellow' );
530                 $colors = array( 'red', 'yellow' );
531                 $result = $this->getColors( R::find( 'flower', ' color IN ('.R::genSlots( $colors ).' ) AND price < ?' , R::flat( array( $colors, 10 ) ) ) );
532                 asrt( $result, '' );
533                 $colors = array( 'red', 'yellow' );
534                 $result = $this->getColors( R::find( 'flower', ' color IN ('.R::genSlots( $colors ).' ) AND price < ?' , R::flat( array( $colors, 15 ) ) ) );
535                 asrt( $result, 'red' );
536                 asrt( json_encode( R::flat( array( 'a', 'b', 'c' ) ) ), '["a","b","c"]' );
537                 asrt( json_encode( R::flat( array( 'a', array( 'b' ), 'c' ) ) ), '["a","b","c"]' );
538                 asrt( json_encode( R::flat( array( 'a', array( 'b', array( 'c' ) ) ) ) ), '["a","b","c"]' );
539                 asrt( json_encode( R::flat( array( array( 'a', array( 'b', array( array( 'c' ) ) ) ) ) ) ), '["a","b","c"]' );
540                 asrt( json_encode( R::flat( array( 'a', 'b', 'c', array() ) ) ), '["a","b","c"]' );
541                 asrt( genslots( array( 1, 2 ) ), '?,?' );
542                 asrt( json_encode( array_flatten( array( array( 'a', array( 'b', array( array( 'c' ) ) ) ) ) ) ), '["a","b","c"]' );
543                 asrt( genslots( array( 1, 2 ), 'IN (%s) AND' ), 'IN (?,?) AND' );
544                 asrt( genslots( array(), ' IN (%s) AND ' ), '' );
545                 $colors = array( 'blue', 'purple', 'red' );
546                 $flowers = R::find( 'flower', genslots( $colors, ' color IN (%s) AND ' ).' price > ? ', array_flatten( array( $colors, 11 ) ) );
547                 asrt( $this->getColors( $flowers ), 'blue,purple' );
548                 $flowers = R::find( 'flower', genslots( array(), ' color IN (%s) AND ' ).' price > ? ', array_flatten( array( array(), 11 ) ) );
549                 asrt( $this->getColors( $flowers ), 'blue,purple,yellow' );
550                 $flowers = R::find( 'flower', ' id > 0 AND '.genslots( $colors, ' color IN (%s) AND ' ).' price > ? ', array_flatten( array( $colors, 11 ) ) );
551                 asrt( $this->getColors( $flowers ), 'blue,purple' );
552                 $flowers = R::find( 'flower', ' id > 0 AND '.genslots( array(), ' color IN (%s) AND ' ).' price > ? ', array_flatten( array( array(), 11 ) ) );
553                 asrt( $this->getColors( $flowers ), 'blue,purple,yellow' );
554         }
555
556         /**
557          * Test findLike.
558          *
559          * @return void
560          */
561         public function testFindLike2()
562         {
563                 list( $flowers, $shop ) = R::dispenseAll( 'flower*4,shop' );
564                 $flowers[0]->color = 'red';
565                 $flowers[1]->color = 'yellow';
566                 $flowers[2]->color = 'blue';
567                 $flowers[3]->color = 'purple';
568                 $flowers[0]->price = 10;
569                 $flowers[1]->price = 15;
570                 $flowers[2]->price = 20;
571                 $flowers[3]->price = 25;
572                 $shop->xownFlowerList = $flowers;
573                 R::store( $shop );
574                 asrt( $this->getColors( R::findLike( 'flower', array( 'color' => array( 'red', 'yellow' )  ), ' price < 20' ) ), 'red,yellow' );
575                 asrt( $this->getColors( R::findLike( 'flower', array( 'color' => array()  ), '' ) ), 'blue,purple,red,yellow' );
576                 asrt( $this->getColors( R::findLike( 'flower', array( 'color' => array()  ) ) ), 'blue,purple,red,yellow' );
577                 asrt( $this->getColors( R::findLike( 'flower', array( 'color' => array('blue')  ), ' OR price = 25' ) ), 'blue,purple' );
578                 asrt( $this->getColors( R::findLike( 'flower', array( 'color' => array()  ), ' price < 25' ) ), 'blue,red,yellow' );
579                 asrt( $this->getColors( R::findLike( 'flower', array( 'color' => array()  ), ' price < 20' ) ), 'red,yellow' );
580                 asrt( $this->getColors( R::findLike( 'flower', array( 'color' => array()  ), ' ORDER BY color DESC' ), TRUE ), 'yellow,red,purple,blue' );
581                 asrt( $this->getColors( R::findLike( 'flower', array( 'color' => array()  ), ' ORDER BY color LIMIT 1' ) ), 'blue' );
582                 asrt( $this->getColors( R::findLike( 'flower', array( 'color' => array( 'yellow', 'blue' )  ), ' ORDER BY color ASC  LIMIT 1' ) ), 'blue' );
583         }
584
585         /**
586          * Tests the findOrCreate method.
587          *
588          * @return void
589          */
590         public function testFindOrCreate()
591         {
592                 R::nuke();
593                 $book = R::findOrCreate( 'book', array( 'title' => 'my book', 'price' => 50 ) );
594                 asrt( ( $book instanceof OODBBean ), TRUE );
595                 $id = $book->id;
596                 $book = R::findOrCreate( 'book', array( 'title' => 'my book', 'price' => 50 ) );
597                 asrt( $book->id, $id );
598                 asrt( $book->title, 'my book' );
599                 asrt( (int) $book->price, 50 );
600         }
601
602         /**
603          * Tests the findLike method.
604          *
605          * @return void
606          */
607         public function testFindLike()
608         {
609                 R::nuke();
610                 $book = R::dispense( array(
611                         '_type' => 'book',
612                         'title' => 'my book',
613                         'price' => 80
614                 ) );
615                 R::store( $book );
616                 $book = R::dispense( array(
617                         '_type' => 'book',
618                         'title' => 'other book',
619                         'price' => 80
620                 ) );
621                 R::store( $book );
622                 $books = R::findLike( 'book', array( 'price' => 80 ) );
623                 asrt( count( $books ), 2 );
624                 foreach( $books as $book ) {
625                         asrt( $book->getMeta( 'type' ), 'book' );
626                 }
627                 $books = R::findLike( 'book' );
628                 asrt( count( $books ), 2 );
629                 $books = R::findLike( 'book', array( 'title' => 'my book' ) );
630                 asrt( count( $books ), 1 );
631                 $books = R::findLike( 'book', array( 'title' => array( 'my book', 'other book' ) ) );
632                 asrt( count( $books ), 2 );
633                 $books = R::findLike( 'book', array( 'title' => 'strange book') );
634                 asrt( is_array( $books ), TRUE );
635                 asrt( count( $books ), 0 );
636                 $books = R::findLike( 'magazine' );
637                 asrt( is_array( $books ), TRUE );
638                 asrt( count( $books ), 0 );
639         }
640
641         /**
642          * Test whether findOne gets a LIMIT 1
643          * clause.
644          *
645          * @return void
646          */
647         public function testFindOneLimitOne()
648         {
649                 R::nuke();
650                 list( $book1, $book2 ) = R::dispense( 'book', 2 );
651                 $book1->title = 'a';
652                 $book2->title = 'b';
653                 R::storeAll( array( $book1, $book2 ) );
654                 $logger = R::debug( 1, 1 );
655                 $logger->clear();
656                 $found = R::findOne( 'book' );
657                 asrt( count( $logger->grep('LIMIT 1') ), 1 );
658                 asrt( ( $found instanceof \RedBeanPHP\OODBBean ), TRUE );
659                 $logger->clear();
660                 $found = R::findOne( 'book', ' title = ? ', array( 'a' ) );
661                 asrt( count( $logger->grep('LIMIT 1') ), 1 );
662                 asrt( ( $found instanceof \RedBeanPHP\OODBBean ), TRUE );
663                 $logger->clear();
664                 $found = R::findOne( 'book', ' title = ? LIMIT 1', array( 'b' ) );
665                 asrt( count( $logger->grep('LIMIT 1') ), 1 );
666                 $logger->clear();
667                 $found = R::findOne( 'book', ' title = ? limit 1', array( 'b' ) );
668                 asrt( count( $logger->grep('LIMIT 1') ), 0 );
669                 asrt( count( $logger->grep('limit 1') ), 1 );
670                 asrt( ( $found instanceof \RedBeanPHP\OODBBean ), TRUE );
671                 $found = R::findOne( 'book', ' title = ? LIMIT 2', array( 'b' ) );
672                 asrt( count( $logger->grep('LIMIT 2') ), 1 );
673                 asrt( ( $found instanceof \RedBeanPHP\OODBBean ), TRUE );
674         }
675
676         /**
677          * Begin testing.
678          * This method runs the actual test pack.
679          *
680          * @return void
681          */
682         public function testFinding()
683         {
684                 $toolbox = R::getToolBox();
685                 $adapter = $toolbox->getDatabaseAdapter();
686                 $writer  = $toolbox->getWriter();
687                 $redbean = $toolbox->getRedBean();
688                 $pdo     = $adapter->getDatabase();
689                 $a = new AssociationManager( $toolbox );
690                 $page = $redbean->dispense( "page" );
691                 $page->name = "John's page";
692                 $idpage = $redbean->store( $page );
693                 $page2 = $redbean->dispense( "page" );
694                 $page2->name = "John's second page";
695                 $idpage2 = $redbean->store( $page2 );
696                 $a->associate( $page, $page2 );
697                 $pageOne = $redbean->dispense( "page" );
698                 $pageOne->name = "one";
699                 $pageMore = $redbean->dispense( "page" );
700                 $pageMore->name = "more";
701                 $pageEvenMore = $redbean->dispense( "page" );
702                 $pageEvenMore->name = "evenmore";
703                 $pageOther = $redbean->dispense( "page" );
704                 $pageOther->name = "othermore";
705                 set1toNAssoc( $a, $pageOther, $pageMore );
706                 set1toNAssoc( $a, $pageOne, $pageMore );
707                 set1toNAssoc( $a, $pageOne, $pageEvenMore );
708                 asrt( count( $redbean->find( "page", array(), " name LIKE '%more%' ", array() ) ), 3 );
709                 asrt( count( $redbean->find( "page", array(), " name LIKE :str ", array( ":str" => '%more%' ) ) ), 3 );
710                 asrt( count( $redbean->find( "page", array(), array( " name LIKE :str ", array( ":str" => '%more%' ) ) ) ), 3 );
711                 asrt( count( $redbean->find( "page", array(), " name LIKE :str ", array( ":str" => '%mxore%' ) ) ), 0 );
712                 asrt( count( $redbean->find( "page", array( "id" => array( 2, 3 ) ) ) ), 2 );
713                 $bean = $redbean->dispense( "wine" );
714                 $bean->name = "bla";
715                 for ( $i = 0; $i < 10; $i++ ) {
716                         $redbean->store( $bean );
717                 }
718                 $redbean->find( "wine", array( "id" => 5 ) ); //  Finder:where call OODB::convertToBeans
719                 $bean2 = $redbean->load( "anotherbean", 5 );
720                 asrt( $bean2->id, 0 );
721                 $keys = $adapter->getCol( "SELECT id FROM page WHERE " . $writer->esc( 'name' ) . " LIKE '%John%'" );
722                 asrt( count( $keys ), 2 );
723                 $pages = $redbean->batch( "page", $keys );
724                 asrt( count( $pages ), 2 );
725                 $p = R::findLast( 'page' );
726                 pass();
727                 $row = R::getRow( 'select * from page ' );
728                 asrt( is_array( $row ), TRUE );
729                 asrt( isset( $row['name'] ), TRUE );
730                 // Test findAll -- should not throw an exception
731                 asrt( count( R::findAll( 'page' ) ) > 0, TRUE );
732                 asrt( count( R::findAll( 'page', ' ORDER BY id ' ) ) > 0, TRUE );
733                 $beans = R::findOrDispense( "page" );
734                 asrt( count( $beans ), 6 );
735                 asrt( is_null( R::findLast( 'nothing' ) ), TRUE );
736                 try {
737                         R::find( 'bean', ' id > 0 ', 'invalid bindings argument' );
738                         fail();
739                 } catch ( RedException $exception ) {
740                         pass();
741                 }
742                 R::nuke();
743                 $bean = R::findOneOrDispense( 'jellybean' );
744                 asrt( is_object( $bean ), TRUE );
745         }
746
747         /**
748          * Test tree traversal with searchIn().
749          *
750          * @return void
751          */
752         public function testTreeTraversal()
753         {
754                 testpack( 'Test Tree Traversal' );
755                 R::nuke();
756                 $page = R::dispense( 'page', 10 );
757                 //Setup the test data for this series of tests
758                 $i = 0;
759                 foreach( $page as $pageItem ) {
760                         $pageItem->name = 'page' . $i;
761                         $pageItem->number = $i;
762                         $i++;
763                         R::store( $pageItem );
764                 }
765                 $page[0]->ownPage  = array( $page[1], $page[2] );
766                 $page[1]->ownPage  = array( $page[3], $page[4] );
767                 $page[3]->ownPage  = array( $page[5] );
768                 $page[5]->ownPage  = array( $page[7] );
769                 $page[9]->document = $page[8];
770                 $page[9]->book = R::dispense('book');
771                 R::store( $page[9] );
772                 $id = R::store( $page[0] );
773                 $book = $page[9]->book;
774         }
775
776         /**
777          * Test find and export.
778          *
779          * @return void
780          */
781         public function testFindAndExport()
782         {
783                 R::nuke();
784                 $pages = R::dispense( 'page', 3 );
785                 $i = 1;
786                 foreach( $pages as $page ) {
787                         $page->pageNumber = $i++;
788                 }
789                 R::storeAll( $pages );
790                 $pages = R::findAndExport( 'page' );
791                 asrt( is_array( $pages ), TRUE );
792                 asrt( isset( $pages[0] ), TRUE );
793                 asrt( is_array( $pages[0] ), TRUE );
794                 asrt( count( $pages ), 3 );
795         }
796
797         /**
798         * Test error handling of SQL states.
799         *
800         * @return void
801         */
802         public function testFindError()
803         {
804                 R::freeze( FALSE );
805                 $page = R::dispense( 'page' );
806                 $page->title = 'abc';
807                 R::store( $page );
808                 //Column does not exist, in fluid mode no error!
809                 try {
810                         R::find( 'page', ' xtitle = ? ', array( 'x' ) );
811                         pass();
812                 } catch ( SQL $e ) {
813                         fail();
814                 }
815                 //Table does not exist, in fluid mode no error!
816                 try {
817                         R::find( 'pagex', ' title = ? ', array( 'x' ) );
818                         pass();
819                 } catch ( SQL $e ) {
820                         fail();
821                 }
822                 //Syntax error, error in fluid mode if possible to infer from SQLSTATE (MySQL/Postgres)
823                 try {
824                         R::find( 'page', ' invalid SQL ' );
825                         //In SQLite only get HY000 - not very descriptive so suppress more errors in fluid mode then.
826                         if (
827                         $this->currentlyActiveDriverID === 'sqlite'
828                         || $this->currentlyActiveDriverID === 'CUBRID' ) {
829                                 pass();
830                         } else {
831                                 fail();
832                         }
833                 } catch ( SQL $e ) {
834                         pass();
835                 }
836                 //Frozen, always error...
837                 R::freeze( TRUE );
838                 //Column does not exist, in frozen mode error!
839                 try {
840                         R::find( 'page', ' xtitle = ? ', array( 'x' ) );
841                         fail();
842                 } catch ( SQL $e ) {
843                         pass();
844                 }
845                 //Table does not exist, in frozen mode error!
846                 try {
847                         R::find( 'pagex', ' title = ? ', array( 'x' ) );
848                         fail();
849                 } catch ( SQL $e ) {
850                         pass();
851                 }
852                 //Syntax error, in frozen mode error!
853                 try {
854                         R::find( 'page', ' invalid SQL ' );
855                         fail();
856                 } catch ( SQL $e ) {
857                         pass();
858                 }
859                 R::freeze( FALSE );
860         }
861 }