ziglite
Version:
A Laravel named routes integration for Javascript
344 lines (269 loc) • 12.7 kB
Markdown
# Ziglite - A Laravel named routes integration for Javascript





## What is Ziglite?
[Ziglite](https://ziglite.spatian.dev) provides a way for Laravel developers to use their route names in the front end.\
This package is inspired by, and a lightweight minimalistic alternative to, the excellent and feature-rich [Ziggy](https://github.com/tighten/ziggy) by [Tighten](https://tighten.com/).
- **[Quick Start](#quick-start)**
- **[Supported Laravel Versions](#supported-laravel-versions)**
- **[Why Ziglite?](#why-ziglite)**
- **[Usage in a Package](#usage-in-a-package)**
- **[Route Filtering](#route-filtering)**
- **[The Routes Manifest](#the-routes-manifest)**
- [Filters](#filters)
- [Custom Origin](#custom-origin)
- [Caching](#caching)
- [Serializing to JSON](#serializing-to-json)
- **[The Blade Directive](#the-blade-directive)**
- [Custom Directives and Custom Generators](#custom-directives-and-custom-generators)
- **[The Front-end Helpers](#the-front-end-helpers)**
- [The `configureRouter()` helper](#the-configurerouter-helper)
- [The `route()` and `hasRoute()` helpers](#the-route-and-hasroute-helpers)
- [Default Parameters](#default-parameters)
- [Generating Absolute URLs](#generating-absolute-urls)
- [Extra Query Parameters](#extra-query-parameters)
- [The Router Class](#the-router-class)
- [Strict Mode](#strict-mode)
- **[Contributing](#contributing)**
- **[Credits](#credits)**
- **[License](#license)**
## Quick Start
Install Ziglite in your Laravel application or package with Composer:
```shell
composer require spatian-dev/ziglite
yarn install ziglite
```
Include the default directive in your Blade `@ziglite("*")`:
> **⚠️Warning:** This will expose all the named routes and their parameters in your application to the front-end, which may not be desirable.\
> See [Route filtering](#route-filtering) to learn about including and excluding routes.
Configure the javascript helpers:
```javascript
import { configureRouter } from 'ziglite';
configureRouter(ziglite_data);
```
Use the `route()` helper to transform a named route to a URL:
```javascript
import { route } from 'ziglite';
route('users.update', {user: 5});
```
> **⚠️Warning:** Ziglite does not support [Encoded Forward Slashes](https://laravel.com/docs/11.x/routing#parameters-encoded-forward-slashes). See [strict mode](#strict-mode) for more information.
## Supported Laravel Versions
| Laravel | Supported |
| ------- | ------------------ |
| 10.x | ✅ |
| 11.x | ✅ |
## Why Ziglite?
Ziglite is intended to be a simplified alternative to Ziggy. By omitting some of Ziggy's more advanced features, Ziglite offers a focused feature set aimed at ease of use. One notable difference is in filtering: Ziglite opts for a simplified and stricter approach by default.
Additionally, Ziglite boasts seamless integration into Laravel packages, providing developers with the ability to:
- Define custom aliases for the default Blade directive
- Utilize custom output generators
- Exert greater control over route filtering.
We hope Ziglite delivers simplicity and versatility for your Laravel projects and packages.\
If you have ideas or suggestions, please see [Contributing](#contributing).
## Usage in a Package
Register Ziglite's service provider inside the `register()` method of your package's service provider:
```php
$this->app->register(\Spatian\Ziglite\Providers\ZigliteServiceProvider::class);
```
You can then use the package the same way you would from a regular Laravel Application.
## Route Filtering
Ziglite only includes named routes that match explicitly provided filters. The filtering is based on [pattern-matching capabilities provided by Laravel's Str helper](https://laravel.com/docs/11.x/strings#method-str-is).
Exclusion can be defined by starting the pattern with a `!`
> **ℹ️** Exclusions patterns are always prioritized over inclusion patterns, regardless of the order in which they are defined.
For example:
- `*` will include all named routes
- `!users.*` will exclude all named routes starting with `users.`
- `!*.index` will exclude all named routes ending with `.index`
- `home` will include the route named `home`
You can provide as many inclusion or exclusion patterns as necessary
```php
use Spatian\Ziglite\Routes\Manifest;
new Manifest(["*", "!admin.*", "!api.*"]);
// in blade
@ziglite(["*", "!admin.*", "!api.*"]);
```
## The Routes Manifest
The core of Ziglite revolves around the [`\Spatian\Ziglite\Routes\Manifest`](src/Routes/Manifest.php) class. This class collects and caches your application's named routes, and generates a list of named routes that match the provided filters.
### Filters
By default, a `Manifest` instance will only include named routes that match the provided filters. If no filters are provided, then no routes are included.\
Filtering patterns can be provided as the first argument to the constructor.
```php
use Spatian\Ziglite\Routes\Manifest;
new Manifest(); // No routes included
new Manifest("*"); // A single pattern
new Manifest(["*", "!admin.*", "!api.*"]); // A list of patterns
```
See [Route filtering](#route-filtering) to learn more about filtering.
### Custom Origin
You can specify a custom origin to be included in the manifest.
```php
use Spatian\Ziglite\Routes\Manifest;
new Manifest(base: 'https://example.test');
```
### Caching
The `Manifest` class caches previously generated manifests for the duration of a request. Subsequent calls with the same filtering patterns simply return the previously calculated result.\
If this is not desirable, you can clear this cache using :
```php
use Spatian\Ziglite\Routes\Manifest;
Manifest::clearRoutes();
```
### Serializing to JSON
The `Manifest` class implements [`JsonSerializable`](https://www.php.net/manual/en/class.jsonserializable.php) and a `toJson()` function to simplify serializing to JSON.
```php
use Spatian\Ziglite\Routes\Manifest;
json_encode(new Manifest());
(new Manifest())->toJson();
```
## The Blade Directive
By default, Ziglite defines a `@ziglite` blade directive. This directive uses the default [`JavascriptDataTagGenerator`](src/Generators/JavascriptDataTagGenerator.php) to include a manifest as a JSON object in a blade.\
The JSON object is assigned to a `ziglite_data` variable on the `window` global.
```php
// home.blade.php
@ziglite("*")
```
```javascript
// home.js
console.log(ziglite_data);
/*
Should print an object similar to :
{
base: ...,
defaults: { ... },
routes: [ ... ],
}
*/
```
### Custom Directives and Custom Generators
You can define your custom generators by implementing the [`OutputGeneratorInterface`](src/Interfaces/OutputGeneratorInterface.php). This requires implementing a `make()` function that generates the output as a string.
```php
make(array|string $filters = [], string $base = null, string $nonce = ''): string
```
- `$filters` pattern or list of patterns for [filtering](#route-filtering)
- `$base` [custom origin](#custom-origin) to be included in the output
- `$nonce` a [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) nonce to be included in the output.
You can also define a custom blade directive that uses your custom generator. In your `AppServiceProvider`:
```php
app('ziglite')->setupBladeDirective('mydirective', new MyCustomOutputGenerator());
```
## The Front-end Helpers
The javascript library is published to a separate NPM package.
```shell
yarn install ziglite
```
By default, Ziglite does not globally import its javascript helpers. Instead, it defers to the developer to include them and use them as they see fit.\
The library does, however, create an internal default instance of [the `Router` class](#the-router-class). You can interact with this instance through the helper functions below.
### The `configureRouter()` helper
Use this function to set the configuration for the default `Router` instance.
```javascript
import { configureRouter } from 'ziglite';
configureRouter(ziglite_data);
configureRouter({
strict: false,
absolute: true,
base: 'https://example.test',
});
```
### The `route()` and `hasRoute()` helpers
The `route()` function transforms a named route to a URL using the default `Router` instance. This function works similarly to Laravel's `route()`.
```javascript
import { route } from 'ziglite';
route('home');
route('users.show', {user: 5});
```
The `hasRoute()` function checks if the default `Router` instance has a configured route that matches the given name.
```javascript
import { hasRoute } from 'ziglite';
hasRoute('home'); // assuming a route named 'home' exits, return true;
hasRoute('inexistant'); // assuming no route named 'inexistant' exits, return false;
```
### Default Parameters
Ziglite supports [Laravel's default parameters](https://laravel.com/docs/11.x/urls#default-values).
```javascript
import { route } from 'ziglite';
/*
Given a route named 'users.show' defined as /{locale}/users/{user},
and configured route default ['locale' => 'en']
*/
route('users.show', {user: 5}); // https://example.test/en/users/5
```
### Generating Absolute URLs
By default, Ziglite will generate relative URLs. To generate absolute URLs instead, set the `absolute` configuration to `true`;
```javascript
import { configureRouter, route } from 'ziglite';
configureRouter({
absolute: true,
base: 'https://example.test',
// ...
});
route('users.show', {user: 5}); // https://example.test/users/5
```
### Extra Query Parameters
Ziglite will automatically add any unmatched parameters as a query string to the resulting URL:
```javascript
import { route } from 'ziglite';
route('users.show', {user: 5, extra: 2}); // https://example.test/users/5?extra=2
```
If you need to add query-string parameters that have the same name as your route's parameter, you can do so under a `_query` object:
```javascript
import { route } from 'ziglite';
route('users.show', {
user: 5,
extra: 2,
_query: {
user: 'Tony'
},
}); // https://example.test/users/5?extra=2&user=Tony
```
By default, booleans are converted to integers
```javascript
import { route } from 'ziglite';
route('users.show', {
user: 5,
locked: true,
}); // https://example.test/users/5?locked=1
```
### The Router Class
The [`Router`](js/classes/Router.ts) provides the core of Ziglite's javascript functionality.\
If you would like to use Ziglite's features without modifying the default instance, you might create your own `Router` instance:
```javascript
import { Router } from 'ziglite';
const config = {
absolute: true,
base: 'https://example.test',
defaults: {
locale: 'en',
},
routes: [ /* ... */],
};
const myRouter = new Router();
myRouter.config = config;
// or
const myRouter = new Router(config);
// check if the given named route exists
myRouter.has('home');
// transform the given named route and parameters to a URL
myRouter.compile('users.show', {user: 5}); // https://example.test/users/5
```
### Strict Mode
Ziglite does not support [Encoded Forward Slashes](https://laravel.com/docs/11.x/routing#parameters-encoded-forward-slashes). This has historically been buggy, inconsistent and overall not a reliable feature to implement.\
By default, Ziglite will issue a warning in your console if it encounters a forward slash, plain (`/`) or encoded (`%2F`), in a parameter value.\
If you enable strict mode, Ziglite will throw an error instead.
```javascript
import { configureRouter, route } from 'ziglite';
configureRouter({
strict: true,
// ...
});
route('users.show', {user: 'test/test'}); // throws
```
## Contributing
Please consult the [contribution guide](CONTRIBUTING.md).
## Credits
- This package is heavily inspired by the excellent and feature-rich [Ziggy](https://github.com/tighten/ziggy) by [Tighten](https://tighten.com/). Shout out to them.
- [Saad Sidqui](https://github.com/saadsidqui)
- [All the contributors](https://github.com/spatian-dev/ziglite/graphs/contributors)
## License
Ziglite is free and open-source software released under the Apache 2.0 license. See [LICENSE](LICENSE) for more information.