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