1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
20:
21: 22: 23: 24:
25: class ConsoleShell extends Shell {
26:
27: 28: 29: 30: 31: 32:
33: var $associations = array('hasOne', 'hasMany', 'belongsTo', 'hasAndBelongsToMany');
34:
35: 36: 37: 38: 39: 40:
41: var $badCommandChars = array('$', ';');
42:
43: 44: 45: 46: 47: 48:
49: var $models = array();
50:
51: 52: 53: 54: 55:
56: function initialize() {
57: require_once CAKE . 'dispatcher.php';
58: $this->Dispatcher = new Dispatcher();
59: $this->models = App::objects('model');
60: App::import('Model', $this->models);
61:
62: foreach ($this->models as $model) {
63: $class = Inflector::camelize(str_replace('.php', '', $model));
64: $this->models[$model] = $class;
65: $this->{$class} =& new $class();
66: }
67: $this->out('Model classes:');
68: $this->out('--------------');
69:
70: foreach ($this->models as $model) {
71: $this->out(" - {$model}");
72: }
73: $this->_loadRoutes();
74: }
75:
76: 77: 78: 79: 80:
81: function help() {
82: $out = 'Console help:';
83: $out .= '-------------';
84: $out .= 'The interactive console is a tool for testing parts of your app before you';
85: $out .= 'write code.';
86: $out .= "\n";
87: $out .= 'Model testing:';
88: $out .= 'To test model results, use the name of your model without a leading $';
89: $out .= 'e.g. Foo->find("all")';
90: $out .= "\n";
91: $out .= 'To dynamically set associations, you can do the following:';
92: $out .= "\tModelA bind <association> ModelB";
93: $out .= "where the supported assocations are hasOne, hasMany, belongsTo, hasAndBelongsToMany";
94: $out .= "\n";
95: $out .= 'To dynamically remove associations, you can do the following:';
96: $out .= "\t ModelA unbind <association> ModelB";
97: $out .= "where the supported associations are the same as above";
98: $out .= "\n";
99: $out .= "To save a new field in a model, you can do the following:";
100: $out .= "\tModelA->save(array('foo' => 'bar', 'baz' => 0))";
101: $out .= "where you are passing a hash of data to be saved in the format";
102: $out .= "of field => value pairs";
103: $out .= "\n";
104: $out .= "To get column information for a model, use the following:";
105: $out .= "\tModelA columns";
106: $out .= "which returns a list of columns and their type";
107: $out .= "\n";
108: $out .= "\n";
109: $out .= 'Route testing:';
110: $out .= "\n";
111: $out .= 'To test URLs against your app\'s route configuration, type:';
112: $out .= "\n";
113: $out .= "\tRoute <url>";
114: $out .= "\n";
115: $out .= "where url is the path to your your action plus any query parameters,";
116: $out .= "minus the application's base path. For example:";
117: $out .= "\n";
118: $out .= "\tRoute /posts/view/1";
119: $out .= "\n";
120: $out .= "will return something like the following:";
121: $out .= "\n";
122: $out .= "\tarray (";
123: $out .= "\t [...]";
124: $out .= "\t 'controller' => 'posts',";
125: $out .= "\t 'action' => 'view',";
126: $out .= "\t [...]";
127: $out .= "\t)";
128: $out .= "\n";
129: $out .= 'Alternatively, you can use simple array syntax to test reverse';
130: $out .= 'To reload your routes config (config/routes.php), do the following:';
131: $out .= "\n";
132: $out .= "\tRoutes reload";
133: $out .= "\n";
134: $out .= 'To show all connected routes, do the following:';
135: $out .= "\tRoutes show";
136: $this->out($out);
137: }
138:
139: 140: 141: 142: 143:
144: function main($command = null) {
145: while (true) {
146: if (empty($command)) {
147: $command = trim($this->in(''));
148: }
149:
150: switch ($command) {
151: case 'help':
152: $this->help();
153: break;
154: case 'quit':
155: case 'exit':
156: return true;
157: break;
158: case 'models':
159: $this->out('Model classes:');
160: $this->hr();
161: foreach ($this->models as $model) {
162: $this->out(" - {$model}");
163: }
164: break;
165: case (preg_match("/^(\w+) bind (\w+) (\w+)/", $command, $tmp) == true):
166: foreach ($tmp as $data) {
167: $data = strip_tags($data);
168: $data = str_replace($this->badCommandChars, "", $data);
169: }
170:
171: $modelA = $tmp[1];
172: $association = $tmp[2];
173: $modelB = $tmp[3];
174:
175: if ($this->_isValidModel($modelA) && $this->_isValidModel($modelB) && in_array($association, $this->associations)) {
176: $this->{$modelA}->bindModel(array($association => array($modelB => array('className' => $modelB))), false);
177: $this->out("Created $association association between $modelA and $modelB");
178: } else {
179: $this->out("Please verify you are using valid models and association types");
180: }
181: break;
182: case (preg_match("/^(\w+) unbind (\w+) (\w+)/", $command, $tmp) == true):
183: foreach ($tmp as $data) {
184: $data = strip_tags($data);
185: $data = str_replace($this->badCommandChars, "", $data);
186: }
187:
188: $modelA = $tmp[1];
189: $association = $tmp[2];
190: $modelB = $tmp[3];
191:
192:
193: $currentAssociations = $this->{$modelA}->getAssociated();
194: $validCurrentAssociation = false;
195:
196: foreach ($currentAssociations as $model => $currentAssociation) {
197: if ($model == $modelB && $association == $currentAssociation) {
198: $validCurrentAssociation = true;
199: }
200: }
201:
202: if ($this->_isValidModel($modelA) && $this->_isValidModel($modelB) && in_array($association, $this->associations) && $validCurrentAssociation) {
203: $this->{$modelA}->unbindModel(array($association => array($modelB)));
204: $this->out("Removed $association association between $modelA and $modelB");
205: } else {
206: $this->out("Please verify you are using valid models, valid current association, and valid association types");
207: }
208: break;
209: case (strpos($command, "->find") > 0):
210:
211: $command = strip_tags($command);
212: $command = str_replace($this->badCommandChars, "", $command);
213:
214:
215: list($modelToCheck, $tmp) = explode('->', $command);
216:
217: if ($this->_isValidModel($modelToCheck)) {
218: $findCommand = "\$data = \$this->$command;";
219: @eval($findCommand);
220:
221: if (is_array($data)) {
222: foreach ($data as $idx => $results) {
223: if (is_numeric($idx)) {
224: foreach ($results as $modelName => $result) {
225: $this->out("$modelName");
226:
227: foreach ($result as $field => $value) {
228: if (is_array($value)) {
229: foreach ($value as $field2 => $value2) {
230: $this->out("\t$field2: $value2");
231: }
232:
233: $this->out();
234: } else {
235: $this->out("\t$field: $value");
236: }
237: }
238: }
239: } else {
240: $this->out($idx);
241:
242: foreach ($results as $field => $value) {
243: if (is_array($value)) {
244: foreach ($value as $field2 => $value2) {
245: $this->out("\t$field2: $value2");
246: }
247:
248: $this->out();
249: } else {
250: $this->out("\t$field: $value");
251: }
252: }
253: }
254: }
255: } else {
256: $this->out("\nNo result set found");
257: }
258: } else {
259: $this->out("$modelToCheck is not a valid model");
260: }
261:
262: break;
263: case (strpos($command, '->save') > 0):
264:
265: $command = strip_tags($command);
266: $command = str_replace($this->badCommandChars, "", $command);
267: list($modelToSave, $tmp) = explode("->", $command);
268:
269: if ($this->_isValidModel($modelToSave)) {
270:
271: list($foo, $data) = explode("->save", $command);
272: $data = preg_replace('/^\(*(array)?\(*(.+?)\)*$/i', '\\2', $data);
273: $saveCommand = "\$this->{$modelToSave}->save(array('{$modelToSave}' => array({$data})));";
274: @eval($saveCommand);
275: $this->out('Saved record for ' . $modelToSave);
276: }
277: break;
278: case (preg_match("/^(\w+) columns/", $command, $tmp) == true):
279: $modelToCheck = strip_tags(str_replace($this->badCommandChars, "", $tmp[1]));
280:
281: if ($this->_isValidModel($modelToCheck)) {
282:
283: $fieldsCommand = "\$data = \$this->{$modelToCheck}->getColumnTypes();";
284: @eval($fieldsCommand);
285:
286: if (is_array($data)) {
287: foreach ($data as $field => $type) {
288: $this->out("\t{$field}: {$type}");
289: }
290: }
291: } else {
292: $this->out("Please verify that you selected a valid model");
293: }
294: break;
295: case (preg_match("/^routes\s+reload/i", $command, $tmp) == true):
296: $router =& Router::getInstance();
297: if (!$this->_loadRoutes()) {
298: $this->out("There was an error loading the routes config. Please check that the file");
299: $this->out("exists and is free of parse errors.");
300: break;
301: }
302: $this->out("Routes configuration reloaded, " . count($router->routes) . " routes connected");
303: break;
304: case (preg_match("/^routes\s+show/i", $command, $tmp) == true):
305: $router =& Router::getInstance();
306: $this->out(implode("\n", Set::extract($router->routes, '{n}.0')));
307: break;
308: case (preg_match("/^route\s+(\(.*\))$/i", $command, $tmp) == true):
309: if ($url = eval('return array' . $tmp[1] . ';')) {
310: $this->out(Router::url($url));
311: }
312: break;
313: case (preg_match("/^route\s+(.*)/i", $command, $tmp) == true):
314: $this->out(var_export(Router::parse($tmp[1]), true));
315: break;
316: default:
317: $this->out("Invalid command\n");
318: break;
319: }
320: $command = '';
321: }
322: }
323:
324: 325: 326: 327: 328: 329: 330:
331: function _isValidModel($modelToCheck) {
332: return in_array($modelToCheck, $this->models);
333: }
334:
335: 336: 337: 338: 339: 340: 341:
342: function _loadRoutes() {
343: $router =& Router::getInstance();
344:
345: $router->reload();
346: extract($router->getNamedExpressions());
347:
348: if (!@include(CONFIGS . 'routes.php')) {
349: return false;
350: }
351: $router->parse('/');
352:
353: foreach (array_keys($router->getNamedExpressions()) as $var) {
354: unset(${$var});
355: }
356: for ($i = 0, $len = count($router->routes); $i < $len; $i++) {
357: $router->routes[$i]->compile();
358: }
359: return true;
360: }
361: }
362: