[6.1] Reactor MVC FormController class #46537
Open
+273
−210
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Pull Request for Issue # .
Summary of Changes
(ChatGPT helped improving my original PR description to make it more clear)
This PR refactors the MVC
FormControllerclass to reduce duplicated logic and improve extensibility.The current
save()method performs many different tasks in a single large block, making it difficult for child controllers to override small parts of the workflow without re-implementing the entire method. This PR introduces several internal helper methods and reorganizes existing logic to make the controller easier to maintain and extend.1. Extracted check-in / check-out logic
Repeated check-in and check-out code in the
edit,cancel, andsavetasks has been moved into two reusable methods:attemptCheckin()attemptCheckout()This isolates record-locking behavior and allows child controllers to override it without touching unrelated logic.
2. Extracted calendar-field request filtering
The repeated logic for normalizing calendar form fields (applying
SERVER_UTCorUSER_UTCfilters and writing the normalized values back to the request data) has been extracted into:applyFilterForCalendarFieldsToRequestData()This logic is used both when validation fails during
save()and in thereload()task.3. Split the large
save()method into smaller stepsThe original
save()method contained many responsibilities (data preprocessing, plugin manipulation, permission checking, validation, saving, messages, redirection, check-in).The method has now been broken into clearly named helper methods:
preprocessSaveData()Allows child controllers to adjust raw request data before any validation or permission checks happen.
normalizeRequestData()Extracted from existing logic. Allows plugins to modify request data before validation.
preSaveHook()Allows child controllers to modify the already-validated data before it is passed to the model for saving.
handleSaveDataValidationErrorMessages()Extracted from existing code to output validation errors.
setSaveSuccessMessage()Extracted from existing code to construct and set success messages after a successful save.
By splitting these steps, child controllers can now override only the parts they need — without copying the entire
save()method.Things to Consider / Open Questions
1. Correct type-hint for the model
FormControllercurrently type-hints$modelasBaseDatabaseModel.However, some of the logic used by
FormController(including check-in/check-out) is available only in:FormModel, orWhile refactoring, I kept
BaseDatabaseModelfor consistency with the existing code.However, the more accurate type hint would be
FormModel.Question:
Should the new helper methods use
FormModelas the parameter type?If desired, I can adjust the PR accordingly.
2.
FormController::save()assumes a database table always existsThe current implementation assumes that every item being edited has an associated Table object.
This is not always true — for example, the
OverrideControllerin com_languages:https://github.com/joomla/joomla-cms/blob/6.1-dev/administrator/components/com_languages/src/Controller/OverrideController.php#L72
OverrideControllerdoes not use a database table, so it cannot reuseFormController::save()at all.Controllers without tables are forced to reimplement the entire save process.
Possible improvement (for a future PR):
FormController::save().This would allow table-less controllers (e.g., for configuration or overrides) to reuse the base controller logic.
Testing Instructions
Actual result BEFORE applying this Pull Request
Expected result AFTER applying this Pull Request
Link to documentations
Please select:
Documentation link for docs.joomla.org:
No documentation changes for docs.joomla.org needed
Pull Request link for manual.joomla.org:
No documentation changes for manual.joomla.org needed