Refer to iGaster / Laravel-Theme example implementation in Laravel 8
1. Use Composer locally to create a new Laravel project: laravel-theme-demo.
PS E:\wwwroot> composer create-project laravel/laravel laravel-theme-demo
Creating a "laravel/laravel" project at "./laravel-theme-demo"
Installing laravel/laravel (v8.6.10)
- Installing laravel/laravel (v8.6.10): Extracting archive
Created project in E:\wwwroot\laravel-theme-demo
> @php -r "file_exists('.env') || copy('.env.example', '.env');"
Loading composer repositories with package information
Updating dependencies
Lock file operations: 110 installs, 0 updates, 0 removals
2. IGaster / Laravel-Theme is a Laravel package that adds basic support for management themes. It allows you to build views and assets in separate folders and support theme extensions. Execute the command: composer require “gaster/laravel-theme” to install igaster/laravel-theme.
PS E:\wwwroot\laravel-theme-demo> composer require "igaster/laravel-theme"
Using version ^2.0 for igaster/laravel-theme
./composer.json has been updated
Running composer update igaster/laravel-theme
Loading composer repositories with package information
Updating dependencies
Lock file operations: 1 install, 0 updates, 0 removals
- Locking igaster/laravel-theme (v2.0.17)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
- Installing igaster/laravel-theme (v2.0.17): Extracting archive
1 package suggestions were added by new dependencies, use `composer suggest` to see details.
Package swiftmailer/swiftmailer is abandoned, you should avoid using it. Use symfony/mailer instead.
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: facade/ignition
Discovered Package: fruitcake/laravel-cors
Discovered Package: igaster/laravel-theme
Discovered Package: laravel/sail
Discovered Package: laravel/sanctum
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.
77 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
> @php artisan vendor:publish --tag=laravel-assets --ansi --force
No publishable resources for tag [laravel-assets].
Publishing complete.
PS E:\wwwroot\laravel-theme-demo>
3. Install Laravel Mix, execute: yarn install, run all mix tasks, execute: npm run dev.
PS E:\wwwroot\laravel-theme-demo> yarn install
yarn install v1.22.15
info No lockfile found.
[1/4] Resolving packages...
warning laravel-mix > node-libs-browser > url > querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
[2/4] Fetching packages...
info fsevents@2.3.2: The platform "win32" is incompatible with this module.
info "fsevents@2.3.2" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
warning " > laravel-mix@6.0.39" has unmet peer dependency "@babel/core@^7.15.8".
warning " > laravel-mix@6.0.39" has unmet peer dependency "@babel/plugin-proposal-object-rest-spread@^7.15.6".
warning " > laravel-mix@6.0.39" has unmet peer dependency "@babel/plugin-syntax-dynamic-import@^7.8.3".
warning " > laravel-mix@6.0.39" has unmet peer dependency "@babel/plugin-transform-runtime@^7.15.8".
warning " > laravel-mix@6.0.39" has unmet peer dependency "@babel/preset-env@^7.15.8".
warning " > laravel-mix@6.0.39" has unmet peer dependency "webpack@^5.60.0".
warning " > laravel-mix@6.0.39" has unmet peer dependency "webpack-cli@^4.9.1".
[4/4] Building fresh packages...
success Saved lockfile.
warning Your current version of Yarn is out of date. The latest version is "1.22.17", while you're on "1.22.15".
Done in 87.72s.
PS E:\wwwroot\laravel-theme-demo> npm run dev
> dev
> npm run development
> development
> mix
● Mix █████████████████████████ emitting (98%)
after emit
● Mix █████████████████████████ done (99%) plugins
WebpackBar:done
✔ Mix
Compiled successfully in 3.82s
Laravel Mix v6.0.39
✔ Compiled Successfully in 3717ms
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────┬─────────┐
│ File │ Size │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────┼─────────┤
│ /js/app.js │ 606 KiB │
│ css/app.css │ 1 bytes │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────┴─────────┘
● Mix █████████████████████████ done (99%)
plugins
webpack compiled successfully
PS E:\wwwroot\laravel-theme-demo>
4. Publish the profile to your application
PS E:\wwwroot\laravel-theme-demo> php artisan vendor:publish --provider="Igaster\LaravelTheme\themeServiceProvider"
Copied File [\vendor\igaster\laravel-theme\src\Config\themes.php] To [\config\themes.php]
Publishing complete.
5. Create a theme and execute the command: theme:create. Generate the directory structure, concocted in the same way, and generate the theme T2. as shown in Figure 1
PS E:\wwwroot\laravel-theme-demo> php artisan theme:create
Give theme name::
> t1
Where will views be located? [t1]:
>
Where will assets be located? [t1]:
>
Extends an other theme? (yes/no) [no]:
>
Summary:
- Theme name: t1
- Views Path: E:\wwwroot\laravel-theme-demo\resources\views/t1
- Asset Path: E:\wwwroot\laravel-theme-demo\public\t1
- Extends Theme: No
Create Theme? (yes/no) [yes]:
> yes
PS E:\wwwroot\laravel-theme-demo> php artisan theme:create
Give theme name::
> t2
Where will views be located? [t2]:
>
Where will assets be located? [t2]:
>
Extends an other theme? (yes/no) [no]:
>
Summary:
- Theme name: t2
- Views Path: E:\wwwroot\laravel-theme-demo\resources\views/t2
- Asset Path: E:\wwwroot\laravel-theme-demo\public\t2
- Extends Theme: No
Create Theme? (yes/no) [yes]:
> yes
6. Open the home page:http://laravel-theme-demo.local/, the default interface. as shown in Figure 2
7. Use the middleware to set the theme and execute the command: php artisan make:middleware setTheme
PS E:\wwwroot\laravel-theme-demo> php artisan make:middleware SetTheme
Middleware created successfully.
8. Register your middleware in the app\http\kernel.php file. For example, if you want to apply the theme to all routes, you should add it to the web array.
/**
* The application's route middleware groups.
*
* @var array>
*/
protected $middlewareGroups = [
'web' => [
// ...
\App\Http\Middleware\SetTheme::class,
],
];
9. Add files: public/images/test.gif, public/t1/images/test.gif, public/t2/images/test.gif, edit the theme file: resources/ views/welcome.blade.php, resources/views/welcome.blade.php, resources/views/welcome.blade.php, the content is exactly the same
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
<!-- Styles -->
<style>
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}a{background-color:transparent}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}a{color:inherit;text-decoration:inherit}svg,video{display:block;vertical-align:middle}video{max-width:100%;height:auto}.bg-white{--bg-opacity:1;background-color:#fff;background-color:rgba(255,255,255,var(--bg-opacity))}.bg-gray-100{--bg-opacity:1;background-color:#f7fafc;background-color:rgba(247,250,252,var(--bg-opacity))}.border-gray-200{--border-opacity:1;border-color:#edf2f7;border-color:rgba(237,242,247,var(--border-opacity))}.border-t{border-top-width:1px}.flex{display:flex}.grid{display:grid}.hidden{display:none}.items-center{align-items:center}.justify-center{justify-content:center}.font-semibold{font-weight:600}.h-5{height:1.25rem}.h-8{height:2rem}.h-16{height:4rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.leading-7{line-height:1.75rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.ml-2{margin-left:.5rem}.mt-4{margin-top:1rem}.ml-4{margin-left:1rem}.mt-8{margin-top:2rem}.ml-12{margin-left:3rem}.-mt-px{margin-top:-1px}.max-w-6xl{max-width:72rem}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.p-6{padding:1.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.pt-8{padding-top:2rem}.fixed{position:fixed}.relative{position:relative}.top-0{top:0}.right-0{right:0}.shadow{box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.text-center{text-align:center}.text-gray-200{--text-opacity:1;color:#edf2f7;color:rgba(237,242,247,var(--text-opacity))}.text-gray-300{--text-opacity:1;color:#e2e8f0;color:rgba(226,232,240,var(--text-opacity))}.text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#718096;color:rgba(113,128,150,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#1a202c;color:rgba(26,32,44,var(--text-opacity))}.underline{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.w-5{width:1.25rem}.w-8{width:2rem}.w-auto{width:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:h-20{height:5rem}.sm\:ml-0{margin-left:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}}@media (min-width:768px){.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--bg-opacity:1;background-color:#2d3748;background-color:rgba(45,55,72,var(--bg-opacity))}.dark\:bg-gray-900{--bg-opacity:1;background-color:#1a202c;background-color:rgba(26,32,44,var(--bg-opacity))}.dark\:border-gray-700{--border-opacity:1;border-color:#4a5568;border-color:rgba(74,85,104,var(--border-opacity))}.dark\:text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.dark\:text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.dark\:text-gray-500{--tw-text-opacity:1;color:#6b7280;color:rgba(107,114,128,var(--tw-text-opacity))}}
</style>
<style>
body {
font-family: 'Nunito', sans-serif;
}
</style>
</head>
<body class="antialiased">
<div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center py-4 sm:pt-0">
@if (Route::has('login'))
<div class="hidden fixed top-0 right-0 px-6 py-4 sm:block">
@auth
<a href="{{ url('/home') }}" class="text-sm text-gray-700 dark:text-gray-500 underline">Home</a>
@else
<a href="{{ route('login') }}" class="text-sm text-gray-700 dark:text-gray-500 underline">Log in</a>
@if (Route::has('register'))
<a href="{{ route('register') }}" class="ml-4 text-sm text-gray-700 dark:text-gray-500 underline">Register</a>
@endif
@endauth
</div>
@endif
<div class="max-w-6xl mx-auto sm:px-6 lg:px-8">
<div class="flex justify-center pt-8 sm:justify-start sm:pt-0">
This is the "{{Theme::get()}}" theme.
</div>
<div class="flex justify-center pt-8 sm:justify-start sm:pt-0">
<img src={{Theme::url('images/test.gif')}}>
</div>
</div>
</div>
</body>
</html>
10. Open the home page, the theme name is empty. as shown in Figure 3
11. Edit app/http/middleware/settheme.php, set the theme to t1, open the home page, and the theme is t1. as shown in Figure 4
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class SetTheme
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
$themeName = "t1";
if(\Theme::exists($themeName)){
\Theme::set($themeName);
}
return $next($request);
}
}
12. Edit app/http/middleware/settheme.php, set the theme to t2, open the home page, and the theme is t2. as shown in Figure 5
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class SetTheme
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
*/
public function handle(Request $request, Closure $next)
{
$themeName = "t2";
if(\Theme::exists($themeName)){
\Theme::set($themeName);
}
return $next($request);
}
}




