Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Patience and the right tools – ways to approach...
Search
stefan judis
February 08, 2018
Technology
690
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Patience and the right tools – ways to approach content modelling
stefan judis
February 08, 2018
More Decks by stefan judis
See All by stefan judis
Back to boring (part 2)
stefanjudis
0
370
Playwright can do this?
stefanjudis
0
250
Things you should know about Frontend Development in 2022
stefanjudis
0
560
Throw yourself out there for fun and profit
stefanjudis
0
140
Back to Boring
stefanjudis
1
530
Wanna scale up? Make sure your CMS is ready for it!
stefanjudis
0
290
Did we(b development) lose the right direction?
stefanjudis
6
2.2k
Regular expressions – my secret love
stefanjudis
1
1.1k
Write a Function
stefanjudis
0
620
Other Decks in Technology
See All in Technology
Claude CodeとAmazon Bedrock AgentCoreでつくる、自分だけのAIアシスタント
ymae
0
110
書籍セキュアAPIについて
riiimparm
0
320
”AIを使う” から ”AIに任せる” へ ─ 開発プロセスを再設計してAIを組織標準にするまで
cyberagentdevelopers
PRO
2
150
2026年のソフトウェア開発を考える(2026/07版) / Agentic Software Engineering 2026-07 Findy Edition
twada
PRO
28
14k
AI驚き屋発見器
yama3133
1
210
現場との対話から始める “作る前に問い直す”業務改善
mochico50
2
290
なぜ、あなたのAPIは使われないのか? AX時代の設計原則、ガードレール、運用体制
yokawasa
1
230
発表と総括 / Presentations and Summary
ks91
PRO
0
210
ソフトウェアアーキテクチャ研修【MIXI 26新卒技術研修】
mixi_engineers
PRO
2
220
reFACToring
moznion
0
300
kaonavi Tech Night#1
kaonavi
0
170
Webアプリ認証の全体像 / The Big Picture of Web App Authentication
kitano_yuichi
1
450
Featured
See All Featured
A better future with KSS
kneath
240
18k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.6k
Site-Speed That Sticks
csswizardry
13
1.3k
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
390
ラッコキーワード サービス紹介資料
rakko
1
4M
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
62
45k
Ethics towards AI in product and experience design
skipperchong
2
330
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
500
4 Signs Your Business is Dying
shpigford
187
22k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
390
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
8.2k
Un-Boring Meetings
codingconduct
0
350
Transcript
Patience and the right tools @stefanjudis Ways to approach content
modelling
Stefan Judis Frontend Developer, Occasional Teacher, Meetup Organizer ❤ Open
Source, Performance and Accessibility ❤ @stefanjudis
Where we come from
Karen McGrane I don’t necessarily know where this content is
going to live. I don’t know how it’s going to be used in the future. But, I do know that I have to create lots of flexible content that has metadata attached to it because I know it’s going to be reused in different places. “
None
None
It's like Lego
NOBODY(!) GETS IT RIGHT THE FIRST TIME...
share Share & reuse
github.com/joshhebb/angularjs-contentful-starter
None
www.npmjs.com/package/contentful-import www.npmjs.com/package/contentful-export
It's just code Setting up a space is quickly done
When you know how...
I believe in tooling and defaults!
Eat your own dog food
None
USEFUL FOR ANYONE?
Share content models?
None
www.contentfulcommunity.com MAYBE HERE?
Analyse
None
www.npmjs.com/package/contentful-graph
None
YOUR CONTENT MODEL VISUALISED.
None
None
CLI is great, but there is more
contentful-graph.yaraslav.com/
Evolve
Clean up time
www.npmjs.com/package/contentful-migration-cli
MIGRATION CLI CONTENT TYPE OPERATIONS Create a content type 01
02 03 04 05 Delete a content type Edit a content type Create/edit/delete fields Change a field ID
FIELDS TO DELETE
Drop fields module.exports = function (migration) { const event =
migration.editContentType('event') const project = migration.editContentType('project') }
module.exports = function (migration) { const event = migration.editContentType('event') event.deleteField('hotel')
event.deleteField('venue') const project = migration.editContentType('project') project.deleteField('screenshot') } Drop fields
None
FIELDS TO RENAME VS
module.exports = function (migration) { const tilPost = migration.editContentType('tilPost') }
Change field ID
module.exports = function (migration) { const tilPost = migration.editContentType('tilPost') tilPost.changeFieldId('categories',
'tags') tilPost.editField('tags', { name: 'Tags' }) tilPost.moveField('tags').afterField('date') } Change field ID
None
FRONTTRENDS 2017 COUNTRY : PL CITY : WARSAW ... FRONTTRENDS
2018 COUNTRY : PL CITY : WARSAW ... DERIVE ENTRIES AND LINK
FT COUNTRY: PL CITY : WARSAW FRONTTRENDS 2017 COUNTRY :
PL CITY : WARSAW ... FRONTTRENDS 2018 COUNTRY : PL CITY : WARSAW ... DERIVE ENTRIES AND LINK
FRONTTRENDS 2017 CONFERENCE : FT ... FRONTTRENDS 2018 CONFERENCE :
FT ... FT COUNTRY: PL CITY : WARSAW DERIVE ENTRIES AND LINK
A looong manual nightshift
No way!
repetetive error-prone not scalable MANUAL CONTENT MODEL CHANGES ARE NOT
THE SOLUTION
Transform an entry in place 01 02 Derive an entry
from another MIGRATION CLI CONTENT TRANSFORMATIONS
module.exports = function (migration) { const conference = migration.createContentType('conference') .name('Conference/Meetup')
.displayField('name') conference.createField('name') .type('Symbol') .required(true) .name('Conference/Meetup name') conference.createField('country') .type('Symbol') .required(true) .name('Country Code') conference.createField('city') .type('Symbol') .required(true) .name('City') Derive entries and link
.name('Country Code') conference.createField('city') .type('Symbol') .required(true) .name('City') const event = migration.editContentType('event')
event.createField('conference') .name('Conference') .type('Link') .linkType('Entry') .validations([ { "linkContentType": ['conference'] } ]) Derive entries and link
]) migration.deriveLinkedEntries({ contentType: 'event', from: ['name', 'country', 'city'], toReferenceField: 'conference',
derivedContentType: 'conference', derivedFields: ['name', 'country', 'city'], identityKey: async (from) => { return getId(from.name['en-US']) }, deriveEntryForLocale: async (inputFields, locale) => { return { name: inputFields.name[locale].replace(/\s(\d{2,4}|#\d+)/g, ''), country: inputFields.country[locale] || 'N/A', city: inputFields.city[locale] || 'N/A' } } }) Derive entries and link
]) migration.deriveLinkedEntries({ contentType: 'event', from: ['name', 'country', 'city'], toReferenceField: 'conference',
derivedContentType: 'conference', derivedFields: ['name', 'country', 'city'], identityKey: async (from) => { return getId(from.name['en-US']) }, deriveEntryForLocale: async (inputFields, locale) => { return { name: inputFields.name[locale].replace(/\s(\d{2,4}|#\d+)/g, ''), country: inputFields.country[locale] || 'N/A', city: inputFields.city[locale] || 'N/A' } } }) Derive entries and link
]) migration.deriveLinkedEntries({ contentType: 'event', from: ['name', 'country', 'city'], toReferenceField: 'conference',
derivedContentType: 'conference', derivedFields: ['name', 'country', 'city'], identityKey: async (from) => { return getId(from.name['en-US']) }, deriveEntryForLocale: async (inputFields, locale) => { return { name: inputFields.name[locale].replace(/\s(\d{2,4}|#\d+)/g, ''), country: inputFields.country[locale] || 'N/A', city: inputFields.city[locale] || 'N/A' } } }) Derive entries and link
]) migration.deriveLinkedEntries({ contentType: 'event', from: ['name', 'country', 'city'], toReferenceField: 'conference',
derivedContentType: 'conference', derivedFields: ['name', 'country', 'city'], identityKey: async (from) => { return getId(from.name['en-US']) }, deriveEntryForLocale: async (inputFields, locale) => { return { name: inputFields.name[locale].replace(/\s(\d{2,4}|#\d+)/g, ''), country: inputFields.country[locale] || 'N/A', city: inputFields.city[locale] || 'N/A' } } }) Derive entries and link
} } }) event.moveField('conference').afterField('name') event.deleteField('country') event.deleteField('city') } Derive entries and
link
None
module.exports = function (migration) { const conference = migration.createContentType('conference') .name('Conference/Meetup')
.displayField('name') conference.createField('name').type('Symbol').required(true).name('Conference/Meetup name') conference.createField('country').type('Symbol').required(true).name('Country Code') conference.createField('city').type('Symbol').required(true).name('City') const event = migration.editContentType('event') event.createField('conference') .name('Conference') .type('Link') .linkType('Entry') .validations([ { "linkContentType": ['conference'] } ]) migration.deriveLinkedEntries({ contentType: 'event', from: ['name', 'country', 'city'], toReferenceField: 'conference', derivedContentType: 'conference', derivedFields: ['name', 'country', 'city'], identityKey: async (from) => { return from.name['en-US'] // remove year .replace(/\s(\d{2,4}|#\d+)/g, '') // clear spaces .replace(/\s/g, '-') // clear "weird characters" .replace(/(,|\/|\\|:|\.|\(|\))/g, '') .toLowerCase() }, deriveEntryForLocale: async (inputFields, locale) => { return { name: inputFields.name[locale].replace(/\s(\d{2,4}|#\d+)/g, ''), country: inputFields.country[locale] || 'N/A', city: inputFields.city[locale] || 'N/A' } } }) event.moveField('conference').afterField('name') event.deleteField('country') event.deleteField('city') } LOC 48 REQUESTS 382 ENTRIES CREATED 88 ENTRIES UPDATED 96
MIGRATION CLI ADVANTAGES Repeatable 01 02 03 04 Can be
kept in VC Includes sanity checks Perfect for CI
NOBODY(!) GETS IT RIGHT THE FIRST TIME...
61 WAYS TO SURVIVE SHARE & REUSE ANALYSE EVOLVE
with the right tools...
Thanks. @stefanjudis Slides ctfl.io/content-modelling-is-tricky Article ctfl.io/content-modelling-is-tricky-article