Files
laralog/app/Blog/Support/ThemeManager.php
T

140 lines
3.8 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Blog\Support;
use App\Models\Setting;
use Illuminate\Support\Facades\File;
class ThemeManager
{
public function path(?string $theme = null): string
{
return config('themes.path').($theme ? '/'.$theme : '');
}
public function active(): string
{
$active = Setting::get('active_theme', config('themes.default'));
return $this->exists($active) ? $active : config('themes.default');
}
public function exists(string $theme): bool
{
return is_dir($this->path($theme)) && File::exists($this->path($theme).'/theme.json');
}
public function manifest(string $theme): array
{
$file = $this->path($theme).'/theme.json';
if (! File::exists($file)) {
return [];
}
return json_decode(File::get($file), true) ?? [];
}
/**
* 扫描主题目录,返回所有主题 manifest。
*/
public function all(): array
{
$themes = [];
foreach (File::directories(config('themes.path')) as $dir) {
$name = basename($dir);
if (! File::exists($dir.'/theme.json')) {
continue;
}
$manifest = $this->manifest($name);
$themes[$name] = [
'name' => $name,
'title' => $manifest['title'] ?? $name,
'version' => $manifest['version'] ?? '0.0.0',
'description' => $manifest['description'] ?? '',
'author' => $manifest['author'] ?? '',
'screenshot' => $manifest['screenshot'] ?? null,
'active' => $name === $this->active(),
];
}
return $themes;
}
public function activate(string $theme): void
{
if (! $this->exists($theme)) {
throw new \InvalidArgumentException("主题不存在: {$theme}");
}
Setting::set('active_theme', $theme);
$this->prependViews();
}
/**
* 把激活主题的 views 目录前置到全局视图路径。
* 在 Provider boot 与运行时切换主题时调用。
*/
public function prependViews(): void
{
// 迁移尚未执行(migrate:fresh 首轮)时跳过 DB 查询,使用默认主题
$active = \Illuminate\Support\Facades\Schema::hasTable('settings')
? $this->active()
: config('themes.default');
$views = $this->path($active).'/views';
if (! is_dir($views)) {
return;
}
$paths = config('view.paths');
array_unshift($paths, $views);
config(['view.paths' => $paths]);
app('view')->getFinder()->setPaths($paths);
}
/**
* 解析主题下的一个视图名。
* 优先使用激活主题的视图,缺失时回退到内置兜底视图(blog-default)。
*/
public function view(string $view): string
{
$active = $this->active();
if (view()->exists("theme.{$active}.{$view}")) {
return "theme.{$active}.{$view}";
}
return "blog-default::{$view}";
}
public function assetUrl(string $path, ?string $theme = null): string
{
$theme = $theme ?? $this->active();
return url('/themes/'.$theme.'/assets/'.ltrim($path, '/'));
}
/**
* 主题变量(原 sablog stylevars 的现代化替代:主题级自定义配置)。
*/
public function variables(?string $theme = null): array
{
$theme = $theme ?? $this->active();
$vars = Setting::get('theme_vars_'.$theme, []);
return is_array($vars) ? $vars : (json_decode((string) $vars, true) ?: []);
}
public function variable(string $key, mixed $default = null, ?string $theme = null): mixed
{
return $this->variables($theme)[$key] ?? $default;
}
}