TripalWebServiceResource.inc 12 KB

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