RESOURCE ROUTING IN EE @michaelrog #EEconf 2014
RESOURCE ROUTING FOR TRIPLE-DIGIT GROWTH!!!!!!1 @michaelrog #EEconf 2014
WELCOME
WHAT IS RESOURCE ROUTING? The stage in a request where the server and/or application decides what resource to provide, and in what form, based on the URL, info in the request, and the characteristics of the server environment.
WHAT IS RESOURCE ROUTING? The stage in a request where the server and/or application decides what resource to provide, and in what form, based on the URL, info in the request, and the characteristics of the server environment.
WHAT IS RESOURCE ROUTING? This happens when… • Apache gives you a file • Apache gives you a file based on an .htaccess rewrite/directive • Wikipedia decides what article to give you • an API decides what controller to instantiate • EE decides what template to process
HOW DOES EE ROUTE URLS TO TEMPLATES?
WHY WOULD WE WANT TO CHANGE IT? To break out of the group/template scheme • Templates should be organized by content type, not content values • Hierarchical content • Ambiguous/conflicting segments To add pre-routing functionality • Business logic should live in the app • Templates are views Performance
THIS IS (USUALLY) BAD: {if segment 2 == "category"} {embed="blog/.category"} {if:elseif segment_2 == "view"} {embed="blog/.single"} {if:else} {embed="blog/.listing"} {/if}
HOW CAN WE CHANGE IT?
NATIVE TEMPLATE ROUTES
NATIVE TEMPLATE ROUTES • One route per template • Can contain static or dynamic segments /products/boots /products/{product_name:alpha_dash} • Segments can contain multiple variables /{month:integer}-{day:integer}-{year:integer} /{date:regex[(d{2}-d{2}-d{4})]} • Segments can be bound by multiple rules {variable:rule1|rule2[arg0, arg1, ...]|...}
NATIVE TEMPLATE ROUTES • Order matters /events/{type:alpha_dash} /events/{date:regex[(d{2}-d{2}-d{4})]} • Include All Segments /name/{first_name:alpha}/{last_name:alpha}/ {suffix:regex[(i|v|x)+]} /name/{first_name:alpha}/{last_name:alpha} /name/{first_name:alpha}
EXTENSION HOOKS
SESSIONS_END $this->extensions->call('sessions_end', $this); if ($this->extensions->end_script === TRUE) { return; }
SESSIONS_END public sessions_end($sess) { $redirect = ... ; $url = ... ; if ($redirect) { $this->EE->session = $sess; $this->EE->functions->redirect($url); } return true; }
CORE_TEMPLATE_ROUTE HOOK $edata = ee()->extensions ->call('core_template_route’, ee()->uri->uri_string); if (is_array($edata) && count($edata) == 2) { list($template_group, $template) = $edata; }
CORE_TEMPLATE_ROUTE HOOK public sessions_end($uri) { $template1 = ... ; $template2 = ... ; $where_to_go = (... ? $template1 : $template2); return array( 'template_group', // Template group name 'template' // Template name ); }
ADD-ONS THAT DO THIS • Structure — http://buildwithstructure.com/ • Overrides front-end routes based on hierarchical URIs • Overrides CP routes based on optional Settings • Resource Router — https://github.com/rsanchez/resource_router • Remap URI routes to an HTTP response, using CodeIgniter-style routings
STRUCTURE Use case: • Entries as pages in a hierarchy or taxonomy.
STRUCTURE Each Channel has a default template for its entries, and a default behavior: Page or Listing
STRUCTURE
STRUCTURE Structure even hijacks CP routes a bit: I can make Structure my CP homepage, and redirect to it after an entry is saved.
RESOURCE ROUTER Use cases: • to break out of the template_group/template_name paradigm of traditional EE routing • to nest URLs on a Structure/Pages URI (pagination, categories, etc.) • to point multiple URL patterns to a single template • to add custom JSON/HTML endpoints that do not warrant a full EE template • to remove excess conditional logic in your templates
RESOURCE ROUTER $config['resource_router'] = array( 'uri/pattern' => ... )
RESOURCE ROUTER {if segment 2 == "category"} {embed="blog/.category"} {if:elseif segment_2 == "view"} {embed="blog/.single"} {if:else} {embed="blog/.listing"} {/if}
RESOURCE ROUTER $config['resource_router'] = array( 'blog/:any' => function ($router, $wildcard) { if ($wildcard->value == 'category') { $router->setTemplate('blog/.category'); } elseif ($wildcard->value === 'view') { $router->setTemplate('blog/.single'); } else { $router->setTemplate('blog/.listing'); } } )
RESOURCE ROUTER Wildcards • :any • :num • :year • :month • :day • :pagination • :category • :page:XX • :all
RESOURCE ROUTER $config['resource_router'] = array( 'blog/:category' => 'site/blog-category', 'blog/:year' => 'site/blog-yearly-archive', );
RESOURCE ROUTER Validating Wildcards • :entry_id • :url_title • :category_id • :category_url_title • :member_id • :username
RESOURCE ROUTER Callbacks • $router->setTemplate(string $template) • $router->set404() • $router->setGlobal(string $key, $value) • $router->setVariable(string $key, mixed $value) • {exp:resource_router:your_var_name} • $router->setHeader(string $name, string $value) • $router->json(mixed $data) • $router->redirect(string $url, int $statusCode)
RESOURCE ROUTER Wildcard functions • $wildcard->isValidEntryId(array $where) • $wildcard->isValidUrlTitle(array $where) • $wildcard->isValidEntry(array $where) • $wildcard->isValidCategoryId(array $where) • $wildcard->isValidCategoryUrlTitle(array $where) • $wildcard->isValidCategory(array $where) • $wildcard->isValidMemberId(array $where) • $wildcard->isValidUsername(array $where) • $wildcard->getMeta(string $column)
CASE STUDIES
CASE 1: TAXONOMY + LISTINGS / about / about / managers / about / developers / events / leasing-info / leasing-info / businesses / leasing-info / tenants / directory / directory / forever-21 / directory / house-of-blues / directory / …
CASE 1: TAXONOMY + LISTINGS
CASE 2: ACADEMIC JOURNAL URLS / ija / ijem / ijo / ijo / v1 / ijo / v1 / i3 / ijo / v1 / i3 / new-frontiers-in-orthopaedics / about / contact
CASE 2: ACADEMIC JOURNAL URLS /ij{alpha} /ij{alpha}/v{integer} /ij{alpha}/v{integer}/i{integer} /ij{alpha}/v{integer}/i{integer}/ {url_title:alpha_dash} /about /contact
CASE 3: FRONT-END CP + GLOBALS / cp • logged in: redirect me to the add-articles screen • logged out: show me login screen / cp / add-articles • logged in: show login screen • logged out: show the login screen • redirect me to the add articles screen / cp / edit-articles • logged out: show login screen • logged in member: show 403 • logged in editor: show edit articles screen
CASE 3: FRONT-END CP + GLOBALS $RR['qmp/:all'] = function($router, $wildcard) { if ( in_array(ee()->session->userdata('group_id'), array(1,5,6,7,8,9)) ) { if ($wildcard->value == null) { $router->redirect('qmp/dashboard'); } } else { if ($wildcard->value == null) { $router->redirect('qmp/login'); } else { $router->setTemplate('qmp/login'); } } };
CASE 4: MEMBER PROFILE JSON API • Members publish profiles on one site • Family sites within the organization display this data
CASE 4: MEMBER PROFILE JSON API $config['resource_router'] = array( 'api/members' => function($router) { $query = ee()->db->select('title, url_title') ->where('status', 'open') ->where('channel_id', 1) ->order_by('entry_date', 'ASC') ->limit(10) ->get('channel_titles'); $result = $query->result(); $router->json($result); } );
CASE 5: STAGED ROUTES I want my URL routes to be different based on my environment. • Multi-env config + Resource Router FTW!
RECAP Break out of the default URL scheme to: • Improve template organization • Deal with hierarchical or complex URL patterns • Decrease logic in templates Tools at our disposal • EE Template Routes • Structure (or Pages) • Resource Router • custom extension
QUESTIONS?
LEARN MORE James Smith’s ExpressionEngine URL Schematic: • http://www.jamessmith.co.uk/articles/expressionengine_url_schematic EE Template Routes docs: • https://ellislab.com/expressionengine/user-guide/ urls/template_routes.html EE Extension Hooks docs: • https://ellislab.com/expressionengine/user-guide/ development/extension_hooks/global/core/index.html Resource Router docs: • https://github.com/rsanchez/resource_router Structure docs: • http://buildwithstructure.com/documentation
RESOURCE ROUTING IN EE @michaelrog #EEconf 2014

Resource Routing in ExpressionEngine

  • 1.
    RESOURCE ROUTING INEE @michaelrog #EEconf 2014
  • 2.
    RESOURCE ROUTING FOR TRIPLE-DIGIT GROWTH!!!!!!1 @michaelrog #EEconf 2014
  • 3.
  • 4.
    WHAT IS RESOURCE ROUTING? The stage in a request where the server and/or application decides what resource to provide, and in what form, based on the URL, info in the request, and the characteristics of the server environment.
  • 5.
    WHAT IS RESOURCE ROUTING? The stage in a request where the server and/or application decides what resource to provide, and in what form, based on the URL, info in the request, and the characteristics of the server environment.
  • 6.
    WHAT IS RESOURCE ROUTING? This happens when… • Apache gives you a file • Apache gives you a file based on an .htaccess rewrite/directive • Wikipedia decides what article to give you • an API decides what controller to instantiate • EE decides what template to process
  • 7.
    HOW DOES EEROUTE URLS TO TEMPLATES?
  • 9.
    WHY WOULD WE WANT TO CHANGE IT? To break out of the group/template scheme • Templates should be organized by content type, not content values • Hierarchical content • Ambiguous/conflicting segments To add pre-routing functionality • Business logic should live in the app • Templates are views Performance
  • 10.
    THIS IS (USUALLY)BAD: {if segment 2 == "category"} {embed="blog/.category"} {if:elseif segment_2 == "view"} {embed="blog/.single"} {if:else} {embed="blog/.listing"} {/if}
  • 11.
    HOW CAN WECHANGE IT?
  • 12.
  • 13.
    NATIVE TEMPLATE ROUTES • One route per template • Can contain static or dynamic segments /products/boots /products/{product_name:alpha_dash} • Segments can contain multiple variables /{month:integer}-{day:integer}-{year:integer} /{date:regex[(d{2}-d{2}-d{4})]} • Segments can be bound by multiple rules {variable:rule1|rule2[arg0, arg1, ...]|...}
  • 14.
    NATIVE TEMPLATE ROUTES • Order matters /events/{type:alpha_dash} /events/{date:regex[(d{2}-d{2}-d{4})]} • Include All Segments /name/{first_name:alpha}/{last_name:alpha}/ {suffix:regex[(i|v|x)+]} /name/{first_name:alpha}/{last_name:alpha} /name/{first_name:alpha}
  • 15.
  • 16.
    SESSIONS_END $this->extensions->call('sessions_end', $this); if ($this->extensions->end_script === TRUE) { return; }
  • 17.
    SESSIONS_END public sessions_end($sess){ $redirect = ... ; $url = ... ; if ($redirect) { $this->EE->session = $sess; $this->EE->functions->redirect($url); } return true; }
  • 18.
    CORE_TEMPLATE_ROUTE HOOK $edata= ee()->extensions ->call('core_template_route’, ee()->uri->uri_string); if (is_array($edata) && count($edata) == 2) { list($template_group, $template) = $edata; }
  • 19.
    CORE_TEMPLATE_ROUTE HOOK publicsessions_end($uri) { $template1 = ... ; $template2 = ... ; $where_to_go = (... ? $template1 : $template2); return array( 'template_group', // Template group name 'template' // Template name ); }
  • 20.
    ADD-ONS THAT DOTHIS • Structure — http://buildwithstructure.com/ • Overrides front-end routes based on hierarchical URIs • Overrides CP routes based on optional Settings • Resource Router — https://github.com/rsanchez/resource_router • Remap URI routes to an HTTP response, using CodeIgniter-style routings
  • 21.
    STRUCTURE Use case: • Entries as pages in a hierarchy or taxonomy.
  • 22.
    STRUCTURE Each Channelhas a default template for its entries, and a default behavior: Page or Listing
  • 23.
  • 24.
    STRUCTURE Structure evenhijacks CP routes a bit: I can make Structure my CP homepage, and redirect to it after an entry is saved.
  • 25.
    RESOURCE ROUTER Usecases: • to break out of the template_group/template_name paradigm of traditional EE routing • to nest URLs on a Structure/Pages URI (pagination, categories, etc.) • to point multiple URL patterns to a single template • to add custom JSON/HTML endpoints that do not warrant a full EE template • to remove excess conditional logic in your templates
  • 26.
    RESOURCE ROUTER $config['resource_router']= array( 'uri/pattern' => ... )
  • 27.
    RESOURCE ROUTER {ifsegment 2 == "category"} {embed="blog/.category"} {if:elseif segment_2 == "view"} {embed="blog/.single"} {if:else} {embed="blog/.listing"} {/if}
  • 28.
    RESOURCE ROUTER $config['resource_router']= array( 'blog/:any' => function ($router, $wildcard) { if ($wildcard->value == 'category') { $router->setTemplate('blog/.category'); } elseif ($wildcard->value === 'view') { $router->setTemplate('blog/.single'); } else { $router->setTemplate('blog/.listing'); } } )
  • 29.
    RESOURCE ROUTER Wildcards • :any • :num • :year • :month • :day • :pagination • :category • :page:XX • :all
  • 30.
    RESOURCE ROUTER $config['resource_router']= array( 'blog/:category' => 'site/blog-category', 'blog/:year' => 'site/blog-yearly-archive', );
  • 31.
    RESOURCE ROUTER ValidatingWildcards • :entry_id • :url_title • :category_id • :category_url_title • :member_id • :username
  • 32.
    RESOURCE ROUTER Callbacks • $router->setTemplate(string $template) • $router->set404() • $router->setGlobal(string $key, $value) • $router->setVariable(string $key, mixed $value) • {exp:resource_router:your_var_name} • $router->setHeader(string $name, string $value) • $router->json(mixed $data) • $router->redirect(string $url, int $statusCode)
  • 33.
    RESOURCE ROUTER Wildcardfunctions • $wildcard->isValidEntryId(array $where) • $wildcard->isValidUrlTitle(array $where) • $wildcard->isValidEntry(array $where) • $wildcard->isValidCategoryId(array $where) • $wildcard->isValidCategoryUrlTitle(array $where) • $wildcard->isValidCategory(array $where) • $wildcard->isValidMemberId(array $where) • $wildcard->isValidUsername(array $where) • $wildcard->getMeta(string $column)
  • 34.
  • 35.
    CASE 1: TAXONOMY+ LISTINGS / about / about / managers / about / developers / events / leasing-info / leasing-info / businesses / leasing-info / tenants / directory / directory / forever-21 / directory / house-of-blues / directory / …
  • 36.
    CASE 1: TAXONOMY+ LISTINGS
  • 37.
    CASE 2: ACADEMICJOURNAL URLS / ija / ijem / ijo / ijo / v1 / ijo / v1 / i3 / ijo / v1 / i3 / new-frontiers-in-orthopaedics / about / contact
  • 38.
    CASE 2: ACADEMICJOURNAL URLS /ij{alpha} /ij{alpha}/v{integer} /ij{alpha}/v{integer}/i{integer} /ij{alpha}/v{integer}/i{integer}/ {url_title:alpha_dash} /about /contact
  • 39.
    CASE 3: FRONT-ENDCP + GLOBALS / cp • logged in: redirect me to the add-articles screen • logged out: show me login screen / cp / add-articles • logged in: show login screen • logged out: show the login screen • redirect me to the add articles screen / cp / edit-articles • logged out: show login screen • logged in member: show 403 • logged in editor: show edit articles screen
  • 40.
    CASE 3: FRONT-ENDCP + GLOBALS $RR['qmp/:all'] = function($router, $wildcard) { if ( in_array(ee()->session->userdata('group_id'), array(1,5,6,7,8,9)) ) { if ($wildcard->value == null) { $router->redirect('qmp/dashboard'); } } else { if ($wildcard->value == null) { $router->redirect('qmp/login'); } else { $router->setTemplate('qmp/login'); } } };
  • 41.
    CASE 4: MEMBERPROFILE JSON API • Members publish profiles on one site • Family sites within the organization display this data
  • 42.
    CASE 4: MEMBERPROFILE JSON API $config['resource_router'] = array( 'api/members' => function($router) { $query = ee()->db->select('title, url_title') ->where('status', 'open') ->where('channel_id', 1) ->order_by('entry_date', 'ASC') ->limit(10) ->get('channel_titles'); $result = $query->result(); $router->json($result); } );
  • 43.
    CASE 5: STAGEDROUTES I want my URL routes to be different based on my environment. • Multi-env config + Resource Router FTW!
  • 44.
    RECAP Break outof the default URL scheme to: • Improve template organization • Deal with hierarchical or complex URL patterns • Decrease logic in templates Tools at our disposal • EE Template Routes • Structure (or Pages) • Resource Router • custom extension
  • 45.
  • 46.
    LEARN MORE JamesSmith’s ExpressionEngine URL Schematic: • http://www.jamessmith.co.uk/articles/expressionengine_url_schematic EE Template Routes docs: • https://ellislab.com/expressionengine/user-guide/ urls/template_routes.html EE Extension Hooks docs: • https://ellislab.com/expressionengine/user-guide/ development/extension_hooks/global/core/index.html Resource Router docs: • https://github.com/rsanchez/resource_router Structure docs: • http://buildwithstructure.com/documentation
  • 47.
    RESOURCE ROUTING INEE @michaelrog #EEconf 2014