Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

On hypermedia #68

Open
nickl- opened this issue Jul 12, 2012 · 8 comments
Open

On hypermedia #68

nickl- opened this issue Jul 12, 2012 · 8 comments
Labels
Milestone

Comments

@nickl-
Copy link
Member

nickl- commented Jul 12, 2012

@alganet on #php-respect 20120711

dunno yet I'd like to see $router->any('/messages/*')->linkTo($mailboxRoute);
something between the route objects
but I'm not sure about the API

@alganet
Copy link
Member

alganet commented Aug 31, 2012

I actually commited the rel() Routine, I believe we should start discussing it here!

What I really wanted is to be able to:

$singleArticle = $router->get('/articles/*', 'MySingleArticleOrSo');
$articleCollection = $router->get('/articles', 'MyArticleHandlerWhatever')
    ->rel($singleArticle);

So one route can create a relation to another. The type and href attributes would be infered from the routeInspector and served using content negotiation, so when a user requests a JSON, the links would be typed as JSON as well if needed.

We can go even further and apply full content negotiation there. If an image has relation to anothers with the same subtype image/something, we can link those using the same algorithm for the Accept() routine.

Partial support is already there like this:

$singleArticle = $router->get('/articles/*', 'MySingleArticleOrSo');
$articleCollection = $router->get('/articles', 'MyArticleHandlerWhatever')
    ->rel(function ($data) use ($singleArticle) {
        return $singleArticle->createUri($data['article_id']);
    });

So what do you think guys?

@nickl-
Copy link
Member Author

nickl- commented Sep 1, 2012

@alganet you lost me somewhere but maybe it's just semantics.

We have the link url but where is the relation type? How would the router infer that referring to an 'item', 'self', 'next', 'prev', 'canonical', etc. These are the values that pertain to "rel" to the endpoint which is what we are collecting here.

Even though the "rel" is a URI that may or may not be dereferencable it is still not the endpoint which it refers to but something that hopefully would not require to change and indicative of the type of relation not the value of said relation type.

I also raised this over here @67915d951da019d4853e496557ce00ba75ce2577 with some suggestions but we seem to be missing each other.

What I am unable to visualize is how this gets used, when and where does this translate into a valid RFC 5988 web-link rendered with the output or is this merely defining the wireframe, but what is the benefit in that.

My apologies but I am so stuck on this part that I am unable to even conceive how conneg will partake, which is even more alien. Maybe if you explain it from the other angle, I might understand and be of some use in these discussions: what exactly is the end result?, how do you get there from here, the rel definition?

You mentioned XML before but there are obviously a few gaps I am not joining: How do we get from:

<?php

$singleArticle = $router->get('/articles/*', 'MySingleArticleOrSo');
$articleCollection = $router->get('/articles', 'MyArticleHandlerWhatever')
    ->rel($singleArticle)->accept('application/json' => 'json_encode');

To something like this perhaps:

{
    "articles": [ {
            "name":"article whatever 1",
            "_link": { 
                          "href": "/articles/1",
                           "rel": "item"
                      }
        },
        {
            "name":"article whatever 2",
            "_link": { 
                          "href": "/articles/2",
                           "rel": "item"
                      }
        }
    ]
}

or similar...

@augustohp
Copy link
Member

I am also missing the relation type, the API is good but if I am missing something it should be like:

<?php
$singleArticle     = $router->get('/articles/*', 'MySingleArticleOrSo');
$articleCollection = $router->get('/articles', 'MyArticleHandlerWhatever')
                            ->rel('item', function ($data) use ($singleArticle) {
                                return $singleArticle->createUri($data['article_id']);
                            });

@nickl-
Copy link
Member Author

nickl- commented Sep 2, 2012

ahhh yes that starts making sense

<?php
$singleArticle  = $router->get('/articles/*', 'MySingleArticleOrSo')
    ->weblink(array( /** callback executed through Routine::createUri() */
       'anchor' => function ($rel, $id) {
           return '<a rel="'.$rel.'" href="/articles/'.$id.'">article '.$id.'</a>'; 
       },
       'link' => function ($id) { 
           return '<link  rel="'.$rel.'"  href="/articles/'.$id.'" title="article '.$id.'">'; 
       },
       'header' => function ($id) { 
           return 'Link:  </articles/'.$id.'>; rel='.$rel.';  title="article '.$id.'";'; 
       },
 ));
$articleCollection = $router->get('/articles', 'MyArticleHandlerWhatever')
   ->rel('item', function ($data) use ($singleArticle) {
       return $singleArticle->createUri($data['article_id']);
   })->accept(array(
       'hypermedia/awesome+format' => function ($data) {
           return AwesomeHypermedia::formatter($data);
      }
  ));

It's still missing the routeInspector knowing what the router path is and passing it to the weblink callback

@alganet
Copy link
Member

alganet commented Sep 3, 2012

I've actually forgot the relation type, it's an array key just like ->accept(), it's already implemented =D

Didn't get the ->weblink(), what does it means?

@nickl-
Copy link
Member Author

nickl- commented Sep 14, 2012

It's the implementation for when createUri is called.

$singleArticle->createUri($data['article_id']);

To retrieve one of the 3 available types we've defined.RFC 5988 etc... maybe if you elaborate I might be able to answer you with something that may help. If this is not correct where do the link part fit into the hyper scheme?

@alganet
Copy link
Member

alganet commented Sep 16, 2012

So, kinda the reverse of the rel? We need to think of that better.

@nickl-
Copy link
Member Author

nickl- commented Oct 3, 2012

Maybe it would help if we have a look at the use cases one after each other, it might just become clear what the best approach and solution would be.

Herewith a brief list of some of the current and upcoming hypermedia trends involving web linking in general and in all its facets, ie not only that which you can click on. Feel free to add any others we should consider if your favourite tech is missing please add it.

text/html

Of course HTML needs no introduction but there are some hypermedia elements easily overlooked at times so lets run through them all quick

  • html - manifest
<!DOCTYPE HTML>
<html manifest="demo.appcache">
<head>
<title>Title of the document</title>
</head>
<body>
The content of the document......
</body>
</html>
  • a - href
<a href="http://www.abc.com">Visit abc.com!</a>
  • base - href
<head>
<base href="http://www.w3schools.com/images/" target="_blank">
</head>
  • link - href
<head>
<link rel="stylesheet" type="text/css" href="theme.css">
</head>
  • script - src
<script src="myscripts.js"></script>

also

window.location='http://foldoc.org/';
  • img - src
<img src="smiley.gif" alt="Smiley face" height="42" width="42">
  • area - href
<img src="planets.gif" width="145" height="126" alt="Planets" usemap="#planetmap">
<map name="planetmap">
  <area shape="rect" coords="0,0,82,126" href="sun.htm" alt="Sun">
  <area shape="circle" coords="90,58,3" href="mercur.htm" alt="Mercury">
  <area shape="circle" coords="124,58,8" href="venus.htm" alt="Venus">
</map>
  • embed - src
<embed src="helloworld.swf">
  • object - data
<object width="400" height="400" data="helloworld.swf"></object>
  • form - action
<form action="demo_form.asp" method="get">
  Last name: <input type="text" name="lname"><br>
  <input type="submit" value="Submit">
</form>
  • button - formaction
<form action="demo_form.asp" method="get">
First name: <input type="text" name="fname"><br>
<button type="submit">Submit</button><br>
<button type="submit" formaction="demo_admin.asp">Submit as admin</button>
</form>
  • input - src & formaction
<form action="demo_form.asp">
  First name: <input type="text" name="fname"><br>
  <input type="image" src="submit.gif" alt="Submit">
</form>
<form action="demo_form.asp">
First name: <input type="text" name="fname"><br>
<input type="submit" value="Submit"><br>
<input type="submit" formaction="demo_admin.asp" value="Submit as admin">
</form>
  • command - icon
<menu>
<command type="radio" label="Left" icon="left.png"
onclick="setAlign('left')">Left</command>
</menu>
  • frame - src
<frameset cols="25%,50%,25%">
  <frame src="frame_a.htm">
  <frame src="frame_b.htm">
  <frame src="frame_c.htm">
</frameset>
  • iframe - src
<iframe src="http://www.abc.com"></iframe>
  • audio - src
<audio src="horse.ogg" controls="controls">
Your browser does not support the audio element.
</audio>
  • video - src & poster
<video controls="controls" src="movie.ogg">
  Your browser does not support the video tag.
</video>
<video controls="controls" poster="/images/w3html5.gif">
  <source src="movie.ogg" type="video/ogg">
  Your browser does not support the video tag.
</video>
  • source - src
<audio controls="controls">
  <source src="horse.ogg" type="audio/ogg">
  <source src="horse.mp3" type="audio/mpeg">
Your browser does not support the audio element.
</audio>
  • track - src
<video width="320" height="240" controls="controls">
  <source src="forrest_gump.mp4" type="video/mp4">
  <source src="forrest_gump.ogg" type="video/ogg">
  <track src="subtitles_en.vtt" kind="subtitles" srclang="en"
  label="English">
  <track src="subtitles_no.vtt" kind="subtitles" srclang="no"
  label="Norwegian">
</video>
  • blockquote - cite
<blockquote cite="http://www.abc.cm/onco-upon.html">
Ance upon a time in a land....
</blockquote>
  • del - cite
<p><del cite="del_demo_cite.htm">This text has been deleted</del></p>
  • ins - cite
<p>This is a text.
<ins cite="why_inserted.htm">This is an inserted text.</ins></p>
  • q - cite
<p>And the giant said: <q cite="http://www.abc.org">Fee fy foe fum</q> but Jack was ...</p>

application/vnd.collection+json

{
  "collection" :
  {
    "version" : "1.0",
    "href" : "http://abc.com/collection",
    "items" :
    [
      {
        "href" : "http://abc.com/collection",
        "data" : [],
        "links" :
        [
          {"href" : "http://abc.com/collection", "rel" : "self", "prompt" : "it's me", "name" : "self", "render" : "image"},
          {"href" : "http://abc.com/collection", "rel" : "alt", "prompt" : "another me", "name" : "alt", "render" : "link"},
          ...
        ]
      }
    ]
  }
}

application/hal+json, application/hal+xml

{
  "_links": {
    .. *snip* ..
  },
  "_embedded": {
    "manufacturer": {
      "_links": {
        "self": { "href": "/manufacturers/328764" },
        "homepage": { "href": "http://hoverdonkey.com" }
      },
      "name": "Manufacturer Inc."
    },
    "review": [
      {
        "_links": {
          "self": { "href": "/review/126" },
          "customer": { "href": "/customer/fred", "title": "Fred Wilson" }
        },
        "title": "Love it!",
        "content": "I love this product. I also bought a Hover Donkey with it.",
        "rating": 10
      },
      {
        "_links": {
          "self": { "href": "/review/178" },
          "customer": { "href": "/customer/tom", "title": "Tom Dee" }
        },
        "title": "Hate it!",
        "content": "My hover donkey choked on it. DO NOT BUY!1!!",
        "rating": 0
      }
    ]
  },
  "name": "A product",
  "weight": 400,
  .. *snip* ..
}

application/schema+json

{
    "id": "http://json-schema.org/hyper-schema",
    "properties": {
        "links": {
            "type": "array",
            "items": { "$ref": "http://json-schema.org/links" }
        },
        "fragmentResolution": {
            "type": "string",
            "default": "slash-delimited"
        },
        "root": {
            "type": "boolean",
            "default": false
        },
        "readonly": {
            "type": "boolean",
            "default": false
        },
        "pathStart": {
            "type": "string",
            "format": "uri"
        },
        "mediaType": {
            "type": "string"
        },
        "alternate": {
            "type": "array",
            "items": { "$ref": "http://json-schema.org/hyper-schema-or-uri" }
        },
        "type": {
            "type": [ "string", "array" ],
            "items": {
                "type": [ "string", { "$ref": "http://json-schema.org/hyper-schema-or-uri" } ]
            },
            "uniqueItems": true,
            "default": "any"
        },
        "properties": {
            "type": "object",
            "additionalProperties": { "$ref": "http://json-schema.org/hyper-schema-or-uri" },
            "default": {}
        },
        "items": {
            "type": [ { "$ref": "http://json-schema.org/hyper-schema-or-uri" }, "array" ],
            "items": { "$ref": "http://json-schema.org/hyper-schema-or-uri" },
            "default": {}
        },
        "additionalProperties": {
            "type": [ { "$ref": "http://json-schema.org/hyper-schema-or-uri" }, "boolean" ],
            "default": {}
        },
        "additionalItems": {
            "type": [ { "$ref": "http://json-schema.org/hyper-schema-or-uri" }, "boolean" ],
            "default": {}
        },
        "contentEncoding": {
            "type": "string"
        },
        "default": {
        },
        "requires": {
            "type": [ "string", { "$ref": "http://json-schema.org/hyper-schema-or-uri" } ]
        },
        "disallow": {
            "type": [ "string", "array", { "$ref": "http://json-schema.org/hyper-schema-or-uri" } ],
            "items": {
                "type": [ "string", { "$ref": "http://json-schema.org/hyper-schema-or-uri" } ]
            },
            "uniqueItems": true
        },
        "extends": {
            "type": [ { "$ref": "http://json-schema.org/hyper-schema-or-uri" }, "array"],
            "items": { "$ref": "http://json-schema.org/hyper-schema-or-uri" },
            "default": {}
        }
    },
    "links": [
        {
            "href": "{$schema}",
            "rel": "describedby"
        },
        {
            "href": "{$ref}",
            "rel": "full"
        }
    ],
    "fragmentResolution": "dot-delimited",
    "extends": { "$ref": "http://json-schema.org/schema" }
}

text/uri-list

# urn:isbn:0-201-08372-8
  http://www.huh.org/books/foo.html
  http://www.huh.org/books/foo.pdf
  ftp://ftp.foo.org/books/foo.txt

text/css

body { 
     background: url("http://www.example.com/pinkish.png") 
}

application/sitemap+xml

 <?xml version="1.0" encoding="UTF-8"?>
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
     <url>
        <loc>http://www.example.com/</loc>
        <lastmod>2005-01-01</lastmod>
        <changefreq>monthly</changefreq>
        <priority>0.8</priority>
     </url>
  </urlset>

SensorML

<contact xlink:href="http://www.myCom.com/personel.xml#JohnDoe" xlink:arcrole="expert"/>

application/rdf+xml

r.string-value <http://www.w3.org/1999/02/22-rdf-syntax-ns#subject> s .
r.string-value <http://www.w3.org/1999/02/22-rdf-syntax-ns#predicate> p .
r.string-value <http://www.w3.org/1999/02/22-rdf-syntax-ns#object> o .
r.string-value <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/1999/02/22-rdf-syntax-ns#Statement> .

application/x-trig

# TriG Example Document 2
@prefix ex: <http://www.example.org/vocabulary#> .
@prefix : <http://www.example.org/exampleDocument#> .
:G1 = { :Monica a ex:Person ;
                 ex:name "Monica Murphy" ;      
                 ex:homepage <http://www.monicamurphy.org> ;
                 ex:email <mailto:monica@monicamurphy.org> ;
                 ex:hasSkill ex:Management ,
                             ex:Programming . } .

TriX : RDF Triples

<TriX xmlns="http://www.w3.org/2004/03/trix/trix-1/"> 
   <graph> 
      <uri>http://example.org/graph1</uri> 
      <triple> 
         <uri>http://example.org/Bob</uri> 
         <uri>http://example.org/wife</uri> 
         <uri>http://example.org/Mary</uri> 
      </triple> 
      <triple> 
         <uri>http://example.org/Bob</uri> 
         <uri>http://example.org/name</uri> 
         <plainLiteral>Bob</plainLiteral> 
      </triple> 
      <triple> 
         <uri>http://example.org/Mary</uri> 
         <uri>http://example.org/age</uri> 
         <typedLiteral 
datatype="http://www.w3.org/2001/XMLSchema#integer">32</typedLiteral> 
      </triple> 
   </grap

Notation 3

@prefix log: <http://www.w3.org/2000/10/swap/log#>.
@keywords.
@forAll x, y, z. {x parent y. y sister z} log:implies {x aunt z}

application/x-turtle

# this is not a complete turtle document
<http://example.org/path/>
<http://example.org/path/#fragment>
</path>
<#fragment>
<>
# or 
# this is a complete turtle document
@prefix foo: <http://example.org/ns#> .
@prefix : <http://other.example.org/ns#> .
foo:bar foo: : .
:bar : foo:bar .

image/svg+xml

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="5cm" height="3cm" viewBox="0 0 5 3" version="1.1"
     xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <desc>Example link01 - a link on an ellipse
  </desc>
  <rect x=".01" y=".01" width="4.98" height="2.98" 
        fill="none" stroke="blue"  stroke-width=".03"/>
  <a xlink:href="http://www.w3.org">
    <ellipse cx="2.5" cy="1.5" rx="2" ry="1"
             fill="red" />
  </a>
</svg>

application/smil, application/smil+xml

<smil xmlns="http://www.w3.org/ns/SMIL" version="3.0" baseProfile="Language">
  <head>
    <layout>
      <region xml:id="source"      height="50%"/>
      <region xml:id="destination" top   ="50%"/>
    </layout>
  </head>
  <body>
    <a href="embeddedSMIL.smil" target="destination" accesskey="a">
      <img region="source" src="source.jpg" dur="indefinite"/>
    </a>
  </body>
</smil>

application/ccxml+xml

  <?xml version="1.0" encoding="UTF-8"?>
<ccxml version="1.0" xmlns="http://www.w3.org/2002/09/ccxml">

  <!-- var to hold the value of the fetch 
          identifier that we care about -->
  <var name="myGoodFetchID"/>

  <eventprocessor>  
    <transition event="ccxml.loaded">
       <!-- stick the value of the fetch 
               identifier in the myGoodFetchID var -->
       <fetch fetchid="myGoodFetchID" 
                 next="'http://www.example.com/goodfetch.ccxml'"/>

       <!-- do not bother saving the fetch id's for these, 
               we would just ignore them anyway -->
       <fetch next="'http://www.example.com/fakefetch1.ccxml'"/>
       <fetch next="'http://www.example.com/fakefetch2.ccxml'"/>
    </transition>

    <transition event="fetch.done">
       <if cond="myGoodFetchID == event$.fetchid">
          <!-- only matched if we have fetched 
                  http://www.example.com/goodfetch.ccxml -->
          <goto fetchid="event$.fetchid"/>
       </if>
    </transition>

    <transition event="error.fetch">
          <!-- Ignore bad fetches in this example -->
    </transition>

  </eventprocessor>
</ccxml>

application/srgs+xml

<grammar xmlns="http://www.w3.org/2001/06/grammar" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://www.w3.org/2001/06/grammar 
                             http://www.w3.org/TR/speech-grammar/grammar.xsd"
         xml:lang="en" version="1.0">

   <lexicon uri="http://www.example.com/lexicon.file"/>
   <lexicon uri="http://www.example.com/strange-city-names.file" type="media-type"/>
   ...

also

 #ABNF V1.0 ISO-8859-1;
   language en-US;

   lexicon <http://www.example.com/lexicon.file>;
   lexicon <http://www.example.com/strange-city-names.file>~<media-type>;
   ...

application/voicexml+xml

<link next="http://www.voicexml.org/books/main.vxml">
  <grammar mode="voice" version="1.0" root="root">
    <rule id="root" scope="public">
       <one-of>
         <item>books</item> 
         <item>VoiceXML books</item> 
       </one-of>
    </rule>
  </grammar>
  <grammar mode="dtmf" version="1.0" root="r2">
     <rule id="r2" scope="public"> 2 </rule>
  </grammar>
</link>
<link expr="'#' + document.helpstate">
  <grammar mode="voice" version="1.0" root="root">
     <rule id="root" scope="public"> help </rule>
  </grammar>
</link>
<link dtmf="2" event="help">
  <grammar mode="voice" version="1.0" root="r5">
    <rule id="r5" scope="public">
       <one-of>
         <item>arrgh</item> 
         <item>alas all is lost</item> 
         <item>fie ye froward machine</item> 
         <item>I don't get it</item> 
       </one-of>
    </rule>
  </grammar>
</link>

application/atom+xml

<app:accept>image/png, image/*</app:accept>
<collection href="http://example.org/blog/main" />
<content src="http://www.example.org/blog-posts/123" />
<generator uri="http://www.example.org/generators/abc" />
<icon>http://www.example.org/images/icon</icon>
<link href="http://www.example.org/data/q1w2e3r4" rel="related" hreflang="en" />
<logo>http://www.example.org/images/logo</logo>
<uri>http://www.example.com/content/doc1</uri>
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title type="text">dive into mark</title>
  <subtitle type="html">
    A &lt;em&gt;lot&lt;/em&gt; of effort
    went into making this effortless
  </subtitle>
  <updated>2005-07-31T12:29:29Z</updated>
  <id>tag:example.org,2003:3</id>
  <link rel="alternate" type="text/html" 
   hreflang="en" href="http://example.org/"/>
  <link rel="self" type="application/atom+xml" 
   href="http://example.org/feed.atom"/>
  <rights>Copyright (c) 2003, Mark Pilgrim</rights>
  <generator uri="http://www.example.com/" version="1.0">
    Example Toolkit
  </generator>
  <entry>
    <title>Atom draft-07 snapshot</title>
    <link rel="alternate" type="text/html" 
     href="http://example.org/2005/04/02/atom"/>
    <link rel="enclosure" type="audio/mpeg" length="1337"
     href="http://example.org/audio/ph34r_my_podcast.mp3"/>
    <id>tag:example.org,2003:3.2397</id>
    <updated>2005-07-31T12:29:29Z</updated>
    <published>2003-12-13T08:29:29-04:00</published>
    <author>
      <name>Mark Pilgrim</name>
      <uri>http://example.org/</uri>
      <email>f8dy@example.com</email>
    </author>
    <contributor>
      <name>Sam Ruby</name>
    </contributor>
    <contributor>
      <name>Joe Gregorio</name>
    </contributor>
    <content type="xhtml" xml:lang="en" 
     xml:base="http://diveintomark.org/">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <p><i>[Update: The Atom draft is finished.]</i></p>
      </div>
    </content>
  </entry>
</feed>

HTTP

  • Refresh
HTTP/1.1 200 ok
Refresh: 0; url=http://www.abc.com/
  • Location
HTTP/1.1 301 Moved Permanently
Location: http://www.abc.org/
  • Content-Location
GET /booking/price HTTP/1.1
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
Content-Location: http://abc.org/booking/price.json
  • Link
HTTP/1.1 200 OK
Link: <http://abc.com?p=3>; rel="next", <http://abc.com?p=10>; rel="last", <http://abc.com?p=1>; rel="first", <https://abc.com?p=1>; rel="prev"

@augustohp augustohp modified the milestone: Ideas May 13, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants