numLS($line);
$line = trim($line);
$tokens = explode("|", $line);
$line = $tokens[0];
$dbTable = null;
$condition = null;
if(count($tokens) >= 2) {
$dbTable = $tokens[1];
// check if table has loading conditions
if(strpos($dbTable, ":")) {
$parts = explode(":", $dbTable);
$dbTable = $parts[0];
$parts = explode("=", $parts[1]);
$condition = [
"field" => $parts[0],
"value" => str_replace("OWN", "Auth::user()->id" , $parts[1])
];
}
}
$hasAdd = in_array("add", $tokens);
$hasView = in_array("view", $tokens);
$hasRemove = in_array("remove", $tokens);
switch($ls) {
case 4: // top level controller OR top level controller action
// top level controller-action
if(strpos($line, "/") !== FALSE) {
if(!empty($currentController)) {
$parts = explode(":", $line);
$line = $parts[0];
$method = explode("/", $line)[1];
$newMethod = $currentController->addMethod($method, "/" . $line);
if(count($parts) > 1) {
$newMethod->api = $parts[count($parts) - 1];
}
// create _SINGLE_ controller if view
if($method === "view") {
$currentSubController = new GenController($currentRoot, $currentController->name . "_SINGLE");
$currentSubController->dbTable = $currentController->dbTable;
$currentSubController->parentRoute = "/" . $line;
$currentSubController->parentControllerName = $currentController->name;
$currentSubController->sub = true;
$newMethod->redirect = "/" . $line . "/SUB_dashboard";
}
else if(strpos($method, "add_new") === 0) {
$newMethod->type = 'add';
}
else if(strpos($method, "remove") === 0) {
$newMethod->type = 'remove';
}
$currentMethod = $newMethod;
}
}
// new top level controller
else if(empty($currentController) || $line !== $currentController->name) {
if(!empty($currentController)) {
$currentController->save();
}
$currentController = new GenController($currentRoot, $line);
$currentController->dbTable = $dbTable;
$currentController->condition = $condition;
$currentController->hasAdd = $hasAdd;
$currentController->hasView = $hasView;
$currentController->hasRemove = $hasRemove;
$currentController->addMethod("index", "/$line");
if(!empty($currentSubController)) {
$currentSubController->save();
}
$currentSubType = '';
$currentSubController = new GenController();
$sideLinks[] = "
route()->getController()->selfName) && strpos(request()->route()->getController()->selfName, '{$currentController->name}') === 0 ? 'active' : '') }}" . " '>" .
"" .
"" . $currentController->snakeToTitleCase($currentController->name) . "
" .
"";
}
break;
case 8: // sub-type declaration | add_new fields
if($line === 'ACTIONS' || $line === 'SUB') {
$currentSubType = $line;
}
else if (!empty($currentMethod) &&
(strpos($currentMethod->name, 'add_new') === 0 ||
$currentMethod->name === 'remove')) { // this is a field in add_new
$currentMethod->data[] = $line;
}
break;
case 12: // ACTIONS | SUB
// check if this has show conditions
$show = false;
if(strpos($line, ":")) {
$parts = explode(":", $line);
$line = $parts[0];
if($parts[1] === 'if') {
$show = $parts[2];
}
}
if($currentSubType === 'ACTIONS') {
$currentMethod = $currentSubController->addMethod(
"ACTION_" . $line,
"/ACTION_" . $line
);
$currentMethod->type = 'action';
$currentMethod->show = $show;
$currentMethod->data = [];
}
else if($currentSubType === 'SUB') {
$currentMethod = $currentSubController->addMethod(
"SUB_" . $line,
"/SUB_" . $line
);
$currentMethod->type = 'sub';
$currentMethod->show = $show;
$currentMethod->data = [];
}
break;
case 16: // data for actions and subs
if(!empty($currentMethod)) {
$currentMethod->data[] = $line;
}
break;
case 20: // SUB add_new fields
if(!empty($currentMethod)) {
$currentMethod->data[] = $line;
}
break;
default:
dump("ERROR: Cannot have $ls leading spaces!");
dump("Line: $line");
exit(1);
}
}
}
// do any pending saves
if(!empty($currentSubController)) {
$currentSubController->save();
}
if(!empty($currentController)) {
$currentController->save();
}
echo "Saved " . base_path("routes/generated.php") . "\n";
// save side links
file_put_contents(resource_path("views/layouts/generated-links.blade.php"), implode("\n", $sideLinks));
echo "Saved " . resource_path("views/layouts/generated-links.blade.php") . "\n";
}
private function numLS($line) {
$count = 0;
for ($i=0; $iroot = $root;
$this->name = $name;
$this->methods = [];
}
public function addMethod($method, $route) {
if($this->parentRoute) {
$route = $this->parentRoute . $route;
}
$method = new GenControllerMethod($method, $route);
$this->methods[] = $method;
return $method;
}
public function save() {
if(!$this->saved && !empty($this->root) && !empty($this->name)) {
$this->saveController();
$this->saveRoutes();
$this->saved = true;
// $this->log();
}
}
public function saveController() {
$text = file_get_contents(base_path('generatecv/tree-templates/controller.template.php'));
$code = [];
// check if any method has a "sub add_new" in it, if yes, add action for the same
$newMethods = [];
foreach ($this->methods as $method) {
if($method->type === 'sub' && count($method->data) > 1 && strpos($method->data[1], 'add_new') === 0) {
$methodName = preg_replace("/^SUB_/", "ACTION_", $method->name) . 'AddNew';
$methodRoute = str_replace("/SUB_", "/ACTION_", $method->route) . 'AddNew';
$newMethod = new GenControllerMethod($methodName, $methodRoute);
$newMethod->hasUID = true;
$newMethod->redirect = false;
$newMethod->type = 'action';
$newMethod->data = [];
for($i = 2; $idata); $i++) {
$newMethod->data[] = $method->data[$i];
}
$newMethod->parentSub = $this->name . '-' . $method->name;
$newMethod->table = explode(":", $method->data[1])[1];
$newMethods[] = $newMethod;
$method->childAddRoute = $this->name . '-' . $methodName;
}
}
$this->methods = array_merge($this->methods, $newMethods);
foreach ($this->methods as $method) {
$code[] = "";
$code[] = "\t// GET {$method->route}";
$code[] = "\t" . 'public function ' . $method->name . '(Request $request' . ($method->hasUID ? ', $uid' : '') . ') {';
if($method->redirect) {
$target = str_replace('{uid}', '$uid', $method->redirect);
$code[] = "\t\t" . 'return redirect("' . $target . '");';
}
else {
if($method->hasUID) {
$code[] = "\t\t\$record = DB::table('{$this->dbTable}')->where('uid', \$uid)->first();";
$input = ["'record'"];
// if sub-index controller, load subRecords
if($method->type === 'sub' && count($method->data)) {
$dbParts = explode("=", $method->data[0]);
$localField = $dbParts[0];
$dbParts = explode(".", $dbParts[1]);
$foreignTable = $dbParts[0];
$foreignField = $dbParts[1];
$code[] = "\t\t\$subRecords = DB::table('$foreignTable')->where('$foreignField', \$record->$localField)->get();";
$input[] = "'subRecords'";
}
$code[] = "\t\treturn view('{$this->root}/{$this->name}/{$method->name}', " .
"compact(" . implode(", ", $input) . "));";
}
else {
$loadingLine[] = "\t\t\$records = DB::table('{$this->dbTable}')";
if($this->condition) {
$loadingLine[] = "->where('{$this->condition['field']}', {$this->condition['value']})";
}
$loadingLine[] = "->get();";
$code[] = implode("", $loadingLine);
$code[] = "\t\treturn view('{$this->root}/{$this->name}/{$method->name}', " .
"compact('records'));";
}
}
$this->saveView($this, $method);
$code[] = "\t}";
}
$text = str_replace("_NAME_", "{$this->name}_Controller", $text);
$text = str_replace("// __METHODS__", implode("\n", $code), $text);
file_put_contents(app_path("Http/Controllers/{$this->name}_Controller.php"), $text);
echo "Generated " . app_path("Http/Controllers/{$this->name}_Controller.php") . "\n";
}
public function saveView(GenController $controller, GenControllerMethod $method) {
if($controller->sub) {
$controller->saveSubLinks($controller, $method);
$controller->saveActionLinks($controller, $method);
if($method->type === 'action') {
$this->saveSubActionView($controller, $method);
}
else if($method->type === 'sub' && count($method->data)) {
$this->saveSubIndexView($controller, $method);
}
else {
$this->saveSubDefaultView($controller, $method);
}
}
else {
if($method->name === 'view' && $controller->hasView) {
$this->saveShowView($controller, $method);
}
else if($method->name === 'index') {
$this->saveIndexView($controller, $method);
}
else if(strpos($method->name, 'add_new') === 0 && $controller->hasAdd) {
$this->saveAddNewView($controller, $method);
}
else if($method->name === 'remove' && $controller->hasRemove) {
$this->saveAddNewView($controller, $method);
}
}
}
public function saveIndexView(GenController $controller, GenControllerMethod $method)
{
$text = file_get_contents(base_path('generatecv/tree-templates/index.template.blade.php'));
$text = str_replace("_NAME_", $this->snakeToTitleCase($controller->name), $text);
if($controller->hasAdd) {
$addLinks = [];
foreach ($controller->methods as $m) {
if($m->type === 'add') {
$addLinks[] = "name}/{$m->name}'>" .
" " .
"{$this->snakeToTitleCase($m->name)}";
}
}
$text = str_replace("", implode("\n", $addLinks), $text);
}
$columns = DB::getSchemaBuilder()->getColumnListing($controller->dbTable);
$ths = [];
$tds = [];
if($controller->hasRemove) {
$ths[] = " | ";
$tds[] = "" .
"" .
" | ";
}
foreach ($columns as $column) {
$ths[] = "{$this->snakeToTitleCase($column)} | ";
$tds[] = "" .
($controller->hasView && $column === 'uid' ? '' : '') .
"= \$record->$column ?>" .
($controller->hasView && $column === 'uid' ? '' : '') .
" | ";
}
$text = str_replace("", implode("\n", $ths), $text);
$text = str_replace("", implode("\n", $tds), $text);
$this->file_force_contents(resource_path("views/{$controller->root}/{$controller->name}/{$method->name}.blade.php"), $text);
echo "Generated " . resource_path("views/{$controller->root}/{$controller->name}/{$method->name}.blade.php") . "\n";
}
public function saveShowView(GenController $controller, GenControllerMethod $method)
{
// delete sub links and action links
if(file_exists(resource_path("views/{$controller->root}/{$controller->parentControllerName}/subs.blade.php"))) {
unlink(resource_path("views/{$controller->root}/{$controller->parentControllerName}/subs.blade.php"));
}
if(file_exists(resource_path("views/{$controller->root}/{$controller->parentControllerName}/actions.blade.php"))) {
unlink(resource_path("views/{$controller->root}/{$controller->parentControllerName}/actions.blade.php"));
}
$text = file_get_contents(base_path('generatecv/tree-templates/show.template.blade.php'));
$text = str_replace("_NAME_", $this->snakeToTitleCase($controller->name), $text);
$text = str_replace("_UID_", '= $record->uid ?>', $text);
$text = str_replace("_INDEX_ROUTE_", $controller->name . '-index', $text);
$text = str_replace("_SUB_LINKS_VIEW_", "{$controller->root}/{$controller->name}/subs", $text);
$this->file_force_contents(resource_path("views/{$controller->root}/{$controller->name}/{$method->name}.blade.php"), $text);
echo "Generated " . resource_path("views/{$controller->root}/{$controller->name}/{$method->name}.blade.php") . "\n";
}
public function saveSubLinks(GenController $controller, GenControllerMethod $method)
{
if ($controller->subLinksSaved) return;
$subLinksView = resource_path("views/{$controller->root}/{$controller->parentControllerName}/subs.blade.php");
$subLinks = [];
foreach ($controller->methods as $meth) {
if (strpos($meth->name, "SUB_") !== 0) continue;
$display = $this->snakeToTitleCase(substr($meth->name, 4));
$subLinks[] = ($meth->show ? "@if(\$record->{$meth->show}) " : "") .
"parentControllerName}/view/= \$record->uid ?>/{$meth->name}' " .
"class='d-block px-3 py-2 border-bottom " .
"{{ request()->route()->getActionMethod() === '{$meth->name}' ? 'bg-secondary text-white font-weight-bold' : '' }}" .
(
$meth->name === 'SUB_dashboard' ?
"{{ strpos(request()->route()->getActionMethod(), 'ACTION_') === 0 ? 'bg-secondary text-white font-weight-bold' : '' }}" :
""
)
. "'>$display" .
($meth->show ? " @endif" : "");
}
$this->file_force_contents($subLinksView, implode("\n", $subLinks));
echo "Generated " . $subLinksView . "\n";
$controller->subLinksSaved = true;
}
public function saveActionLinks(GenController $controller, GenControllerMethod $method)
{
if ($controller->actionLinksSaved) return;
$actionLinksView = resource_path("views/{$controller->root}/{$controller->parentControllerName}/actions.blade.php");
$actionLinks = [];
foreach ($controller->methods as $meth) {
if (strpos($meth->name, "ACTION_") !== 0) continue;
$display = $this->camelToTitleCase(substr($meth->name, 7));
$actionLinks[] = ($meth->show ? "@if(\$record->{$meth->show}) " : "") .
"parentControllerName}/view/= \$record->uid ?>/{$meth->name}' " .
"class='d-block btn btn-sm btn-default mb-3'>$display" .
($meth->show ? " @endif" : "");
}
$this->file_force_contents($actionLinksView, implode("\n", $actionLinks));
echo "Generated " . $actionLinksView . "\n";
$controller->actionLinksSaved = true;
}
public function saveSubDefaultView(GenController $controller, GenControllerMethod $method)
{
$text = file_get_contents(base_path('generatecv/tree-templates/sub.template.blade.php'));
$text = str_replace("_LAYOUT_", "{$controller->root}.{$controller->parentControllerName}.view", $text);
$text = $this->generateSubContent($controller, $method, $text);
$this->file_force_contents(resource_path("views/{$controller->root}/{$controller->name}/{$method->name}.blade.php"), $text);
echo "Generated " . resource_path("views/{$controller->root}/{$controller->name}/{$method->name}.blade.php") . "\n";
}
public function saveSubActionView(GenController $controller, GenControllerMethod $method) {
$text = file_get_contents(base_path('generatecv/tree-templates/sub-action.template.blade.php'));
$text = str_replace("_LAYOUT_", "{$controller->root}.{$controller->parentControllerName}.view", $text);
$text = str_replace("_NAME_", $this->camelToTitleCase($this->snakeToTitleCase($method->name)), $text);
if(!$method->parentSub) {
$text = str_replace("_BACK_ROUTE_", "{$controller->parentControllerName}-view", $text);
}
else {
$text = str_replace("_BACK_ROUTE_", $method->parentSub, $text);
}
if(!$method->table) {
$text = str_replace("_API_", "/api/{$this->snakeToCamelCase($controller->dbTable)}/" . substr($method->name, 7), $text);
}
else {
$text = str_replace("_API_", "/api/{$this->snakeToCamelCase($method->table)}/create", $text);
}
$text = str_replace("_RETURN_ROUTE_", "{$controller->name}-{$method->name}", $text);
$fields = [];
if(count($method->data)) {
foreach ($method->data as $field) {
$fields[] = $this->generateFormField($field);
}
}
$text = str_replace("", implode("\n", $fields), $text);
$this->file_force_contents(resource_path("views/{$controller->root}/{$controller->name}/{$method->name}.blade.php"), $text);
echo "Generated " . resource_path("views/{$controller->root}/{$controller->name}/{$method->name}.blade.php") . "\n";
}
public function saveSubIndexView(GenController $controller, GenControllerMethod $method) {
$text = file_get_contents(base_path('generatecv/tree-templates/sub-index.template.blade.php'));
$text = str_replace("_LAYOUT_", "{$controller->root}.{$controller->parentControllerName}.view", $text);
$text = str_replace("_NAME_", $this->camelToTitleCase($this->snakeToTitleCase($method->name)), $text);
if(count($method->data) > 1 && strpos($method->data[1], 'add_new') === 0) {
$addLink = 'childAddRoute . '\', [\'uid\' => $record->uid])}}">' .
" Add New";
$text = str_replace("", $addLink, $text);
}
$dbParts = explode("=", $method->data[0]);
$dbParts = explode(".", $dbParts[1]);
$table = $dbParts[0];
$columns = DB::getSchemaBuilder()->getColumnListing($table);
$ths = [];
$tds = [];
foreach ($columns as $column) {
$ths[] = "{$this->snakeToTitleCase($column)} | ";
$tds[] = "= \$subRecord->$column ?> | ";
}
$text = str_replace("", implode("\n", $ths), $text);
$text = str_replace("", implode("\n", $tds), $text);
$this->file_force_contents(resource_path("views/{$controller->root}/{$controller->name}/{$method->name}.blade.php"), $text);
echo "Generated " . resource_path("views/{$controller->root}/{$controller->name}/{$method->name}.blade.php") . "\n";
}
public function generateSubContent(GenController $controller, GenControllerMethod $method, $text) {
if($method->name === 'SUB_dashboard') {
$html = file_get_contents(base_path('generatecv/tree-templates/dashboard.template.blade.php'));
$text = str_replace("_SUB_VIEW_", $html, $text);
$text = str_replace("_ACTION_LINKS_VIEW_", "{$controller->root}/{$controller->parentControllerName}/actions", $text);
}
else {
$text = str_replace("_SUB_VIEW_",
"" .
$this->camelToTitleCase($this->snakeToTitleCase($method->name)) . "
" .
"Controller: {$controller->name}
" .
"Action: {$method->name}()
" .
"View: {$controller->root}/{$controller->name}/{$method->name}.blade.php
",
$text);
}
return $text;
}
public function saveAddNewView(GenController $controller, GenControllerMethod $method)
{
$text = file_get_contents(base_path('generatecv/tree-templates/add_new.template.blade.php'));
$text = str_replace("_NAME_", $this->snakeToTitleCase($controller->name), $text);
$text = str_replace("_ADD_TITLE_", $this->snakeToTitleCase($method->name), $text);
$text = str_replace("_API_", "/api/{$this->snakeToCamelCase($controller->dbTable)}/{$method->api}", $text);
$text = str_replace("_BACK_ROUTE_", "{$controller->name}-index", $text);
$text = str_replace("_RETURN_ROUTE_", "{$controller->name}-{$method->name}", $text);
$columns = $method->data;
$fields = [];
foreach ($columns as $column) {
$fields[] = $this->generateFormField($column);
}
$text = str_replace("", implode("\n", $fields), $text);
$this->file_force_contents(resource_path("views/{$controller->root}/{$controller->name}/{$method->name}.blade.php"), $text);
echo "Generated " . resource_path("views/{$controller->root}/{$controller->name}/{$method->name}.blade.php") . "\n";
}
public function saveRoutes() {
$lines = ["// --- {$this->root}: {$this->name} --- //"];
foreach ($this->methods as $method) {
// FORMAT:
// Route::get('/foo/bar/{uid}', 'FooController@bar')->name('foo-action');
$lines[] = "Route::get('{$method->route}', '{$this->name}_Controller@{$method->name}')->name('{$this->name}-{$method->name}');";
}
$lines[] = '';
$lines[] = '';
file_put_contents(base_path("routes/generated.php"), implode("\n", $lines), FILE_APPEND);
}
public function log() {
$this->w('');
$this->w("Controller: app/Http/Controllers/{$this->name}_Controller");
$this->w("Table: {$this->dbTable}");
$this->w('---------------------------------------------');
foreach ($this->methods as $method) {
$this->w('Rout: ' . $method->route, 1);
$this->w('Meth: ' . $method->name . '($request' . ($method->hasUID ? ', $uid' : '') . ')', 1);
if(!empty($method->data)) $this->w('Data: ' . implode(", ", $method->data), 1);
if(!$method->redirect) {
$this->w('View: ' . resource_path("views/{$this->root}/{$this->name}/{$method->name}.blade.php"), 1);
}
else {
$this->w('Redi: ' . $method->redirect, 1);
}
$this->w('---------------------------------------------');
}
}
public function w($line, $level = -1) {
for($i=0; $i<$level; $i++) echo "\t";
echo "$line\n";
}
public function snakeToTitleCase($text) {
$text = preg_replace("/^(SUB|ACTION)_/", "", $text);
return ucwords(str_replace("_", " ", $text));
}
public function camelToTitleCase($text) {
$text = preg_replace("/^(SUB|ACTION)_/", "", $text);
$text = preg_replace("/([a-z])([A-Z0-9])/", "$1 $2", $text);
return ucwords($text);
}
public function snakeToCamelCase($text) {
$text = ucwords(str_replace("_", " ", $text));
$text = str_replace(" ", "", $text);
$text[0] = strtolower($text[0]);
return $text;
}
private function file_force_contents($dir, $contents){
$dir = str_replace("\\", "/", $dir);
$dir = str_replace( '//', '/', $dir);
$parts = explode('/', $dir);
$file = array_pop($parts);
$dir = '';
foreach($parts as $part) {
if($part[strlen($part) - 1] !== ':') {
if(!is_dir($dir .= "/$part")) mkdir($dir);
}
}
file_put_contents("$dir/$file", $contents);
}
private function generateFormField($line) {
$tokens = explode("=", $line);
$default = false;
if(count($tokens) > 1) {
$default = $tokens[1];
}
$tokens = explode(":", $tokens[0]);
$name = $tokens[0];
$display = $name;
$dotPos = strpos($name, ".");
if($dotPos !== FALSE) {
$display = substr($name, $dotPos + 1);
}
$display = preg_replace('/uid$/i', "", $display);
$type = "text";
$options = [];
if(count($tokens) > 1) {
$type = $tokens[1];
switch ($type) {
case "select":
$options = explode(",", $tokens[2]);
break;
case "record":
$options['table'] = $tokens[2];
$parts = explode(",", $tokens[3]);
$options['valueField'] = $parts[0];
$options['displayField'] = $parts[1];
break;
}
}
if($type !== 'hidden') {
$code[] = "";
$code[] = "";
}
$valueLine = "value='{{ old('$name') ? old('$name') : " . ($default ? "\$record->$default" : '\'\'') . " }}' ";
switch ($type) {
case "select":
$code[] = "";
break;
case "record":
$code[] = "";
break;
default:
$code[] = "";
}
if($type !== 'hidden') {
$code[] = "
";
}
return implode("\n", $code);
}
}
class GenControllerMethod {
public $name;
public $route;
public $hasUID = false;
public $redirect = false;
public $type = '';
public $data = [];
public $parentSub = false;
public $childAddRoute = false;
public $table = false;
public $api = 'create';
public $show = null;
public function __construct($name, $route)
{
$this->name = $name;
$this->route = $route;
if(strpos($this->route, "{uid}") !== FALSE) {
$this->hasUID = true;
}
}
}