TripalWebServiceResource.inc

File

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