Open nickl- opened 12 years ago
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?
@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...
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']);
});
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
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?
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?
So, kinda the reverse of the rel? We need to think of that better.
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.
Of course HTML needs no introduction but there are some hypermedia elements easily overlooked at times so lets run through them all quick
<!DOCTYPE HTML>
<html manifest="demo.appcache">
<head>
<title>Title of the document</title>
</head>
<body>
The content of the document......
</body>
</html>
<a href="http://www.abc.com">Visit abc.com!</a>
<head>
<base href="http://www.w3schools.com/images/" target="_blank">
</head>
<head>
<link rel="stylesheet" type="text/css" href="theme.css">
</head>
<script src="myscripts.js"></script>
also
window.location='http://foldoc.org/';
<img src="smiley.gif" alt="Smiley face" height="42" width="42">
<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="helloworld.swf">
<object width="400" height="400" data="helloworld.swf"></object>
<form action="demo_form.asp" method="get">
Last name: <input type="text" name="lname"><br>
<input type="submit" value="Submit">
</form>
<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>
<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>
<menu>
<command type="radio" label="Left" icon="left.png"
onclick="setAlign('left')">Left</command>
</menu>
<frameset cols="25%,50%,25%">
<frame src="frame_a.htm">
<frame src="frame_b.htm">
<frame src="frame_c.htm">
</frameset>
<iframe src="http://www.abc.com"></iframe>
<audio src="horse.ogg" controls="controls">
Your browser does not support the audio element.
</audio>
<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>
<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>
<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="http://www.abc.cm/onco-upon.html">
Ance upon a time in a land....
</blockquote>
<p><del cite="del_demo_cite.htm">This text has been deleted</del></p>
<p>This is a text.
<ins cite="why_inserted.htm">This is an inserted text.</ins></p>
<p>And the giant said: <q cite="http://www.abc.org">Fee fy foe fum</q> but Jack was ...</p>
{
"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"},
...
]
}
]
}
}
{
"_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* ..
}
{
"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" }
}
# 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
body {
background: url("http://www.example.com/pinkish.png")
}
<?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>
<contact xlink:href="http://www.myCom.com/personel.xml#JohnDoe" xlink:arcrole="expert"/>
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> .
# 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 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
@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}
# 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 .
<?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>
<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>
<?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>
<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>;
...
<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>
<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 <em>lot</em> 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/1.1 200 ok
Refresh: 0; url=http://www.abc.com/
HTTP/1.1 301 Moved Permanently
Location: http://www.abc.org/
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
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"
@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