Skip to content

Commit 2edf945

Browse files
Peter Amiriclaude
andcommitted
docs(web/guides): add v4 Overriding Core Methods guide (#3343)
Ports the v3-0-0 page into the v4-0-0 tree, corrected for 4.0.x: the super<name> convention now applies to models AND controllers/view helpers (parity landed with the #3325 fix, PR #3357). Documents placement conventions (single controller, app/controllers/Controller.cfc, app/views/helpers.cfm), the superLinkTo(argumentCollection=arguments) delegation pattern pinned by SuperOverrideSpec.cfc, the dataConfirm data-* pass-through alternative for the jsconfirm use case, and a version callout with the CreateObject workaround for 4.0.x builds predating the fix. Explicitly notes mapper internals are not an override surface. Registers the page in the v4-0-0 sidebar and section index, and adds a version-scope note to the v3-0-0 page (model-only in 3.x). Fixes #3343 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Peter Amiri <petera@pai.com>
1 parent 8bc8304 commit 2edf945

4 files changed

Lines changed: 173 additions & 0 deletions

File tree

web/sites/guides/src/content/docs/v3-0-0/working-with-wheels/overriding-core-methods.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ title: Overriding Core Methods
33
---
44
## Overriding Core Wheels Methods (Wheels 3.x)
55

6+
> **Version scope:** In Wheels 3.x the `super`-prefixed convention described on this page applies to **model** methods only. Overriding controller and view helpers (e.g. `linkTo()`) with `superLinkTo()` delegation is not available in 3.x — that parity arrived in Wheels 4.0.x ([#3325](https://github.com/wheels-dev/wheels/issues/3325)). See the v4 guide: [Overriding Core Methods](/v4-0-0/digging-deeper/overriding-core-methods/).
7+
68
In Wheels 2.5, developers could override core framework methods (such as `findAll`) in their models and call the original Wheels implementation using the `super` scope.
79

810
Due to internal framework restructuring in Wheels 3.0, this behavior no longer works using the traditional `super.methodName()` syntax. To restore this capability in a predictable and explicit way, Wheels now provides a new **`super`-prefixed method convention**.

web/sites/guides/src/content/docs/v4-0-0/digging-deeper/index.mdx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ The Digging Deeper section covers Wheels' advanced features — the ones you rea
9696
href="/v4-0-0/digging-deeper/dependency-injection-usage/"
9797
description="Practical DI patterns — test-double swapping, per-request resolvers, factories."
9898
/>
99+
<LinkCard
100+
title="Overriding Core Methods"
101+
href="/v4-0-0/digging-deeper/overriding-core-methods/"
102+
description="Replace a framework method with your own and delegate to the original via super<name>."
103+
/>
99104
</CardGrid>
100105

101106
## See also
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
---
2+
title: Overriding Core Methods
3+
description: Replace a framework method with your own and delegate to the original via the super-prefixed convention — in models, controllers, and view helpers.
4+
type: howto
5+
sidebar:
6+
order: 16
7+
---
8+
9+
import { Aside, CardGrid, LinkCard } from '@astrojs/starlight/components';
10+
11+
This page shows you how to override a core Wheels method — a model finder, a controller helper, or a view helper like `linkTo()` — while still being able to call the framework's original implementation from inside your override. You'll use the `super`-prefixed method convention, pick the right place for each kind of override, and see when a pass-through argument (like `dataConfirm`) makes an override unnecessary in the first place.
12+
13+
**You'll learn:**
14+
15+
- How the `super<name>` convention works and why it replaced `super.methodName()`
16+
- How to override a model method and delegate with `superFindAll()`
17+
- How to override a controller or view helper and delegate with `superLinkTo()`
18+
- Where each override lives: a single controller, `app/controllers/Controller.cfc`, or `app/views/helpers.cfm`
19+
- When to skip the override entirely — the `data-*` pass-through pattern for confirm dialogs
20+
- Which 4.0.x versions support controller/view overrides, and the workaround for older builds
21+
22+
## How the convention works
23+
24+
Wheels assembles models and controllers by mixing framework methods into your components at startup. Because the framework's `findAll()`, `linkTo()`, and friends arrive as mixins rather than through CFML inheritance, `super.findAll()` does not reach them — that syntax stopped working in Wheels 3.0.
25+
26+
Instead, when your component defines a method whose name collides with a framework mixin, Wheels keeps **your** version and registers the framework original under the same name prefixed with `super`. Override `findAll()` and the original becomes `superFindAll()`; override `linkTo()` and the original becomes `superLinkTo()`. Method names are case-insensitive, following normal CFML rules.
27+
28+
The alias is only created when you actually override something. A controller or model that overrides nothing gains no extra `super*` keys.
29+
30+
<Aside type="note" title="Model-only in 3.x, symmetric in 4.0.x">
31+
In Wheels 3.x this convention worked for **model** methods only. Controller and view helper parity arrived in the 4.0.x patch release carrying the fix for [#3325](https://github.com/wheels-dev/wheels/issues/3325) — see [the version callout below](#version-notes-and-the-pre-fix-workaround) if you're on an earlier 4.0.x build.
32+
</Aside>
33+
34+
## Overriding a model method
35+
36+
Define the method in your model CFC. Delegate to the original with `argumentCollection = arguments` so every argument your caller passed flows through unchanged:
37+
38+
```cfm {test:compile} title="app/models/Post.cfc"
39+
component extends="Model" {
40+
41+
function config() {
42+
}
43+
44+
public any function findAll() {
45+
// custom logic before calling the framework original
46+
return superFindAll(argumentCollection = arguments);
47+
}
48+
49+
}
50+
```
51+
52+
This works for any public framework method mixed into models — `findOne()`, `columnNames()`, `save()`, and so on.
53+
54+
## Overriding a controller or view helper
55+
56+
The same convention applies to everything mixed into controllers, which includes all view helpers (views execute in the controller's `variables` scope). Overriding `linkTo()` and delegating to the framework original looks like this:
57+
58+
```cfm {test:compile} title="app/controllers/Posts.cfc"
59+
component extends="Controller" {
60+
61+
function config() {
62+
}
63+
64+
public string function linkTo() {
65+
// example: give every generated link a default class
66+
if (!StructKeyExists(arguments, "class")) {
67+
arguments.class = "app-link";
68+
}
69+
return superLinkTo(argumentCollection = arguments);
70+
}
71+
72+
}
73+
```
74+
75+
Every call to `#linkTo(...)#` in that controller's views now runs your version, and `superLinkTo()` hands off to the real framework helper — arguments, routing, and HTML escaping all behave exactly as stock.
76+
77+
### Where the override lives
78+
79+
- **One controller** — define the override in that controller's CFC, as above.
80+
- **Every controller** — define it in `app/controllers/Controller.cfc`, the base controller your controllers extend.
81+
- **View helpers, app-wide** — define it in `app/views/helpers.cfm`. That file is included into every controller before the framework mixins are integrated, so your version is already in place when the framework helper arrives — your version wins and the original is registered as `super<name>`.
82+
83+
## Version notes and the pre-fix workaround
84+
85+
Controller and view helper overrides gained `super<name>` delegation in the 4.0.x patch release that fixed [#3325](https://github.com/wheels-dev/wheels/issues/3325). Model overrides have supported it since 3.x.
86+
87+
<Aside type="caution" title="On 4.0.x builds predating the #3325 fix">
88+
On earlier 4.0.x releases, overriding a controller or view helper works, but calling `superLinkTo()` (or any controller-side `super<name>`) throws at render time — the alias was never registered outside the model layer. If you can't upgrade yet, capture the framework original directly:
89+
90+
```cfm
91+
component extends="Controller" {
92+
93+
function config() {
94+
variables.coreLinkTo = CreateObject("component", "wheels.view.links").linkTo;
95+
}
96+
97+
public string function linkTo() {
98+
return coreLinkTo(argumentCollection = arguments);
99+
}
100+
101+
}
102+
```
103+
104+
Delete the workaround and switch to `superLinkTo()` once you're on a build containing the fix.
105+
</Aside>
106+
107+
<Aside type="note" title="What's overridable">
108+
The convention covers methods mixed into **models** and **controllers** (including view helpers). Router/mapper internals are not an app-override surface — there is no `super<name>` alias for mapper methods, and overriding them is not supported.
109+
</Aside>
110+
111+
## Before you override: the `data-*` pass-through
112+
113+
A common reason people reach for a `linkTo()` override is to add behavior like a JavaScript confirm dialog to generated links. You usually don't need an override for that: every HTML helper passes unknown arguments through as HTML attributes, and arguments starting with `data` are automatically hyphenized — `dataConfirm` becomes `data-confirm`, `data_confirm` works too.
114+
115+
```cfm {test:compile} title="app/views/posts/index.cfm"
116+
<cfoutput>
117+
#linkTo(
118+
route = "editPost",
119+
key = post.id,
120+
text = "Edit",
121+
dataConfirm = "Discard your current draft and edit this post?"
122+
)#
123+
124+
#buttonTo(
125+
route = "post",
126+
key = post.id,
127+
text = "Delete",
128+
method = "delete",
129+
dataConfirm = "Delete this post permanently?"
130+
)#
131+
</cfoutput>
132+
```
133+
134+
Pair it with one delegated listener instead of per-link JavaScript:
135+
136+
```js title="app/assets/js/confirm.js"
137+
document.addEventListener("click", function (e) {
138+
var el = e.target.closest("[data-confirm]");
139+
if (el && !confirm(el.dataset.confirm)) {
140+
e.preventDefault();
141+
e.stopImmediatePropagation();
142+
}
143+
}, true);
144+
```
145+
146+
This replaces what plugins like `jsconfirm` did in older Wheels versions — no override, no plugin, and the attribute shows up on any helper that renders a tag.
147+
148+
## Migrating from Wheels 2.5
149+
150+
If you're upgrading an application from Wheels 2.5 that overrode core methods:
151+
152+
1. Search for usages of `super.methodName()`
153+
2. Replace them with `superMethodName()` — for example, `super.findAll()` becomes `superFindAll()`
154+
3. Test the overridden behavior to make sure results match
155+
156+
No changes are required for applications that don't override core methods.
157+
158+
## Related guides
159+
160+
<CardGrid>
161+
<LinkCard title="Conventions over Configuration" href="/v4-0-0/core-concepts/conventions-over-configuration/" description="The naming and placement conventions that decide where overrides live." />
162+
<LinkCard title="Controllers and Actions" href="/v4-0-0/basics/controllers-and-actions/" description="How controllers are assembled — the mixin surface your overrides sit on top of." />
163+
<LinkCard title="Packages" href="/v4-0-0/digging-deeper/packages/" description="Package mixins can also override framework methods — the same super delegation applies." />
164+
<LinkCard title="Upgrading to 4.x" href="/v4-0-0/upgrading/" description="Version-by-version upgrade notes, including behavior changes in the 4.0.x line." />
165+
</CardGrid>

web/sites/guides/src/sidebars/v4-0-0.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
{ "label": "CORS", "link": "/v4-0-0/digging-deeper/cors/" },
100100
{ "label": "Rate Limiting", "link": "/v4-0-0/digging-deeper/rate-limiting/" },
101101
{ "label": "Dependency Injection Usage", "link": "/v4-0-0/digging-deeper/dependency-injection-usage/" },
102+
{ "label": "Overriding Core Methods", "link": "/v4-0-0/digging-deeper/overriding-core-methods/" },
102103
{ "label": "Debug Panel", "link": "/v4-0-0/digging-deeper/debug-panel/" }
103104
]
104105
},

0 commit comments

Comments
 (0)