CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Team
    • Issues (Github)
    • YouTube Channel
    • Get Involved
    • Bakery
    • Featured Resources
    • Newsletter
    • Certification
    • My CakePHP
    • CakeFest
    • Facebook
    • Twitter
    • Help & Support
    • Forum
    • Stack Overflow
    • IRC
    • Slack
    • Paid Support
CakePHP

C CakePHP 3.8 Red Velvet API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 3.8
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Namespaces

  • Cake
    • Auth
      • Storage
    • Cache
      • Engine
    • Collection
      • Iterator
    • Command
    • Console
      • Exception
    • Controller
      • Component
      • Exception
    • Core
      • Configure
        • Engine
      • Exception
      • Retry
    • Database
      • Driver
      • Exception
      • Expression
      • Schema
      • Statement
      • Type
    • Datasource
      • Exception
    • Error
      • Middleware
    • Event
      • Decorator
    • Filesystem
    • Form
    • Http
      • Client
        • Adapter
        • Auth
      • Cookie
      • Exception
      • Middleware
      • Session
    • I18n
      • Formatter
      • Middleware
      • Parser
    • Log
      • Engine
    • Mailer
      • Exception
      • Transport
    • Network
      • Exception
    • ORM
      • Association
      • Behavior
        • Translate
      • Exception
      • Locator
      • Rule
    • Routing
      • Exception
      • Filter
      • Middleware
      • Route
    • Shell
      • Helper
      • Task
    • TestSuite
      • Fixture
      • Stub
    • Utility
      • Exception
    • Validation
    • View
      • Exception
      • Form
      • Helper
      • Widget
  • None

Classes

  • FixtureInjector
  • FixtureManager
  • TestFixture
  1: <?php
  2: /**
  3:  * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4:  * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5:  *
  6:  * Licensed under The MIT License
  7:  * For full copyright and license information, please see the LICENSE.txt
  8:  * Redistributions of files must retain the above copyright notice.
  9:  *
 10:  * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 11:  * @link          https://cakephp.org CakePHP(tm) Project
 12:  * @since         2.0.0
 13:  * @license       https://opensource.org/licenses/mit-license.php MIT License
 14:  */
 15: namespace Cake\TestSuite\Fixture;
 16: 
 17: loadPHPUnitAliases();
 18: 
 19: use Cake\Core\Configure;
 20: use Cake\Core\Exception\Exception;
 21: use Cake\Database\Schema\TableSchema;
 22: use Cake\Database\Schema\TableSchemaAwareInterface;
 23: use Cake\Datasource\ConnectionManager;
 24: use Cake\Utility\Inflector;
 25: use PDOException;
 26: use UnexpectedValueException;
 27: 
 28: /**
 29:  * A factory class to manage the life cycle of test fixtures
 30:  */
 31: class FixtureManager
 32: {
 33:     /**
 34:      * Was this instance already initialized?
 35:      *
 36:      * @var bool
 37:      */
 38:     protected $_initialized = false;
 39: 
 40:     /**
 41:      * Holds the fixture classes that where instantiated
 42:      *
 43:      * @var \Cake\Datasource\FixtureInterface[]
 44:      */
 45:     protected $_loaded = [];
 46: 
 47:     /**
 48:      * Holds the fixture classes that where instantiated indexed by class name
 49:      *
 50:      * @var \Cake\Datasource\FixtureInterface[]
 51:      */
 52:     protected $_fixtureMap = [];
 53: 
 54:     /**
 55:      * A map of connection names and the fixture currently in it.
 56:      *
 57:      * @var array
 58:      */
 59:     protected $_insertionMap = [];
 60: 
 61:     /**
 62:      * List of TestCase class name that have been processed
 63:      *
 64:      * @var array
 65:      */
 66:     protected $_processed = [];
 67: 
 68:     /**
 69:      * Is the test runner being run with `--debug` enabled.
 70:      * When true, fixture SQL will also be logged.
 71:      *
 72:      * @var bool
 73:      */
 74:     protected $_debug = false;
 75: 
 76:     /**
 77:      * Modify the debug mode.
 78:      *
 79:      * @param bool $debug Whether or not fixture debug mode is enabled.
 80:      * @return void
 81:      */
 82:     public function setDebug($debug)
 83:     {
 84:         $this->_debug = $debug;
 85:     }
 86: 
 87:     /**
 88:      * Inspects the test to look for unloaded fixtures and loads them
 89:      *
 90:      * @param \Cake\TestSuite\TestCase $test The test case to inspect.
 91:      * @return void
 92:      */
 93:     public function fixturize($test)
 94:     {
 95:         $this->_initDb();
 96:         if (empty($test->fixtures) || !empty($this->_processed[get_class($test)])) {
 97:             return;
 98:         }
 99:         if (!is_array($test->fixtures)) {
100:             $test->fixtures = array_map('trim', explode(',', $test->fixtures));
101:         }
102:         $this->_loadFixtures($test);
103:         $this->_processed[get_class($test)] = true;
104:     }
105: 
106:     /**
107:      * Get the loaded fixtures.
108:      *
109:      * @return array
110:      */
111:     public function loaded()
112:     {
113:         return $this->_loaded;
114:     }
115: 
116:     /**
117:      * Add aliases for all non test prefixed connections.
118:      *
119:      * This allows models to use the test connections without
120:      * a pile of configuration work.
121:      *
122:      * @return void
123:      */
124:     protected function _aliasConnections()
125:     {
126:         $connections = ConnectionManager::configured();
127:         ConnectionManager::alias('test', 'default');
128:         $map = [];
129:         foreach ($connections as $connection) {
130:             if ($connection === 'test' || $connection === 'default') {
131:                 continue;
132:             }
133:             if (isset($map[$connection])) {
134:                 continue;
135:             }
136:             if (strpos($connection, 'test_') === 0) {
137:                 $map[$connection] = substr($connection, 5);
138:             } else {
139:                 $map['test_' . $connection] = $connection;
140:             }
141:         }
142:         foreach ($map as $testConnection => $normal) {
143:             ConnectionManager::alias($testConnection, $normal);
144:         }
145:     }
146: 
147:     /**
148:      * Initializes this class with a DataSource object to use as default for all fixtures
149:      *
150:      * @return void
151:      */
152:     protected function _initDb()
153:     {
154:         if ($this->_initialized) {
155:             return;
156:         }
157:         $this->_aliasConnections();
158:         $this->_initialized = true;
159:     }
160: 
161:     /**
162:      * Looks for fixture files and instantiates the classes accordingly
163:      *
164:      * @param \Cake\TestSuite\TestCase $test The test suite to load fixtures for.
165:      * @return void
166:      * @throws \UnexpectedValueException when a referenced fixture does not exist.
167:      */
168:     protected function _loadFixtures($test)
169:     {
170:         if (empty($test->fixtures)) {
171:             return;
172:         }
173:         foreach ($test->fixtures as $fixture) {
174:             if (isset($this->_loaded[$fixture])) {
175:                 continue;
176:             }
177: 
178:             if (strpos($fixture, '.')) {
179:                 list($type, $pathName) = explode('.', $fixture, 2);
180:                 $path = explode('/', $pathName);
181:                 $name = array_pop($path);
182:                 $additionalPath = implode('\\', $path);
183: 
184:                 if ($type === 'core') {
185:                     $baseNamespace = 'Cake';
186:                 } elseif ($type === 'app') {
187:                     $baseNamespace = Configure::read('App.namespace');
188:                 } elseif ($type === 'plugin') {
189:                     list($plugin, $name) = explode('.', $pathName);
190:                     // Flip vendored plugin separators
191:                     $path = str_replace('/', '\\', $plugin);
192:                     $uninflected = $path;
193:                     $baseNamespace = Inflector::camelize(str_replace('\\', '\ ', $path));
194:                     if ($baseNamespace !== $uninflected) {
195:                         deprecationWarning(sprintf(
196:                             'Declaring fixtures in underscored format in TestCase::$fixtures is deprecated.' . "\n" .
197:                             'Expected "%s" instead in "%s".',
198:                             str_replace('\\', '/', $baseNamespace),
199:                             get_class($test)
200:                         ));
201:                     }
202:                     $additionalPath = null;
203:                 } else {
204:                     $baseNamespace = '';
205:                     $name = $fixture;
206:                 }
207: 
208:                 $uninflected = $name;
209:                 // Tweak subdirectory names, so camelize() can make the correct name
210:                 if (strpos($name, '/') > 0) {
211:                     $name = str_replace('/', '\\', $name);
212:                     $uninflected = $name;
213:                     $name = str_replace('\\', '\ ', $name);
214:                 }
215: 
216:                 $name = Inflector::camelize($name);
217:                 if ($name !== $uninflected) {
218:                     deprecationWarning(sprintf(
219:                         'Declaring fixtures in underscored format in TestCase::$fixtures is deprecated.' . "\n" .
220:                         'Found "%s.%s" in "%s". Expected "%s.%s" instead.',
221:                         $type,
222:                         $uninflected,
223:                         get_class($test),
224:                         $type,
225:                         str_replace('\\', '/', $name)
226:                     ));
227:                 }
228: 
229:                 $nameSegments = [
230:                     $baseNamespace,
231:                     'Test\Fixture',
232:                     $additionalPath,
233:                     $name . 'Fixture'
234:                 ];
235:                 $className = implode('\\', array_filter($nameSegments));
236:             } else {
237:                 $className = $fixture;
238:                 $name = preg_replace('/Fixture\z/', '', substr(strrchr($fixture, '\\'), 1));
239:             }
240: 
241:             if (class_exists($className)) {
242:                 $this->_loaded[$fixture] = new $className();
243:                 $this->_fixtureMap[$name] = $this->_loaded[$fixture];
244:             } else {
245:                 $msg = sprintf(
246:                     'Referenced fixture class "%s" not found. Fixture "%s" was referenced in test case "%s".',
247:                     $className,
248:                     $fixture,
249:                     get_class($test)
250:                 );
251:                 throw new UnexpectedValueException($msg);
252:             }
253:         }
254:     }
255: 
256:     /**
257:      * Runs the drop and create commands on the fixtures if necessary.
258:      *
259:      * @param \Cake\Datasource\FixtureInterface $fixture the fixture object to create
260:      * @param \Cake\Database\Connection $db The Connection object instance to use
261:      * @param string[] $sources The existing tables in the datasource.
262:      * @param bool $drop whether drop the fixture if it is already created or not
263:      * @return void
264:      */
265:     protected function _setupTable($fixture, $db, array $sources, $drop = true)
266:     {
267:         $configName = $db->configName();
268:         $isFixtureSetup = $this->isFixtureSetup($configName, $fixture);
269:         if ($isFixtureSetup) {
270:             return;
271:         }
272: 
273:         $table = $fixture->sourceName();
274:         $exists = in_array($table, $sources);
275: 
276:         $hasSchema = $fixture instanceof TableSchemaAwareInterface && $fixture->getTableSchema() instanceof TableSchema;
277: 
278:         if (($drop && $exists) || ($exists && !$isFixtureSetup && $hasSchema)) {
279:             $fixture->drop($db);
280:             $fixture->create($db);
281:         } elseif (!$exists) {
282:             $fixture->create($db);
283:         } else {
284:             $fixture->truncate($db);
285:         }
286: 
287:         $this->_insertionMap[$configName][] = $fixture;
288:     }
289: 
290:     /**
291:      * Creates the fixtures tables and inserts data on them.
292:      *
293:      * @param \Cake\TestSuite\TestCase $test The test to inspect for fixture loading.
294:      * @return void
295:      * @throws \Cake\Core\Exception\Exception When fixture records cannot be inserted.
296:      * @throws \RuntimeException
297:      */
298:     public function load($test)
299:     {
300:         if (empty($test->fixtures)) {
301:             return;
302:         }
303: 
304:         $fixtures = $test->fixtures;
305:         if (empty($fixtures) || !$test->autoFixtures) {
306:             return;
307:         }
308: 
309:         try {
310:             $createTables = function ($db, $fixtures) use ($test) {
311:                 $tables = $db->getSchemaCollection()->listTables();
312:                 $configName = $db->configName();
313:                 if (!isset($this->_insertionMap[$configName])) {
314:                     $this->_insertionMap[$configName] = [];
315:                 }
316: 
317:                 foreach ($fixtures as $fixture) {
318:                     if (in_array($fixture->table, $tables)) {
319:                         try {
320:                             $fixture->dropConstraints($db);
321:                         } catch (PDOException $e) {
322:                             $msg = sprintf(
323:                                 'Unable to drop constraints for fixture "%s" in "%s" test case: ' . "\n" . '%s',
324:                                 get_class($fixture),
325:                                 get_class($test),
326:                                 $e->getMessage()
327:                             );
328:                             throw new Exception($msg, null, $e);
329:                         }
330:                     }
331:                 }
332: 
333:                 foreach ($fixtures as $fixture) {
334:                     if (!in_array($fixture, $this->_insertionMap[$configName])) {
335:                         $this->_setupTable($fixture, $db, $tables, $test->dropTables);
336:                     } else {
337:                         $fixture->truncate($db);
338:                     }
339:                 }
340: 
341:                 foreach ($fixtures as $fixture) {
342:                     try {
343:                         $fixture->createConstraints($db);
344:                     } catch (PDOException $e) {
345:                         $msg = sprintf(
346:                             'Unable to create constraints for fixture "%s" in "%s" test case: ' . "\n" . '%s',
347:                             get_class($fixture),
348:                             get_class($test),
349:                             $e->getMessage()
350:                         );
351:                         throw new Exception($msg, null, $e);
352:                     }
353:                 }
354:             };
355:             $this->_runOperation($fixtures, $createTables);
356: 
357:             // Use a separate transaction because of postgres.
358:             $insert = function ($db, $fixtures) use ($test) {
359:                 foreach ($fixtures as $fixture) {
360:                     try {
361:                         $fixture->insert($db);
362:                     } catch (PDOException $e) {
363:                         $msg = sprintf(
364:                             'Unable to insert fixture "%s" in "%s" test case: ' . "\n" . '%s',
365:                             get_class($fixture),
366:                             get_class($test),
367:                             $e->getMessage()
368:                         );
369:                         throw new Exception($msg, null, $e);
370:                     }
371:                 }
372:             };
373:             $this->_runOperation($fixtures, $insert);
374:         } catch (PDOException $e) {
375:             $msg = sprintf(
376:                 'Unable to insert fixtures for "%s" test case. %s',
377:                 get_class($test),
378:                 $e->getMessage()
379:             );
380:             throw new Exception($msg, null, $e);
381:         }
382:     }
383: 
384:     /**
385:      * Run a function on each connection and collection of fixtures.
386:      *
387:      * @param string[] $fixtures A list of fixtures to operate on.
388:      * @param callable $operation The operation to run on each connection + fixture set.
389:      * @return void
390:      */
391:     protected function _runOperation($fixtures, $operation)
392:     {
393:         $dbs = $this->_fixtureConnections($fixtures);
394:         foreach ($dbs as $connection => $fixtures) {
395:             $db = ConnectionManager::get($connection);
396:             $newMethods = method_exists($db, 'isQueryLoggingEnabled');
397:             if ($newMethods) {
398:                 $logQueries = $db->isQueryLoggingEnabled();
399:             } else {
400:                 $logQueries = $db->logQueries();
401:             }
402: 
403:             if ($logQueries && !$this->_debug) {
404:                 if ($newMethods) {
405:                     $db->disableQueryLogging();
406:                 } else {
407:                     $db->logQueries(false);
408:                 }
409:             }
410:             $db->transactional(function ($db) use ($fixtures, $operation) {
411:                 $db->disableConstraints(function ($db) use ($fixtures, $operation) {
412:                     $operation($db, $fixtures);
413:                 });
414:             });
415:             if ($logQueries) {
416:                 if ($newMethods) {
417:                     $db->enableQueryLogging(true);
418:                 } else {
419:                     $db->logQueries(true);
420:                 }
421:             }
422:         }
423:     }
424: 
425:     /**
426:      * Get the unique list of connections that a set of fixtures contains.
427:      *
428:      * @param string[] $fixtures The array of fixtures a list of connections is needed from.
429:      * @return array An array of connection names.
430:      */
431:     protected function _fixtureConnections($fixtures)
432:     {
433:         $dbs = [];
434:         foreach ($fixtures as $name) {
435:             if (!empty($this->_loaded[$name])) {
436:                 $fixture = $this->_loaded[$name];
437:                 $dbs[$fixture->connection()][$name] = $fixture;
438:             }
439:         }
440: 
441:         return $dbs;
442:     }
443: 
444:     /**
445:      * Truncates the fixtures tables
446:      *
447:      * @param \Cake\TestSuite\TestCase $test The test to inspect for fixture unloading.
448:      * @return void
449:      */
450:     public function unload($test)
451:     {
452:         if (empty($test->fixtures)) {
453:             return;
454:         }
455:         $truncate = function ($db, $fixtures) {
456:             $configName = $db->configName();
457: 
458:             foreach ($fixtures as $name => $fixture) {
459:                 if ($this->isFixtureSetup($configName, $fixture)) {
460:                     $fixture->dropConstraints($db);
461:                 }
462:             }
463: 
464:             foreach ($fixtures as $fixture) {
465:                 if ($this->isFixtureSetup($configName, $fixture)) {
466:                     $fixture->truncate($db);
467:                 }
468:             }
469:         };
470:         $this->_runOperation($test->fixtures, $truncate);
471:     }
472: 
473:     /**
474:      * Creates a single fixture table and loads data into it.
475:      *
476:      * @param string $name of the fixture
477:      * @param \Cake\Datasource\ConnectionInterface|null $db Connection instance or leave null to get a Connection from the fixture
478:      * @param bool $dropTables Whether or not tables should be dropped and re-created.
479:      * @return void
480:      * @throws \UnexpectedValueException if $name is not a previously loaded class
481:      */
482:     public function loadSingle($name, $db = null, $dropTables = true)
483:     {
484:         if (!isset($this->_fixtureMap[$name])) {
485:             throw new UnexpectedValueException(sprintf('Referenced fixture class %s not found', $name));
486:         }
487: 
488:         $fixture = $this->_fixtureMap[$name];
489:         if (!$db) {
490:             $db = ConnectionManager::get($fixture->connection());
491:         }
492: 
493:         if (!$this->isFixtureSetup($db->configName(), $fixture)) {
494:             $sources = $db->getSchemaCollection()->listTables();
495:             $this->_setupTable($fixture, $db, $sources, $dropTables);
496:         }
497: 
498:         if (!$dropTables) {
499:             $fixture->dropConstraints($db);
500:             $fixture->truncate($db);
501:         }
502: 
503:         $fixture->createConstraints($db);
504:         $fixture->insert($db);
505:     }
506: 
507:     /**
508:      * Drop all fixture tables loaded by this class
509:      *
510:      * @return void
511:      */
512:     public function shutDown()
513:     {
514:         $shutdown = function ($db, $fixtures) {
515:             $connection = $db->configName();
516:             foreach ($fixtures as $fixture) {
517:                 if ($this->isFixtureSetup($connection, $fixture)) {
518:                     $fixture->drop($db);
519:                     $index = array_search($fixture, $this->_insertionMap[$connection]);
520:                     unset($this->_insertionMap[$connection][$index]);
521:                 }
522:             }
523:         };
524:         $this->_runOperation(array_keys($this->_loaded), $shutdown);
525:     }
526: 
527:     /**
528:      * Check whether or not a fixture has been inserted in a given connection name.
529:      *
530:      * @param string $connection The connection name.
531:      * @param \Cake\Datasource\FixtureInterface $fixture The fixture to check.
532:      * @return bool
533:      */
534:     public function isFixtureSetup($connection, $fixture)
535:     {
536:         return isset($this->_insertionMap[$connection]) && in_array($fixture, $this->_insertionMap[$connection]);
537:     }
538: }
539: 
Follow @CakePHP
#IRC
OpenHub
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Logos & Trademarks
  • Community
  • Team
  • Issues (Github)
  • YouTube Channel
  • Get Involved
  • Bakery
  • Featured Resources
  • Newsletter
  • Certification
  • My CakePHP
  • CakeFest
  • Facebook
  • Twitter
  • Help & Support
  • Forum
  • Stack Overflow
  • IRC
  • Slack
  • Paid Support

Generated using CakePHP API Docs