PageRenderTime 27ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureManager.php

https://gitlab.com/vannh/portal_training
PHP | 372 lines | 234 code | 25 blank | 113 comment | 36 complexity | 14ed8eae81b0b8b01aea961187c725c2 MD5 | raw file
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://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. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 2.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\TestSuite\Fixture;
  16. use Cake\Core\Configure;
  17. use Cake\Core\Exception\Exception;
  18. use Cake\Database\Connection;
  19. use Cake\Datasource\ConnectionManager;
  20. use Cake\TestSuite\Fixture\TestFixture;
  21. use Cake\Utility\Inflector;
  22. /**
  23. * A factory class to manage the life cycle of test fixtures
  24. *
  25. */
  26. class FixtureManager
  27. {
  28. /**
  29. * Was this class already initialized?
  30. *
  31. * @var bool
  32. */
  33. protected $_initialized = false;
  34. /**
  35. * Holds the fixture classes that where instantiated
  36. *
  37. * @var array
  38. */
  39. protected $_loaded = [];
  40. /**
  41. * Holds the fixture classes that where instantiated indexed by class name
  42. *
  43. * @var array
  44. */
  45. protected $_fixtureMap = [];
  46. /**
  47. * Inspects the test to look for unloaded fixtures and loads them
  48. *
  49. * @param \Cake\TestSuite\TestCase $test The test case to inspect.
  50. * @return void
  51. */
  52. public function fixturize($test)
  53. {
  54. $this->_initDb();
  55. if (empty($test->fixtures) || !empty($this->_processed[get_class($test)])) {
  56. return;
  57. }
  58. if (!is_array($test->fixtures)) {
  59. $test->fixtures = array_map('trim', explode(',', $test->fixtures));
  60. }
  61. $this->_loadFixtures($test);
  62. $this->_processed[get_class($test)] = true;
  63. }
  64. /**
  65. * Get the loaded fixtures.
  66. *
  67. * @return array
  68. */
  69. public function loaded()
  70. {
  71. return $this->_loaded;
  72. }
  73. /**
  74. * Add aliases for all non test prefixed connections.
  75. *
  76. * This allows models to use the test connections without
  77. * a pile of configuration work.
  78. *
  79. * @return void
  80. */
  81. protected function _aliasConnections()
  82. {
  83. $connections = ConnectionManager::configured();
  84. ConnectionManager::alias('test', 'default');
  85. $map = [];
  86. foreach ($connections as $connection) {
  87. if ($connection === 'test' || $connection === 'default') {
  88. continue;
  89. }
  90. if (isset($map[$connection])) {
  91. continue;
  92. }
  93. if (strpos($connection, 'test_') === 0) {
  94. $map[$connection] = substr($connection, 5);
  95. } else {
  96. $map['test_' . $connection] = $connection;
  97. }
  98. }
  99. foreach ($map as $alias => $connection) {
  100. ConnectionManager::alias($alias, $connection);
  101. }
  102. }
  103. /**
  104. * Initializes this class with a DataSource object to use as default for all fixtures
  105. *
  106. * @return void
  107. */
  108. protected function _initDb()
  109. {
  110. if ($this->_initialized) {
  111. return;
  112. }
  113. $this->_aliasConnections();
  114. $this->_initialized = true;
  115. }
  116. /**
  117. * Looks for fixture files and instantiates the classes accordingly
  118. *
  119. * @param \Cake\TestSuite\TestCase $test The test suite to load fixtures for.
  120. * @return void
  121. * @throws \UnexpectedValueException when a referenced fixture does not exist.
  122. */
  123. protected function _loadFixtures($test)
  124. {
  125. if (empty($test->fixtures)) {
  126. return;
  127. }
  128. foreach ($test->fixtures as $fixture) {
  129. if (isset($this->_loaded[$fixture])) {
  130. continue;
  131. }
  132. list($type, $pathName) = explode('.', $fixture, 2);
  133. $path = explode('/', $pathName);
  134. $name = array_pop($path);
  135. $additionalPath = implode('\\', $path);
  136. if ($type === 'core') {
  137. $baseNamespace = 'Cake';
  138. } elseif ($type === 'app') {
  139. $baseNamespace = Configure::read('App.namespace');
  140. } elseif ($type === 'plugin') {
  141. list($plugin, $name) = explode('.', $pathName);
  142. $path = implode('\\', explode('/', $plugin));
  143. $baseNamespace = Inflector::camelize(str_replace('\\', '\ ', $path));
  144. $additionalPath = null;
  145. } else {
  146. $baseNamespace = '';
  147. $name = $fixture;
  148. }
  149. $name = Inflector::camelize($name);
  150. $nameSegments = [
  151. $baseNamespace,
  152. 'Test\Fixture',
  153. $additionalPath,
  154. $name . 'Fixture'
  155. ];
  156. $className = implode('\\', array_filter($nameSegments));
  157. if (class_exists($className)) {
  158. $this->_loaded[$fixture] = new $className();
  159. $this->_fixtureMap[$name] = $this->_loaded[$fixture];
  160. } else {
  161. $msg = sprintf(
  162. 'Referenced fixture class "%s" not found. Fixture "%s" was referenced in test case "%s".',
  163. $className,
  164. $fixture,
  165. get_class($test)
  166. );
  167. throw new \UnexpectedValueException($msg);
  168. }
  169. }
  170. }
  171. /**
  172. * Runs the drop and create commands on the fixtures if necessary.
  173. *
  174. * @param \Cake\TestSuite\Fixture\TestFixture $fixture the fixture object to create
  175. * @param Connection $db the datasource instance to use
  176. * @param array $sources The existing tables in the datasource.
  177. * @param bool $drop whether drop the fixture if it is already created or not
  178. * @return void
  179. */
  180. protected function _setupTable($fixture, $db, array $sources, $drop = true)
  181. {
  182. if (!empty($fixture->created) && in_array($db->configName(), $fixture->created)) {
  183. return;
  184. }
  185. $table = $fixture->table;
  186. $exists = in_array($table, $sources);
  187. if ($drop && $exists) {
  188. $fixture->drop($db);
  189. $fixture->create($db);
  190. } elseif (!$exists) {
  191. $fixture->create($db);
  192. } else {
  193. $fixture->created[] = $db->configName();
  194. $fixture->truncate($db);
  195. }
  196. }
  197. /**
  198. * Creates the fixtures tables and inserts data on them.
  199. *
  200. * @param \Cake\TestSuite\TestCase $test The test to inspect for fixture loading.
  201. * @return void
  202. * @throws \Cake\Core\Exception\Exception When fixture records cannot be inserted.
  203. */
  204. public function load($test)
  205. {
  206. if (empty($test->fixtures)) {
  207. return;
  208. }
  209. $fixtures = $test->fixtures;
  210. if (empty($fixtures) || !$test->autoFixtures) {
  211. return;
  212. }
  213. try {
  214. $createTables = function ($db, $fixtures) use ($test) {
  215. $tables = $db->schemaCollection()->listTables();
  216. foreach ($fixtures as $fixture) {
  217. if (!in_array($db->configName(), (array)$fixture->created)) {
  218. $this->_setupTable($fixture, $db, $tables, $test->dropTables);
  219. } else {
  220. $fixture->truncate($db);
  221. }
  222. }
  223. };
  224. $this->_runOperation($fixtures, $createTables);
  225. // Use a separate transaction because of postgres.
  226. $insert = function ($db, $fixtures) {
  227. foreach ($fixtures as $fixture) {
  228. $fixture->insert($db);
  229. }
  230. };
  231. $this->_runOperation($fixtures, $insert);
  232. } catch (\PDOException $e) {
  233. $msg = sprintf('Unable to insert fixtures for "%s" test case. %s', get_class($test), $e->getMessage());
  234. throw new Exception($msg);
  235. }
  236. }
  237. /**
  238. * Run a function on each connection and collection of fixtures.
  239. *
  240. * @param array $fixtures A list of fixtures to operate on.
  241. * @param callable $operation The operation to run on each connection + fixture set.
  242. * @return void
  243. */
  244. protected function _runOperation($fixtures, $operation)
  245. {
  246. $dbs = $this->_fixtureConnections($fixtures);
  247. foreach ($dbs as $connection => $fixtures) {
  248. $db = ConnectionManager::get($connection, false);
  249. $logQueries = $db->logQueries();
  250. if ($logQueries) {
  251. $db->logQueries(false);
  252. }
  253. $db->transactional(function ($db) use ($fixtures, $operation) {
  254. $db->disableForeignKeys();
  255. $operation($db, $fixtures);
  256. $db->enableForeignKeys();
  257. });
  258. if ($logQueries) {
  259. $db->logQueries(true);
  260. }
  261. }
  262. }
  263. /**
  264. * Get the unique list of connections that a set of fixtures contains.
  265. *
  266. * @param array $fixtures The array of fixtures a list of connections is needed from.
  267. * @return array An array of connection names.
  268. */
  269. protected function _fixtureConnections($fixtures)
  270. {
  271. $dbs = [];
  272. foreach ($fixtures as $f) {
  273. if (!empty($this->_loaded[$f])) {
  274. $fixture = $this->_loaded[$f];
  275. $dbs[$fixture->connection][$f] = $fixture;
  276. }
  277. }
  278. return $dbs;
  279. }
  280. /**
  281. * Truncates the fixtures tables
  282. *
  283. * @param \Cake\TestSuite\TestCase $test The test to inspect for fixture unloading.
  284. * @return void
  285. */
  286. public function unload($test)
  287. {
  288. if (empty($test->fixtures)) {
  289. return;
  290. }
  291. $truncate = function ($db, $fixtures) {
  292. $connection = $db->configName();
  293. foreach ($fixtures as $fixture) {
  294. if (!empty($fixture->created) && in_array($connection, $fixture->created)) {
  295. $fixture->truncate($db);
  296. }
  297. }
  298. };
  299. $this->_runOperation($test->fixtures, $truncate);
  300. }
  301. /**
  302. * Creates a single fixture table and loads data into it.
  303. *
  304. * @param string $name of the fixture
  305. * @param \Cake\Database\Connection $db Connection instance or leave null to get a Connection from the fixture
  306. * @param bool $dropTables Whether or not tables should be dropped and re-created.
  307. * @return void
  308. * @throws \UnexpectedValueException if $name is not a previously loaded class
  309. */
  310. public function loadSingle($name, $db = null, $dropTables = true)
  311. {
  312. if (isset($this->_fixtureMap[$name])) {
  313. $fixture = $this->_fixtureMap[$name];
  314. if (!$db) {
  315. $db = ConnectionManager::get($fixture->connection);
  316. }
  317. if (!in_array($db->configName(), (array)$fixture->created)) {
  318. $sources = $db->schemaCollection()->listTables();
  319. $this->_setupTable($fixture, $db, $sources, $dropTables);
  320. }
  321. if (!$dropTables) {
  322. $fixture->truncate($db);
  323. }
  324. $fixture->insert($db);
  325. } else {
  326. throw new \UnexpectedValueException(sprintf('Referenced fixture class %s not found', $name));
  327. }
  328. }
  329. /**
  330. * Drop all fixture tables loaded by this class
  331. *
  332. * @return void
  333. */
  334. public function shutDown()
  335. {
  336. $shutdown = function ($db, $fixtures) {
  337. $connection = $db->configName();
  338. foreach ($fixtures as $fixture) {
  339. if (!empty($fixture->created) && in_array($connection, $fixture->created)) {
  340. $fixture->drop($db);
  341. }
  342. }
  343. };
  344. $this->_runOperation(array_keys($this->_loaded), $shutdown);
  345. }
  346. }