3 namespace RedUNIT\Base;
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;
16 * Tests whether we can find beans by passing SQL queries to the
17 * Facade or the Finder Service Class.
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
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.
28 class Finding extends Base {
31 * Helper for testing findLike.
33 * @param array $flowers beans
34 * @param boolean $noSort sorting?
38 private function getColors( $flowers, $noSort = FALSE )
41 foreach( $flowers as $flower ) $colors[] = $flower->color;
42 if ( !$noSort) sort( $colors );
43 return implode( ',', $colors );
47 * Inserts data for findMulti-tests.
51 private function insertBookData()
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 );
96 * A custom record-to-bean mapping function for findMulti test.
98 * @param string $parentName name of the parent bean
99 * @param string $childName name of the child bean
103 private function map($parentName,$childName) {
107 'matcher' => function( $parent, $child ) use ( $parentName ) {
108 $property = "{$parentName}ID";
109 return ( $child->$property == $parent->id );
111 'do' => function( $parent, $child ) use ( $childName ) {
112 $list = 'own'.ucfirst( $childName ).'List';
113 $parent->noLoad()->{$list}[] = $child;
119 * FindMulti should not throw errors in case of
120 * a record-type mismatch.
124 public function testFindMultiErrorHandling()
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 );
136 $result = R::findMulti( 'book', array(
137 array( 'book__title' => 'The missing ID.' )
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 );
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.
153 * This method also tests whether empty records (resulting from LEFT JOINS for
154 * instance) do not produce unnecessary, empty beans.
158 public function testFindMultiExtFunc()
161 $shop = R::dispense( 'shop' );
162 $shop2 = R::dispense( 'shop' );
163 $products = R::dispense( 'product', 3 );
164 $price = R::dispense( 'price' );
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
179 '0' => $this->map( 'shop', 'product' ),
180 '1' => $this->map( 'product', 'price' ),
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 );
192 foreach( $shop->ownProduct as $product ) {
193 if ( $product->name == 'candle' ) {
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 );
205 * Test findMuli with self-made arrays.
209 public function testFindMultiDirectArray()
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 ),
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' );
231 $shop = R::dispense('shop');
232 $shop2 = R::dispense('shop');
233 $products = R::dispense('product', 3);
234 $price = R::dispense('price');
236 $products[0]->name = 'vase';
237 $products[1]->name = 'candle';
238 $products[2]->name = 'plate';
239 $products[1]->ownPriceList[] = $price;
240 $shop->ownProduct = $products;
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
247 '0' => $this->map('shop', 'product'),
248 '1' => $this->map('product', 'price'),
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)
259 'matcher' => function( $a, $b ) { return ( $b->shopID == $a->id ); },
260 'do' => function( $a, $b ) { return $a->noLoad()->ownProductList[] = $b; }
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' );
285 * Test findMulti() with manual crafted fields.
289 public function testFindMultiDIY()
292 $movie = R::dispense( 'movie' );
293 $review = R::dispense( 'review' );
294 $movie->ownReviewList[] = $review;
296 $movie->title = 'Gambit';
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
305 LEFT JOIN review ON review.movie_id = movie.id
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 );
319 $movie = R::dispense( 'movie' );
320 $review = R::dispense( 'review' );
321 $movie->ownReviewList[] = $review;
323 $movie->title = 'Gambit';
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
332 LEFT JOIN review ON review.movie_id = movie.id
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 );
348 * Test findMulti(). Basic version.
352 public function testFindMulti()
354 $book = R::dispense( 'book' );
355 $book->title = 'My Book';
356 $book->ownPageList = R::dispense( 'page', 3 );
358 foreach( $book->ownPageList as $page ) {
362 $collection = R::findMulti( 'book,page', '
363 SELECT book.*, page.* FROM book
364 LEFT JOIN page ON page.book_id = book.id
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' );
376 foreach( $collection['page'] as $page ) asrt( (int) $page->num, $no++ );
378 $book->noLoad()->ownPageList = $collection['page'];
379 asrt( count( $book->ownPageList ), 3 );
383 * Tests the complex use case for findMulti().
387 public function testMultiAdvanced()
389 $this->insertBookData();
390 $collection = R::findMulti( 'book,page,text,category', '
391 SELECT book.*, page.*, text.*, category.*
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
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.*
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(
430 'do' => function( $a, $b ) {
431 $b->noLoad()->ownTextList[] = $a;
434 'matcher' => function( $a, $b ){ return ($a->page_id == $b->id); }
439 'do' => function( $a, $b ) {
440 $b->noLoad()->ownPageList[] = $a;
443 'matcher' => function( $a, $b ){ return ($a->book_id == $b->id); }
448 'do' => function($a, $b) {
449 $a->noLoad()->sharedCategoryList[] = $b;
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;
461 $books = $collection['book'];
462 $book = reset( $books );
463 asrt( $book->title, 'Adventures in JavaScript' );
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;
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 );
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;
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;
501 asrt( implode( ',', $categories ), 'Programming' );
502 //should have no effect, nothing should have changed
504 asrt( R::count('book'), 0 );
505 asrt( R::count('page'), 0 );
506 asrt( R::count('text'), 0 );
510 * Test forming IN-clause using genSlots and flat.
514 public function testINClause()
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;
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 ) ) ) );
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' );
561 public function testFindLike2()
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;
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' );
586 * Tests the findOrCreate method.
590 public function testFindOrCreate()
593 $book = R::findOrCreate( 'book', array( 'title' => 'my book', 'price' => 50 ) );
594 asrt( ( $book instanceof OODBBean ), TRUE );
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 );
603 * Tests the findLike method.
607 public function testFindLike()
610 $book = R::dispense( array(
612 'title' => 'my book',
616 $book = R::dispense( array(
618 'title' => 'other 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' );
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 );
642 * Test whether findOne gets a LIMIT 1
647 public function testFindOneLimitOne()
650 list( $book1, $book2 ) = R::dispense( 'book', 2 );
653 R::storeAll( array( $book1, $book2 ) );
654 $logger = R::debug( 1, 1 );
656 $found = R::findOne( 'book' );
657 asrt( count( $logger->grep('LIMIT 1') ), 1 );
658 asrt( ( $found instanceof \RedBeanPHP\OODBBean ), TRUE );
660 $found = R::findOne( 'book', ' title = ? ', array( 'a' ) );
661 asrt( count( $logger->grep('LIMIT 1') ), 1 );
662 asrt( ( $found instanceof \RedBeanPHP\OODBBean ), TRUE );
664 $found = R::findOne( 'book', ' title = ? LIMIT 1', array( 'b' ) );
665 asrt( count( $logger->grep('LIMIT 1') ), 1 );
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 );
678 * This method runs the actual test pack.
682 public function testFinding()
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" );
715 for ( $i = 0; $i < 10; $i++ ) {
716 $redbean->store( $bean );
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' );
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 );
737 R::find( 'bean', ' id > 0 ', 'invalid bindings argument' );
739 } catch ( RedException $exception ) {
743 $bean = R::findOneOrDispense( 'jellybean' );
744 asrt( is_object( $bean ), TRUE );
748 * Test tree traversal with searchIn().
752 public function testTreeTraversal()
754 testpack( 'Test Tree Traversal' );
756 $page = R::dispense( 'page', 10 );
757 //Setup the test data for this series of tests
759 foreach( $page as $pageItem ) {
760 $pageItem->name = 'page' . $i;
761 $pageItem->number = $i;
763 R::store( $pageItem );
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;
777 * Test find and export.
781 public function testFindAndExport()
784 $pages = R::dispense( 'page', 3 );
786 foreach( $pages as $page ) {
787 $page->pageNumber = $i++;
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 );
798 * Test error handling of SQL states.
802 public function testFindError()
805 $page = R::dispense( 'page' );
806 $page->title = 'abc';
808 //Column does not exist, in fluid mode no error!
810 R::find( 'page', ' xtitle = ? ', array( 'x' ) );
815 //Table does not exist, in fluid mode no error!
817 R::find( 'pagex', ' title = ? ', array( 'x' ) );
822 //Syntax error, error in fluid mode if possible to infer from SQLSTATE (MySQL/Postgres)
824 R::find( 'page', ' invalid SQL ' );
825 //In SQLite only get HY000 - not very descriptive so suppress more errors in fluid mode then.
827 $this->currentlyActiveDriverID === 'sqlite'
828 || $this->currentlyActiveDriverID === 'CUBRID' ) {
836 //Frozen, always error...
838 //Column does not exist, in frozen mode error!
840 R::find( 'page', ' xtitle = ? ', array( 'x' ) );
845 //Table does not exist, in frozen mode error!
847 R::find( 'pagex', ' title = ? ', array( 'x' ) );
852 //Syntax error, in frozen mode error!
854 R::find( 'page', ' invalid SQL ' );