Theme Management for Laravel
Laravel-Theme is a theme management for Laravel 5+ (last check 6.3), it is the easiest way to organize your skins, layouts and assets., (*1)
This package is based on teepluss\theme, (*2)
Differences with teepluss version:
- Compatible with laravel 5+
- Removed twig compatibility (Reduces the package by 94%).
- Blade directives
- Better base template.
- Simplified configuration.
- More commands and helper functions.
- Better README file.
- Manifest file (Get and set theme info)
- Middleware to define theme and layout
Usage
Theme has many features to help you get started with Laravel, (*3)
Installation
To get the latest version of laravel-themes simply require it in your composer.json
file., (*4)
"facuz/laravel-themes": "^3.2"
You'll then need to run composer install
to download it and have the autoloader updated., (*5)
Once Theme is installed you need to register the service provider with the application. Open up config/app.php
and find the providers
key., (*6)
'providers' => [
...
Facuz\Theme\ThemeServiceProvider::class,
]
Theme also ships with a facade which provides the static syntax for creating collections. You can register the facade in the aliases
key of your config/app.php
file., (*7)
'aliases' => [
...
'Theme' => Facuz\Theme\Facades\Theme::class,
]
Publish config using artisan CLI., (*8)
php artisan vendor:publish --provider="Facuz\Theme\ThemeServiceProvider"
It's recommended to add to the .env
file the theme that we are going to use, (*9)
APP_THEME=default
Create new theme
The first time you have to create theme "default" structure, using the artisan command:, (*10)
php artisan theme:create default
If you change the facade name you can add an option --facade="Alias"., (*11)
This will create the following directory structure:, (*12)
โโโ public/
โโโ themes/
โโโ default/
โโโ assets
| โโโ css/
| โโโ img/
| โโโ js/
โโโ layouts/
โโโ partials/
| โโโ sections/
โโโ views/
โโโ widgets/
To delete an existing theme, use the command:, (*13)
php artisan theme:destroy default
If you want to list all installed themes use the command:, (*14)
php artisan theme:list
You can duplicate an existing theme:, (*15)
php artisan theme:duplicate name new-theme
Create from the application without CLI., (*16)
Artisan::call('theme:create', ['name' => 'foo']);
Basic usage
To display a view from the controller:, (*17)
namespace App\Http\Controllers;
use Theme;
class HomeController extends Controller {
public function getIndex()
{
return Theme::view('index');
}
...
}
This will use the theme and layout set by default on .env
, (*18)
You can add data or define the theme and layout:, (*19)
...
Theme::uses('themename');
$data['info'] = 'Hello World';
return Theme::view('index', $data);
...
Or you can do:, (*20)
$cookie = Cookie::make('name', 'Tee');
return Theme::view([
'view' => 'index',
'theme' => 'default',
'layout' => 'layout',
'statusCode' => 200,
'cookie' => $cookie,
'args' => $data
]);
All values except 'view'
are optional, (*21)
To check whether a theme exists., (*22)
Theme::exists('themename');
Each theme must come supplied with a manifest file theme.json
stored at the root of the theme, which defines supplemental details about the theme., (*23)
{
"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"
}
The manifest file can store any property that you'd like. These values can be retrieved and even set through a couple helper methods:, (*24)
// Get all: (array)
Theme::info();
// Get:
Theme::info("property");
// Set:
Theme::info("property", "new data");
Other ways to display a view:
$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();
Finding from both theme's view and application's view:, (*25)
$theme = Theme::uses('default')->layout('default');
return $theme->watch('home.index')->render();
To find the location of a view:, (*26)
$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
Render from string:
return $theme->string('<h1>{{ $name }}</h1>', ['name' => 'Teepluss'], 'blade')->render();
Compile string:, (*27)
$template = `<h1>Name: {{ $name }}</h1>
<p>
{{ Theme::widget("WidgetIntro", ["title" => "Demo Widget"])->render() }}
</p>`;
echo Theme::blader($template, ['name' => 'Teepluss']);
Symlink from another view
This is a nice feature when you have multiple files that have the same name, but need to be located as a separate one., (*28)
// 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
Configuration
After the config is published, you will see a global config file /config/theme.php
, all the configuration can be replaced by a config file inside a theme: /public/themes/[theme]/config.php
, (*29)
The config is convenient for setting up basic CSS/JS, partial composer, breadcrumb template and also metas., (*30)
'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');
}
]
];
Basic usage of assets
You can add assets on the asset
method of the config file. If yo want to add assets in your route you can get $asset
variable from $theme->asset()
., (*31)
$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', array('core-script'));
// path: public/themes/[current theme]/assets/js/custom.js
// This case has dependency with "core-script".
You can force use theme to look up existing theme by passing parameter to method: $asset->themePath('default')
, (*32)
Writing in-line style or script:, (*33)
// 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', '
', $dependencies);
Render styles and scripts in your blade layout:, (*34)
// Without container
@styles()
// With "footer" container
@scripts('footer')
// Get a specific path from the asset folder
@asset('img/image.png')
Scripts and Style can be used with or without container, (*35)
or a more complex way:, (*36)
{!! Theme::asset()->styles() !!}
{!! Theme::asset()->container('footer')->scripts() !!}
Direct path to theme asset:, (*37)
{!! Theme::asset()->url('img/image.png') !!}
Preparing group of assets:
Some assets you don't want to add on a page right now, but you still need them sometimes, so cook
and serve
is your magic., (*38)
Cook your assets., (*39)
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');
});
You can prepare on a global in package config:, (*40)
// Location: config/theme/config.php
....
'events' => array(
....
// 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');
});
}
)
....
Serve theme when you need:, (*41)
// At the controller.
Theme::asset()->serve('backbone');
Then you can get output:, (*42)
<html>
<head>
@styles()
@styles('your-container')
</head>
<body>
...
@scripts()
@scripts('your-container')
</body>
<html>
Partials
Render a partial in your layouts or views:, (*43)
@partial('header', ['title' => 'Header']);
This will look up to /public/themes/[theme]/partials/header.php
, and will add a variable $title
(optional), (*44)
Partial with current layout specific:, (*45)
Theme::partialWithLayout('header', ['title' => 'Header']);
This will look up up to /public/themes/[theme]/partials/[CURRENT_LAYOUT]/header.php
, (*46)
Finding from both theme's partial and application's partials:, (*47)
Theme::watchPartial('header', ['title' => 'Header']);
Partial composer:
$theme->partialComposer('header', function($view)
{
$view->with('key', 'value');
});
// Working with partialWithLayout.
$theme->partialComposer('header', function($view)
{
$view->with('key', 'value');
}, 'layout-name');
Sections
The @sections
blade directive simplify the access to /partials/sections/
path:, (*48)
@sections('main')
It's the same as:, (*49)
@partial('sections.main')
Magic methods
Magic methods allow you to set, prepend and append anything., (*50)
$theme->setTitle('Your title');
$theme->appendTitle('Your appended title');
$theme->prependTitle('Hello: ....');
$theme->setAnything('anything');
$theme->setFoo('foo');
$theme->set('foo', 'foo');
Render in your blade layout or view:, (*51)
@get('foo')
@get('foo', 'Default msj')
Theme::getAnything();
Theme::getFoo();
Theme::get('foo', 'Default msj');
Check if the place exists or not:
@getIfHas('title')
It's the same as:, (*52)
@if(Theme::has('title'))
{{ Theme::get('title') }}
@endif
~~~php
@if(Theme::hasTitle())
{{ Theme::getTitle() }}
@endif, (*53)
Get argument assigned to content in layout or region:
~~~php
Theme::getContentArguments();
Theme::getContentArgument('name');
To check if it exists:, (*54)
Theme::hasContentArgument('name');
Theme::place('content') is a reserve region to render sub-view., (*55)
Preparing data to view
Sometimes you don't need to execute heavy processing, so you can prepare and use when you need it., (*56)
$theme->bind('something', function()
{
return 'This is bound parameter.';
});
Using bound data on view:, (*57)
echo Theme::bind('something');
Breadcrumb
In order to use breadcrumbs, follow the instruction below:, (*58)
$theme->breadcrumb()->add('label', 'http://...')->add('label2', 'http:...');
// or
$theme->breadcrumb()->add([[
'label' => 'label1',
'url' => 'http://...'
],[
'label' => 'label2',
'url' => 'http://...'
]]);
To render breadcrumbs:, (*59)
{!! $theme->breadcrumb()->render() !!}
or, (*60)
{!! Theme::breadcrumb()->render() !!}
You can set up breadcrumbs template anywhere you want by using a blade template., (*61)
$theme->breadcrumb()->setTemplate('
@foreach ($crumbs as $i => $crumb)
@if ($i != (count($crumbs) - 1))
-
{{ $crumb["label"] }}/
@else
- {{ $crumb["label"] }}
@endif
@endforeach
');
Theme has many useful features called "widget" that can be anything.
You can create a global widget class using artisan command:, (*62)
php artisan theme:widget demo --global
Widget tpl is located in "resources/views/widgets/{widget-tpl}.blade.php", (*63)
Creating a specific theme name., (*64)
php artisan theme:widget demo default
Widget tpl is located in "public/themes/[theme]/widgets/{widget-tpl}.blade.php", (*65)
Now you will see a widget class at /app/Widgets/WidgetDemo.php, (*66)
User Id: {{ $label }}
@widget('demo', ['label' => 'Hi!'])
or, (*67)
{!! Theme::widget('demo', ['label' => 'Hi!'])->render() !!}
Using theme global
use Facuz\Theme\Contracts\Theme;
use App\Http\Controllers\Controller;
class BaseController extends Controller {
/**
* Theme instance.
*
* @var \Facuz\Theme\Theme
*/
protected $theme;
/**
* Construct
*
* @return void
*/
public function __construct(Theme $theme)
{
// Using theme as a global.
$this->theme = $theme->uses('default')->layout('ipad');
}
}
To override theme or layout., (*68)
public function getIndex()
{
$this->theme->uses('newone');
// or just override layout
$this->theme->layout('desktop');
$this->theme->of('somewhere.index')->render();
}
Middleware:
A middleware is included out of the box if you want to define a theme or layout per route. For Laravel 5.4+ the middleware is installed by default., (*69)
To install it in Laravel before 5.4:
Only register it in app\Http\Kernel.php
, (*70)
protected $routeMiddleware = [
...
'setTheme' => \Facuz\Theme\Middleware\ThemeLoader::class,
];
Usage:
You can apply the middleware to a route or route-group with the string 'theme:[theme],[layout]'
, (*71)
Route::get('/', function () {
...
return Theme::view('index');
})->middleware('theme:default,layout');
Or using groups:, (*72)
Route::group(['middleware'=>'theme:default,layout'], function() {
...
});
Helpers
Protect emails:
Protect the email address against bots or spiders that index or harvest addresses for sending you spam., (*73)
{!! protectEmail('email@example.com') !!}
or shorter, (*74)
@protect('email@example.com')
Print meta tags with common metadata., (*75)
{!! meta_init() !!}
Returns: <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">
, (*76)
Cheatsheet
Commands:
Command |
Description |
artisan theme:create name |
Generate theme structure. |
artisan theme:destroy name |
Remove a theme. |
artisan theme:list |
Show a list of all themes. |
artisan theme:duplicate name new |
Duplicate theme structure from other theme. |
artisan theme:widget demo default |
Generate widget structure. |
Blade Directives:
Blade |
Description |
@get('value') |
Magic method for get. |
@getIfHas('value') |
Like @get but show only if exist. |
@partial('value', ['var'=> 'optional']) |
Load the partial from current theme. |
@section('value', ['var'=> 'optional']) |
Like @partial but load from sections folder. |
@content() |
Load the content of the selected view. |
@styles('optional') |
Render styles declared in theme config. |
@scripts('optional') |
Render scripts declared in theme config. |
@widget('value', ['var'=> 'optional']) |
Render widget. |
@protect('value') |
Protect the email address against bots or spiders. |
@dd('value') |
Dump and Die. |
@d('value') |
Only dump. |
@dv() |
Dump, Die and show all defined variables. |
Helpers:
Helper |
Description |
protectEmail('email') |
Protect the email address against bots or spiders. |
meta_init() |
Print meta tags with common metadata. |