3 · Vendor customizations
Objective — when no event hook can do the job and a vendor file must be edited, store the edited copy in an overlay that survives composer install, so your patch is never silently lost.
Background
Section titled “Background”Zajaly architecture aims for zero vendor edits — and most changes can hit that bar through event listeners (the previous step). But some CodeCanyon apps hardcode logic with no hook to listen to: a migration scanner that only checks vendor paths, a calculation with no override point. For those, you edit the file — but you never let the edit live only in vendor/, because the next composer install restores the vanilla copy and your change vanishes.
The overlay solves it. You keep a copy of the edited file under resources/vendor-customizations/, mirroring the vendor’s own folder layout. After every install or vendor update, a restore step copies your overlaid files back over the freshly-installed vanilla ones.
flowchart LR C["composer install<br/>restores vanilla vendor/"] --> R["restore step<br/>copies overlay back"] R --> P["vendor/ now carries<br/>your patches again"]1. Confirm the edit is unavoidable
Section titled “1. Confirm the edit is unavoidable”A vendor edit is a last resort. Before reaching for one, prove the event-driven path can’t do the work — otherwise build a ZajModule instead.
-
Check for a listenable event or override. If the vendor fires an event you can react to, build a ZajModule that listens — do not edit the file.
- ✅ You have confirmed the vendor’s logic is hardcoded with no hook, so an edit is genuinely required.
-
Assign a
MOD-NNNidentifier to the edit so it can be tracked and re-applied.- ✅ The change has a reserved
MOD-NNNnumber.
- ✅ The change has a reserved
2. Mirror the vendor path in the overlay
Section titled “2. Mirror the vendor path in the overlay”The overlay’s power is that its folder layout matches the vendor’s exactly. The restore step copies file-for-file from the overlay onto the same path inside vendor/, so the mirroring must be precise.
-
Copy the edited file into the overlay at the same relative path the vendor uses. For a customized package, the overlay path mirrors
vendor/{package}/....Terminal window mkdir -p resources/vendor-customizations/{package}/src/Path/Tocp vendor/{package}/src/Path/To/EditedFile.php \resources/vendor-customizations/{package}/src/Path/To/EditedFile.php# Expected: the file now exists under resources/vendor-customizations/ at the mirrored path- ✅ The overlay holds your edited copy at the same relative path the vendor uses.
For a vendor edit that lives in the app’s own tree rather than an installed package — like the HomeController.php example below — the same principle applies: keep the patched copy where the restore step can re-apply it, and track it as a MOD-NNN.
3. Mark your changes with ZAJ markers
Section titled “3. Mark your changes with ZAJ markers”Inside the overlaid file, wrap only the lines you added so anyone (including a future vendor diff) can see exactly what is yours and what is the vendor’s. Unmarked vendor code stays identical to the original.
-
Wrap each inserted block in
ZAJ:BEGIN/ZAJ:ENDmarker comments, with a one-line note of what the patch does and theMOD-NNNit belongs to.// ZAJ:BEGIN MOD-001 — extend the migration scanner to include ZajModules paths$zajCoreMigrations = collect(File::glob(database_path('migrations-zaj/*.php')))->map(fn ($path) => File::name($path));$migrationFiles = $migrationFiles->merge($zajCoreMigrations);$zajModulePaths = File::glob(base_path('packages/ZajModules/*/src/Database/Migrations/*.php'));$migrationFiles = $migrationFiles->merge(collect($zajModulePaths)->map(fn ($path) => File::name($path)));// ZAJ:END MOD-001- ✅ Every change you made is bracketed by
ZAJ:BEGIN/ZAJ:END; nothing outside the markers differs from the vendor original.
- ✅ Every change you made is bracketed by
-
For a wholly new file the overlay owns (not a patch to an existing one), mark it with a
ZAJ:FILEheader so it reads as Zajaly-owned at a glance.- ✅ New overlay files carry a
ZAJ:FILEmarker; patched files useZAJ:BEGIN/ZAJ:END.
- ✅ New overlay files carry a
4. Re-apply after every vendor install
Section titled “4. Re-apply after every vendor install”The overlay only protects you if it’s re-applied. A plain composer install overwrites vendor/ with vanilla files, so the restore step must run after it.
-
Run the restore step after
composer install(and after any vendor update). It scans the overlay and copies each customized file back over its vendor counterpart.Terminal window composer installphp Admin-Local/.../restore-vendor-customizations.php# Expected: a per-package "Restored N file(s)" summary- ✅ The summary reports your customized package(s) restored; the vendor files carry your patches again.
The mechanics of the restore script — and the _CUSTOMIZATIONS log that records every MOD-NNN — are the next step.
Checklist
Section titled “Checklist”Do not mark this step done until every box below is checked.
- 👤 Edit justified — confirmed no event hook or override could do the job before editing a vendor file.
- 🤖 Path mirrored — the edited copy sits under
resources/vendor-customizations/at the same relative path as the vendor file. - 🤖 Changes marked — inserted lines are wrapped in
ZAJ:BEGIN/ZAJ:END(or the file carriesZAJ:FILE) and reference aMOD-NNN. - 🔀 Re-applied — the restore step runs after
composer installand reports the file(s) restored.