Leonardo Losoviz
query {
featuredDirector {
name
country
avatar
films {
title
thumbnail
actors {
name
avatar
}
}
}
}
{
data: {
featuredDirector: {
name: "George Lucas",
country: "USA",
avatar: "...",
films: [
{
title: "Star Wars: Episode I",
thumbnail: "...",
actors: [
{
name: "Ewan McGregor",
avatar: "...",
},
{
name: "Natalie Portman",
avatar: "...",
}
]
},
{
title: "Star Wars: Episode II",
thumbnail: "...",
actors: [
{
name: "Natalie Portman",
avatar: "...",
},
{
name: "Hayden Christensen",
avatar: "...",
}
]
}
]
}
}
}
Name
Country
Avatar
Thumbnail
Title
Avatar
Name
Render <FeaturedDirector>:
<div>
<img src="{avatar}" class="pull-left">
<h3>{name}</h3>
<strong>Country:</strong> {country}
{foreach films as film}
<Film film={film} />
{/foreach}
</div>
Render <Film>:
<div>
<img src="{thumbnail}">
<h4>{title}</h4>
{foreach actors as actor}
<Actor actor={actor} />
{/foreach}
</div>
Render <Actor>:
<div>
<img src="{avatar}">
<h5>{name}</h5>
</div>
Render <FeaturedDirector>:
<div>
<img src="{avatar}" class="pull-left">
<h3>{name}</h3>
<strong>Country:</strong> {country}
{foreach films as film}
<Film film={film} />
{/foreach}
</div>
Render <Film>:
<div>
<img src="{thumbnail}">
<h4>{title}</h4>
{foreach actors as actor}
<Actor actor={actor} />
{/foreach}
</div>
Render <FeaturedDirector>:
<div>
<img src="{avatar}" class="pull-left">
<h3>{name}</h3>
<strong>Country:</strong> {country}
{foreach films as film}
<Film film={film} />
{/foreach}
</div>
query {
featuredDirector {
name
country
avatar
films {
title
thumbnail
actors {
name
avatar
}
}
}
}
Name
Country
Avatar
Thumbnail
Title
Avatar
Name
Director
name: George Lucas
country: USA
avatar: george-lucas.jpg
Film
title: The Phantom Menace
thumbnail: episode-1.jpg
title: Attack of the Clones
thumbnail: episode-2.jpg
Film
films
Actor
name: Nathalie Portman
avatar: portman.jpg
Actor
name: Ewan McGregor
avatar: mcgregor.jpg
actors
Actor
name: Hayden Christensen
avatar: christensen.jpg
Actor
actors
name: Nathalie Portman
avatar: portman.jpg
Director
name: George Lucas
country: USA
avatar: george-lucas.jpg
Film
title: The Phantom Menace
thumbnail: episode-1.jpg
title: Attack of the Clones
thumbnail: episode-2.jpg
Film
films
Actor
name: Nathalie Portman
avatar: portman.jpg
Actor
name: Ewan McGregor
avatar: mcgregor.jpg
actors
Actor
name: Hayden Christensen
avatar: christensen.jpg
Actor
actors
name: Nathalie Portman
avatar: portman.jpg
Director
Film
Actor
Director
name: George Lucas
country: USA
avatar: george-lucas.jpg
Film
title: The Phantom Menace
thumbnail: episode-1.jpg
title: Attack of the Clones
thumbnail: episode-2.jpg
filmType: Film
name: Nathalie Portman
avatar: portman.jpg
Actor
name: Ewan McGregor
avatar: mcgregor.jpg
name: Hayden Christensen
avatar: christensen.jpg
actorType: Actor
films: [3, 8]
actors: [4, 6]
actors: [6, 7]
ID: 3
ID: 8
ID: 4
ID: 6
ID: 7
ID: 2
Director
name: George Lucas
country: USA
avatar: george-lucas.jpg
Film
title: The Phantom Menace
thumbnail: episode-1.jpg
title: Attack of the Clones
thumbnail: episode-2.jpg
filmType: Film
name: Nathalie Portman
avatar: portman.jpg
Actor
name: Ewan McGregor
avatar: mcgregor.jpg
name: Hayden Christensen
avatar: christensen.jpg
actorType: Actor
films: [3, 8]
actors: [4, 6]
actors: [6, 7]
ID: 3
ID: 8
ID: 4
ID: 6
ID: 7
ID: 2
preferredActors: [4, 9]
name: Leonardo DiCaprio
avatar: dicaprio.jpg
ID: 9
Director
name: George Lucas
country: USA
avatar: george-lucas.jpg
Film
title: The Phantom Menace
thumbnail: episode-1.jpg
title: Attack of the Clones
thumbnail: episode-2.jpg
filmType: Film
name: Nathalie Portman
avatar: portman.jpg
Actor
name: Ewan McGregor
avatar: mcgregor.jpg
name: Hayden Christensen
avatar: christensen.jpg
actorType: Actor
films: [3, 8]
actors: [4, 6]
actors: [6, 7]
ID: 3
ID: 8
ID: 4
ID: 6
ID: 7
ID: 2
preferredActors: [4, 9]
name: Leonardo DiCaprio
avatar: dicaprio.jpg
ID: 9
Director
name: James Cameron
ID: 3
direcorType: Director
preferredDirector: 3
preferredDirector: 2
preferredDirector: 5
name: Steven Spielberg
ID: 5
Director
Film
Actor
Director
query {
featuredDirector {
name
country
avatar
films {
title
thumbnail
actors {
name
avatar
}
}
}
}
GET
/featured-director/?fields=name|country|avatar
GET
/featured-director/?fields=name|country|avatar,
films.title|thumbnail
GET
/featured-director/?fields=name|country|avatar,
films.title|thumbnail,films.actors.name|avatar
query {
featuredDirector {
name
country
avatar
}
}
query {
featuredDirector {
name
country
avatar
films {
title
thumbnail
}
}
}
query {
featuredDirector {
name
country
avatar
films {
title
thumbnail
actors {
name
avatar
}
}
}
}
{
data: {
featuredDirector: {
name: "George Lucas",
country: "United States",
avatar: "...",
films: [
{
title: "Star Wars: Episode I",
thumbnail: "...",
actors: [
{
name: "Ewan McGregor",
avatar: "...",
},
{
name: "Natalie Portman",
avatar: "...",
}
]
},
{
title: "Star Wars: Episode II",
thumbnail: "...",
actors: [
{
name: "Natalie Portman",
avatar: "...",
},
{
name: "Hayden Christensen",
avatar: "...",
}
]
}
]
}
}
}
{
databases: {
primary: {
people {
1: {
name: "George Lucas",
country: "United States",
avatar: "...",
films: [1, 2]
},
2: {
name: "Ewan McGregor",
avatar: "..."
},
3: {
name: "Natalie Portman",
avatar: "..."
},
4: {
name: "Hayden Christensen",
avatar: "..."
}
},
films: {
1: {
title: "Star Wars: Episode I",
thumbnail: "...",
actors: [2, 3]
},
2: {
title: "Star Wars: Episode II",
thumbnail: "...",
actors: [3, 4]
}
}
}
}
}
query {
posts {
title
author {
name
followers {
name
recommendedPosts {
title
author {
name
}
}
}
recommendedPosts {
title
author {
name
}
}
}
}
}
{
databases: {
primary: {
posts {
1: {
title: "My first post",
author: 1
},
2: {
title: "Some other post",
author: 1
},
3: {
title: "Yet another post",
author: 3
},
},
users: {
1: {
name: "Leo",
followers: [2, 3],
recommendedPosts: [2, 3]
},
2: {
name: "Julia",
followers: [1],
recommendedPosts: [1, 2, 3]
},
3: {...}
}
}
}
}
{
data: {
"featured-director": {
dbobjectids: [1]
}
},
settings: {
"featured-director": {
dbkeys: {
id: "people",
films: "films",
films.actors: "people"
}
}
},
databases: {
primary: {
people {
1: {
name: "George Lucas",
country: "United States",
avatar: "...",
films: [1, 2]
},
2: {
name: "Ewan McGregor",
avatar: "..."
},
3: {
name: "Natalie Portman",
avatar: "..."
},
4: {
name: "Hayden Christensen",
avatar: "..."
}
},
films: {
1: {
title: "Star Wars: Episode I",
thumbnail: "...",
actors: [2, 3]
},
2: {
title: "Star Wars: Episode II",
thumbnail: "...",
actors: [3, 4]
}
}
}
}
}
GET
/featured-director/?fields=name|country|avatar
featured-director
GET
/featured-director/?fields=name|country|avatar,
films.title|thumbnail
GET
/featured-director/?fields=name|country|avatar,
films.title|thumbnail,films.actors.name|avatar
(Back-end)
(Front-end)
<div class="dropdown {{classes.dropdown}}">
<button class="dropdown-toggle {{classes.btn}}">
{{{text}}}
</button>
<ul class="dropdown-menu" role="menu">
{{#each submodules}}
<li role="presentation">
{{#withModule ../. this}}
{{enterModule ../../.}}
{{/withModule}}
</li>
{{/each}}
</ul>
</div>
class Components extends AbstractComponents
{
public function getSubmodules($module)
{
...
}
public function initProps($module, &$props)
{
...
}
public function getDataFields($module, $props)
{
...
}
public function getConfiguration($module, $props)
{
...
}
}
Back-end
Front-end
Every component creates/receives its own context
The view doesn't know or care about its subcomponents
Props can be set vertically and horizontally
View (Handlebars)
GET /posts/lovely-tango/
post
post-title
post-thumbnail
post-content
"post"
submodules
"post-title"
"post-thumbnail"
"post-content"
class LayoutComponents extends AbstractComponents
{
public const MODULE_POST = 'post';
public const MODULE_POSTTITLE = 'post-title';
public const MODULE_POSTTHUMBNAIL = 'post-thumbnail';
public const MODULE_POSTCONTENT = 'post-content';
public function getSubmodules($module)
{
switch ($module) {
case self::MODULE_POST:
return [
self::MODULE_POSTTITLE,
self::MODULE_POSTTHUMBNAIL,
self::MODULE_POSTCONTENT,
];
}
return [];
}
}
Component hierarchy
GET /posts/lovely-tango/
post
post-title
post-thumbnail
post-content
SELECT
title, thumbnail, content
FROM
posts
WHERE
id = 37
"title"
"thumbnail"
"content"
/posts/lovely-tango/?fields=title|
thumbnail|content
Set domain to current post
Endpoint URL
DB Query
GET /posts/lovely-tango/
post
post-author
SELECT
p.title, p.content, p.author,
u.name, u.avatar
FROM
posts p
INNER JOIN
users u
WHERE
p.id = 37 AND p.author = u.id
"avatar"
/posts/lovely-tango/?fields=title|
thumbnail|content,author.name|avatar
Set domain to current post.author
user-avatar
user-name
"name"
Set domain to current post
Endpoint URL
DB Query
GET /posts/lovely-tango/
<div class="{{class}}">
{{#each submodules}}
{{#withModule ../. this}}
{{enterModule ../../.
dbObjectKey=../../dbkeys.id
dbObjectID=../../dbObjectIDs
}}
{{/withModule}}
{{/each}}
</div>
View (Handlebars)
class LayoutComponents extends AbstractComponents
{
public const MODULE_POST = 'post';
public const MODULE_POSTTITLE = 'post-title';
public const MODULE_POSTAUTHOR = 'post-author';
public function getSubmodules($module)
{
switch ($module) {
case self::MODULE_POST:
return [
self::MODULE_POSTTITLE,
self::MODULE_POSTAUTHOR,
];
}
return [];
}
}
Configuration (PHP)
post
post-title
post-author
GET /posts/lovely-tango/
{{#with dbObject}}
<{{../header}} class="{{../class}}">
{{{title}}}
</{{../header}}>
{{/with}}
View (Handlebars)
class LayoutComponents extends AbstractComponents
{
public function getDataFields($module, $props)
{
switch ($module) {
case self::MODULE_POSTTITLE:
return [
'title',
];
}
return [];
}
public function getConfiguration($module, $props)
{
switch ($module) {
case self::MODULE_POSTTITLE:
return [
'header' => 'h1',
'class' => 'main-title',
];
}
return [];
}
}
Configuration (PHP)
post-title
{{#with dbObject}}
<h1>
{{{title}}}
</h1>
{{/with}}
class LayoutComponents extends AbstractComponents
{
public function getDataFields($module, $props)
{
switch ($module) {
case self::MODULE_POSTTITLE:
return [
'title',
];
}
return [];
}
}
GET /posts/lovely-tango/
<div class="{{class}}">
{{#each submodules}}
{{#withModule ../. this}}
{{enterModule ../../.
dbObjectKey=../../dbkeys.author
dbObjectID=../../dbObject.author
}}
{{/withModule}}
{{/each}}
</div>
View (Handlebars)
class LayoutComponents extends AbstractComponents
{
public const MODULE_POSTAUTHOR = 'post-author';
public const MODULE_AUTHORNAME = 'author-name';
public const MODULE_AUTHORAVATAR = 'author-avatar';
public function getSubmodules($module)
{
switch ($module) {
case self::MODULE_POSTAUTHOR:
return [
self::MODULE_AUTHORNAME,
self::MODULE_AUTHORAVATAR,
];
}
return [];
}
}
Configuration (PHP)
post-author
[
"top-module" => [
"submodules" => [
"module-level1" => [
"submodules" => [
"module-level11" => [
"submodules" => [...]
],
"module-level12" => [
"submodules" => [
"module-level121" => [
"submodules" => [...]
]
]
]
]
]
]
]
]
{
"top-module": {
submodules: {
"module-level1": {
submodules: {
"module-level11": {
...
},
"module-level12": {
submodules: {
"module-level121": {
...
}
}
}
}
}
}
}
}
PHP
JSON
GET /posts/a-lovely-tango/?modulepaths[]=post.post-title&modulepaths[]=post.post-content
post
post-title
post-content
GET /posts/a-lovely-tango/?modulepaths[]=post.post-title
GET /posts/a-lovely-tango/
(Create Once, Publish everywhere)
class HTMLCSSLayoutComponents extends AbstractComponents
{
}
class JSLayoutComponents extends HTMLCSSLayoutComponents
{
}
Leonardo Losoviz