update controllers logic
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* Sync Laravel $fillable + $timestamps from CodeIgniter *Model.php (sibling app_codeigniter).
|
||||
* Run: node tools/sync_fillable_from_codeigniter.mjs
|
||||
*/
|
||||
import { readdir, readFile, writeFile } from "node:fs/promises";
|
||||
import { join, dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const laravelRoot = join(__dirname, "..");
|
||||
const workspaceRoot = join(laravelRoot, "..");
|
||||
const ciModelsDir = join(workspaceRoot, "app_codeigniter", "app", "Models");
|
||||
const laravelModelsDir = join(laravelRoot, "app", "Models");
|
||||
|
||||
const manualCiToLaravel = new Map([
|
||||
["SettingsModel.php", "Setting.php"],
|
||||
]);
|
||||
|
||||
/** CI models that must not sync (legacy duplicate of another model). */
|
||||
const skipCiBasenames = new Set([
|
||||
"FlagModel.php", // table `flag`; Laravel uses CurrentFlagModel → current_flag
|
||||
]);
|
||||
|
||||
const skipLaravel = new Set([
|
||||
"Teacher.php",
|
||||
"Communication.php",
|
||||
"EventCharge.php",
|
||||
"ClassModel.php",
|
||||
"Calendar.php",
|
||||
]);
|
||||
|
||||
function ciFileToLaravel(ciBasename) {
|
||||
if (manualCiToLaravel.has(ciBasename)) return manualCiToLaravel.get(ciBasename);
|
||||
const m = ciBasename.match(/^(.+)Model\.php$/);
|
||||
if (!m) return null;
|
||||
const base = m[1];
|
||||
if (base === "Parent" || base === "Class") return `${base}Model.php`;
|
||||
return `${base}.php`;
|
||||
}
|
||||
|
||||
/** Find array after `protected $allowedFields =` (flexible spacing). */
|
||||
function extractAllowedFieldsArrayLiteral(src) {
|
||||
const m = src.match(/protected\s+\$allowedFields\s*=\s*/);
|
||||
if (!m || m.index === undefined) return null;
|
||||
let pos = m.index + m[0].length;
|
||||
while (pos < src.length && /\s/.test(src[pos])) pos++;
|
||||
if (pos >= src.length || src[pos] !== "[") return null;
|
||||
const start = pos;
|
||||
let depth = 0;
|
||||
for (let i = pos; i < src.length; i++) {
|
||||
const c = src[i];
|
||||
if (c === "[") depth++;
|
||||
else if (c === "]") {
|
||||
depth--;
|
||||
if (depth === 0) return src.slice(start, i + 1);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function parseQuotedStrings(arrayLiteral) {
|
||||
const out = [];
|
||||
const re = /'((?:\\'|[^'])*)'/g;
|
||||
let m;
|
||||
while ((m = re.exec(arrayLiteral))) out.push(m[1].replace(/\\'/g, "'"));
|
||||
return out;
|
||||
}
|
||||
|
||||
function renderFillable(items) {
|
||||
const lines = items.map((it) => {
|
||||
const escaped = String(it).replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
||||
return ` '${escaped}',`;
|
||||
});
|
||||
return `[\n${lines.join("\n")}\n ]`;
|
||||
}
|
||||
|
||||
function replaceFillable(lv, inner) {
|
||||
const re = /protected\s+\$fillable\s*=\s*\[[\s\S]*?\];/m;
|
||||
if (!re.test(lv)) return null;
|
||||
return lv.replace(re, `protected $fillable = ${inner};`);
|
||||
}
|
||||
|
||||
function replaceTimestamps(lv, useTs) {
|
||||
const line = useTs ? " public $timestamps = true;" : " public $timestamps = false;";
|
||||
// Normalize any leading whitespace so re-sync does not duplicate lines or pile up indent.
|
||||
if (/^\s*public\s+\$timestamps\s*=\s*(true|false)\s*;/m.test(lv))
|
||||
return lv.replace(/^\s*public\s+\$timestamps\s*=\s*(true|false)\s*;/m, line);
|
||||
return lv.replace(/(class\s+\w+[^\n]*\n\{)/, `$1\n${line}\n`);
|
||||
}
|
||||
|
||||
let updated = 0,
|
||||
skipped = 0;
|
||||
const missing = [];
|
||||
|
||||
const files = await readdir(ciModelsDir).catch(() => []);
|
||||
for (const ciBasename of files) {
|
||||
if (!ciBasename.endsWith("Model.php")) continue;
|
||||
if (skipCiBasenames.has(ciBasename)) {
|
||||
console.error(`Skip CI (legacy): ${ciBasename}`);
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
const laravelBasename = ciFileToLaravel(ciBasename);
|
||||
if (!laravelBasename) continue;
|
||||
if (skipLaravel.has(laravelBasename)) {
|
||||
console.error(`Skip (alias/base): ${laravelBasename}`);
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const lvPath = join(laravelModelsDir, laravelBasename);
|
||||
try {
|
||||
await readFile(lvPath, "utf8");
|
||||
} catch {
|
||||
missing.push(`${laravelBasename} (from ${ciBasename})`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const ciSrc = await readFile(join(ciModelsDir, ciBasename), "utf8");
|
||||
let lvSrc = await readFile(lvPath, "utf8");
|
||||
|
||||
let useTs = true;
|
||||
const tm = ciSrc.match(/protected\s+\$useTimestamps\s*=\s*(true|false)\s*;/);
|
||||
if (tm) useTs = tm[1] === "true";
|
||||
|
||||
const allowedBody = extractAllowedFieldsArrayLiteral(ciSrc);
|
||||
if (!allowedBody) {
|
||||
console.error(`No allowedFields in ${ciBasename}`);
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const fields = parseQuotedStrings(allowedBody);
|
||||
if (!fields.length) {
|
||||
console.error(`Empty allowedFields in ${ciBasename}`);
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!/protected\s+\$fillable\s*=\s*\[/.test(lvSrc)) {
|
||||
console.error(`No $fillable in ${laravelBasename}`);
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const rendered = renderFillable(fields);
|
||||
let next = replaceFillable(lvSrc, rendered);
|
||||
if (next == null) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
next = replaceTimestamps(next, useTs);
|
||||
|
||||
if (next === lvSrc) {
|
||||
console.error(`Unchanged: ${laravelBasename}`);
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
await writeFile(lvPath, next, "utf8");
|
||||
console.log(`Updated ${laravelBasename}`);
|
||||
updated++;
|
||||
}
|
||||
|
||||
console.log(`\nDone. Updated: ${updated}, skipped: ${skipped}`);
|
||||
if (missing.length) {
|
||||
console.error(`Missing (${missing.length}): ${missing.slice(0, 25).join(", ")}${missing.length > 25 ? "…" : ""}`);
|
||||
process.exitCode = 2;
|
||||
}
|
||||
Reference in New Issue
Block a user