> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lunarphp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# URLs

> Create SEO-friendly slugs for products, collections, brands, and custom models.

URLs provide SEO-friendly slugs for models such as products, collections, and brands.

## Overview

URLs provide SEO-friendly slugs for models such as products, collections, and brands. Instead of exposing database IDs in storefront routes:

```
/products/1
```

A URL slug allows routes like:

```
/products/apple-iphone
```

URLs are polymorphic, so any Eloquent model can support them. Each URL belongs to a language, and each model can have one default URL per language.

<Info>
  URLs are not to be confused with Laravel routes. They provide a slug-based lookup mechanism for storefront routing, not route definitions.
</Info>

## Model

```php theme={null}
Lunar\Models\Url
```

### Fields

| Field          | Type                 | Description                                                               |
| :------------- | :------------------- | :------------------------------------------------------------------------ |
| `id`           | `bigIncrements`      | Primary key                                                               |
| `language_id`  | `foreignId`          | The language this URL belongs to                                          |
| `element_type` | `string`             | Morph type of the parent model                                            |
| `element_id`   | `unsignedBigInteger` | Morph ID of the parent model                                              |
| `slug`         | `string`             | The URL slug                                                              |
| `default`      | `boolean`            | Whether this is the default URL for this element and language combination |
| `created_at`   | `timestamp`          |                                                                           |
| `updated_at`   | `timestamp`          |                                                                           |

### Relationships

| Relationship | Type      | Related Model           | Description                                        |
| :----------- | :-------- | :---------------------- | :------------------------------------------------- |
| `element`    | MorphTo   | Polymorphic             | The parent model (e.g. Product, Collection, Brand) |
| `language`   | BelongsTo | `Lunar\Models\Language` | The language this URL is for                       |

### Scopes

| Scope       | Description                              |
| :---------- | :--------------------------------------- |
| `default()` | Filter to URLs where `default` is `true` |

## Creating a URL

URLs can be created directly or through a model's `urls()` relationship.

```php theme={null}
use Lunar\Models\Url;

Url::create([
    'slug' => 'apple-iphone',
    'language_id' => $language->id,
    'element_type' => $product->getMorphClass(),
    'element_id' => $product->id,
    'default' => true,
]);

// Or through the relationship
$product->urls()->create([
    'slug' => 'apple-iphone',
    'language_id' => $language->id,
    'default' => true,
]);
```

### Default URL behavior

Only one URL can be the default per element and language combination. When a new URL is created or updated with `default` set to `true`, any existing default URL for that same element and language is automatically set to `false`.

```php theme={null}
$urlA = $product->urls()->create([
    'slug' => 'apple-iphone',
    'language_id' => 1,
    'default' => true,
]);

$urlA->default; // true

$urlB = $product->urls()->create([
    'slug' => 'apple-iphone-16',
    'language_id' => 1,
    'default' => true,
]);

$urlA->refresh()->default; // false
$urlB->default; // true
```

This behavior is scoped to each language independently. Setting a default for one language does not affect defaults in another language.

```php theme={null}
// This URL is for a different language, so $urlB remains the default for language 1
$urlC = $product->urls()->create([
    'slug' => 'apple-iphone-fr',
    'language_id' => 2,
    'default' => true,
]);

$urlB->refresh()->default; // true (still default for language 1)
$urlC->default; // true (default for language 2)
```

## Deleting a URL

When a default URL is deleted, Lunar automatically promotes another URL for the same element and language to become the new default.

```php theme={null}
$urlA = $product->urls()->create([
    'slug' => 'apple-iphone',
    'language_id' => 1,
    'default' => true,
]);

$urlB = $product->urls()->create([
    'slug' => 'apple-iphone-16',
    'language_id' => 1,
    'default' => false,
]);

$urlA->delete();

$urlB->refresh()->default; // true
```

## Adding URL support to models

Lunar ships with URL support on the following models:

* `Lunar\Models\Product`
* `Lunar\Models\Collection`
* `Lunar\Models\Brand`

To add URL support to a custom model, use the `HasUrls` trait:

```php theme={null}
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Lunar\Base\Traits\HasUrls;

class MyModel extends Model
{
    use HasUrls;
}
```

### Available relationships

The `HasUrls` trait provides the following relationships:

```php theme={null}
// All URLs for the model
$model->urls;

// The default URL (where default is true)
$model->defaultUrl;

// The URL for the application's current locale
$model->localeUrl;

// The URL for a specific locale (matches against the language code)
$model->localeUrl('fr')->first();
```

<Info>
  When a model using the `HasUrls` trait is deleted (hard delete only), all associated URLs are automatically removed.
</Info>

## Automatic URL generation

Lunar can automatically generate a URL when a model with the `HasUrls` trait is created. This is controlled by the `generator` option in `config/lunar/urls.php`.

```php theme={null}
<?php

use Lunar\Generators\UrlGenerator;

return [
    'required' => true,
    'generator' => UrlGenerator::class,
];
```

| Option      | Type           | Default               | Description                                                                                                               |
| :---------- | :------------- | :-------------------- | :------------------------------------------------------------------------------------------------------------------------ |
| `required`  | `bool`         | `true`                | Whether URLs are required when creating or editing models in the admin panel. Has no effect if a generator is configured. |
| `generator` | `string\|null` | `UrlGenerator::class` | The class responsible for generating URLs on model creation. Set to `null` to disable automatic generation.               |

### Default generator behavior

The built-in `Lunar\Generators\UrlGenerator` performs the following steps when a model is created:

1. Checks whether the model already has any URLs (skips generation if it does).
2. Reads the model's `name` column, falling back to the `name` attribute (via `attr('name')`).
3. Converts the name to a slug using `Str::slug()`.
4. Ensures uniqueness by appending a numeric suffix if the slug already exists (e.g. `test-product`, `test-product-2`, `test-product-3`).
5. Creates the URL as the default for the system's default language.

### Custom generator

To customize URL generation, create a class with a `handle` method that accepts an Eloquent model:

```php theme={null}
<?php

namespace App\Generators;

use Illuminate\Database\Eloquent\Model;

class CustomUrlGenerator
{
    public function handle(Model $model)
    {
        // Custom URL generation logic
    }
}
```

Then reference it in the config:

```php theme={null}
// config/lunar/urls.php
return [
    'required' => true,
    'generator' => \App\Generators\CustomUrlGenerator::class,
];
```

To disable automatic generation entirely, set the generator to `null`:

```php theme={null}
return [
    'required' => true,
    'generator' => null,
];
```
