Slide 1

Slide 1 text

Demystifying the Cra API (for Twig developers)

Slide 2

Slide 2 text

¡Hola! I’m Prateek !

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Sheets to Tables Contact Form Tuner Cryptographer Router Obfuscator

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

Why When How

Slide 7

Slide 7 text

Why bother using the API

Slide 8

Slide 8 text

Once upon a time… There was a web developer

Slide 9

Slide 9 text

Started building sites… using Cra CMS

Slide 10

Slide 10 text

craft.entries craft.categories craft.users To the developer, templates meant {% for %} {% set %} {% block %}

Slide 11

Slide 11 text

craft.entries craft.categories craft.users But over time realised… {% for %} {% set %} {% block %}

Slide 12

Slide 12 text

Could use more than just… Element Queries Twig Tags

Slide 13

Slide 13 text

Twig Functions Could use more than just… Element Queries Twig Tags Twig Filters

Slide 14

Slide 14 text

Twig Functions Cra Helpers Cra Components Yii Framework Composer Packages PHP

Slide 15

Slide 15 text

And the developer’s templates became be er, faster, stronger!

Slide 16

Slide 16 text

fin

Slide 17

Slide 17 text

This developer was none other than me "

Slide 18

Slide 18 text

API* Element Queries Twig Tags Twig Filters Twig Functions Cra Helpers Cra Components Yii Framework Composer Packages PHP

Slide 19

Slide 19 text

Recreate the templating journey for loops & cra .entries # Haz API

Slide 20

Slide 20 text

Site for a “Dot All” Music Festival

Slide 21

Slide 21 text

(Send in petitions to Leah)

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

3 content types 1. Artists 2. Tickets 3. Performances

Slide 29

Slide 29 text

Template

Slide 30

Slide 30 text

Query Entries

Slide 31

Slide 31 text

Loop & Render

Slide 32

Slide 32 text

Artists Tickets Types Performances

Slide 33

Slide 33 text

Should be quite familiar to anyone who's built a Cra site

Slide 34

Slide 34 text

Okay, back to the “Why” Benefits I’ve experienced using the API

Slide 35

Slide 35 text

Readability

Slide 36

Slide 36 text

example 1 of 1

Slide 37

Slide 37 text

{{ seatsAvailable }} seats

Slide 38

Slide 38 text

Using tag() {{ tag('span', { class: [ 'text-xs', seatsAvailable < 10 ? 'text-red-500', seatsAvailable >= 10 and seatsAvailable <= 50 ? 't…', seatsAvailable > 50 ? 'text-green-500', ], text: seatsAvailable ~ ' seats', }) }}

Slide 39

Slide 39 text

{{ seatsAvailable }} seats {{ tag('span', { class: [ 'text-xs', seatsAvailable < 10 ? 'text-red-500', seatsAvailable >= 10 and seatsAvailable <= 50 ? 'text…', seatsAvailable > 50 ? 'text-green-500', ], text: seatsAvailable ~ ' seats', }) }} More Readable

Slide 40

Slide 40 text

HTML A ributes as key-value pairs | attr({ … }) attr({ … }) tag('a', { … }) ul(list, { … }) ol(list, { … }) input('tel', 'p', { … }) {% tag 'a' with { … } %}

Slide 41

Slide 41 text

Readable :: Easier to understand

Slide 42

Slide 42 text

Readable :: Easier to understand collaborate maintain spot bugs

Slide 43

Slide 43 text

Developer Efficiency

Slide 44

Slide 44 text

example 1 of 2

Slide 45

Slide 45 text

{% set eventsList = craft.entries .section('events') .orderBy('startDate') .all() %} {% set eventsList = craft.entries .section('events') .orderBy('startDate') .all() %}

Slide 46

Slide 46 text

{% set eventsList = craft.entries .section('events') .orderBy('startDate') .all() %}

Slide 47

Slide 47 text

{% set eventsList = craft.entries .section('events') .all() |sort((a, b) => a.startDate == b.startDate ? a.venue.one().title <=> b.venue.one().title : a.startDate <=> b.startDate ) %}

Slide 48

Slide 48 text

{% set eventsList = craft.entries .section('events') .all() |sort((a, b) => a.startDate == b.startDate ? a.venue.one().title <=> b.venue.one().title : a.startDate <=> b.startDate ) %}

Slide 49

Slide 49 text

{% set eventsList = craft.entries .section('events') .all() |multisort([ 'startDate', 'venue.0.title', ]) %}

Slide 50

Slide 50 text

{% set eventsList = craft.entries .section('events') .all() |sort((a, b) => a.startDate == b.startDate ? a.venue.one().title <=> b.venue.one().title : a.startDate <=> b.startDate ) %} {% set eventsList = craft.entries .section('events') .all() |multisort([ 'startDate', 'venue.0.title', ]) %} Less mental math

Slide 51

Slide 51 text

example 2 of 2

Slide 52

Slide 52 text

Truncate to avoid crazy big pop-ups

Slide 53

Slide 53 text

How many of you have wri en your own truncation code? $ Guilty

Slide 54

Slide 54 text

{% macro truncate(string, chars = -1) %} {% set string = string | trim %} {% if truncate > 0 and string|length > truncate %} {% set string = string|slice(0, truncate) ~ '…' %} {% endif %} {{- string -}} {% endmacro %} ⚠ Bug: Partially Splits Words

Slide 55

Slide 55 text

{% macro truncate(string, maxLength) %} {% set words = string|split(' ') %} {% set truncatedString = '' %} {% for word in words %} {% set newString = truncatedString ~ ' ' ~ word %} {% if newString|length < maxLength %} {% set truncatedString = newString %} {% endif %} {% endfor %} {{ (truncatedString ~ '...')|trim }} {% endmacro %} ⚠ Bug: Can Skip Long Words

Slide 56

Slide 56 text

{% macro truncate(content, words = 50, truncator = '...') %} {% set symbols = content | striptags | split(" ") %} {% set content = (symbols|length <= words) ? ( symbols | join(" ") | replace("\t", '') ) : ( symbols | slice(0, words) | join(" ") | replace("\t", '') ~ (truncator ? ' ' ~ truncator : '' ) ) %} {{ content|replace(' ', '') }} {% endmacro %} ⚠ Bug: Might double escape

Slide 57

Slide 57 text

{{ artist.bio | truncate(200) }} It Just Works™

Slide 58

Slide 58 text

{{ artist.bio | truncate(200) }} & Made me feel stupid It Just Works™

Slide 59

Slide 59 text

I trust P&T code over mine any day '

Slide 60

Slide 60 text

Use system lib :: Less custom logic

Slide 61

Slide 61 text

Use system lib :: Less custom logic Less mental math Fewer bugs Ship faster Widely deployed Future proof Less code More robust

Slide 62

Slide 62 text

Superpowers

Slide 63

Slide 63 text

example 1 of 2

Slide 64

Slide 64 text

{{ artist.bio | replace( artist.title, tag('strong', { text: artist.title, class: 'text-base font-semibold', }) ) }}

Slide 65

Slide 65 text

{{ artist.bio | replace( artist.title, tag('strong', { text: artist.title, class: 'text-base font-semibold', }) ) }}

Slide 66

Slide 66 text

{{ create('craft\\helpers\\StringHelper') .replaceFirst( artist.bio, artist.title, tag('strong', { text: artist.title, class: 'text-base font-semibo…', }) ) }}

Slide 67

Slide 67 text

{{ create('craft\\helpers\\StringHelper') .replaceFirst( artist.bio, artist.title, tag('strong', { text: artist.title, class: 'text-base font-semibo…', }) ) }} Free Boost

Slide 68

Slide 68 text

create() :: Cra & Yii Helpers

Slide 69

Slide 69 text

create() :: Cra & Yii Helpers Array Helper HTML Helper JSON Helper String Helper DateTime Helper URL Helper

Slide 70

Slide 70 text

example 2 of 2

Slide 71

Slide 71 text

Pull in missing artist bios from Wikipedia

Slide 72

Slide 72 text

Wikipedia provides an API with JSON responses

Slide 73

Slide 73 text

{% set response = create('GuzzleHttp\\Client') .get( 'https://en.wikipedia.org/api/rest…', { headers: { accept: 'application/json', charset: 'utf-8', }, }) %} {% if response.getStatusCode() == 200 %} {% set response = apiResponse .getBody() .getContents() | json_decode

Slide 74

Slide 74 text

accept: 'application/json', charset: 'utf-8', }, }) %} {% if response.getStatusCode() == 200 %} {% set response = apiResponse .getBody() .getContents() | json_decode %} {{ response.description }} {{ response.extract_html }} {% endif %}

Slide 75

Slide 75 text

{% set response = create('GuzzleHttp\\Client') .get( 'https://en.wikipedia.org/api/rest…', { headers: { accept: 'application/json', charset: 'utf-8', }, }) %} Immensely Powerful {% if response.getStatusCode() == 200 %} {% set response = apiResponse .getBody() .getContents() | json_decode %} {{ response.description }} {{ response.extract_html }} {% endif %}

Slide 76

Slide 76 text

create() :: Composer Packages

Slide 77

Slide 77 text

create() :: Composer Packages any PHP class Guzzle Stringy Cookie

Slide 78

Slide 78 text

Performance

Slide 79

Slide 79 text

example 1 of 2

Slide 80

Slide 80 text

Wikipedia API calls need to be cached

Slide 81

Slide 81 text

Cra has a Cache component

Slide 82

Slide 82 text

cra .app.cache

Slide 83

Slide 83 text

{# Store #} {% craft.app.cache.set(cacheKey, value) %} {# Re-use #} {% set value = craft.app.cache.get(cacheKey) %}

Slide 84

Slide 84 text

{% set cacheKey = [url, params] %} {% set response = craft.app.cache.get(cacheKey) %} {% if response is same as(false) %} {# Make API call #} {% set response = ... %} {# Cache response #} {% do craft.app.cache.set(cacheKey, response) %} {% endif %}

Slide 85

Slide 85 text

Fetch once, Store & Re-use 20x(

Slide 86

Slide 86 text

craft.app.Components & Services

Slide 87

Slide 87 text

craft.app.Components & Services Fields Globals Session Request Config Site Sections

Slide 88

Slide 88 text

example 2 of 2

Slide 89

Slide 89 text

Reduce Database queries

Slide 90

Slide 90 text

{% set eventsList = craft.entries .section('events') .orderBy('startDate') .all() %} {% for event in eventsList %} {{ event.title }} {{ event.artists.all()|join }} {{ event.venue.one() }} {{ event.ticketTypes.all()|join }} {% endfor %}

Slide 91

Slide 91 text

{% set eventsList = craft.entries .section('events') .orderBy('startDate') .all() %} {% for event in eventsList %} {{ event.title }} {{ event.artists.all()|join }} {{ event.venue.one() }} {{ event.ticketTypes.all()|join }} {% endfor %}

Slide 92

Slide 92 text

{% set eventsList = craft.entries .section('events') .orderBy('startDate') .with(['artists', 'venue', 'ticketTypes']) .all() %} {% for event in eventsList %} {{ event.title }} {{ event.artists.all()|join }} {{ event.venue.one() }} {{ event.ticketTypes.all()|join }} {% endfor %}

Slide 93

Slide 93 text

{% set eventsList = craft.entries .section('events') .orderBy('startDate') .with(['artists', 'venue', 'ticketTypes']) .all() %} {% for event in eventsList %} {{ event.title }} {{ event.artists.all()|join }} {{ event.venue.one() }} {{ event.ticketTypes.all()|join }} {% endfor %} 3x(

Slide 94

Slide 94 text

Eager-load all the element fields in a loop

Slide 95

Slide 95 text

Recap Better readability Easier to maintain More future proof Fewer bugs Write lesser code Ship faster Improved performance Robustness Free system features “Superpowers”

Slide 96

Slide 96 text

When to use the API

Slide 97

Slide 97 text

Along your templating journey…

Slide 98

Slide 98 text

Ask yourself if the feature you’re developing is…

Slide 99

Slide 99 text

…common across other websites? • Truncation • Sorting • Filtering

Slide 100

Slide 100 text

…present in the Cra Control Panel? • Caching • Validations • External API requests

Slide 101

Slide 101 text

…not unique to the context of websites? • Math • Date & Time • Formatting • Encoding / decoding

Slide 102

Slide 102 text

If the answer is “Yes” The API has (probably) solved it already

Slide 103

Slide 103 text

As projects grew in complexity… Pa erns started to emerge

Slide 104

Slide 104 text

Fetch Content

Slide 105

Slide 105 text

Fetch Cra Elements — Entries, MatrixBlocks, Categories, Users… URL & Route Variables External Sources

Slide 106

Slide 106 text

Fetch Content Transform Content

Slide 107

Slide 107 text

Transform Match a certain structure or format Apply biz logic Combine various sources

Slide 108

Slide 108 text

Fetch Content Transform Content Render Content

Slide 109

Slide 109 text

Render HTML tags & attributes Images, media, assets Content styling

Slide 110

Slide 110 text

Fetch Content Transform Content Render Content

Slide 111

Slide 111 text

How to use the API

Slide 112

Slide 112 text

Small workflow changes

Slide 113

Slide 113 text

Pause and reflect

Slide 114

Slide 114 text

Pause and reflect Look for the Patterns

Slide 115

Slide 115 text

Advanced DB queries Eager-loading Caching External API call Fetching Content Patterns

Slide 116

Slide 116 text

Transforming Content | multisort | map | filter | reduce Collections | merge Patterns

Slide 117

Slide 117 text

Rendering Content | attr() | truncate StringHelper .replaceFirst() svg() url() Patterns

Slide 118

Slide 118 text

Pause and reflect Look for the Patterns Answer the Prompts

Slide 119

Slide 119 text

For common web features Twig Filters Twig Functions Cra Helpers

Slide 120

Slide 120 text

For features in the Cra Control Panel Cra Components Yii Framework Cra Helpers

Slide 121

Slide 121 text

For features not unique to websites Cra Helpers Cra Components Yii Framework Composer Packages PHP

Slide 122

Slide 122 text

Pause and reflect Search the API

Slide 123

Slide 123 text

Search the API Embrace the docs

Slide 124

Slide 124 text

Use pinned tabs to keep them handy

Slide 125

Slide 125 text

Search, dig deep, repeat — docs are updated constantly

Slide 126

Slide 126 text

Zoom into specifics in the Class Reference

Slide 127

Slide 127 text

Pause and reflect Search the API Experiment, familiarise

Slide 128

Slide 128 text

Experiment, familiarise Explore quickly, freely, and without worry

Slide 129

Slide 129 text

templates/test.twig For quick trials and experiments of whatever you spot in the docs

Slide 130

Slide 130 text

Use version control to easily roll back any change

Slide 131

Slide 131 text

./cra db/backup If you’re ever concerned about damaging your content

Slide 132

Slide 132 text

Pause and reflect Search the API Experiment, familiarise

Slide 133

Slide 133 text

Docs may not be your starting point

Slide 134

Slide 134 text

Alternate Entry Point Manipulating Lists

Slide 135

Slide 135 text

We’re o en working with lists Arrays Lists of entries Key-value pairs Nested arrays

Slide 136

Slide 136 text

Tools for manipulating lists | map | filter | reduce | sort | multisort | column | unique

Slide 137

Slide 137 text

Bunch of them take “arrow functions” |map(n => n * 2) |filter(n => n > 20) |sort((a,b) => a<=>b)

Slide 138

Slide 138 text

example

Slide 139

Slide 139 text

No content

Slide 140

Slide 140 text

{% set artistsList = craft.entries .section('artists') .orderBy('title asc') .relatedTo(eventsList) .all() %}

Slide 141

Slide 141 text

No content

Slide 142

Slide 142 text

{% set eventsList = craft.entries .section('events') .with(['venue', 'ticketTypes', 'artists']) .all() %}

Slide 143

Slide 143 text

{% set artistsList = eventsList |map(event => event.artists.all()) %}

Slide 144

Slide 144 text

{% set artistsList = eventsList |column('artists') %}

Slide 145

Slide 145 text

{% set artistsList = eventsList |column('artists') |reduce((flatList, artists) => flatList|merge(artists), []) |unique |multisort('title') %}

Slide 146

Slide 146 text

Even more succinct using Laravel Collections

Slide 147

Slide 147 text

{% set artistsList = collect(eventsList) .pluck('artists') .flatten() .unique() .sortBy('title') .all() %}

Slide 148

Slide 148 text

create()

Slide 149

Slide 149 text

Wrapping Up

Slide 150

Slide 150 text

Why Better readability Easier to maintain More future proof Fewer bugs Write lesser code Ship faster Improved performance Robustness Free system features “Superpowers”

Slide 151

Slide 151 text

When Ask yourself, is it: …common across other websites? …present in the Cra Control Panel? …not unique to the context of websites?

Slide 152

Slide 152 text

When Identify the patterns: Fetching Content Transforming Content Rendering Content

Slide 153

Slide 153 text

How Small workflow changes Pause & reflect Search the docs Experiment, familiarise

Slide 154

Slide 154 text

✈ ) + , - . / 0 1

Slide 155

Slide 155 text

As a tourist, mostly ge ing by using English & a translator app Navigating the way, visiting sites, taking in experiences

Slide 156

Slide 156 text

However, a resident needs to pickup the local language Adopting local culture, making connections, feeling at home

Slide 157

Slide 157 text

For developers, each technology is like a different country APIs are the local language

Slide 158

Slide 158 text

What would you like to be…

Slide 159

Slide 159 text

…a tourist or a resident?

Slide 160

Slide 160 text

Recall the templating journey? for loops & cra .entries # Haz API

Slide 161

Slide 161 text

for loops & cra .entries # Haz API Tourist Resident

Slide 162

Slide 162 text

for loops & cra .entries # Haz API Tourist Resident Fear, Uncertainty, Doubt Discover, Try, Thrive

Slide 163

Slide 163 text

Gracias 2 github.com/miranj/demystifying-cra -api