没有不值得去解决的问题,也没有不值得去学习的技术!

Implementing With the igaster/laravel-theme Example in Laravel 8

Edit app/Http/Middleware/SetTheme.php, set the theme to t1, open the homepage, the theme name is t1

1. Use Composer locally to create a new Laravel project: laravel-theme-demo.

Plaintext
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 managing themes. It allows you to organize views and assets in separate folders and supports theme inheritance. Run the command: composer require “igaster/laravel-theme” to install igaster/laravel-theme.

Plaintext
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 by running: yarn install. To run all Mix tasks, execute: npm run dev.

Plaintext
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 configuration file to your application.

Plaintext
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 by running the command: theme:create. This generates the directory structure. Follow the same process to generate theme t2. See Figure 1.

Create a theme by running the command: theme:create. This generates the directory structure. Follow the same process to generate theme t2
Figure 1
Plaintext
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 homepage: http://laravel-theme-demo.local/ , which shows the default interface. See Figure 2.

Open the homepage: http://laravel-theme-demo.local/ , the default interface
Figure 2

7. Use middleware to set the theme by running the command: php artisan make:middleware SetTheme.

Plaintext
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.

PHP
    /**
     * The application's route middleware groups.
     *
     * @var array>
     */
    protected $middlewareGroups = [
        'web' => [
            // ...
            \App\Http\Middleware\SetTheme::class,
        ],
    ];

9. Add the files: public/images/test.gif, public/t1/images/test.gif, public/t2/images/test.gif. Edit the theme files: resources/views/welcome.blade.php, resources/views/welcome.blade.php, resources/views/welcome.blade.php. Their contents are exactly the same.

HTML
<!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 homepage, the theme name is empty. See Figure 3.

Open the homepage, the theme name is empty
Figure 3

11. Edit app/Http/Middleware/SetTheme.php to set the theme to t1. Open the homepage, and the theme name is t1. See Figure 4.

Edit app/Http/Middleware/SetTheme.php, set the theme to t1, open the homepage, the theme name is t1
Figure 4
PHP
<?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 to set the theme to t2. Open the homepage, and the theme name is t2. See Figure 5.

Edit app/Http/Middleware/SetTheme.php, set the theme to t2, open the homepage, the theme name is t2
Figure 5
PHP
<?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);
    }
}

关于作者 · 持续学习与技术实践

我是一名拥有 15 年以上开发经验的技术从业者,自 2013 年起持续运营个人技术博客,记录工作与个人项目中遇到的真实问题,以及解决问题过程中的探索、实践和思考。

除了技术写作,我也在持续开发和维护自己的独立项目,探索新技术和新工具在实际场景中的应用。我始终相信,没有不值得去解决的问题,也没有不值得去学习的技术。

如果这篇文章对你有所帮助,欢迎浏览博客中的其他文章,也欢迎交流技术经验与想法。

关于我  ·  GitHub  ·  邮件联系