File "LoadChannelContent.php"
Full Path: /var/www/drive/foundation/src/Channels/LoadChannelContent.php
File size: 6.61 KB
MIME-type: text/x-php
Charset: utf-8
<?php
namespace Common\Channels;
use App\Models\Channel;
use BadMethodCallException;
use Common\Core\Prerender\Actions\ReplacePlaceholders;
use Common\Database\Datasource\Datasource;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Pagination\AbstractPaginator;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Str;
class LoadChannelContent
{
public function execute(
Channel $channel,
array $params = [],
Channel $parent = null,
): ?AbstractPaginator {
$params['perPage'] = $params['perPage'] ?? 50;
$params['paginate'] = $this->resolvePaginateType($channel, $params);
if (!isset($params['orderBy']) && !isset($params['order'])) {
$params['order'] = Arr::get($channel->config, 'contentOrder');
}
if (
$channel->shouldRestrictContent()
// Arr::get($params, 'loader') !== 'editChannelPage'
) {
$this->applyRestriction($channel, $params, $parent);
// If restriction could not be loaded bail. This is used to cancel content loading and return 404,
// if, for example, loading genre channel, but specified genre does not exist.
if (!$channel->restriction) {
return new Paginator([], 50);
}
}
$contentType = Arr::get($channel->config, 'contentType');
if ($contentType === 'listAll') {
return $this->paginateAllContentFromDatabase(
$channel,
$params,
$parent,
);
} else {
// only cache channels that have other nested channels as content
$shouldCache =
Arr::get($channel->config, 'contentModel') ===
Channel::MODEL_TYPE;
if (!$shouldCache) {
return $this->loadCuratedContent($channel, $params);
} else {
$paramsKey = json_encode($params);
return Cache::remember(
// use "updated at" so channel changes from admin area will automatically
// cause new cache item, without having to clear cache manually
"channels.$channel->id.$channel->updated_at.$paramsKey",
now()->addHours(24),
fn() => $this->loadCuratedContent($channel, $params),
);
}
}
}
private function paginateAllContentFromDatabase(
Channel $channel,
array $params,
Channel $parent = null,
): AbstractPaginator {
$contentModel = Arr::get($channel->config, 'contentModel');
$methodName = sprintf('all%s', ucfirst(Str::plural($contentModel)));
// prevent channel from erroring when trying to sort
// by channelables table when paginating all content
if (
isset($params['order']) &&
Str::startsWith($params['order'], 'channelables')
) {
unset($params['order']);
}
// if channel specifies a method to load this model, use that
if (method_exists($channel, $methodName)) {
return $channel->{$methodName}($params, null, $parent);
// otherwise do a basic pagination for the model
} else {
$namespace = modelTypeToNamespace($contentModel);
$datasource = new Datasource(app($namespace)::query(), $params);
return $datasource->paginate();
}
}
private function loadCuratedContent(
Channel $channel,
array $params,
): AbstractPaginator {
$contentModel = Arr::get($channel, 'config.contentModel');
$methodName = sprintf('all%s', ucfirst(Str::plural($contentModel)));
$builder = $channel->{Str::plural($contentModel)}();
// if channel specifies a method to load this model, use that
if (method_exists($channel, $methodName)) {
$pagination = $channel->{$methodName}($params, $builder);
} else {
$datasource = new Datasource($builder, $params);
$order = $datasource->getOrder();
// get only column name, in case it's prefixed with table name
if (last(explode('.', $order['col'])) === 'popularity') {
$datasource->order = false;
try {
$builder->orderByPopularity($order['dir']);
} catch (BadMethodCallException $e) {
//
}
}
$pagination = $datasource->paginate();
}
$pagination
->filter(fn($model) => $model->pivot)
->transform(function (Model $model) use ($channel, $params) {
$model['channelable_id'] = $model->pivot->id;
$model['channelable_order'] = $model->pivot->order;
if ($model instanceof Channel) {
$model->loadContent(
array_merge($params, [
// clear parent channel pagination params and only load 15 items per nested channel
'perPage' => 15,
'page' => 1,
'paginate' => 'simple',
// clear this so nested channel always uses sorting order set in that channel's config
'order' => null,
]),
$channel,
);
}
return $model;
});
return $pagination;
}
private function applyRestriction(
Channel $channel,
array $params = [],
Channel $parent = null,
): void {
$urlParam = Arr::get($params, 'restriction');
$restriction =
$parent->restriction ?? $channel->loadRestrictionModel($urlParam);
if ($restriction) {
$channel->setAttribute('restriction', $restriction);
$channel->name =
app(ReplacePlaceholders::class)->execute($channel->name, [
'channel' => $channel,
]) ?:
$channel->name;
}
}
protected function resolvePaginateType(
Channel $channel,
array $params,
): string {
if (isset($params['paginate'])) {
return $params['paginate'];
}
if (isset($channel->config['paginationType'])) {
return match ($channel->config['paginationType']) {
'lengthAware' => 'lengthAware',
// simple and infinite scroll
default => 'simple',
};
}
return 'simple';
}
}