Skip to content

Instantly share code, notes, and snippets.

@jreviews
Last active November 7, 2020 19:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jreviews/a53e5092cb002a00489b70e8a94e26d6 to your computer and use it in GitHub Desktop.
Save jreviews/a53e5092cb002a00489b70e8a94e26d6 to your computer and use it in GitHub Desktop.
JReviews Filter for Listings and Reviews Read-Only REST API

Building and Consuming a Read-Only REST API

The code below makes use of JReviews filter hooks to create a simple read-only API for listings and reviews. To get started, read the developers hooks documentation.

Once you have the code in place in overrides, to test the API, add the format=json parameter to any listings or reviews page. For example:

Listings endpoint

https://domain.com/most-recent?format=json

Reviews endpoint

https://domain.com/latest-user-reviews?format=json
<?php
defined('MVC_FRAMEWORK') or die;

use Clickfwd\Hook\Filter;

class JreviewsHookRestApi 
{
	protected $routes;

	public function __construct()
	{
		Filter::add('render_reviews', [$this, 'reviews']);

		Filter::add('render_categories', [$this, 'listings']);
	}

	public function listings($output, $params) 
	{
		if (! $this->isApiRequest()) {
			return $output;
		}

		// Only continue if 'listings' were sent to the view
		if (! isset($params['viewVars']['listings'])) 
		{
			return $output;
		}

		// List of listings for the current page
		$listings = $params['viewVars']['listings'] ?? [];

		$items = [];

		foreach ($listings as $listing)
		{
			$items[] = $this->listingResource($listing);
		}

		return $this->send([
			'items' => $items,
			'pagination' => $this->pagination($params['viewVars']['pagination']),
		]);
	}

	public function	reviews($output, $params) 
	{
		if (! $this->isApiRequest()) {
			return $output;
		}
	
		// Only continue if 'reviews' were sent to the view
		if (! isset($params['viewVars']['reviews'])) 
		{
			return $output;
		}

		S2App::import('Helper','routes','jreviews');

		$this->routes = ClassRegistry::getClass('RoutesHelper');

		// List of reviews for the current page
		$reviews = $params['viewVars']['reviews'] ?? [];

		$items = [];

		foreach ($reviews as $review)
		{
			$items[] = $this->reviewResource($review);
		}

		return $this->send([
			'items' => $items,
			'pagination' => $this->pagination($params['viewVars']['pagination']),
		]);
	}

	protected function isApiRequest()
	{
		$request = S2Object::make('request');

		return $request->method() == 'GET' && $request->get('format') == 'json';
	}

	protected function listingResource($listing)
	{
		$url = cmsFramework::makeAbsUrl(cmsFramework::route($listing['Listing']['url']));

		return [
			'title' => $listing['Listing']['title'],
			'href' => $url,
			'summary' => $listing['Listing']['summary'] ?? null,
			'image' => $this->imageResource($listing),
			'thumbnail' => $this->thumbnailResource($listing),
			'created' => $listing['Listing']['created'] ?? null,
			'category' => $this->categoryResource($listing)
		];
	}

	protected function reviewResource($review)
	{
		$url = $this->routes->reviewDiscuss('',$review,['listing'=>$review,'return_url'=>true]);

		return [
			'title' => $review['Review']['title'],
			'href' => $url,
			'comment' => $review['Review']['comments'],
			'average_rating' => (float) ($review['Rating']['average_rating'] ?? null),
			'created' => $review['Review']['created'],
			'listing' => $this->listingResource($review),
		];		
	}

	protected function imageResource($item)
	{
		$image = $item['MainMedia']['media_url'] ?? $item['Media']['photo'][0] ?? null;

		$categoryImage = !empty($item['MainMedia']['category'])
							? cmsFramework::makeAbsUrl($item['MainMedia']['category'])
							: null;

		return $image ?? $categoryImage ?? null;
	}

	protected function thumbnailResource($item)
	{
		return $item['MainMedia']['media_info']['thumbnail']['640x640s']['url'] 
				?? $this->imageResource($item);
	}

	protected function categoryResource($item)
	{
		return [
			'title' => $item['Category']['title'],
		];
	}

	protected function pagination($vars)
	{
		$total = $vars['total'];

		$per_page = $vars['limit'];

		$page = $vars['page'] !== '' ? $vars['page'] : 1;

		$total_pages = ceil($total/$per_page);

		return [
			'total' => (int) $total,
			'per_page' => $per_page,
			'page' => $page,
			'total_pages' => $total_pages,
			'links' => $this->paginationPreviousNextLinks($page, $total_pages),
		];
	}	

	protected function paginationPreviousNextLinks($page, $pages)
	{
		$url = cmsFramework::makeAbsUrl(cmsFramework::getCurrentUrl());

		$urlParts = parse_url($url);

		$links = [];

		if ($page > 1 && $pages > 1) 
		{
			$links['previous'] = $this->pageLink($urlParts, $page - 1);
		}

		if ($page < $pages && $pages > 1) 
		{
			$links['next'] = $this->pageLink($urlParts, $page + 1);
		}

		return $links;		
	}

	protected function pageLink($urlParts, $page)
	{
		parse_str($urlParts['query'] ?? '', $queryString);
		
		$queryString['format'] = 'json';

		$baseUrl = $urlParts['scheme'].'://'.$urlParts['host'].$urlParts['path'];

		return $baseUrl.'?'.http_build_query(array_merge($queryString,[S2_QVAR_PAGE => $page]));
	}

	protected function send($data)
	{
		header("Content-type: application/json");
		header("Access-Control-Allow-Origin: *");
		header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");

		return json_encode($data);
	}	
}

new JreviewsHookRestApi();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment