3 namespace RedUNIT\Base;
5 use RedUNIT\Base as Base;
6 use RedBeanPHP\Facade as R;
7 use RedBeanPHP\ToolBox as ToolBox;
8 use RedBeanPHP\OODB as OODB;
9 use RedBeanPHP\AssociationManager as AssociationManager;
10 use RedBeanPHP\RedException\SQL as SQL;
15 * Originally meant to test R::associate - which is no longer
16 * used, this class tests all kinds of relations from
17 * one-to-one to polymorph relations using the poly() method.
19 * @file RedUNIT/Base/Association.php
20 * @desc Tests Association API (N:N associations)
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 Association extends Base
31 * MySQL specific tests.
32 * Test MySQL specific issues with constraints.
36 public function testMySQL()
38 if ( $this->currentlyActiveDriverID !== 'mysql' ) {
41 testpack( 'Throw exception in case of issue with assoc constraint' );
42 $bunny = R::dispense( 'bunny' );
43 $carrot = R::dispense( 'carrot' );
44 $faultyWriter = new \FaultyWriter( R::getToolBox()->getDatabaseAdapter() );
45 $faultyOODB = new OODB( $faultyWriter );
46 $faultyOODB->setBeanHelper( R::getRedBean()->getBeanHelper() );
47 $faultyToolbox = new ToolBox( $faultyOODB, R::getToolBox()->getDatabaseAdapter(), $faultyWriter );
48 $faultyAssociationManager = new AssociationManager( $faultyToolbox );
49 $faultyWriter->setSQLState( '23000' );
50 $faultyAssociationManager->associate( $bunny, $carrot );
52 $faultyWriter->setSQLState( '42S22' );
55 $faultyAssociationManager->associate( $bunny, $carrot );
57 } catch ( SQL $exception ) {
63 * Test fast-track deletion, i.e. bypassing FUSE.
68 public function testFastTrackDeletion()
70 testpack( 'Test fast-track deletion' );
71 $ghost = R::dispense( 'ghost' );
72 $house = R::dispense( 'house' );
73 $house->sharedGhost[] = $ghost;
74 \Model_Ghost_House::$deleted = FALSE;
75 R::getRedBean()->getAssociationManager()->unassociate( $house, $ghost );
76 // No fast-track, assoc bean got trashed
77 asrt( \Model_Ghost_House::$deleted, TRUE );
78 \Model_Ghost_House::$deleted = FALSE;
79 R::getRedBean()->getAssociationManager()->unassociate( $house, $ghost, TRUE );
80 // Fast-track, assoc bean got deleted right away
81 asrt( \Model_Ghost_House::$deleted, FALSE );
85 * Test self-referential associations.
89 public function testCrossAssociation()
91 $ghost = R::dispense( 'ghost' );
92 $ghost2 = R::dispense( 'ghost' );
93 R::getRedBean()->getAssociationManager()->associate( $ghost, $ghost2 );
94 \Model_Ghost_Ghost::$deleted = FALSE;
95 R::getRedBean()->getAssociationManager()->unassociate( $ghost, $ghost2 );
96 // No fast-track, assoc bean got trashed
97 asrt( \Model_Ghost_Ghost::$deleted, TRUE );
98 \Model_Ghost_Ghost::$deleted = FALSE;
99 R::getRedBean()->getAssociationManager()->unassociate( $ghost, $ghost2, TRUE );
100 // Fast-track, assoc bean got deleted right away
101 asrt( \Model_Ghost_Ghost::$deleted, FALSE );
105 * Test limited support for polymorph associations.
106 * RedBeanPHP does not really feature polymorph relations since
107 * they are not really compatible with traditional relational databases.
108 * However a light-weight, basic implementation has been added for
109 * those circumstances where you can't live without...
110 * i.e... possible legacy systems and so on.
114 public function testPoly()
116 testpack( 'Testing poly' );
117 $shoe = R::dispense( 'shoe' );
118 $lace = R::dispense( 'lace' );
119 $lace->color = 'white';
120 $id = R::store( $lace );
121 $shoe->itemType = 'lace';
123 $id = R::store( $shoe );
124 $shoe = R::load( 'shoe', $id );
125 $x = $shoe->poly( 'itemType' )->item;
126 asrt( $x->color, 'white' );
130 * Test limited support for 1-to-1 associations.
131 * The rule is, one-to-ones are supposes to be in the same table,
132 * this is just for some legacy tables not designed to work
133 * with RedBeanPHP at all.
137 public function testOneToOne()
139 testpack( 'Testing one-to-ones' );
140 $author = R::dispense( 'author' )->setAttr( 'name', 'a' );;
141 $bio = R::dispense( 'bio' )->setAttr( 'name', 'a' );
142 R::storeAll( array( $author, $bio ) );
144 $author = R::dispense( 'author' )->setAttr( 'name', 'b' );;
145 $bio = R::dispense( 'bio' )->setAttr( 'name', 'b' );
146 R::storeAll( array( $author, $bio ) );
147 $x = $author->one( 'bio' );
148 $y = $bio->one('author');
149 asrt( $x->name, $bio->name );
150 asrt( $y->name, $author->name );
151 asrt( $x->id, $bio->id );
152 asrt( $y->id, $author->id );
154 list( $a, $b ) = R::loadMulti( 'author,bio', $id1 );
155 asrt( $a->name, $b->name );
156 asrt( $a->name, 'a' );
157 list( $a, $b ) = R::loadMulti( 'author,bio', $id2 );
158 asrt( $a->name, $b->name );
159 asrt( $a->name, 'b' );
160 list( $a, $b ) = R::loadMulti( array( 'author', 'bio' ), $id1 );
161 asrt( $a->name, $b->name );
162 asrt( $a->name, 'a' );
163 list( $a, $b ) = R::loadMulti( array( 'author', 'bio' ), $id2 );
164 asrt( $a->name, $b->name );
165 asrt( $a->name, 'b' );
166 asrt( is_array( R::loadMulti( NULL, 1 ) ), TRUE );
167 asrt( ( count( R::loadMulti( NULL, 1 ) ) === 0 ), TRUE );
171 * Test single column bases unique constraints.
175 public function testSingleColUniqueConstraint()
177 testpack( 'Testing unique constraint on single column' );
178 $book = R::dispense( 'book' );
179 $book->title = 'bla';
181 $id = R::store( $book );
182 R::getWriter()->addUniqueIndex( 'book', array( 'title' ) );
183 $book = R::dispense( 'book' );
184 $book->title = 'bla';
193 asrt( ( $expected instanceof SQL ), TRUE );
194 asrt( R::count( 'book' ), 1 );
195 $book = R::load( 'book', $id );
196 // Causes failure, table will be rebuild
197 $book->extra = 'CHANGE';
198 $id2 = R::store( $book );
199 $book2 = R::load( 'book', $id2 );
200 $book = R::dispense( 'book' );
201 $book->title = 'bla';
209 asrt( ( $expected instanceof SQL ), TRUE );
210 asrt( R::count( 'book' ), 1 );
214 * Test multiple assiociation.
218 public function testMultiAssociationDissociation()
220 $wines = R::dispense( 'wine', 3 );
221 $cheese = R::dispense( 'cheese', 3 );
222 $olives = R::dispense( 'olive', 3 );
223 R::getRedBean()->getAssociationManager()->associate( $wines, array_merge( $cheese, $olives ) );
224 asrt( R::count( 'cheese' ), 3 );
225 asrt( R::count( 'olive' ), 3 );
226 asrt( R::count( 'wine' ), 3 );
227 asrt( count( $wines[0]->sharedCheese ), 3 );
228 asrt( count( $wines[0]->sharedOlive ), 3 );
229 asrt( count( $wines[1]->sharedCheese ), 3 );
230 asrt( count( $wines[1]->sharedOlive ), 3 );
231 asrt( count( $wines[2]->sharedCheese ), 3 );
232 asrt( count( $wines[2]->sharedOlive ), 3 );
233 R::getRedBean()->getAssociationManager()->unassociate( $wines, $olives );
234 asrt( count( $wines[0]->sharedCheese ), 3 );
235 asrt( count( $wines[0]->sharedOlive ), 0 );
236 asrt( count( $wines[1]->sharedCheese ), 3 );
237 asrt( count( $wines[1]->sharedOlive ), 0 );
238 asrt( count( $wines[2]->sharedCheese ), 3 );
239 asrt( count( $wines[2]->sharedOlive ), 0 );
240 R::getRedBean()->getAssociationManager()->unassociate( array( $wines[1] ), $cheese );
241 asrt( count( $wines[0]->sharedCheese ), 3 );
242 asrt( count( $wines[0]->sharedOlive ), 0 );
243 asrt( count( $wines[1]->sharedCheese ), 0 );
244 asrt( count( $wines[1]->sharedOlive ), 0 );
245 asrt( count( $wines[2]->sharedCheese ), 3 );
246 asrt( count( $wines[2]->sharedOlive ), 0 );
247 R::getRedBean()->getAssociationManager()->unassociate( array( $wines[2] ), $cheese );
248 asrt( count( $wines[0]->sharedCheese ), 3 );
249 asrt( count( $wines[0]->sharedOlive ), 0 );
250 asrt( count( $wines[1]->sharedCheese ), 0 );
251 asrt( count( $wines[1]->sharedOlive ), 0 );
252 asrt( count( $wines[2]->sharedCheese ), 0 );
253 asrt( count( $wines[2]->sharedOlive ), 0 );
257 * Tests error handling related to association.
258 * On database systems providing informative SQL STATE error codes
259 * RedBeanPHP should not mind non-existing tables or columns in
264 public function testErrorHandling()
267 list( $book, $page ) = R::dispenseAll( 'book,page' );
268 $book->sharedPage[] = $page;
270 $redbean = R::getRedBean();
271 $am = $redbean->getAssociationManager();
272 //SQLite and CUBRID do not comply with ANSI SQLState codes.
273 $catchAll = ( $this->currentlyActiveDriverID == 'sqlite' || $this->currentlyActiveDriverID === 'CUBRID' );
275 $am->related( $book, 'page', 'invalid SQL' );
276 if ($catchAll) pass(); else fail();
278 if ($catchAll) fail(); else pass();
281 $am->related( $book, 'cover');
287 $am->related( R::dispense('cover'), 'book' );