Rename path to route in Router & Source APIs

I have an idea that could solve some problems I have with the design.

path is a bit confusing as we are naming different things with that name. Also, this doesn’t fit well with URLs containing a query, and that’s something we need if we want to support searches.

// API right now:
actions.router.set(pathOrObj)
actions.source.fetch(pathOrObj)
state.source.get(pathOrObj)

 // This is a path.
source.get("/category/nature/")
// This is a path too...
source.get("/category/nature/page/2/")
// This is a path with a page (but actually is the path above).
source.get({ path: "/category/nature/", page: 2 })
// And what's this? Also a path? How do you express it as an object?
source.get("/category/nature/page/2/?s=gullfoss") 

My idea is to rename path to route.

The API would be then like this:

actions.router.set(routeOrObj)
actions.source.fetch(routeOrObj)
state.source.get(routeOrObj)

// A route can be equal to a path...
route = "/category/nature/";
obj = { path: "/category/nature" }

// ...but not always.
route = "/category/nature/page/2/";
obj = { path: "/category/nature", page: 2 }

// It can even have a query!
route = "/category/nature/page/2/?s=something";
obj = { path: "/category/nature", page: 2, query: "?s=something" }

The logic to pass from route to object and vice versa could be implemented with two functions, routeToObj and objToRoute.

data objects would be generated for each route and stored in state.source.data[route].

What I’m not sure about is if route should be the part of a URL without the site URL, or just without the site domain, as this would only affect to the library resolver in wp-source, and it could that library what handles those different cases.

// WordPress site in a domain's root
url = "https://test.frontity.io/category/nature/"
route = "/category/nature/"

// WordPress site in a domain's subpage
url = "https://test2.frontity.io/wp-site/category/nature/"
// how should be route in this case?
route = "/category/nature/"
route = "/wp-site/category/nature/"

@orballo, @luisherranz, opinions? Suggestions?

1 Like

I love it.

I wonder if we can find a better name for obj but I really like the route idea.

routeToObj and objToRoute will be in libraries.source, right?


If we strip out the domain from post.link in the API response, it contains the folder. That’s great because the theme can just use it like it is, right?

const PostTitle = ({ post }) => (
  <a href={post.link}>{post.title}</a>  // <a href="/wp-site/my-post">
)

In that situation the developer may want to use the same post.link field to access data:

const PostTitle = ({ post, state }) => {
  const data = state.source.get(post.link);
  const type = data.isPage ? 'page' : 'post';

  return <a href={post.link}>{type}: {post.title}</a>
}

So I guess in this case it makes sense to store with the folder.

For state.router.path, I also think it makes sense to have the folder included: "/wp-site/my-post".

For state.frontity.url, I think it should be also have the folder included (or else it should be renamed to domain or host): "https://mysite.com/wp-site/my-post". This complicates things when you want to join both together:

state.frontity.url + state.router.path // "https://mysite.com/wp-site/wp-site/my-post"

We need to think all the scenarios when the user is going to need to build the full URL, like in links, social shares, analytics…

Ok, this one is tough :laughing:

I think it’s a great idea.

Maybe we should also make that query could be a string or an object of parameters.

obj = { path: "/category/nature/", page: 2, query: "?s=something" };
// or
obj = { path: "/category/nature/", page: 2, query: { s: "something" } };

This is one of my favorite features ever :smile:

In which situation is this useful?

I’d love to take decisions on all the things we need for wp-source, including:

  • The routeOrObj API.
  • How to store links of posts, taxonomies…
  • How to deal with internal links in content.
  • How to deal with blogs that are in a folder.
  • How to use a page in the home and the blog in a page
  • How to add new post types.
  • How to add custom taxonomies.
  • How to chose a new slug for category and tag.
  • How to use a page in the place of a category or tag.

How to store links of posts, taxonomies…

// site.com
link: "https://site.com/my-post"

// site.com/sub
link: "https://site.com/sub/my-post"

If we remove the domain from links, they are:

// site.com
<a href="/my-post" />

// site.com/sub
<a href="/sub/my-post" />

CHANGE: Remove domain from links before storing them in source.data.

How to deal with internal links in content

// site.com
state: {
  frontity: {
    url: "https://site.com",
  },
  source: {
    api: "https://wp.site.com/wp-json"
  }
}

A Link component needs to know if a link is internal to do a state.router.set instead of a full SSR.

<a href="https://wp.site.com/my-post" />

CHANGE: Make wp-source convert to relative paths those hrefs using the state.source.api setting.

<a href="https://site.com/my-post" />

CHANGE: Make wp-source convert to relative paths those hrefs using the state.frontity.url setting.

How to deal with blogs that are in a folder

I think this is the most difficult case we’ll face. At least is the most difficult one we faced last year with the old version of the framework: Two blogs, one named “site.com” and the other inside a folder “site.com/sub”.

For that, the frontity.settings.js would be something like:

export default [
  {
    name: "site",
    state: {
      frontity: {
        url: "https://site.com"
      }
    },
    packages: ["...", 
      {
        name: "@frontity/wp-source",
        state: {
          source: {
             api: "https://wp.site.com/wp-json"
          }
        }
      }
    ]
  },
  {
    name: "subsite",
    match: "https://site.com/sub"
    state: {
      frontity: {
        url: "https://site.com/sub"
      }
    },
    packages: ["...", 
      {
        name: "@frontity/wp-source",
        state: {
          source: {
             api: "https://subwp.site.com/wp-json"
          }
        }
      }
    ]
  }
]

Router set:

// site.com
actions.router.set("/my-post")

// site.com/sub
actions.router.set("/sub/my-post")

This works fine.

Source fetch and get:

// site.com
actions.source.fetch("/my-post")

// site.com/sub
actions.source.fetch("/sub/my-post")

This doesn’t work right now but we know that subsite is in a folder because of state.frontity.url.

CHANGE: We need wp-source to look the url and work with folders.

Links inside content:

// link to site
<a href="https://site.com/my-post" />
// link to subsite
<a href="https://site.com/sub/my-post" />

A general Link component can compare state.frontity.url with the href to know if it is an internal link.

There’s no way for the main site (site) to know that "/sub/my-post" doesn’t belong to their blog in a general Link component.

These blogs should use their own Link component. They could add it in a separate package with a higher priority and it should work.

Full urls

const fullUrl = state.frontity.url + state.router.path // WRONG

const fullUrl = new URL(state.router.path, state.frontity.url); // RIGHT

We have to be careful not to assume that state.frontity.url won’t have a folder.

CHANGE: I’d remove state.route.url to avoid confusion with state.router.path.

How to use a page in the home and the blog in a page

For example, a page named about for the home and the blog moved to blog.

Using strings:

state: {
  source: {
    redirects: {
     "/": "/about",
     "/blog": "/"
    }
  }
}

Using regexps

state: {
  source: {
    redirects: {
     "^\\/$": "/about",
     "^\\/blog$": "/"
    }
  }
}

This NEEDS TO BE DECIDED.

How to chose a new slug for category and tag

Using strings:

state: {
  source: {
    redirects: [
      "/wp-cat/:slug", "/category/:slug",
    ]
  }
}

Using regexps

state: {
  source: {
    redirects: [
      "\/wp-cat\/", "category"
    ]
  }
}

This NEEDS TO BE DECIDED.

How to use a page in the place of a category or tag.

Using strings:

state: {
  source: {
    redirects: [
      "/pruebas", "/category/pruebas",
    ]
  }
}

Using regexps

state: {
  source: {
    redirects: [
      "^\\/pruebas$", "/category/pruebas",
    ]
  }
}

This NEEDS TO BE DECIDED.


To be continued…

Things that we had left without deciding:

How to add new post types

Example of custom post type:

// Post Type
"/movie/full-metal-jacket"

// Archive
"/movies/"
"/movies/page/2/"

Possible settings:

{
  state: {
    source: {
      postTypes: {
        "movie": {
          slug: "movie",
          endpoint: "movies",
          archive: "movies"
        }
      }
    }
  }
}

The wp-source package should create handlers for each custom post type:

Object
  .values(state.source.postTypes)
  .forEach(({ slug, endpoint, archive }) => {
    const { resolver } = libraries.source;
    resolver.add(`/${slug}/:slug`, postType({ endpoint }));
    resolver.add(`/${archive}/`, postTypeArchive({ endpoint }));
  })

Then, you can use custom post type routes as normally.

source.fetch("/movie/full-metal-jacket/")
source.fetch({ path: "/movies/", page: 2 })

The post type handlers should store entities in the correct place.

data = source.get("/movie/full-metal-jacket/")
data = {
  type: "movie",
  id: "72",
  isPostType: true,
}

data = source.get({ path: "/movies/", page: 2 })
data = {
  type: "movie",
  isArchive: true,
  isPostTypeArchive: true,
  items: [...]
}

// this should be added by POPULATE
source.movie[72] = entity

CHANGE: the code to add custom post type handlers automatically from settings should be added.

How to add custom taxonomies

This case is more or less the same as custom post types.
Possible settings:

{
  state: {
    source: {
      taxonomies: {
        "actor": {
          slug: "actor",
          endpoints: ["movies"],
        }
      }
    }
  }
}

The wp-source package should create handlers for each custom taxonomy:

Object
  .values(state.source.taxonomies)
  .forEach(({ slug, endpoints }) => {
    const { resolver } = libraries.source;
    resolver.add(`/${slug}/:slug`, taxonomy({ endpoints }));
  })

The taxonomy handlers should store entities in the correct place.

data = source.get("/actor/matthew-modine/")
data = {
  taxonomy: "actor",
  id: "22",
  isArchive: true,
  isTaxonomy: true,
  items: [{ type: "movie", id: 72, link: "..." }]
}

CHANGE: the code to add custom taxonomy handlers automatically from settings should be added.