Skip to content

Instantly share code, notes, and snippets.

@clarkwinkelmann
Last active May 4, 2018 15:54
Show Gist options
  • Save clarkwinkelmann/da5eda886b2e2d7a6619ce25c7a87518 to your computer and use it in GitHub Desktop.
Save clarkwinkelmann/da5eda886b2e2d7a6619ce25c7a87518 to your computer and use it in GitHub Desktop.
Vanilla PHP helper to load discussions from Flarum API
<?php
/**
* Vanilla PHP helper to load discussions from Flarum API
* @author Clark Winkelmann
* @license MIT (c) Clark Winkelmann 2018
*/
class FlarumDiscussionStream
{
/**
* Flarum url without ending slash (same as the url in Flarum config.php)
* @var string
*/
protected $flarumUrl = 'https://discuss.flarum.org';
protected $parsedData;
protected $limit = 5;
protected $tag = null;
protected $include = [
'startUser',
'startPost',
];
public function tag($tag)
{
$this->tag = $tag;
return $this;
}
public function limit($limit)
{
$this->limit = intval($limit);
return $this;
}
public function replaceInclude($include)
{
$this->include = $include;
return $this;
}
public function fetch()
{
$url = "{$this->flarumUrl}/api/discussions?include=" . implode(',', $this->include) . "&page[limit]={$this->limit}" . ($this->tag ? '&filter[q]=tag:' . urlencode($this->tag) : '');
$this->queryAndParse($url);
return $this;
}
protected function queryAndParse($url)
{
$response = file_get_contents($url);
if ($response === false) {
throw new \Exception('Could not fetch the posts');
}
$document = json_decode($response);
if ($document === null) {
throw new \Exception('Could not decode the posts');
}
if (!property_exists($document, 'data') || !is_array($document->data)) {
throw new \Exception('Invalid or missing data key in the posts response');
}
$this->parsedData = [
'data' => [],
'included' => [],
];
foreach ($document->data as $data) {
if (
property_exists($data, 'id') &&
property_exists($data, 'type') &&
$data->type === 'discussions' &&
property_exists($data, 'attributes') &&
is_object($data->attributes) &&
property_exists($data->attributes, 'slug')
) {
// Create the url attribute for discussions as it is not part of the payload
$data->attributes->url = "{$this->flarumUrl}/d/{$data->id}-{$data->attributes->slug}";
}
$this->parsedData['data'][] = $data;
}
if (property_exists($document, 'included')) {
if (!is_array($document->included)) {
throw new \Exception('Invalid or missing data key in the posts response');
}
foreach ($document->included as $included) {
if (!property_exists($included, 'type') || !property_exists($included, 'id')) {
throw new \Exception('Missing type of id in included payload');
}
if (!array_key_exists($included->type, $this->parsedData['included'])) {
$this->parsedData['included'][$included->type] = [];
}
$this->parsedData['included'][$included->type][$included->id] = $included;
}
}
}
/**
* @return \stdClass[]
* @throws \Exception
*/
public function discussions()
{
if (!$this->parsedData) {
throw new \Exception('Data not ready. Use fetch first');
}
return $this->parsedData['data'];
}
/**
* @param \stdClass $data
* @return \stdClass
* @throws Exception
*/
public function relationship($data)
{
// If we pass the whole relationships object instead of one of the relationship element
if (property_exists($data, 'data')) {
$data = $data->data;
}
if (is_null($data)) {
throw new \Exception('No data for this relationship. This is not handled by this code.');
}
if (!property_exists($data, 'type') || !property_exists($data, 'id')) {
throw new \Exception('Missing type or id in relatinship query');
}
if (!array_key_exists($data->type, $this->parsedData['included']) || !array_key_exists($data->id, $this->parsedData['included'][$data->type])) {
throw new \Exception("No resource matches type {$data->type} and id {$data->id}");
}
return $this->parsedData['included'][$data->type][$data->id];
}
}
/**
* Simple function to create a plain text excerpt from html code
* @param string $text Input text
* @param int $length Maximum length of the result
* @param string $ending Text added in case of elipsis
* @return string Resulting excerpt text
*/
function excerpt($text, $length = 200, $ending = '...')
{
$noHtml = strip_tags($text);
if (strlen($noHtml) <= $length) {
return $noHtml;
}
return substr($noHtml, 0, $length) . $ending;
}
// Let's fetch some discussions
$discussions = (new FlarumDiscussionStream())->tag('dev')->fetch();
try {
?>
<?php foreach ($discussions->discussions() as $discussion): ?>
<article>
<h1><a href="<?= htmlspecialchars($discussion->attributes->url) ?>">
<?= htmlspecialchars($discussion->attributes->title) ?>
</a></h1>
<p>
By <?= htmlspecialchars($discussions->relationship($discussion->relationships->startUser)->attributes->username) ?>
on <?= DateTime::createFromFormat(DATE_ATOM, $discussion->attributes->startTime)->format('Y-m-d H:i') ?>
</p>
<p>
<?php if ($discussion->relationships->startPost): ?>
<?= htmlspecialchars(excerpt($discussions->relationship($discussion->relationships->startPost)->attributes->contentHtml)) ?>
<?php else: ?>
<em>The content of this post isn't available</em>
<?php endif; ?>
</p>
</article>
<?php endforeach; ?>
<?php if (count($discussions->discussions()) === 0): ?>
<p>Looks like there are no discussions here</p>
<?php endif; ?>
<?php
} catch (\Exception $exception) {
echo "<p><strong>Oops, something went wrong.</strong></p>";
throw $exception;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment