Yaffs site version 1.1
[yaffs-website] / vendor / gabordemooij / redbean / testing / RedUNIT / Base / Association.php
1 <?php
2
3 namespace RedUNIT\Base;
4
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;
11
12 /**
13  * Association
14  *
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.
18  *
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
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 Association extends Base
29 {
30         /**
31          * MySQL specific tests.
32          * Test MySQL specific issues with constraints.
33          *
34          * @return void
35          */
36         public function testMySQL()
37         {
38                 if ( $this->currentlyActiveDriverID !== 'mysql' ) {
39                         return;
40                 }
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 );
51                 pass();
52                 $faultyWriter->setSQLState( '42S22' );
53                 R::nuke();
54                 try {
55                         $faultyAssociationManager->associate( $bunny, $carrot );
56                         fail();
57                 } catch ( SQL $exception ) {
58                         pass();
59                 }
60         }
61
62         /**
63          * Test fast-track deletion, i.e. bypassing FUSE.
64          * For link beans.
65          *
66          * @return void
67          */
68         public function testFastTrackDeletion()
69         {
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 );
82         }
83
84         /**
85          * Test self-referential associations.
86          *
87          * @return void
88          */
89         public function testCrossAssociation()
90         {
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 );
102         }
103
104         /**
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.
111          *
112          * @return void
113          */
114         public function testPoly()
115         {
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';
122                 $shoe->item     = $lace;
123                 $id = R::store( $shoe );
124                 $shoe = R::load( 'shoe', $id );
125                 $x = $shoe->poly( 'itemType' )->item;
126                 asrt( $x->color, 'white' );
127         }
128
129         /**
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.
134          *
135          * @return void
136          */
137         public function testOneToOne()
138         {
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 ) );
143                 $id1 = $author->id;
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 );
153                 $id2 = $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 );
168         }
169
170         /**
171          * Test single column bases unique constraints.
172          *
173          * @return void
174          */
175         public function testSingleColUniqueConstraint()
176         {
177                 testpack( 'Testing unique constraint on single column' );
178                 $book = R::dispense( 'book' );
179                 $book->title = 'bla';
180                 $book->extra = 2;
181                 $id = R::store( $book );
182                 R::getWriter()->addUniqueIndex( 'book', array( 'title' ) );
183                 $book = R::dispense( 'book' );
184                 $book->title = 'bla';
185                 $expected = NULL;
186                 try {
187                         R::store( $book );
188
189                         fail();
190                 } catch ( SQL $e ) {
191                         $expected = $e;
192                 }
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';
202                 try {
203                         R::store( $book );
204
205                         fail();
206                 } catch ( SQL $e ) {
207                         $expected = $e;
208                 }
209                 asrt( ( $expected instanceof SQL ), TRUE );
210                 asrt( R::count( 'book' ), 1 );
211         }
212
213         /**
214          * Test multiple assiociation.
215          *
216          * @return void
217          */
218         public function testMultiAssociationDissociation()
219         {
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 );
254         }
255
256         /**
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
260          * fluid mode.
261          *
262          * @return void
263          */
264         public function testErrorHandling()
265         {
266                 R::nuke();
267                 list( $book, $page ) = R::dispenseAll( 'book,page' );
268                 $book->sharedPage[] = $page;
269                 R::store( $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' );
274                 try {
275                         $am->related( $book, 'page', 'invalid SQL' );
276                         if ($catchAll) pass(); else fail();
277                 } catch ( SQL $e ) {
278                         if ($catchAll) fail(); else pass();
279                 }
280                 try {
281                         $am->related( $book, 'cover');
282                         pass();
283                 } catch ( SQL $e ) {
284                         fail();
285                 }
286                 try {
287                         $am->related( R::dispense('cover'), 'book' );
288                         pass();
289                 } catch ( SQL $e ) {
290                         fail();
291                 }
292         }
293 }