2022/05/30/more data-class rubberducking
Again with the indecision over naming things...
...which then twines into indecision over underlying design...
So, right now there are two ways of handling the display of a record.
One is premised on the idea that you're working with records one at a time. A record corresponds to a page.
The other is premised on the idea that you want to see multiple records, and possibly operate on subsets of them (e.g. with checkboxes or action-links or whatever). The exact format doesn't matter, though it's usually a table, with one record per row (but we sometimes want to see multiple records references in a single line... I've been doing that as a separate area of functionality, but it may be worth keeping in mind as a variant on this theme).
This dichotomy is pretty easy to handle at the Card level -- I just have one class for the Page display and another for the Line display; the Rows class chooses which one to spawn.
The problem I've bumped into has to do with the Rows classes.
It'd be convenient if I could do the same dichotomy with Rows classes -- one for Page view, one for Line view... but then I need something higher up to choose which class, and for now I've decided that the Rows class needs to be where the bifurcation happens (because the next level up is the Bank, which splits by type of I/O - display, storage, or native (internal) -- which starts to get messy when you're also doing variations on the same I/O type).
So instead of the obvious, tidy class-dichotomy, we have (a) standard Rows class which only knows one type of display (so far, have only used this for Page view), and (b) a dual-mode type which can either display a single Page Card or multiple Line Cards.
So first off, this messes up the naming a bit.
I want to have "line" be the slug I use for naming the Rows subclass -- but that's confusing, because it doesn't just handle Line view; it can handle both Line and Page views, choosing which one to use depending on context.
Maybe "what do I mean by 'context'?" is a good question for me to be asking...
The most obvious context is whether a specific row has been requested. If not, then the Bank (well... ok, Feature, which is like a Bank that registers itself as a thing which can be invoked by URI data) assumes we want to see all of the rows...
...okay, there are two stages to this:
- (a) the process of selecting the rows to be displayed (can be in code -- e.g. an Account page will show all Groups assigned to said Account -- or, in the future, user-configurable (e.g. show only the first 100 records, or those between two dates, or whatever)
- (b) the process of displaying whatever is in the recordset
So first we have an outer method which looks at the URI, decides whether it's requesting a specific Row or multiple Rows, selects the data, and then passes it on to a different method -- either a Page-Mode-display method or a Line-Mode-display method -- which then loads the appropriate Card objects and iterates through whatever data it has been given (which should only be a single Row if it's a Page-mode Card).
The design question I was bumping into: are Page and Line modes just the most common examples of what might in some cases be multiple different modes I'd want to be able to support? E.g. that "keep in mind" I mentioned earlier -- maybe we want to be able to page-view a record, to multiple-lines-list records, and to inline-list them as well (e.g. just the name, or name + some kind of quick stat, linked to the page).
Thing is, there are two distinct modes at work here, even in that trio. One mode iterates; the other doesn't... or at least shouldn't need to.
Like... we can make assumptions, in Page mode, that certain things won't be needed. There's no point in a header or footer when you're only showing one thing.
But is it simpler to stub in the functionality regardless, or simpler to only add it in when needed?
I've been assuming it's simpler to add it in when needed -- because if you do need Line as well as Page, then you need knowledge of two different Card classes. If you only need one view, then you only need one Card class -- which is how all the other Rows classes work.
But let's say that once we get past single-mode, we're not thinking in terms of always exactly two display-modes. What if we wanted to support, say, three different views -- page, lines, and inline (see above for an example). (edited)
One thing we can simplify is to just always iterate. If it's a single row, we'll just only iterate once; same result. The header and footer can be blank.
For just that part, we don't even need a subclass to handle more than one mode of data-management; just build iteration into the base class.
What does change is how we configure each Rows class's corresponding Card class(es). The base Rows class would only have one; the subclass would need to be able to specify different Card classes depending on display mode.
So, the question is how hard I want to push to bake in the idea of multiple Card classes now, vs. just implementing a two-mode system for now and saving multiples for later.
(...and on that note, I must go shopping and then probably eat something.)
I think that if I'm actually remembering to ask "do I need to solve this problem now, or can it wait?", that's a good opportunity to try waiting rather than risking yet more cascading refactors.
So, next: naming.
I was calling it "bimodal", which is a tad awkwardly long for my taste... how about "dual"? I think my brain may find that less squicky.
Oh arf, I have to be careful about where the content template string is set, because it needs to be different depending on which mode -- so I can't just cache the Template object with that string set in it; I've got to update it whenever the mode changes... bleh.
...and when these two traits are used together, one trait from A needs to override one from B, and one from B needs to override one from A...
I think PHP provides a syntax for doing exactly that, but I have to figure it out and maybe make a compound trait so I don't have to paste lots of code whenever I use them together.