TripalWebServiceResource.inc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. <?php
  2. class TripalWebServiceResource {
  3. /**
  4. * The unique identifier for this resource.
  5. */
  6. protected $id;
  7. /**
  8. * The unique type of resource. The type must exists in the
  9. * context for the web service.
  10. */
  11. protected $type;
  12. /**
  13. * The JSON-LD compatible context for this resource.
  14. */
  15. protected $context;
  16. /**
  17. * Holds the data portion of the JSON-LD response for this resource.
  18. */
  19. protected $data;
  20. /**
  21. * The URL path that the service is providing to access this resource.
  22. * This path plus the $id are concatenated to form the IRI for this resource.
  23. */
  24. protected $service_path;
  25. /**
  26. * Implements the constructor.
  27. *
  28. * @param $service_path
  29. *
  30. */
  31. public function __construct($service_path) {
  32. $this->context = array();
  33. $this->data = array();
  34. $this->service_path = $service_path;
  35. // First, add the RDFS and Hydra vocabularies to the context. All Tripal
  36. // web services should use these.
  37. $vocab = tripal_get_vocabulary_details('rdf');
  38. $this->addContextItem('rdf', $this->cleanVocabURL($vocab));
  39. $vocab = tripal_get_vocabulary_details('rdfs');
  40. $this->addContextItem('rdfs', $this->cleanVocabURL($vocab));
  41. $vocab = tripal_get_vocabulary_details('hydra');
  42. $url = $this->cleanVocabURL($vocab);
  43. // $remove the '#' from the end of the URL as the HydraConsole automatically
  44. // wants to add it.
  45. //$url = preg_replace('/#/', '', $url);
  46. $this->addContextItem('hydra', $url);
  47. $vocab = tripal_get_vocabulary_details('dc');
  48. $this->addContextItem('dc', $this->cleanVocabURL($vocab));
  49. $vocab = tripal_get_vocabulary_details('schema');
  50. $this->addContextItem('schema', $this->cleanVocabURL($vocab));
  51. $vocab = tripal_get_vocabulary_details('local');
  52. $this->addContextItem('local', url($vocab['urlprefix'], array('absolute' => TRUE)). '/');
  53. $this->data['@id'] = $service_path;
  54. $this->data['@type'] = '';
  55. }
  56. /**
  57. * A helper function to remove the {db} and {accession} from the URL prefix.
  58. */
  59. private function cleanVocabURL($vocab) {
  60. $url = $vocab['urlprefix'];
  61. $url = preg_replace('/{db}:{accession}/', '', $url);
  62. $url = preg_replace('/{db}/', '', $url);
  63. $url = preg_replace('/{accession}/', '', $url);
  64. return $url;
  65. }
  66. /**
  67. * Adds a term to the '@context' section for this resource.
  68. *
  69. * This function should not be called directory. Rather, the
  70. * addContextTerm() and addContextVocab() functions built
  71. * into the TripalWebService class should be used as these will help
  72. * ensure terms are added proper for the context of the web service.
  73. *
  74. * @param $name
  75. * The term name.
  76. * @param $iri
  77. * The Internationalized Resource Identifiers or it's shortcut.
  78. *
  79. */
  80. public function addContextItem($name, $iri) {
  81. if (array_key_exists($name, $this->context)) {
  82. return;
  83. }
  84. $this->context[$name] = $iri;
  85. }
  86. /**
  87. * Removes a term for the '@context' section for this resource.
  88. *
  89. * @param $name
  90. * The term name.
  91. * @param $iri
  92. * The Internationalized Resource Identifiers or it's shortcut.
  93. */
  94. public function removeContextItem($name, $iri) {
  95. // TODO: make sure that if a shortcut is used that the parent is present.
  96. unset($this->context[$name]);
  97. }
  98. /**
  99. * Sets the resource type.
  100. *
  101. * The type exist in the context of the web service.
  102. *
  103. * @param $type
  104. * The type
  105. */
  106. public function setType($type) {
  107. $this->checkKey($type);
  108. $this->type = $type;
  109. $this->data['@type'] = $type;
  110. }
  111. /**
  112. * Checks a key to ensure it is in the Context before being used.
  113. *
  114. * This function should be used before adding a property or type to this
  115. * resource. It ensures that the key for the property is already in the
  116. * context.
  117. *
  118. * @param $key
  119. * The key to check.
  120. * @throws Exception
  121. */
  122. private function checkKey($key) {
  123. // Make sure the key is already present in the context.
  124. $keys = array_keys($this->context);
  125. // Keys that are full HTML are acceptable
  126. if (preg_match('/^(http|https):\/\/.*/', $key)) {
  127. return;
  128. }
  129. // If the key has a colon separating the vocabulary and the term then we
  130. // just need to make sure that the vocabulary is present.
  131. $matches = array();
  132. if (preg_match('/^(.*?):(.*?)$/', $key, $matches)) {
  133. $vocab = $matches[1];
  134. $accession = $matches[2];
  135. // The underscore represents the blank node. So, these keys are okay.
  136. if ($vocab == '_') {
  137. return;
  138. }
  139. // If the vocabulary is not in the.
  140. if (!in_array($vocab, $keys)) {
  141. throw new Exception(t("The key, !key, has a vocabulary that has not yet been added to the " .
  142. "context. Use the addContextItem() function to add the vocabulary prior to adding a value for it.", array('!key' => $key)));
  143. }
  144. }
  145. else {
  146. // If the key is not in the context then throw an error.
  147. if (!in_array($key, $keys)) {
  148. throw new Exception(t("The key, !key, has not yet been added to the " .
  149. "context. Use the addContextItem() function to add this key prior to adding a value for it.", array('!key' => $key)));
  150. }
  151. }
  152. }
  153. /**
  154. * Checks the value to make sure there are no problems with.
  155. *
  156. * Will also expand any TriaplWebServiceResource by adding their
  157. * context to this resource and substitute their data in place of the
  158. * value.
  159. *
  160. * @param $value
  161. *
  162. */
  163. private function checkValue(&$value) {
  164. if (is_array($value)) {
  165. foreach ($value as $k => $v) {
  166. // If this is an integer then this is a numeric indexed array
  167. // and we can just check the value. If not, then make sure the
  168. // key has been added to the context.
  169. if (preg_match('/^\d+$/', $k)) {
  170. $this->checkValue($value[$k]);
  171. }
  172. else {
  173. if ($k != '@id' and $k != '@type') {
  174. $this->checkKey($k);
  175. }
  176. $this->checkValue($value[$k]);
  177. }
  178. }
  179. }
  180. else {
  181. // If this is a TripalWebServiceResource then get it's data and use that
  182. // as the new value. Also, add in the elements context to this resource.
  183. if (is_a($value, 'TripalWebServiceResource') or is_subclass_of($value, 'TripalWebServiceResource')) {
  184. $context = $value->getContext();
  185. foreach ($context as $k => $v) {
  186. $this->addContextItem($k, $v);
  187. }
  188. $value = $value->getData();
  189. }
  190. }
  191. }
  192. /**
  193. * Set's the unique identifier for the resource.
  194. *
  195. * Every resource must have a unique Idientifer. In JSON-LD the '@id' element
  196. * identifies the IRI for the resource which will include the unique
  197. * identifier. The TraiplWebService to which a resource is added will
  198. * build the IRIs but it needs the unique ID of each resource.
  199. *
  200. * @param $id
  201. * The unique identifier for the resource.
  202. */
  203. public function setID($id) {
  204. $this->id = $id;
  205. $this->data['@id'] = $this->getURI($id);
  206. }
  207. /**
  208. * Retreives the IRI for an entity of a given ID in this web service.
  209. *
  210. * @param $id
  211. * The unique identifier for the resource.
  212. */
  213. public function getURI($id) {
  214. // If this is a key/value pair for an id and if the vocab portion is
  215. // an underscore then this represents a blank node and we don't want
  216. // to add the full path.
  217. $matches = array();
  218. if (preg_match('/^(.*?):(.*?)$/', $id, $matches)) {
  219. $vocab = $matches[1];
  220. if ($vocab == '_') {
  221. return $id;
  222. }
  223. return $id;
  224. }
  225. else {
  226. return $this->service_path . '/' . $id;
  227. }
  228. }
  229. /**
  230. * Retrieves the unique identifier for this resource.
  231. *
  232. * Every resource must have a unique Idientifer. In JSON-LD the '@id' element
  233. * identifies the IRI for the resource which will include the unique
  234. * identifier. The TraiplWebService to which a resource is added will
  235. * build the IRIs but it needs the unique ID of each resource.
  236. *
  237. * @return
  238. * The unique identifier for the resource.
  239. */
  240. public function getID() {
  241. return $this->id;
  242. }
  243. /**
  244. * Retreives the type of this resource.
  245. *
  246. * @return
  247. * The name of the resource.
  248. */
  249. public function getType() {
  250. return $this->type;
  251. }
  252. /**
  253. * Adds a new key/value pair to the web serivces response.
  254. *
  255. * The value must either be a scalar or another TripalWebServiceResource
  256. * object. If the same key is usesd multiple times then the resulting
  257. * resource will be presented as an array of elements.
  258. *
  259. * @param unknown $key
  260. * The name of the $key to add. This key must already be present in the
  261. * web service context by first adding it using the addContextItem()
  262. * member function.
  263. * @param unknown $value
  264. * The value of the key which must either be a scalar or a
  265. * TripalWebServiceResource instance.
  266. */
  267. public function addProperty($key, $value) {
  268. $this->checkKey($key);
  269. $this->checkValue($value);
  270. // If this is a numeric keyed array then add each item.
  271. if (is_array($value) and count(array_filter(array_keys($value), 'is_int')) == count(array_keys($value))) {
  272. if (!array_key_exists($key, $this->data)) {
  273. $this->data[$key] = array();
  274. }
  275. foreach ($value as $item) {
  276. $this->addProperty($key, $item);
  277. }
  278. return;
  279. }
  280. // If this key doesn't exist then add the value directly to the key.
  281. if (!array_key_exists($key, $this->data)) {
  282. $this->data[$key] = $value;
  283. }
  284. // Else if it does exist then we need to make sure that the element is
  285. // an array and add it.
  286. else {
  287. // If this is the second element, then convert it to an array. The
  288. // second test in the condition below is for for a numeric array.
  289. if (!is_array($this->data[$key]) or count(array_filter(array_keys($this->data[$key]), 'is_string')) > 0) {
  290. $element = $this->data[$key];
  291. $this->data[$key] = array();
  292. $this->data[$key][] = $element;
  293. }
  294. $this->data[$key][] = $value;
  295. }
  296. }
  297. /**
  298. * Retrieves the value of a property.
  299. *
  300. * @param $key
  301. * The name of the property.
  302. *
  303. * @param
  304. * The value of the property. This could be a scalar, array or
  305. * a TripalWebService object.
  306. */
  307. function getProperty($key) {
  308. return $this->data[$key];
  309. }
  310. /**
  311. * Retrieves the data section of the resource.
  312. *
  313. * The JSON-LD response constists of two sections the '@context' section
  314. * and the data section. This function only returns the data section
  315. * for this resource
  316. *
  317. * @return
  318. * An associative array containing the data section of the response.
  319. */
  320. public function getData() {
  321. return $this->data;
  322. }
  323. /**
  324. * Retrieves the data section of the resource.
  325. *
  326. * The JSON-LD response constists of two sections the '@context' section
  327. * and the data section. This function only returns the data section
  328. * for this resource
  329. *
  330. * @return
  331. * An associative array containing the data section of the response.
  332. */
  333. public function getContext() {
  334. return $this->context;
  335. }
  336. /**
  337. * Copies the context from a given TripalWebService resource to this
  338. * resource.
  339. *
  340. * @param $resource
  341. */
  342. public function setContext($resource) {
  343. if (!is_a($resource, 'TripalWebServiceResource')) {
  344. throw new Exception("The \$resource argument provided to the TripalWebServiceResource::setContext() function must be an instance of a TripalWebServiceResource.");
  345. }
  346. $this->context = $resource->getContext();
  347. }
  348. }