freemancontingent/laravel-themes

主题可以帮助您轻松地在Laravel项目中组织主题,并维护主题相关的资产、布局和部分,统一存储在单个目录中。(基于teepluss/theme)

v7.0 2022-05-20 09:16 UTC

README

Laravel-Theme是Laravel 6+的主题管理工具,它是最简单的方式组织您的皮肤、布局和资产。

本包基于 teepluss\theme

与teepluss版本的区别
  • 兼容Laravel 6+。
  • 移除了twig兼容性(减少了包体积94%)。
  • Blade指令
  • 更好的基础模板。
  • 简化配置。
  • 更多命令和辅助函数。
  • 更好的README文件。
  • 清单文件(获取和设置主题信息)
  • 中间件定义主题和布局

用法

主题具有许多功能,可以帮助您轻松开始使用Laravel。

安装

要获取laravel-themes的最新版本,只需在您的composer.json文件中引入它。

"freemancontingent/laravel-themes": "^4.0"

然后您需要运行composer install来下载它,并更新自动加载器。

一旦安装了主题,您需要将服务提供者注册到应用程序中。打开config/app.php文件,找到providers键。

'providers' => [
	...
	Fcl\Theme\ThemeServiceProvider::class,

]

主题还提供了一个门面,它提供了创建集合的静态语法。您可以在config/app.php文件的aliases键中注册门面。

'aliases' => [
	...
	'Theme' => Fcl\Theme\Facades\Theme::class,

]

使用artisan CLI发布配置。

php artisan vendor:publish --provider="Fcl\Theme\ThemeServiceProvider"

建议将我们要使用的主题添加到.env文件中

APP_THEME=default

创建新主题

第一次使用时,需要使用artisan命令创建“default”主题结构

php artisan theme:create default

如果您更改了门面名称,可以添加一个选项--facade="别名"

这将创建以下目录结构

├── public/
    └── themes/
	└── default/
		├── assets
        	|	├── css/
		|	├── img/
            	|	└── js/
            	├── layouts/
            	├── partials/
           	|	└── sections/
            	├── views/
	        └── widgets/

要删除现有的主题,使用以下命令

php artisan theme:destroy default

如果您想列出所有已安装的主题,使用以下命令

php artisan theme:list

您还可以复制现有的主题

php artisan theme:duplicate name new-theme

不使用CLI从应用程序创建

Artisan::call('theme:create', ['name' => 'foo']);

基本用法

从控制器中显示视图

namespace App\Http\Controllers;

use Theme;

class HomeController extends Controller {

	public function getIndex()
	{
		return Theme::view('index');
	}
	...
}

这将使用默认在.env中设置的主题和布局

您可以添加数据或定义主题和布局

...
Theme::uses('themename');

$data['info'] = 'Hello World';

return Theme::view('index', $data);
...

或者您可以这样做

$cookie = Cookie::make('name', 'Tee');

return Theme::view([
		    'view' => 'index',
		    'theme' => 'default',
		    'layout' => 'layout',
		    'statusCode' => 200,
		    'cookie'  => $cookie,
		    'args' => $data
		   ]);

除了'view'之外的所有值都是可选的

要检查主题是否存在。

Theme::exists('themename');

每个主题都必须提供存储在主题根目录下的清单文件theme.json,该文件定义了关于主题的补充信息。

{
    "slug": "default",
    "name": "Default",
    "author": "John Doe",
    "email": "johndoe@example.com",
    "description": "This is an example theme.",
    "web": "www.example.com",
    "license": "MIT",
    "version": "1.0"
}

清单文件可以存储您想要的任何属性。您可以通过几个辅助方法检索甚至设置这些值

// Get all: (array)
Theme::info();
// Get:
Theme::info("property");
// Set:
Theme::info("property", "new data");

其他显示视图的方式

$theme = Theme::uses('default')->layout('mobile');

$data = ['info' => 'Hello World'];
// It will look up the path 'resources/views/home/index.php':
return $theme->of('home.index', $data)->render();
// Specific status code with render:
return $theme->of('home.index', $data)->render(200);
// It will look up the path 'resources/views/mobile/home/index.php':
return $theme->ofWithLayout('home.index', $data)->render();
// It will look up the path 'public/themes/default/views/home/index.php':
return $theme->scope('home.index', $data)->render();
// It will look up the path 'public/themes/default/views/mobile/home/index.php':
return $theme->scopeWithLayout('home.index', $data)->render();
// Looking for a custom path:
return $theme->load('app.somewhere.viewfile', $data)->render();
// Working with cookie:
$cookie = Cookie::make('name', 'Tee');
return $theme->of('home.index', $data)->withCookie($cookie)->render();
// Get only content:
return $theme->of('home.index')->content();

从主题的视图和应用程序的视图中查找

$theme = Theme::uses('default')->layout('default');

return $theme->watch('home.index')->render();

要查找视图的位置

$which = $theme->scope('home.index')->location();

echo $which; // theme::views.home.index

$which = $theme->scope('home.index')->location(true);

echo $which; // ./public/themes/name/views/home/index.blade.php

从字符串渲染

return $theme->string('<h1>{{ $name }}</h1>', ['name' => 'Teepluss'], 'blade')->render();

编译字符串

$template = `<h1>Name: {{ $name }}</h1>
		     <p>
		      {{ Theme::widget("WidgetIntro", ["title" => "Demo Widget"])->render() }}
		     </p>`;

echo Theme::blader($template, ['name' => 'Teepluss']);

从另一个视图创建符号链接

当您有多个文件具有相同的名称,但需要作为单独的文件定位时,这是一个很好的功能。

// Theme A : /public/themes/a/views/welcome.blade.php
// Theme B : /public/themes/b/views/welcome.blade.php

// File welcome.blade.php at Theme B is the same as Theme A, so you can do link below:

Theme::symlink('a');
// Location: public/themes/b/views/welcome.blade.php

配置

配置发布后,您将看到一个全局配置文件/config/theme.php,所有配置都可以替换为主题内部配置文件/public/themes/[theme]/config.php中的配置文件

配置方便设置基本的CSS/JS、部分composer、面包屑模板以及元数据。

'events' => [

	/*
	 * Before event inherit from package config and the theme that call
	 * before, you can use this event to set meta, breadcrumb
	 * template or anything you want inheriting.
	 */
	'before' => function($theme)
	{
		// You can remove this lines anytime.
		$theme->setTitle('Title Example');
		$theme->setAuthor('John Doe');
		$theme->setKeywords('Example, Web');

		// Breadcrumb template.
		$theme->breadcrumb()->setTemplate(`
			 <ul class="breadcrumb">
			 @foreach($crumbs as $i => $crumb)
				 @if($i != (count($crumbs) - 1))
					<li>
                    	<a href="{{ $crumb["url"] }}">{{ $crumb["label"] }}</a>
                        <span class="divider">/</span>
					</li>
				 @else
					<li class="active">{{ $crumb["label"] }}</li>
				 @endif
			 @endforeach
			 </ul>
		 `);
	 },

    /*
	 * Listen on event before render a theme, this
	 * event should call to assign some assets.
	 */
	'asset' => function($asset)
	{
		$asset->themePath()->add([
					['style', 'css/style.css'],
					['script', 'js/script.js']
					 ]);

		// You may use elixir to concat styles and scripts.
		$asset->themePath()->add([
					['styles', 'dist/css/styles.css'],
					['scripts', 'dist/js/scripts.js']
					 ]);

		// Or you may use this event to set up your assets.
		$asset->themePath()->add('core', 'core.js');
		$asset->add([
			['jquery', 'vendor/jquery/jquery.min.js'],
			['jquery-ui', 'vendor/jqueryui/jquery-ui.min.js', ['jquery']]
			 ]);
	},


	/*
	 * Listen on event before render a theme, this event should
	 * call to assign some partials or breadcrumb template.
	 */
	'beforeRenderTheme' => function($theme)
	{
		$theme->partialComposer('header', function($view){
			$view->with('auth', Auth::user());
		});
	},

	/*
	 * Listen on event before render a layout, this should
	 * call to assign style, script for a layout.
	 */
	'beforeRenderLayout' => [
		'mobile' => function($theme){
			$theme->asset()->usePath()->add('ipad', 'css/layouts/ipad.css');
		}
	]
];

资产的基本用法

您可以在配置文件的 asset 方法中添加资产。如果您想在路由中添加资产,可以通过从 $theme->asset() 获取 $asset 变量来实现。

$asset->add('core-style', 'css/style.css');
// path: public/css/style.css

$asset->container('footer')->add('core-script', 'js/script.js');
// path: public/js/script.css

$asset->themePath()->add('custom', 'css/custom.css', ['core-style']);
// path: public/themes/[current-theme]/assets/css/custom.css
// This case has dependency with "core-style".

$asset->container('footer')->themePath()->add('custom', 'js/custom.js', ['core-script']);
// path: public/themes/[current theme]/assets/js/custom.js
// This case has dependency with "core-script".

通过向方法传递参数,您可以强制使用主题来查找现有主题:$asset->themePath('default')

编写内联样式或脚本

// Dependency with.
$dependencies = [];

// Writing an in-line script.
$asset->writeScript('inline-script', '
	$(function() {
		console.log("Running");
	})', $dependencies);

// Writing an in-line style.
$asset->writeStyle('inline-style', 'h1{ font-size: 0.9em; }', $dependencies);

// Writing an in-line script, style without tag wrapper.
$asset->writeContent('custom-inline-script', '
	<script>
		$(function() {
			console.log("Running");
		});
	</script>', $dependencies);

在您的 blade 布局中渲染样式和脚本

// Without container
@styles()

// With "footer" container
@scripts('footer')

// Get a specific path from the asset folder
@asset('img/image.png')

脚本和样式可以带容器或不带容器使用

或者更复杂的方法

{!! Theme::asset()->styles() !!}

{!! Theme::asset()->container('footer')->scripts() !!}

直接路径到主题资产

{!! Theme::asset()->url('img/image.png') !!}

准备资产组

有些资产您现在不想添加到页面上,但有时您仍然需要它们,所以 cookserve 是您的魔法。

烹饪您的资产。

Theme::asset()->cook('backbone', function($asset)
{
	$asset->add('backbone', '//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone-min.js');
	$asset->add('underscorejs', '//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js');
});

您可以在包的配置文件中全局准备

// Location: config/theme/config.php
....
	'events' => [
		....
		// This event will fire as a global you can add any assets you want here.
		'asset' => function($asset)
		{
			// Preparing asset you need to serve after.
			$asset->cook('backbone', function($asset)
			{
				$asset->add('backbone', '//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.0.0/backbone-min.js');
				$asset->add('underscorejs', '//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min.js');
			});
		}
	]
....

当需要时提供主题

// At the controller.
Theme::asset()->serve('backbone');

然后您可以得到输出

<html>
  <head>
      @styles()
      @styles('your-container')
  </head>
  <body>
      ...
      @scripts()
      @scripts('your-container')
  </body>
<html>

部分

在布局或视图中渲染部分

@partial('header', ['title' => 'Header']);

这将查找 /public/themes/[theme]/partials/header.php,并将添加一个变量 $title(可选)

与当前布局特定的部分

Theme::partialWithLayout('header', ['title' => 'Header']);

这将查找 /public/themes/[theme]/partials/[CURRENT_LAYOUT]/header.php

从主题的部分和应用程序的部分中查找

Theme::watchPartial('header', ['title' => 'Header']);
部分组件
$theme->partialComposer('header', function($view)
{
	$view->with('key', 'value');
});

// Working with partialWithLayout.
$theme->partialComposer('header', function($view)
{
	$view->with('key', 'value');
}, 'layout-name');

部分

@sections blade 指令简化了对 /partials/sections/ 路径的访问

@sections('main')

它与以下相同

@partial('sections.main')

魔法方法

魔法方法允许您设置、预置和附加任何内容。

$theme->setTitle('Your title');

$theme->appendTitle('Your appended title');

$theme->prependTitle('Hello: ....');

$theme->setAnything('anything');

$theme->setFoo('foo');

$theme->set('foo', 'foo');

在 blade 布局或视图中渲染

@get('foo')

@get('foo', 'Default msj')

Theme::getAnything();

Theme::getFoo();

Theme::get('foo', 'Default msj');
检查位置是否存在
@getIfHas('title')

它与以下相同

@if(Theme::has('title'))
	{{ Theme::get('title') }}
@endif
@if(Theme::hasTitle())
	{{ Theme::getTitle() }}
@endif

获取在布局或区域中分配给内容的参数

Theme::getContentArguments();
Theme::getContentArgument('name');

以检查它是否存在

Theme::hasContentArgument('name');

Theme::place('content') 是一个预留区域,用于渲染子视图。

准备数据供视图使用

有时您不需要执行繁重的处理,因此您可以在需要时准备和使用它。

$theme->bind('something', function()
{
	return 'This is bound parameter.';
});

在视图中使用绑定数据

echo Theme::bind('something');

面包屑

为了使用面包屑,请按照以下说明操作

$theme->breadcrumb()->add('label', 'http://...')->add('label2', 'http:...');

// or

$theme->breadcrumb()->add([[
			 'label' => 'label1',
			 'url'   => 'http://...'
			],[
			 'label' => 'label2',
			 'url'   => 'http://...'
			]]);

以渲染面包屑

{!! $theme->breadcrumb()->render() !!}

或者

{!! Theme::breadcrumb()->render() !!}

您可以使用 blade 模板在任何您想要的地方设置面包屑模板。

$theme->breadcrumb()->setTemplate('
	<ul class="breadcrumb">
	  @foreach ($crumbs as $i => $crumb)
		  @if ($i != (count($crumbs) - 1))
			  <li><a href="{{ $crumb["url"] }}">{{ $crumb["label"] }}</a><span class="divider">/</span></li>
		  @else
			  <li class="active">{{ $crumb["label"] }}</li>
		  @endif
	  @endforeach
	</ul>
');

小部件

主题有许多称为“小部件”的有用功能,可以是任何东西。您可以使用 artisan 命令创建一个全局小部件类

php artisan theme:widget demo --global

小部件模板位于 "resources/views/widgets/{widget-tpl}.blade.php"

创建特定的主题名称。

php artisan theme:widget demo default

小部件模板位于 "public/themes/[theme]/widgets/{widget-tpl}.blade.php"

现在您将在 /app/Widgets/WidgetDemo.php 中看到一个小部件类

<h1>User Id: {{ $label }}</h1>
在布局或视图中调用您的部件
@widget('demo', ['label' => 'Hi!'])

或者

{!! Theme::widget('demo', ['label' => 'Hi!'])->render() !!}

使用全局主题

use Fcl\Theme\Contracts\Theme;
use App\Http\Controllers\Controller;

class BaseController extends Controller {

	/**
	 * Theme instance.
	 *
	 * @var \Fcl\Theme\Theme
	 */
	protected $theme;

	/**
	 * Construct
	 *
	 * @return void
	 */
	public function __construct(Theme $theme)
	{
		// Using theme as a global.
		$this->theme = $theme->uses('default')->layout('ipad');
	}

}

以覆盖主题或布局。

public function getIndex()
{
	$this->theme->uses('newone');

	// or just override layout
	$this->theme->layout('desktop');

	$this->theme->of('somewhere.index')->render();
}

中间件

如果想要为每个路由定义一个主题或布局,那么默认情况下包含了一个中间件。对于 Laravel 6+,中间件默认安装。

在 Laravel 5.4 之前安装它

仅在 app\Http\Kernel.php 中注册它

protected $routeMiddleware = [
    ...
    'setTheme' => \Fcl\Theme\Middleware\ThemeLoader::class,
];
用法

您可以使用字符串 'theme:[theme],[layout]' 将中间件应用于路由或路由组

Route::get('/', function () {
	...
	return Theme::view('index');
})->middleware('theme:default,layout');

或者使用组

Route::group(['middleware'=>'theme:default,layout'], function() {
    ...
});

辅助函数

保护电子邮件

保护电子邮件地址,防止机器人或蜘蛛索引或收集地址以向您发送垃圾邮件。

{!! protectEmail('email@example.com') !!}

或更短

@protect('email@example.com')
元数据初始化

打印常见的元标签。

{!! meta_init() !!}

返回:<meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width, initial-scale=1">

速查表

命令
Blade 指令
辅助函数