1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
14: namespace Cake\TestSuite\Fixture;
15:
16: use Cake\Core\Exception\Exception as CakeException;
17: use Cake\Database\Schema\TableSchema;
18: use Cake\Database\Schema\TableSchemaAwareInterface;
19: use Cake\Database\Schema\TableSchemaInterface as DatabaseTableSchemaInterface;
20: use Cake\Datasource\ConnectionInterface;
21: use Cake\Datasource\ConnectionManager;
22: use Cake\Datasource\FixtureInterface;
23: use Cake\Datasource\TableSchemaInterface;
24: use Cake\Log\Log;
25: use Cake\ORM\Locator\LocatorAwareTrait;
26: use Cake\Utility\Inflector;
27: use Exception;
28:
29: 30: 31: 32:
33: class TestFixture implements FixtureInterface, TableSchemaInterface, TableSchemaAwareInterface
34: {
35: use LocatorAwareTrait;
36:
37: 38: 39: 40: 41:
42: public $connection = 'test';
43:
44: 45: 46: 47: 48:
49: public $table;
50:
51: 52: 53: 54: 55: 56: 57: 58: 59:
60: public $fields = [];
61:
62: 63: 64: 65: 66: 67: 68: 69: 70:
71: public $import;
72:
73: 74: 75: 76: 77:
78: public $records = [];
79:
80: 81: 82: 83: 84:
85: protected $_schema;
86:
87: 88: 89: 90: 91:
92: protected $_constraints = [];
93:
94: 95: 96: 97: 98:
99: public function __construct()
100: {
101: if (!empty($this->connection)) {
102: $connection = $this->connection;
103: if (strpos($connection, 'test') !== 0) {
104: $message = sprintf(
105: 'Invalid datasource name "%s" for "%s" fixture. Fixture datasource names must begin with "test".',
106: $connection,
107: $this->table
108: );
109: throw new CakeException($message);
110: }
111: }
112: $this->init();
113: }
114:
115: 116: 117:
118: public function connection()
119: {
120: return $this->connection;
121: }
122:
123: 124: 125:
126: public function sourceName()
127: {
128: return $this->table;
129: }
130:
131: 132: 133: 134: 135: 136:
137: public function init()
138: {
139: if ($this->table === null) {
140: $this->table = $this->_tableFromClass();
141: }
142:
143: if (empty($this->import) && !empty($this->fields)) {
144: $this->_schemaFromFields();
145: }
146:
147: if (!empty($this->import)) {
148: $this->_schemaFromImport();
149: }
150:
151: if (empty($this->import) && empty($this->fields)) {
152: $this->_schemaFromReflection();
153: }
154: }
155:
156: 157: 158: 159: 160:
161: protected function _tableFromClass()
162: {
163: list(, $class) = namespaceSplit(get_class($this));
164: preg_match('/^(.*)Fixture$/', $class, $matches);
165: $table = $class;
166:
167: if (isset($matches[1])) {
168: $table = $matches[1];
169: }
170:
171: return Inflector::tableize($table);
172: }
173:
174: 175: 176: 177: 178:
179: protected function _schemaFromFields()
180: {
181: $connection = ConnectionManager::get($this->connection());
182: $this->_schema = new TableSchema($this->table);
183: foreach ($this->fields as $field => $data) {
184: if ($field === '_constraints' || $field === '_indexes' || $field === '_options') {
185: continue;
186: }
187: $this->_schema->addColumn($field, $data);
188: }
189: if (!empty($this->fields['_constraints'])) {
190: foreach ($this->fields['_constraints'] as $name => $data) {
191: if (!$connection->supportsDynamicConstraints() || $data['type'] !== TableSchema::CONSTRAINT_FOREIGN) {
192: $this->_schema->addConstraint($name, $data);
193: } else {
194: $this->_constraints[$name] = $data;
195: }
196: }
197: }
198: if (!empty($this->fields['_indexes'])) {
199: foreach ($this->fields['_indexes'] as $name => $data) {
200: $this->_schema->addIndex($name, $data);
201: }
202: }
203: if (!empty($this->fields['_options'])) {
204: $this->_schema->setOptions($this->fields['_options']);
205: }
206: }
207:
208: 209: 210: 211: 212: 213:
214: protected function _schemaFromImport()
215: {
216: if (!is_array($this->import)) {
217: return;
218: }
219: $import = $this->import + ['connection' => 'default', 'table' => null, 'model' => null];
220:
221: if (!empty($import['model'])) {
222: if (!empty($import['table'])) {
223: throw new CakeException('You cannot define both table and model.');
224: }
225: $import['table'] = $this->getTableLocator()->get($import['model'])->getTable();
226: }
227:
228: if (empty($import['table'])) {
229: throw new CakeException('Cannot import from undefined table.');
230: }
231:
232: $this->table = $import['table'];
233:
234: $db = ConnectionManager::get($import['connection'], false);
235: $schemaCollection = $db->getSchemaCollection();
236: $table = $schemaCollection->describe($import['table']);
237: $this->_schema = $table;
238: }
239:
240: 241: 242: 243: 244: 245:
246: protected function _schemaFromReflection()
247: {
248: $db = ConnectionManager::get($this->connection());
249: $schemaCollection = $db->getSchemaCollection();
250: $tables = $schemaCollection->listTables();
251:
252: if (!in_array($this->table, $tables)) {
253: throw new CakeException(
254: sprintf(
255: 'Cannot describe schema for table `%s` for fixture `%s` : the table does not exist.',
256: $this->table,
257: get_class($this)
258: )
259: );
260: }
261:
262: $this->_schema = $schemaCollection->describe($this->table);
263: }
264:
265: 266: 267: 268: 269: 270: 271:
272: public function schema(TableSchema $schema = null)
273: {
274: deprecationWarning(
275: 'TestFixture::schema() is deprecated. ' .
276: 'Use TestFixture::setTableSchema()/getTableSchema() instead.'
277: );
278: if ($schema) {
279: $this->setTableSchema($schema);
280: }
281:
282: return $this->getTableSchema();
283: }
284:
285: 286: 287:
288: public function create(ConnectionInterface $db)
289: {
290: if (empty($this->_schema)) {
291: return false;
292: }
293:
294: if (empty($this->import) && empty($this->fields)) {
295: return true;
296: }
297:
298: try {
299: $queries = $this->_schema->createSql($db);
300: foreach ($queries as $query) {
301: $stmt = $db->prepare($query);
302: $stmt->execute();
303: $stmt->closeCursor();
304: }
305: } catch (Exception $e) {
306: $msg = sprintf(
307: 'Fixture creation for "%s" failed "%s"',
308: $this->table,
309: $e->getMessage()
310: );
311: Log::error($msg);
312: trigger_error($msg, E_USER_WARNING);
313:
314: return false;
315: }
316:
317: return true;
318: }
319:
320: 321: 322:
323: public function drop(ConnectionInterface $db)
324: {
325: if (empty($this->_schema)) {
326: return false;
327: }
328:
329: if (empty($this->import) && empty($this->fields)) {
330: return true;
331: }
332:
333: try {
334: $sql = $this->_schema->dropSql($db);
335: foreach ($sql as $stmt) {
336: $db->execute($stmt)->closeCursor();
337: }
338: } catch (Exception $e) {
339: return false;
340: }
341:
342: return true;
343: }
344:
345: 346: 347:
348: public function insert(ConnectionInterface $db)
349: {
350: if (isset($this->records) && !empty($this->records)) {
351: list($fields, $values, $types) = $this->_getRecords();
352: $query = $db->newQuery()
353: ->insert($fields, $types)
354: ->into($this->table);
355:
356: foreach ($values as $row) {
357: $query->values($row);
358: }
359: $statement = $query->execute();
360: $statement->closeCursor();
361:
362: return $statement;
363: }
364:
365: return true;
366: }
367:
368: 369: 370:
371: public function createConstraints(ConnectionInterface $db)
372: {
373: if (empty($this->_constraints)) {
374: return true;
375: }
376:
377: foreach ($this->_constraints as $name => $data) {
378: $this->_schema->addConstraint($name, $data);
379: }
380:
381: $sql = $this->_schema->addConstraintSql($db);
382:
383: if (empty($sql)) {
384: return true;
385: }
386:
387: foreach ($sql as $stmt) {
388: $db->execute($stmt)->closeCursor();
389: }
390:
391: return true;
392: }
393:
394: 395: 396:
397: public function dropConstraints(ConnectionInterface $db)
398: {
399: if (empty($this->_constraints)) {
400: return true;
401: }
402:
403: $sql = $this->_schema->dropConstraintSql($db);
404:
405: if (empty($sql)) {
406: return true;
407: }
408:
409: foreach ($sql as $stmt) {
410: $db->execute($stmt)->closeCursor();
411: }
412:
413: foreach ($this->_constraints as $name => $data) {
414: $this->_schema->dropConstraint($name);
415: }
416:
417: return true;
418: }
419:
420: 421: 422: 423: 424:
425: protected function _getRecords()
426: {
427: $fields = $values = $types = [];
428: $columns = $this->_schema->columns();
429: foreach ($this->records as $record) {
430: $fields = array_merge($fields, array_intersect(array_keys($record), $columns));
431: }
432: $fields = array_values(array_unique($fields));
433: foreach ($fields as $field) {
434: $types[$field] = $this->_schema->getColumn($field)['type'];
435: }
436: $default = array_fill_keys($fields, null);
437: foreach ($this->records as $record) {
438: $values[] = array_merge($default, $record);
439: }
440:
441: return [$fields, $values, $types];
442: }
443:
444: 445: 446:
447: public function truncate(ConnectionInterface $db)
448: {
449: $sql = $this->_schema->truncateSql($db);
450: foreach ($sql as $stmt) {
451: $db->execute($stmt)->closeCursor();
452: }
453:
454: return true;
455: }
456:
457: 458: 459:
460: public function getTableSchema()
461: {
462: return $this->_schema;
463: }
464:
465: 466: 467:
468: public function setTableSchema(DatabaseTableSchemaInterface $schema)
469: {
470: $this->_schema = $schema;
471:
472: return $this;
473: }
474: }
475: