toanld / ziggy
生成一个Blade指令,导出所有命名的Laravel路由。同时提供JavaScript中非常棒的route()辅助函数。
Requires
- ext-json: *
- laravel/framework: >=5.4@dev
Requires (Dev)
- orchestra/testbench: ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0
- phpunit/phpunit: ^6.0 || ^7.0 || ^8.0 || ^9.0
README
Ziggy – 在JavaScript中使用Laravel路由
Ziggy提供了一个JavaScript route()
辅助函数,其工作方式与Laravel的类似,使得在JavaScript中使用Laravel命名路由变得非常简单。
Ziggy支持从5.4
版本开始的所有Laravel版本和所有现代浏览器。
安装
使用composer require toanld/ziggy
将Ziggy安装到您的Laravel应用中。
将@routes
Blade指令添加到主布局中(在您的应用程序的JavaScript之前),现在全局范围内将可用route()辅助函数!
默认情况下,
@routes
Blade指令的输出包括所有应用程序路由及其参数的列表。此路由列表包含在页面HTML中,可以被最终用户查看。要配置要包含在此列表中的路由,或在不同的页面上显示或隐藏不同的路由,请参阅过滤路由。
使用方法
route()辅助函数
Ziggy的route()
辅助函数的工作方式与Laravel的类似——您可以传递一个路由的名称和要传递给该路由的参数,它将返回一个URL。
基本用法
// routes/web.php Route::get('posts', fn (Request $request) => /* ... */)->name('posts.index');
// app.js route('posts.index'); // 'https://ziggy.test/posts'
带参数的用法
// routes/web.php Route::get('posts/{post}', fn (Request $request, Post $post) => /* ... */)->name('posts.show');
// app.js route('posts.show', 1); // 'https://ziggy.test/posts/1' route('posts.show', [1]); // 'https://ziggy.test/posts/1' route('posts.show', { post: 1 }); // 'https://ziggy.test/posts/1'
带多个参数的用法
// routes/web.php Route::get('events/{event}/venues/{venue}', fn (Request $request, Event $event, Venue $venue) => /* ... */)->name('events.venues.show');
// app.js route('events.venues.show', [1, 2]); // 'https://ziggy.test/events/1/venues/2' route('events.venues.show', { event: 1, venue: 2 }); // 'https://ziggy.test/events/1/venues/2'
带查询参数的用法
// routes/web.php Route::get('events/{event}/venues/{venue}', fn (Request $request, Event $event, Venue $venue) => /* ... */)->name('events.venues.show');
// app.js route('events.venues.show', { event: 1, venue: 2, page: 5, count: 10, }); // 'https://ziggy.test/events/1/venues/2?page=5&count=10'
如果您有一个与路由参数名称相同的查询参数,请将其嵌套在_query
键下
route('events.venues.show', { event: 1, venue: 2, _query: { event: 3, page: 5, }, }); // 'https://ziggy.test/events/1/venues/2?event=3&page=5'
与Laravel的route()
辅助函数类似,Ziggy会自动将布尔查询参数编码为查询字符串中的整数
route('events.venues.show', { event: 1, venue: 2, _query: { draft: false, overdue: true, }, }); // 'https://ziggy.test/events/1/venues/2?draft=0&overdue=1'
带默认参数值的用法
// routes/web.php Route::get('{locale}/posts/{post}', fn (Request $request, Post $post) => /* ... */)->name('posts.show');
// app/Http/Middleware/SetLocale.php URL::defaults(['locale' => $request->user()->locale ?? 'de']);
// app.js route('posts.show', 1); // 'https://ziggy.test/de/posts/1'
实际的AJAX示例
const post = { id: 1, title: 'Ziggy Stardust' }; return axios.get(route('posts.show', post)).then((response) => response.data);
Router类
使用不带参数调用Ziggy的route()
辅助函数将返回JavaScript Router
类的实例,该类具有一些其他有用的属性和方法。
检查当前路由:route().current()
// Route called 'events.index', with URI '/events' // Current window URL is https://ziggy.test/events route().current(); // 'events.index' route().current('events.index'); // true route().current('events.*'); // true route().current('events.show'); // false
current()
方法可以接受参数作为其第二个参数,并将检查这些值是否与当前URL中的值匹配
// Route called 'events.venues.show', with URI '/events/{event}/venues/{venue}' // Current window URL is https://myapp.com/events/1/venues/2?authors=all route().current('events.venues.show', { event: 1, venue: 2 }); // true route().current('events.venues.show', { authors: 'all' }); // true route().current('events.venues.show', { venue: 6 }); // false
检查路由是否存在:route().has()
// App has only one named route, 'home' route().has('home'); // true route().has('orders'); // false
检索当前路由参数:route().params
// Route called 'events.venues.show', with URI '/events/{event}/venues/{venue}' // Current window URL is https://myapp.com/events/1/venues/2?authors=all route().params; // { event: '1', venue: '2', authors: 'all' }
注意:使用
route().params
检索到的参数值始终以字符串的形式返回。
路由模型绑定
Ziggy支持Laravel的路由模型绑定,甚至可以识别自定义的路由键名。如果您将JavaScript对象作为route()的一个路由参数传递,Ziggy将使用为该路由注册的路由模型绑定键来在对象中找到参数值并将其插入到URL中(如果路由模型绑定键不存在,则回退到id键)。
// app/Models/Post.php class Post extends Model { public function getRouteKeyName() { return 'slug'; } }
// app/Http/Controllers/PostController.php class PostController { public function show(Request $request, Post $post) { return view('posts.show', ['post' => $post]); } }
// routes/web.php Route::get('blog/{post}', [PostController::class, 'show'])->name('posts.show');
// app.js const post = { title: 'Introducing Ziggy v1', slug: 'introducing-ziggy-v1', date: '2020-10-23T20:59:24.359278Z', }; // Ziggy knows that this route uses the 'slug' route-model binding key name: route('posts.show', post); // 'https://ziggy.test/blog/introducing-ziggy-v1'
Ziggy还支持在路由定义中对范围绑定的自定义键(需要Laravel 7+)
// routes/web.php Route::get('authors/{author}/photos/{photo:uuid}', fn (Request $request, Author $author, Photo $photo) => /* ... */)->name('authors.photos.show');
// app.js const photo = { uuid: '714b19e8-ac5e-4dab-99ba-34dc6fdd24a5', filename: 'sunset.jpg', } route('authors.photos.show', [{ id: 1, name: 'Jacob' }, photo]); // 'https://ziggy.test/authors/1/photos/714b19e8-ac5e-4dab-99ba-34dc6fdd24a5'
TypeScript支持
由 benallfree 维护的 Ziggy 的非官方 TypeScript 类型定义作为 Definitely Typed 的一部分,可以通过 npm install @types/ziggy-js
安装。
高级设置
JavaScript框架
如果你不使用 Blade,或者不想使用 @routes
指令,Ziggy 提供了一个 Artisan 命令来将配置和路由输出到文件:php artisan ziggy:generate
。默认情况下,此命令将路由存储在 resources/js/ziggy.js
,但你可以通过将不同的值作为 Artisan 命令的参数传递或设置 ziggy.output.path
配置值来自定义此路径。或者,你也可以从你的 Laravel API 的端点中以 JSON 格式返回 Ziggy 的配置(见下文 从 API 端点检索 Ziggy 的路由和配置 的示例,了解如何设置此操作)。
php artisan ziggy:generate
生成的文件可能看起来像这样
// ziggy.js const Ziggy = { routes: {"home":{"uri":"\/","methods":["GET","HEAD"],"domain":null},"login":{"uri":"login","methods":["GET","HEAD"],"domain":null}}, url: 'http://ziggy.test', port: false }; export { Ziggy };
你可以选择创建一个别名,以使导入 Ziggy 的核心源文件更容易
// vite.config.js export default defineConfig({ resolve: { alias: { ziggy: 'vendor/tightenco/ziggy/dist', // 'vendor/tightenco/ziggy/dist/vue.es.js' if using the Vue plugin }, }, });
// webpack.mix.js // Mix v6 const path = require('path'); mix.alias({ ziggy: path.resolve('vendor/tightenco/ziggy/dist'), // 'vendor/tightenco/ziggy/dist/vue' if using the Vue plugin }); // Mix v5 const path = require('path'); mix.webpackConfig({ resolve: { alias: { ziggy: path.resolve('vendor/tightenco/ziggy/dist'), }, }, });
最后,像其他任何 JavaScript 库一样导入和使用 Ziggy。由于 Ziggy 配置对象在此设置中不可用,通常需要手动将其传递给 route()
函数
// app.js import route from 'ziggy'; import { Ziggy } from './ziggy'; // ... route('home', undefined, undefined, Ziggy);
Vue
Ziggy 包含一个 Vue 插件,使其在 Vue 应用中轻松使用 route()
辅助函数
import { createApp } from 'vue'; import { ZiggyVue } from 'ziggy'; import { Ziggy } from './ziggy'; import App from './App'; createApp(App).use(ZiggyVue, Ziggy); // Vue 2 import Vue from 'vue' import { ZiggyVue } from 'ziggy'; import { Ziggy } from './ziggy'; Vue.use(ZiggyVue, Ziggy);
如果你使用上述 ziggy
导入别名,请确保将其更新为 vendor/tightenco/ziggy/dist/vue.es.js
(Vite)或 vendor/tightenco/ziggy/dist/vue
(Laravel Mix)。
注意:如果你在你的视图中使用了
@routes
Blade 指令,Ziggy 的配置已经全局可用,因此你不需要导入Ziggy
配置对象并将其传递给use()
。
现在你可以在 Vue 组件和模板的任何地方使用 route()
,如下所示
<a class="nav-link" :href="route('home')">Home</a>
React
要使用 React 与 Ziggy 结合,首先导入 route()
函数和你的 Ziggy 配置。由于 Ziggy 配置对象在此设置中不可用,你必须手动将其传递给 route()
函数
// app.js import route from 'ziggy'; import { Ziggy } from './ziggy'; // ... route('home', undefined, undefined, Ziggy);
我们正在为 Ziggy 添加一个 Hook 以使其更简洁,但到目前为止,请确保将配置对象作为上述 route()
函数的第四个参数传递。
注意:如果你在你的视图中包含了
@routes
Blade 指令,包括在你的 React 应用中,route
辅助函数已经全局可用,因此你不需要在任何地方导入route
或Ziggy
。
SPAs或单独的仓库
Ziggy 的 route()
辅助函数也作为 NPM 包提供,可用于独立于 Laravel 后端管理的 JavaScript 项目(即没有 Composer 或 vendor
目录)。你可以使用 npm install ziggy-js
安装此 NPM 包。
为了使此函数可用,你可以运行 php artisan ziggy:generate
并将生成的路由文件添加到你的项目中,或者你可以从你的 Laravel API 的端点中以 JSON 格式返回 Ziggy 的配置(见下文 从 API 端点检索 Ziggy 的路由和配置 的示例,了解如何设置此操作)。
然后,如上所述导入和使用 Ziggy
// app.js import route from 'ziggy-js'; import { Ziggy } from './ziggy'; // or... const response = await fetch('/api/ziggy'); const Ziggy = await response.json(); // ... route('home', undefined, undefined, Ziggy);
过滤路由
Ziggy 支持过滤对 JavaScript 可用的路由,这在您有某些不想在客户端返回的源中包含和可见的路由时非常有用。过滤路由是可选的——默认情况下,Ziggy 包括应用程序的所有命名路由。
基本过滤
要设置基本路由过滤,在你的 Laravel 应用中创建一个配置文件 config/ziggy.php
并将 一个 或 except
设置定义为路由名称模式的数组。
注意:你必须选择其中一个。同时设置
only
和except
将完全禁用过滤并返回所有命名路由。
// config/ziggy.php return [ 'only' => ['home', 'posts.index', 'posts.show'], ];
您还可以在路由过滤器中使用星号作为通配符。在下面的示例中,admin.*
将排除名为admin.login
和admin.register
的路由。
// config/ziggy.php return [ 'except' => ['_debugbar.*', 'horizon.*', 'admin.*'], ];
使用分组进行过滤
您还可以使用配置文件中的groups
键定义您希望在应用的不同位置可用的路由组。
// config/ziggy.php return [ 'groups' => [ 'admin' => ['admin.*', 'users.*'], 'author' => ['posts.*'], ], ];
然后,您可以通过传递分组名称到@routes
Blade指令来公开特定的分组。
{{-- authors.blade.php --}} @routes('author')
要公开多个分组,您可以传递一个分组名称数组。
{{-- admin.blade.php --}} @routes(['admin', 'author'])
注意:将分组名称传递给
@routes
指令将始终优先于您的其他only
或except
设置。
其他
TLS/SSL终止和可信代理
如果您的应用正在使用TLS/SSL终止或位于负载均衡器或代理后面,或者托管在支持此功能的平台上,Ziggy可能会生成方案为http
的URL,即使您的应用URL使用https
。为了避免这种情况,请根据配置可信代理的文档设置Laravel应用的TrustProxies
中间件。
使用@routes
与内容安全策略(CSP)
内容安全策略(CSP)CSP可能会阻止内联脚本,包括由Ziggy的@routes
Blade指令输出的脚本。如果您有CSP并使用nonce标记安全的内联脚本,可以将nonce作为@routes
指令的第二个参数传递,它将被添加到Ziggy的脚本标签中。
// PHP ^8.0 @routes(nonce: 'your-nonce-here') // PHP <=7.4 @routes(null, 'your-nonce-here')
禁用route()
助手
如果您只想使用@routes
指令来使您的应用路由在JavaScript中可用,但不需要route()
助手函数,请将skip-route-function
配置值设置为true
。
// config/ziggy.php return [ 'skip-route-function' => true, ];
从API端点检索Ziggy的路由和配置
Ziggy可以轻松地将配置对象作为JSON从Laravel应用的端点返回。例如,您可以设置一个类似于下面的/api/ziggy
路由
// routes/api.php use Tightenco\Ziggy\Ziggy; Route::get('api/ziggy', fn () => response()->json(new Ziggy));
然后,客户端可以通过HTTP请求检索配置
// app.js import route from 'ziggy-js'; const response = await fetch('/api/ziggy'); const Ziggy = await response.toJson(); // ... route('home', undefined, undefined, Ziggy);
当您的应用路由更改时重新生成路由文件
如果您通过运行php artisan ziggy:generate
将Ziggy路由作为文件导出,您可能希望监视应用的路由文件,并在它们更新时自动重新运行命令。下面的示例是Laravel Mix插件,但可以在不使用Mix的情况下实现类似的功能。衷心感谢Nuno Rodrigues的想法和示例实现!
代码示例
const mix = require('laravel-mix'); const { exec } = require('child_process'); mix.extend('ziggy', new class { register(config = {}) { this.watch = config.watch ?? ['routes/**/*.php']; this.path = config.path ?? ''; this.enabled = config.enabled ?? !Mix.inProduction(); } boot() { if (!this.enabled) return; const command = () => exec( `php artisan ziggy:generate ${this.path}`, (error, stdout, stderr) => console.log(stdout) ); command(); if (Mix.isWatching() && this.watch) { ((require('chokidar')).watch(this.watch)) .on('change', (path) => { console.log(`${path} changed...`); command(); }); }; } }()); mix.js('resources/js/app.js', 'public/js') .postCss('resources/css/app.css', 'public/css', []) .ziggy();
贡献
要开始为Ziggy做出贡献,请查看贡献指南。
鸣谢
感谢Caleb Porzio、Adam Wathan和Jeffrey Way的帮助,巩固了这个想法。
安全
请审查我们的安全策略,了解如何报告安全漏洞。
许可证
Ziggy是在MIT许可证下发布的开源软件。有关更多信息,请参阅LICENSE。