Skip to content

Instantly share code, notes, and snippets.

@ryanmr
Created February 8, 2016 13:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ryanmr/2ca51085a7b4655b86dd to your computer and use it in GitHub Desktop.
Save ryanmr/2ca51085a7b4655b86dd to your computer and use it in GitHub Desktop.
<?php
namespace App\Http\Controllers\Frontend;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use \Carbon\Carbon;
use \App\Episode;
class FeedsController extends Controller
{
public function feedMaster() {
$episodes = \App\Episode::visible()->recent()->get();
return view('frontend.feeds.master', ['episodes' => $episodes]);
}
/**
* getFeedReplayQuery
*
* URL example: nexus.app/{series_slug}/feed/{fringe?}?begin=1453726381
*
* Based on the existence of the ?begin query string, determine
* the interval of now and the ?begin DateTimes, and then add that to the first published_at.
*
* Flow:
* 1. Get the timestamp ?begin or bail early by returning the $query.
* 2. Convert it from a UNIX timestamp to Carbon
* 3. Get the published_at Carbon of the first episode in the series
* 4. Find the difference in days between that episode and ?begin
* 5. Calculate an ending date from the first episode's published at, plus the difference in days.
* 6. Return a query modification whereBetween, those dates.
*/
private function getFeedReplayQuery(Request $request, $series_slug, $query) {
// find the ?query parameter `begin`
$timestamp = $request->input('begin', null);
// if it's null or not numeric, bail
// TODO: more cleaning is likely required here for $timestamp
if ($timestamp == null || !is_numeric($timestamp)) {
// this is malformed, maybe, and let's get out
return $query;
}
// when did this timestamp'd version of the feed begin?
$feedBegan = Carbon::createFromTimestamp($timestamp);
// try/catch here to prevent an episode-less series from
// throwing an unhandled exception firstOrFail does fail
try {
$firstEpisode = Episode::visible()->withSlug($series_slug)->firstOrFail();
} catch (\Exception $e) {
return $query;
}
// in case there's a glitch, assume that published_at and created_at are effectively the same
$firstPublished = $firstEpisode->published_at != null ? $firstEpisode->published_at : $firstEpisode->created_at;
$now = Carbon::now();
$days = $now->diffInDays($feedBegan);
// add the now-begin difference to $firstPublished
$untilPublished = $firstPublished->copy()->addDays($days);
return $query->whereBetween('published_at', [$firstPublished, $untilPublished]);
}
/**
* feedGeneral
*
* Get the series and feed in a single query,
* and if required by the ?begin query, fetch them from a
* limited range.
*/
public function feedGeneral(Request $request, $series_slug) {
// this is a constraint closure that is applied to the
// episodes eager loading subquery; inside of this is a
// condition that triggers only if begin is present
$episodesConstraint = function($query) use ($request, $series_slug) {
$query->visible()->recent();
if ($request->has('begin')) {
$this->getFeedReplayQuery($request, $series_slug, $query);
}
};
$series = \App\Series::with(['episodes' => $episodesConstraint])
->visible()
->whereSlug($series_slug)
->firstOrFail();
return view('frontend.feeds.primary', ['series' => $series, 'episodes' => $series->episodes]);
}
/**
* feedFringe
*
* Handle the feed when there is a fringe to be included.
*
* TODO: optimize this, a lot.
*/
public function feedFringe(Request $request, $series_slug) {
// get the series information
$series = \App\Series::visible()
->withSlug($series_slug)
->firstOrFail();
// get all of the recent episodes
// related are loaded because they are queried next
$episodes = \App\Episode::with(['related'])
->visible()
->recent()
->withSlug($series_slug);
if ($request->has('begin')) {
$this->getFeedReplayQuery($request, $series_slug, $episodes);
}
$episodes = $episodes->get();
// build a list of ids that need to be included
// the episode itself and the fringe of that episode
$included_ids = array();
foreach ($episodes as $episode) {
$included_ids[] = $episode->id;
if ( $episode->related->count() > 0 ) {
foreach ($episode->related as $related) {
if ( $related->pivot->type == 'fringe' ) {
$included_ids[] = $related->id;
}
}
}
}
/**
* TODO: Ideally, the included_ids or the following query would be cached,
* and only cleared if something was saved.
*/
/*
* TODO:
* Convert this multi-query version to the better unified SQL single query version.
*/
$episodes = \App\Episode::with(['related', 'media', 'series'])
->visible()
->recent()
->whereIn('id', $included_ids)
->get();
return view('frontend.feeds.primary', ['series' => $series, 'episodes' => $episodes]);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment