Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Stash: development strategies

Stash: development strategies

Presentation to #eeconf 15 October 2013

Mark Croxton

October 15, 2013
Tweet

More Decks by Mark Croxton

Other Decks in Technology

Transcript

  1. Stash embeds can be included at different points in the

    parse order of the host template: at the start, inline with {tags} or, like EE embeds, near the end of template parsing. They can be parsed after retrieval from the cache (like EE embeds), before being cached or at both stages. Non-caching regions can be escaped. They can be nested, but unlike EE embeds you can pre-parse the parent template to assemble a single compiled cache of all child templates. Stas embed v EE embed
  2. stash_templates/foo.html {stash:embed name="foo"} OR {stash:embed:foo} Other file types {stash:embed name="foo.js"}

    OR {stash:embed:foo.js} stash_templates/foo/bar.html {stash:embed context="foo" name="bar"} OR {stash:embed:foo:bar} Passing variables {stash:embed:foo:bar stash:my_var="value"} Basi embe synt
  3. @ static context pointer {stash:embed context="@" name="bar"} OR {stash:embed:@:bar} {!--

    elsewhere, set the @ context --} {exp:stash:context name="foo"} @URI context pointer {stash:embed context="@URI" name="my_var" file_name="foo:bar"} OR {stash:embed:@URI:my_var file_name="foo:bar"} Embe cont
  4. {stash:embed name="layout" context="@"} {exp:switchee variable="{segment_1}" parse="inward"} {!-- Latin languages: stash_templates/ltr/layout.html

    --} {case value="en|fr|it"} {exp:stash:context name="ltr"} {/case} {!-- Arabic/hebrew: stash_templates/rtl/layout.html --} {case value="ar|iw"} {exp:stash:context name="rtl"} {/case} {/exp:switchee}
  5. Determines how the template is parsed and cached by Stash.

    parse_stage="set" Read the file and parse it immediately, then cache the rendered result. Subsequent retrievals return the cache. parse_stage="get" (default) Read the file and cache the un-parsed template code to the database. Parsing occurs on subsequent retrievals. parse_stage="both" Do both the above. Use {stash:nocache} to escape regions so they remain unparsed during SET and are parsed on GET. Pars stag
  6. {!-- templates/default_site/index.html --} {stash:embed name="foo" parse_stage="both"} {!-- stash_templates/foo.html --} {exp:channel:entries}{title}

    | {/exp:channel:entries} {stash:embed:bar stash:message="The time is"} {!-- stash_templates/bar.html --} {stash:message}: {stash:nocache}{current_time format="%Y %m %d %H:%i:%s"}{/stash:nocache} {!-- cached instance "foo" created on SET, saved to db: --} My entry 1 | My entry 2 | My entry 3 The time is: {current_time format="%Y %m %d %H:%i:%s"} {!-- final output on GET: --} My entry 1 | My entry 2 | My entry 3 The time is: 2013 11 14 15:40:30
  7. The point at which an embed is included in the

    host template. process="start" Embed the template before any other variables and tags in your template are parsed (similar to a snippet). process="inline" Embed the template in the natural parse order of the template (similar to a tag). process="end" (default) Embed the template at the end of template parsing after other tags and variables have been parsed (similar to an EE embed). Proces
  8. {!-- templates/default_site/index.html --} {exp:channel:entries channel="people" entry_id="{exp:stash:entry_id}" dynamic="no" parse="inward"} {exp:stash:message}, <strong>{title}</strong>

    {/exp:channel:entries} {stash:embed:foo process="start" stash:message="hello"} {!-- stash_templates/foo.html --} {exp:stash:entry_id}1{/exp:stash:entry_id} {!-- output --} Hello, <strong>Brian</strong>
  9. priority="X" (0) When two or more embeds share the same

    process, determine the order in which they are parsed. parse_depth="X" (4) How many layers deep to parse the template. replace="yes|no" (no) When yes, cached instances of a template are regenerated from the original file every time. Can be overridden globally with $assign_to_config['stash_file_sync']. bundle="my_bundle" ("default") Categories for Stash variables - create your own in Mustash. Tunin embed
  10. Wrapper template with placeholders for content. Partial template that populates

    the placeholders with content and embeds the wrapper. Partials and wrappers are decoupled. Templat partial
  11. templates default_site blog index.html (list of entries) story.html (single entry)

    stash_templates wrappers standard.html Templat partial
  12. {!-- stash_templates/wrappers/standard.html --} <!DOCTYPE html> <html> <head> <title>{stash:pg_title}</title> {snippet:head} </head>

    <body> <h1>{stash:pg_title}</h1> <div role="main"> {stash:pg_content} </div> <footer role="contentinfo"> {snippet:footer} </footer> </body> </html>
  13. {!-- templates/default_site/blog/story.html --} {stash:embed:wrappers:standard} {!-- capture channel data for a

    single entry --} {exp:channel:entries channel="blog"} {exp:stash:set} {!-- set page title --} {stash:pg_title}{title}{/stash:pg_title} {!-- set content --} {stash:pg_content} <section>{cf_content}</section> {/stash:pg_content} {/exp:stash:set} {/exp:channel:entries}
  14. {!-- templates/default_site/blog/index.html --} {stash:embed:wrappers:standard} {!-- set page title --} {exp:stash:pg_title}My

    blog{/exp:stash:pg_title} {!-- capture a list of entries --} {exp:stash:set name="pg_content" parse_tags="yes"} <ul> {exp:channel:entries channel="blog" dynamic="no"} <li> <a href="{title_permalink='blog'}">{title}</a> </li> {/exp:channel:entries} </ul> {/exp:stash:set}
  15. Code re-use – DRY principle Solves fundamental, long-standing parse order

    issues Fewer embeds, better performance Templat partial Pros Logic and markup are closely coupled and now fragmented Routing – requires Structure to re-use partials Adds abstraction / complexity. Document your code! Cons
  16. Wrapper template with variables and blocks that act as placeholders

    for content. ViewModel template that captures/caches structured data, populates variables with content, and injects partials containing localised formatting into blocks on demand. ViewModels, partials and wrappers are decoupled. Mode -View ViewMode
  17. templates default_site blog index.html (viewModel: list of entries) story.html (viewModel:

    single entry) stash_templates views standard.html (wrapper) _list.html (partial) Mode -View ViewMode
  18. {!-- stash_templates/views/standard.html --} <!DOCTYPE html> <html> {snippet:head} <body> <h1>{stash:pg_title}</h1> <div

    role="main"> {exp:stash:block name="pg_content"} <p class="intro">{stash:pg_intro}</p> {stash:pg_body} {/exp:stash:block} </div> {snippet:footer} </body> </html>
  19. {!-- templates/default_site/blog/story.html --} {stash:embed:views:standard} {!-- capture channel data for a

    single entry --} {exp:channel:entries channel="blog" limit="1"} {exp:stash:set} {stash:pg_title}{title}{/stash:pg_title} {stash:pg_intro}{cf_blog_intro}{/stash:pg_intro} {stash:pg_body}{cf_blog_body}{/stash:pg_body} {/exp:stash:set} {/exp:channel:entries}
  20. {!-- templates/default_site/blog/index.html --} {stash:embed:views:standard} {!-- set the page title --}

    {exp:stash:pg_title}My blog{/exp:stash:pg_title} {!-- inject the 'list' partial into 'pg_content' block --} {exp:stash:set_value name="pg_content" value="{exp:stash:embed:views:_list}"} {!-- set a list of blog entries for the partial to render --} {exp:stash:set_list name="list_items" parse_tags="yes"} {exp:channel:entries channel="blog" dynamic="no"} {stash:item}{title}{/stash:item} {/exp:channel:entries} {/exp:stash:set_list}
  21. Logic and markup are decoupled allowing modular re-use of partials,

    and presentation layer can be swapped on the fly. Cache data in a structured form and re-use with different formatting and without extra overhead. DRY, legible code helps you maintain a sane codebase. Mode -View ViewMode Pros Routing is still not within the content manager's control. Hardcoded page meta data. Cons
  22. A base view template that contains the skeletal markup of

    your site and defines placeholders and blocks that child templates can override. A base viewModel template that sets up common variables and default content, and loads child viewModel templates on demand. Optional: a base channel (or channels sharing a fieldgroup) to build the URI structure of your site. Child viewModels and child views are discrete, self-contained, reusable modules. Templat Inheritanc
  23. Templat Inheritanc compiled template cache vM child view child view

    child view child view child view parent view parent vm child vm child vm child vm
  24. templates default_site blog index.html stash_templates viewmodels base.html (parent vm) _blog.html

    (child vm) views standard.html (parent view) _list.html (child view) Templat Inheritanc
  25. {!-- stash_templates/viewmodels/base.html --} {stash:embed:views:standard} {!-- grab data from our base

    channel --} {exp:channel:entries channel="page" limit="1"} {exp:stash:set} {stash:pg_title}{title}{/stash:pg_title} {stash:pg_intro}{cf_page_intro}{/stash:pg_intro} {stash:pg_body}{cf_page_body}{/stash:pg_body} {/exp:stash:set} {!-- optionally load a module (child viewModel) --} {exp:switchee variable="{cf_module}" parse="inward"} {case value="#^S+#"} {stash:embed:viewmodels:_{cf_module} process="inline"} {/case} {/exp:switchee} {/exp:channel:entries}
  26. {!-- stash_templates/viewmodels/_blog.html --} {exp:switchee variable="{segment_3}" parse="inward"} {!-- blog listing --}

    {case value=""} {exp:stash:set_value name="pg_content" value="{exp:stash:embed:views:_list}"} {exp:stash:set_list name="list_items" parse_tags="yes"} {exp:channel:entries channel="blog" dynamic="no"} {stash:item}{title}{/stash:item} {/exp:channel:entries} {/exp:stash:set_list} {/case} continues…
  27. {!-- blog single entry --} {case default="yes"} {exp:channel:entries channel="blog" limit="1"

    url_title="{segment_3}" dynamic="no"} {exp:stash:set} {stash:pg_title}{title}{/stash:pg_title} {stash:pg_intro}{cf_blog_intro}{/stash:pg_intro} {stash:pg_body}{cf_blog_body}{/stash:pg_body} {/exp:stash:set} {/exp:channel:entries} {/case} {/exp:switchee}
  28. Logic and markup are fully decoupled. Both viewModels and view

    partials are self-contained modules that can be combined and re-used. Use the Stash embed compile effect for progressive caching. Permits Structure-like template control but with native template_group/template routing. Templat Inheritanc Pros Complex; best for large projects with many moving parts. Cons
  29. Wha t cach ? Global fragments: rendered partials, such as

    navigational elements, footers, sidebars and other reusable, globally visible page elements (scope="site"). Addressable pages: the unique output of individual pages in your site, where the URI alone is sufficient to generate all possible interaction states ("URI addressibility"). Non-global fragments: Partials that generate output that is specific to the URI or user. Non-addressable pages: where the URI of a page alone is not sufficent to determine it's interaction state. E.g. forms that POST data, shopping carts, 404 pages.
  30. Build for URI addressability: as far as possible plan you

    site so that all interaction states are represented by a unique URI. Stay DRY: separate out commonly used global page fragments into snippets, Low Variables or Stash embeds. Where possible cache structured data, not marked-up data, so you can re-use the data with different markups. Group like items: use context and/or bundles to create groups of related cached items, so that they can be targetted for cache-breaking. Use layers: Use fragment-caching inside page-caching to allow for progressive cache rebuilds and avoid ‘stampedes’. Cach plannin
  31. {!-- save a list of the 5 latest news stories

    for an hour --} {exp:stash:set_list name="news_headlines" parse_tags="yes" save="yes" scope="site" refresh="60" replace="no" } {exp:channel:entries channel="news" limit="5"} {stash:headline}{title}{/stash:headline} {stash:url}{url_title}{/stash:url} {/exp:channel:entries} {/exp:stash:set_list}
  32. {!-- templates/default_site/blog/index.html --} {exp:stash:cache bundle="blog"} {stash:embed:layouts:standard} {!-- capture channel data

    for a single entry --} {exp:channel:entries channel="blog" limit="1"} {exp:stash:set} {stash:pg_title}{title}{/stash:pg_title} {stash:pg_intro}{cf_blog_intro}{/stash:pg_intro} {stash:pg_body}{cf_blog_body}{/stash:pg_body} {/exp:stash:set} {/exp:channel:entries} {!-- this will be escaped --} {stash:nocache} Hi, {screen_name} {/stash:nocache} {/exp:stash:cache}
  33. {!-- templates/default_site/blog/index.html --} {stash:embed name="base" context="@URI" file_name="viewmodels:base" parse_stage="both"} {!-- stash_templates/viewmodels/base.html

    --} {stash:embed:layouts:standard} {exp:channel:entries channel="blog" limit="1"} {exp:stash:set} {stash:pg_title}{title}{/stash:pg_title} {stash:pg_intro}{cf_blog_intro}{/stash:pg_intro} {stash:pg_body}{cf_blog_body}{/stash:pg_body} {/exp:stash:set} {/exp:channel:entries} {stash:nocache}Hi, {screen_name}{/stash:nocache}
  34. Htaccess is used to bypass EE and PHP entirely. Typically

    >10 times greater concurrency than through PHP. Stati cachin Pros Pages cannot contain escaped regions that remain dynamic*. Cache never expires. Needs Mustash for cache-breaking. Cons Use in combination with Varnish and Edge Side Includes (ESI). *Experimental/advanced use
  35. {!-- {stash:embed} static caching --} {stash:embed name="base" context="@URI" file_name="viewmodels:base_vm" process="static"}

    {!-- {exp:stash:cache} static caching --} {exp:stash:cache bundle="blog" process="static"} <h1>Page to cache</h1> {/exp:stash:cache} {!-- Or use this tag ONCE anywhere in your template --} {exp:stash:static}
  36. Manage variables: Search, filter, edit and delete cached Stash variables

    and static file caches. Flush caches: Flush cache globally, by scope or bundle. Bundles: Create and edit your own bundles. Rules: Setup cache-breaking rules for core and third-party modules. Target specific bundles, groups and patterns. Web API: endpoint with custom hooks to trigger rules. Access permissions: limit access by member group. Plugins: extensible architecture for plugin developers Mustas – ke feature
  37. Hook Rules are attached to specific editing events in the

    control panel. Group Limit rule to parent group of the item being edited (e.g. channel). Bundle Limit rule to variables in a particular Stash 'bundle' (category). Scope Limit rule to variables with the selected visibility (site, or user). Pattern Limit rule to variables matching a string or regular expression. Definin rule
  38. Hook Core module hooks Channel Entries, Categories, Comments, Members, Forum.

    E.g. 'entry_submission_end' - triggered when an entry is edited. Third-party module hooks Low Reorder, Low Variables, Navee, Structure, Taxonomy + more. Web API custom hooks User-defined hooks for use as an URL endpoint, e.g. http://mysite.com/?ACT=27&key=12345test&hook=my_hook Recursive hooks Hooks that are triggered when Stash caches are deleted, so external caching layers can be flushed. E.g. Varnish.
  39. Strings my_variable my_context:my_variable Regular expressions #^my_variable1$|^my_variable2$|P\d+$# ^ = start of

    line $ = end of line | = OR \d+ = one or more digits Using {markers} #^{channel_name}:{url_title}$# Pattern
  40. Clear ALL variables when channel entries are edited/added. ample Clear

    the cached page where the name matches the channel and url title of the entry that was just edited. E.g. 'blog/my_story'. Clear variables in the 'Navigation' bundle when the 'Main menu' tree is updated in Taxonomy.
  41. Stash embeds are a powerful alternative to EE embeds. Create

    a common nonemaclature for template placeholders. Use template design patterns to separate logic from markup. Modularize your code into discrete viewmodels and partials, so that it less closely coupled and more re-usuable. Plan for addressability when designing your URIs. Avoid stampedes by caching in layers: global fragments underneath full-page caching. Organise your cached variables into bundles and design granular cache-breaking rules. Summar