1: <?php
2:
3: /*****************************************************************************************
4: * X2Engine Open Source Edition is a customer relationship management program developed by
5: * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc.
6: *
7: * This program is free software; you can redistribute it and/or modify it under
8: * the terms of the GNU Affero General Public License version 3 as published by the
9: * Free Software Foundation with the addition of the following permission added
10: * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11: * IN WHICH THE COPYRIGHT IS OWNED BY X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY
12: * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
13: *
14: * This program is distributed in the hope that it will be useful, but WITHOUT
15: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16: * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
17: * details.
18: *
19: * You should have received a copy of the GNU Affero General Public License along with
20: * this program; if not, see http://www.gnu.org/licenses or write to the Free
21: * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22: * 02110-1301 USA.
23: *
24: * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley,
25: * California 95067, USA. or at email address contact@x2engine.com.
26: *
27: * The interactive user interfaces in modified source and object code versions
28: * of this program must display Appropriate Legal Notices, as required under
29: * Section 5 of the GNU Affero General Public License version 3.
30: *
31: * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32: * these Appropriate Legal Notices must retain the display of the "Powered by
33: * X2Engine" logo. If the display of the logo is not reasonably feasible for
34: * technical reasons, the Appropriate Legal Notices must display the words
35: * "Powered by X2Engine".
36: *****************************************************************************************/
37:
38: Yii::import('application.components.util.*');
39:
40: /**
41: * Exports a table in the live database (or a range of records in it) to a fixture/init script
42: *
43: * @package application.commands
44: * @author Demitri Morgan <demitri@x2engine.com>
45: */
46: class ExportFixtureCommand extends CConsoleCommand {
47:
48: private $_mode;
49:
50: /**
51: * @var array Specification for the command line arguments.
52: *
53: * Each entry takes this form:
54: * array(
55: * [local var name],
56: * [command line description],
57: * [default value],
58: * [validation expression],
59: * [validation error message]
60: * )
61: */
62: public $args = array(
63: 0 => array(
64: 'tableName',
65: 'table name',
66: null,
67: '$pass = array_key_exists($arg_in, Yii::app()->db->schema->tables);',
68: "Table doesn't exist"
69: ),
70: 1 => array(
71: 'type',
72: 'fixture (f) or init script (i)',
73: 'f',
74: '$pass = in_array($arg_in, array("i", "f"));',
75: 'Must be "i" or "f"'
76: ),
77: 2 => array(
78: 'range',
79: '"WHERE" clause',
80: '1',
81: '$pass=($arg_in != null);',
82: 'cannot be null'
83: ),
84: 3 => array(
85: 'columns',
86: 'table columns to include',
87: '*',
88: '$pass=($arg_in != null);',
89: 'cannot be null'
90: ),
91: 4 => array(
92: 'writeCond',
93: 'overwrite (o), rename existing (r), output to stdout (s), output to file ([filename])',
94: 's',
95: '$pass=true;',
96: '',
97: //'$pass=in_array($arg_in, array("o","r","s"));',
98: //'Must be "o", "r", or "s"'
99: ),
100: );
101: public $fixtureDir;
102:
103: public function errorMessage($spec, $arg) {
104: return "\nInvalid value for {$spec[0]}: \"" . $arg . "\" " . ($spec[4] != null ? "({$spec[4]})" : '') . "\n";
105: }
106:
107: public function formatRecord($data, $alias = null) {
108: return (!in_array($alias, array(null, ''), true) ? ("'" . addslashes($alias) . "' => ") : '') . var_export($data, true) . ",\n";
109: }
110:
111: public function validInput($arg_in, $validator) {
112: $pass = false;
113: eval($validator);
114: return $pass;
115: }
116:
117: /**
118: * Export the contents of a table in the live database as a fixture or init script.
119: *
120: * Usage:
121: * <tt>./yiic exportfixture interactive [table name] [f|i] [range] [columns] [o|r]</tt>
122: *
123: * @param array $args
124: */
125: public function actionInteractive($args) {
126: $this->_mode = 'interactive';
127: $this->fixtureDir = Yii::app()->basePath . '/tests/fixtures';
128: foreach ($this->args as $pos => $spec) {
129: $valid = false;
130: while (!$valid) {
131: if (array_key_exists($pos, $args)) {
132: ${$spec[0]} = $args[$pos];
133: $valid = $this->validInput($args[$pos], $spec[3]);
134: if (!$valid) {
135: echo $this->errorMessage($spec, ${$spec[4]});
136: echo $this->getHelp();
137: Yii::app()->end();
138: }
139: } else {
140: ${$spec[0]} = $this->prompt("{$spec[0]} ({$spec[1]})", $spec[2]);
141: $valid = $this->validInput(${$spec[0]}, $spec[3]);
142: if (!$valid)
143: echo $this->errorMessage($spec, ${$spec[0]});
144: }
145: }
146: }
147: if (!$valid) {
148: echo $this->getHelp();
149: Yii::app()->end();
150: }
151: $this->actionExport ($tableName, $type, $range, $columns, $writeCond);
152: }
153:
154: /**
155: * Non-interactive fixture export with option to specify aliases as command line args
156: */
157: public function actionExport (
158: $tableName, $type='f', $range=1, $columns='*', $writeCond='s', array $aliases=array ()) {
159:
160: $fileName = $tableName . ($type == 'i' ? '.init' : '') . '.php';
161: $filePath = $this->fixtureDir . '/' . $tableName . ($type == 'i' ? '.init' : '') . '.php';
162:
163: if (file_exists(FileUtil::rpath($filePath))) {
164: switch ($writeCond) {
165: case 'r':
166: $i = 0;
167: $backup = $filePath;
168: while (file_exists(FileUtil::rpath($backup))) {
169: $backup = "$filePath.$i";
170: $i++;
171: }
172: $this->copyFiles(
173: array(
174: "backup of existing: $fileName" => array(
175: 'source' => $filePath,
176: 'target' => $backup
177: )
178: ));
179: break;
180: case 'o':
181: echo "\nOverwriting existing file $fileName\n";
182: break;
183: case 's':
184: break;
185: default: // filename
186: echo "\nWriting to file $writeCond\n";
187: }
188: }
189:
190: $aliasPrompt = false;
191: if ($type == 'f' && $this->_mode==='interactive') {
192: $aliasPrompt = $this->confirm('Prompt for row aliases?');
193: }
194:
195: $records = Yii::app()->db->createCommand()
196: ->select($columns)
197: ->from($tableName)
198: ->where($range)
199: ->queryAll();
200: $fileCont = "<?php\nreturn array(\n";
201: foreach ($records as $index => $record) {
202: $alias = null;
203: if ($type == 'f') {
204: if (!$aliasPrompt && isset ($aliases[$index])) {
205: $alias = $aliases[$index];
206: } else {
207: $alias = $index;
208: }
209: if ($aliasPrompt) {
210: var_dump($record);
211: $alias = $this->prompt("Alias for this record (enter for \"$index\"):");
212: if (empty($alias)) {
213: $alias = $index;
214: }
215: while (in_array($alias, $aliases)) {
216: $alias = $this->prompt("Alias in use already. Enter another:");
217: if (empty($alias)) {
218: $alias = $index;
219: }
220: }
221: $aliases[] = $alias;
222: } else {
223:
224: }
225: }
226: $fileCont .= $this->formatRecord($record, $alias);
227: }
228: $fileCont .= ");\n?>";
229:
230: if (!in_array ($writeCond, array ('s', 'r', 'o'))) {
231: file_put_contents($writeCond, $fileCont);
232: } elseif ($writeCond !== 's') {
233: file_put_contents($filePath, $fileCont);
234: } else {
235: /**/print ($fileCont);
236: }
237: echo "\nExport complete.\n";
238: }
239:
240: // public function getHelp() {
241: // return "\n***Usage:***\n\tyiic exportfixture [tableName] [type (f|i)] [range] [columns] [writeCond (o|r|s)]\n\n";
242: // }
243:
244: }
245:
246: ?>
247: