comparison cake/console/libs/tasks/fixture.php @ 0:261e66bd5a0c

hg init
author Shoshi TAMAKI <shoshi@cr.ie.u-ryukyu.ac.jp>
date Sun, 24 Jul 2011 21:08:31 +0900
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:261e66bd5a0c
1 <?php
2 /**
3 * The FixtureTask handles creating and updating fixture files.
4 *
5 * PHP versions 4 and 5
6 *
7 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
8 * Copyright 2005-2010, Cake Software Foundation, Inc.
9 *
10 * Licensed under The MIT License
11 * Redistributions of files must retain the above copyright notice.
12 *
13 * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
14 * @link http://cakephp.org CakePHP(tm) Project
15 * @package cake
16 * @subpackage cake.cake.console.libs.tasks
17 * @since CakePHP(tm) v 1.3
18 * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
19 */
20 include_once dirname(__FILE__) . DS . 'bake.php';
21 /**
22 * Task class for creating and updating fixtures files.
23 *
24 * @package cake
25 * @subpackage cake.cake.console.libs.tasks
26 */
27 class FixtureTask extends BakeTask {
28
29 /**
30 * Tasks to be loaded by this Task
31 *
32 * @var array
33 * @access public
34 */
35 var $tasks = array('DbConfig', 'Model', 'Template');
36
37 /**
38 * path to fixtures directory
39 *
40 * @var string
41 * @access public
42 */
43 var $path = null;
44
45 /**
46 * Schema instance
47 *
48 * @var object
49 * @access protected
50 */
51 var $_Schema = null;
52
53 /**
54 * Override initialize
55 *
56 * @access public
57 */
58 function __construct(&$dispatch) {
59 parent::__construct($dispatch);
60 $this->path = $this->params['working'] . DS . 'tests' . DS . 'fixtures' . DS;
61 }
62
63 /**
64 * Execution method always used for tasks
65 * Handles dispatching to interactive, named, or all processess.
66 *
67 * @access public
68 */
69 function execute() {
70 if (empty($this->args)) {
71 $this->__interactive();
72 }
73
74 if (isset($this->args[0])) {
75 $this->interactive = false;
76 if (!isset($this->connection)) {
77 $this->connection = 'default';
78 }
79 if (strtolower($this->args[0]) == 'all') {
80 return $this->all();
81 }
82 $model = $this->_modelName($this->args[0]);
83 $this->bake($model);
84 }
85 }
86
87 /**
88 * Bake All the Fixtures at once. Will only bake fixtures for models that exist.
89 *
90 * @access public
91 * @return void
92 */
93 function all() {
94 $this->interactive = false;
95 $this->Model->interactive = false;
96 $tables = $this->Model->listAll($this->connection, false);
97 foreach ($tables as $table) {
98 $model = $this->_modelName($table);
99 $this->bake($model);
100 }
101 }
102
103 /**
104 * Interactive baking function
105 *
106 * @access private
107 */
108 function __interactive() {
109 $this->DbConfig->interactive = $this->Model->interactive = $this->interactive = true;
110 $this->hr();
111 $this->out(sprintf("Bake Fixture\nPath: %s", $this->path));
112 $this->hr();
113
114 $useDbConfig = $this->connection;
115 if (!isset($this->connection)) {
116 $this->connection = $this->DbConfig->getConfig();
117 }
118 $modelName = $this->Model->getName($this->connection);
119 $useTable = $this->Model->getTable($modelName, $this->connection);
120 $importOptions = $this->importOptions($modelName);
121 $this->bake($modelName, $useTable, $importOptions);
122 }
123
124 /**
125 * Interacts with the User to setup an array of import options. For a fixture.
126 *
127 * @param string $modelName Name of model you are dealing with.
128 * @return array Array of import options.
129 * @access public
130 */
131 function importOptions($modelName) {
132 $options = array();
133 $doSchema = $this->in(__('Would you like to import schema for this fixture?', true), array('y', 'n'), 'n');
134 if ($doSchema == 'y') {
135 $options['schema'] = $modelName;
136 }
137 $doRecords = $this->in(__('Would you like to use record importing for this fixture?', true), array('y', 'n'), 'n');
138 if ($doRecords == 'y') {
139 $options['records'] = true;
140 }
141 if ($doRecords == 'n') {
142 $prompt = sprintf(__("Would you like to build this fixture with data from %s's table?", true), $modelName);
143 $fromTable = $this->in($prompt, array('y', 'n'), 'n');
144 if (strtolower($fromTable) == 'y') {
145 $options['fromTable'] = true;
146 }
147 }
148 return $options;
149 }
150
151 /**
152 * Assembles and writes a Fixture file
153 *
154 * @param string $model Name of model to bake.
155 * @param string $useTable Name of table to use.
156 * @param array $importOptions Options for var $import
157 * @return string Baked fixture content
158 * @access public
159 */
160 function bake($model, $useTable = false, $importOptions = array()) {
161 if (!class_exists('CakeSchema')) {
162 App::import('Model', 'CakeSchema', false);
163 }
164 $table = $schema = $records = $import = $modelImport = null;
165 $importBits = array();
166
167 if (!$useTable) {
168 $useTable = Inflector::tableize($model);
169 } elseif ($useTable != Inflector::tableize($model)) {
170 $table = $useTable;
171 }
172
173 if (!empty($importOptions)) {
174 if (isset($importOptions['schema'])) {
175 $modelImport = true;
176 $importBits[] = "'model' => '{$importOptions['schema']}'";
177 }
178 if (isset($importOptions['records'])) {
179 $importBits[] = "'records' => true";
180 }
181 if ($this->connection != 'default') {
182 $importBits[] .= "'connection' => '{$this->connection}'";
183 }
184 if (!empty($importBits)) {
185 $import = sprintf("array(%s)", implode(', ', $importBits));
186 }
187 }
188
189 $this->_Schema = new CakeSchema();
190 $data = $this->_Schema->read(array('models' => false, 'connection' => $this->connection));
191
192 if (!isset($data['tables'][$useTable])) {
193 $this->err('Could not find your selected table ' . $useTable);
194 return false;
195 }
196
197 $tableInfo = $data['tables'][$useTable];
198 if (is_null($modelImport)) {
199 $schema = $this->_generateSchema($tableInfo);
200 }
201
202 if (!isset($importOptions['records']) && !isset($importOptions['fromTable'])) {
203 $recordCount = 1;
204 if (isset($this->params['count'])) {
205 $recordCount = $this->params['count'];
206 }
207 $records = $this->_makeRecordString($this->_generateRecords($tableInfo, $recordCount));
208 }
209 if (isset($this->params['records']) || isset($importOptions['fromTable'])) {
210 $records = $this->_makeRecordString($this->_getRecordsFromTable($model, $useTable));
211 }
212 $out = $this->generateFixtureFile($model, compact('records', 'table', 'schema', 'import', 'fields'));
213 return $out;
214 }
215
216 /**
217 * Generate the fixture file, and write to disk
218 *
219 * @param string $model name of the model being generated
220 * @param string $fixture Contents of the fixture file.
221 * @return string Content saved into fixture file.
222 * @access public
223 */
224 function generateFixtureFile($model, $otherVars) {
225 $defaults = array('table' => null, 'schema' => null, 'records' => null, 'import' => null, 'fields' => null);
226 $vars = array_merge($defaults, $otherVars);
227
228 $path = $this->getPath();
229 $filename = Inflector::underscore($model) . '_fixture.php';
230
231 $this->Template->set('model', $model);
232 $this->Template->set($vars);
233 $content = $this->Template->generate('classes', 'fixture');
234
235 $this->out("\nBaking test fixture for $model...");
236 $this->createFile($path . $filename, $content);
237 return $content;
238 }
239
240 /**
241 * Get the path to the fixtures.
242 *
243 * @return void
244 */
245 function getPath() {
246 $path = $this->path;
247 if (isset($this->plugin)) {
248 $path = $this->_pluginPath($this->plugin) . 'tests' . DS . 'fixtures' . DS;
249 }
250 return $path;
251 }
252
253 /**
254 * Generates a string representation of a schema.
255 *
256 * @param array $table Table schema array
257 * @return string fields definitions
258 * @access protected
259 */
260 function _generateSchema($tableInfo) {
261 $schema = $this->_Schema->generateTable('f', $tableInfo);
262 return substr($schema, 10, -2);
263 }
264
265 /**
266 * Generate String representation of Records
267 *
268 * @param array $table Table schema array
269 * @return array Array of records to use in the fixture.
270 * @access protected
271 */
272 function _generateRecords($tableInfo, $recordCount = 1) {
273 $records = array();
274 for ($i = 0; $i < $recordCount; $i++) {
275 $record = array();
276 foreach ($tableInfo as $field => $fieldInfo) {
277 if (empty($fieldInfo['type'])) {
278 continue;
279 }
280 switch ($fieldInfo['type']) {
281 case 'integer':
282 case 'float':
283 $insert = $i + 1;
284 break;
285 case 'string':
286 case 'binary':
287 $isPrimaryUuid = (
288 isset($fieldInfo['key']) && strtolower($fieldInfo['key']) == 'primary' &&
289 isset($fieldInfo['length']) && $fieldInfo['length'] == 36
290 );
291 if ($isPrimaryUuid) {
292 $insert = String::uuid();
293 } else {
294 $insert = "Lorem ipsum dolor sit amet";
295 if (!empty($fieldInfo['length'])) {
296 $insert = substr($insert, 0, (int)$fieldInfo['length'] - 2);
297 }
298 }
299 $insert = "'$insert'";
300 break;
301 case 'timestamp':
302 $ts = time();
303 $insert = "'$ts'";
304 break;
305 case 'datetime':
306 $ts = date('Y-m-d H:i:s');
307 $insert = "'$ts'";
308 break;
309 case 'date':
310 $ts = date('Y-m-d');
311 $insert = "'$ts'";
312 break;
313 case 'time':
314 $ts = date('H:i:s');
315 $insert = "'$ts'";
316 break;
317 case 'boolean':
318 $insert = 1;
319 break;
320 case 'text':
321 $insert = "'Lorem ipsum dolor sit amet, aliquet feugiat.";
322 $insert .= " Convallis morbi fringilla gravida,";
323 $insert .= " phasellus feugiat dapibus velit nunc, pulvinar eget sollicitudin";
324 $insert .= " venenatis cum nullam, vivamus ut a sed, mollitia lectus. Nulla";
325 $insert .= " vestibulum massa neque ut et, id hendrerit sit,";
326 $insert .= " feugiat in taciti enim proin nibh, tempor dignissim, rhoncus";
327 $insert .= " duis vestibulum nunc mattis convallis.'";
328 break;
329 }
330 $record[$field] = $insert;
331 }
332 $records[] = $record;
333 }
334 return $records;
335 }
336
337 /**
338 * Convert a $records array into a a string.
339 *
340 * @param array $records Array of records to be converted to string
341 * @return string A string value of the $records array.
342 * @access protected
343 */
344 function _makeRecordString($records) {
345 $out = "array(\n";
346 foreach ($records as $record) {
347 $values = array();
348 foreach ($record as $field => $value) {
349 $values[] = "\t\t\t'$field' => $value";
350 }
351 $out .= "\t\tarray(\n";
352 $out .= implode(",\n", $values);
353 $out .= "\n\t\t),\n";
354 }
355 $out .= "\t)";
356 return $out;
357 }
358
359 /**
360 * Interact with the user to get a custom SQL condition and use that to extract data
361 * to build a fixture.
362 *
363 * @param string $modelName name of the model to take records from.
364 * @param string $useTable Name of table to use.
365 * @return array Array of records.
366 * @access protected
367 */
368 function _getRecordsFromTable($modelName, $useTable = null) {
369 if ($this->interactive) {
370 $condition = null;
371 $prompt = __("Please provide a SQL fragment to use as conditions\nExample: WHERE 1=1 LIMIT 10", true);
372 while (!$condition) {
373 $condition = $this->in($prompt, null, 'WHERE 1=1 LIMIT 10');
374 }
375 } else {
376 $condition = 'WHERE 1=1 LIMIT ' . (isset($this->params['count']) ? $this->params['count'] : 10);
377 }
378 App::import('Model', 'Model', false);
379 $modelObject =& new Model(array('name' => $modelName, 'table' => $useTable, 'ds' => $this->connection));
380 $records = $modelObject->find('all', array(
381 'conditions' => $condition,
382 'recursive' => -1
383 ));
384 $db =& ConnectionManager::getDataSource($modelObject->useDbConfig);
385 $schema = $modelObject->schema(true);
386 $out = array();
387 foreach ($records as $record) {
388 $row = array();
389 foreach ($record[$modelObject->alias] as $field => $value) {
390 $row[$field] = $db->value($value, $schema[$field]['type']);
391 }
392 $out[] = $row;
393 }
394 return $out;
395 }
396
397 /**
398 * Displays help contents
399 *
400 * @access public
401 */
402 function help() {
403 $this->hr();
404 $this->out("Usage: cake bake fixture <arg1> <params>");
405 $this->hr();
406 $this->out('Arguments:');
407 $this->out();
408 $this->out("<name>");
409 $this->out("\tName of the fixture to bake. Can use Plugin.name");
410 $this->out("\tas a shortcut for plugin baking.");
411 $this->out();
412 $this->out('Commands:');
413 $this->out("\nfixture <name>\n\tbakes fixture with specified name.");
414 $this->out("\nfixture all\n\tbakes all fixtures.");
415 $this->out();
416 $this->out('Parameters:');
417 $this->out("\t-count When using generated data, the number of records to include in the fixture(s).");
418 $this->out("\t-connection Which database configuration to use for baking.");
419 $this->out("\t-plugin CamelCased name of plugin to bake fixtures for.");
420 $this->out("\t-records Used with -count and <name>/all commands to pull [n] records from the live tables");
421 $this->out("\t Where [n] is either -count or the default of 10.");
422 $this->out();
423 $this->_stop();
424 }
425 }