Search code examples
luamediawikimediawiki-templates

Best way to make Mediawiki template which displays different content depending on parameter?


I will have hundreds of biographies for characters which consist of some text, markup and image(s).

I want to have a template where you can write e.g. {{Biography|Steven}} and it grabs that biography from a page of all bios where each one is linked to a parameter.

Further, the option to only show part of a bio, e.g. {{Biography|Steven|Personality}}

I know I could make a template for each biography, but then I would have hundreds of templates that editors have to find, and I dont know if thats good performance wise (its kind of ok?).

Then I still wouldnt know how to set up the template to conditionally display on parts of the bio.

I also wonder if I should possibly have some data storage extension, but I dont see how that is possibly faster than text on a page transcluded.

Do I need conditional code using ParserFunctions or my preference, Lua, and use its many possible versions of a switch statement.

I see this template as a good feature because people can just edit any bio on one page, or one page for each character classification, then anyone can transclude it instead of writing out the same content but making errors, wasting time, etc.

This template will use extremely high-use, so its important that it performs well and easy to use for editors.

I'm not asking for someone to write it for me, just advice on how to go about it.


Solution

  • To do this you will need to make your data granular enough to be able to select individual biography fields. There are several ways to do this in MediaWiki.

    Parser functions

    You can use #switch statements to create a template that will do the job, without adding any extensions. For example, you can have a data module at Template:Biography/data:

    {{#switch: {{{character|}}}
    | Steven = {{#switch: {{{trait|}}}
      | Personality = Outgoing
      | Age = 25
      }}
    | Dennis = {{#switch: {{{trait|}}}
      | Personality = Shy
      | Age = 34
      }}
    }}
    

    You can then call that from Template:Biography like this:

    {{#if: {{{2|}}}
     | {{Biography/data|character={{{1|}}}|trait={{{2|}}}}}
     |
    * Name: {{{1|}}}
    * Age: {{Biography/data|character={{{1|}}}|trait=Age}}
    * Personality: {{Biography/data|character={{{1|}}}|trait=Personality}}
     }}
    

    You can organise this #switch statement in several different ways. For example, you can have the data for each character stored in a different subtemplate of Template:Biography, like Template:Biography/data/Steven, Template:Biography/data/Dennis, etc.

    Template:Biography:

    {{#if: {{{2|}}}
     | {{Biography/data|{{{1|}}}|{{{2|}}}}}
     |
    * Name: {{{1|}}}
    * Age: {{Biography/data|{{{1|}}}|Age}}
    * Personality: {{Biography/data|{{{1|}}}|Personality}}
     }}
    

    Template:Biography/data

    {{Biography/data/{{{1|}}}|trait={{{2|}}}}}
    

    Template:Biography/data/Steven:

    {{#switch: {{{trait|}}}
    | Personality = Outgoing
    | Age = 25
    }}
    

    This will be better than our first attempt, as long switch statements are notoriously slow in MediaWiki. However, you still need two levels of subtemplates to make it work, and if you don't specify a trait, you have to call the subtemplates multiple times. So it is still going to be slow.

    Scribunto

    You can speed things up by writing the template in Lua, via the Scribunto extension. You can write a data module like this (let's say it is called Module:Biography/data):

    return {
        ['Steven'] = {
            Personality = 'Outgoing',
            Age = 25,
        },
        ['Dennis'] = {
            Personality = 'Shy',
            Age = 34,
        },
    }
    

    Then you can load the data using mw.loadData, which will load the entire table once per page, instead of every time you use the {{Biography}} template. For example:

    local data = mw.loadData('Module:Biography/data')
    local p = {}
    
    local BIO_TEMPLATE = [[
    * Name: %s
    * Age: %d
    * Personality: %s]]
    
    -- Trim whitespace from args, and treat blank args as nil
    local function preprocessArg(s)
        if not s then
            return nil
        end
        s = s:match('^%s*(.-)%s*$') -- trim whitespace
        if s == '' then
            return nil
        else
            return s
        end
    end
    
    function p.main(frame)
        local args = frame.args
        local character = preprocessArg(args[1])
        local trait = preprocessArg(args[2])
    
        -- Check for blank character arguments
        if not character then
            return ''
        end
    
        -- Get the data for the specified character
        local characterData = data[character]
        if not characterData then
            return ''
        end
    
        if trait then
            -- User specified a trait, so return it
            return characterData[trait] or ''
        else
            -- Return the biography template with all the traits in it
            return string.format(
                BIO_TEMPLATE,
                character,
                characterData.Age,
                characterData.Personality
            )
        end
    end
    
    return p
    

    You can also split the data pages up by character or by trait, in much the same way as you can with #switch.

    The disadvantage of using Lua is that your editors will need to learn Lua syntax as well as wikitext syntax to add entries to the data module(s). Lua syntax is generally harder to master than wikitext, so this may put people off contributing.

    Extensions

    The problem of wikitext data being unstructured is a long-standing one, so there have been lots of attempts to fix it over the years. Before settling on pure parser functions or pure Scribunto, you would do well to investigate some of the extensions that have been made to try and address this problem. (Remember that you can access these extensions via templates, and generally, via Scribunto as well.)

    For example, the labeled section transclusion extension will allow you to write a normal biography for your character, delimit the different sections, and then transclude those sections individually onto other pages.

    There are also more data-focused extensions that can help you to make more granular templates without getting bogged down in switch functions or data modules:

    These will store your data fields separately in a database (usually the same one as your MediaWiki installation) so the performance characteristics will depend on how many database accesses you need to generate a page, and how you have your caching set up.

    If I were you I would have a look at the available extensions first and see if there is anything that would fit your situation well, then fine-tune that using a template or Scribunto.