Skip to content

Commit

Permalink
Added reserved slugs.
Browse files Browse the repository at this point in the history
  • Loading branch information
LowSociety committed Apr 10, 2024
1 parent b2304d9 commit 0821563
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 0 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,20 @@ public function getSlugOptions() : SlugOptions
}
```

### Reserving slugs

If you have words that you wish to avoid having as slugs you can reserve them. Common when preventing collission with existing routes (eg. `admin`, `api` etc).

```php
public function getSlugOptions() : SlugOptions
{
return SlugOptions::create()
->generateSlugsFrom('name')
->saveSlugsTo('slug')
->slugsShouldNotEqual(['admin', 'api']);
}
```

### Integration with laravel-translatable

You can use this package along with [laravel-translatable](https://github.com/spatie/laravel-translatable) to generate a slug for each locale. Instead of using the `HasSlug` trait, you must use the `HasTranslatableSlug` trait, and add the name of the slug field to the `$translatable` array. For slugs that are generated from a single field _or_ multiple fields, you don't have to change anything else.
Expand Down
16 changes: 16 additions & 0 deletions src/HasSlug.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ protected function addSlug(): void

$slug = $this->generateNonUniqueSlug();

if (!empty($this->slugOptions->reservedSlugs)) {
$slug = $this->ensureSlugIsNotReserved($slug, $this->slugOptions->reservedSlugs);
}

if ($this->slugOptions->generateUniqueSlugs) {
$slug = $this->makeSlugUnique($slug);
}
Expand Down Expand Up @@ -125,6 +129,18 @@ protected function getSlugSourceStringFromCallable(): string
return call_user_func($this->slugOptions->generateSlugFrom, $this);
}

protected function ensureSlugIsNotReserved(string $slug, array $reservedSlugs): string
{
$originalSlug = $slug;
$i = $this->slugOptions->startSlugSuffixFrom;

while (in_array($slug, $reservedSlugs)) {
$slug = $originalSlug.$this->slugOptions->slugSeparator.$i++;
}

return $slug;
}

protected function makeSlugUnique(string $slug): string
{
$originalSlug = $slug;
Expand Down
8 changes: 8 additions & 0 deletions src/HasTranslatableSlug.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ protected function addSlug(): void
$this->withLocale($locale, function () use ($locale) {
$slug = $this->generateNonUniqueSlug();

if (!empty($this->slugOptions->reservedSlugs)) {
$slug = $this->ensureSlugIsNotReserved($slug, $this->slugOptions->reservedSlugs);
}

if (array_key_exists($locale, $this->slugOptions->reservedSlugsForLocales) && !empty($this->slugOptions->reservedSlugsForLocales[$locale])) {
$slug = $this->ensureSlugIsNotReserved($slug, $this->slugOptions->reservedSlugsForLocales[$locale]);
}

$slugField = $this->slugOptions->slugField;

if ($this->slugOptions->generateUniqueSlugs) {
Expand Down
24 changes: 24 additions & 0 deletions src/SlugOptions.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ class SlugOptions

public int $startSlugSuffixFrom = 1;

public array $reservedSlugs = [];

public array $reservedSlugsForLocales = [];

public static function create(): static
{
return new static();
Expand Down Expand Up @@ -133,4 +137,24 @@ public function startSlugSuffixFrom(int $startSlugSuffixFrom): self

return $this;
}

public function slugsShouldNotEqual(string | array $slugs): self
{
if (is_string($slugs)) {
$slugs = [$slugs];
}
$this->reservedSlugs = $slugs;

return $this;
}

public function slugsShouldNotEqualForLocale(string $locale, string | array $slugs): self
{
if (is_string($slugs)) {
$slugs = [$slugs];
}
$this->reservedSlugsForLocales[$locale] = $slugs;

return $this;
}
}
33 changes: 33 additions & 0 deletions tests/HasSlugTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -349,3 +349,36 @@ public function getSlugOptions(): SlugOptions

expect($savedModel->id)->toEqual($model->id);
});

it('can reserve a slug', function () {
$model = new class () extends TestModel {
public function getSlugOptions(): SlugOptions
{
return parent::getSlugOptions()->slugsShouldNotEqual('reserved');
}
};

$model->name = 'reserved';
$model->save();

expect($model->url)->toEqual('reserved-1');
});

it('can reserve multiple slugs', function () {
$model = new class () extends TestModel {
public function getSlugOptions(): SlugOptions
{
return parent::getSlugOptions()->slugsShouldNotEqual(['reserved', 'admin']);
}
};

$model->name = 'reserved';
$model->save();

expect($model->url)->toEqual('reserved-1');

$model->name = 'admin';
$model->save();

expect($model->url)->toEqual('admin-1');
});
19 changes: 19 additions & 0 deletions tests/HasTranslatableSlugTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,25 @@
expect($this->testModel->getTranslation('slug', 'nl'))->toBe('name-nl-2');
});

it('can reserve slugs for a certain locale', function () {
$this->testModel->useSlugOptions(
SlugOptions::create()
->generateSlugsFrom('name')
->saveSlugsTo('slug')
->slugsShouldNotEqual('reserved')
->slugsShouldNotEqualForLocale('nl', 'gereserveerd')
);

$this->testModel->setTranslation('name', 'en', 'reserved');
$this->testModel->setTranslation('name', 'nl', 'gereserveerd');
$this->testModel->setTranslation('name', 'sv', 'gereserveerd');
$this->testModel->save();

expect($this->testModel->getTranslation('slug', 'en'))->toBe('reserved-1');
expect($this->testModel->getTranslation('slug', 'nl'))->toBe('gereserveerd-1');
expect($this->testModel->getTranslation('slug', 'sv'))->toBe('gereserveerd');
});

it('can handle overwrites when creating a model', function () {
$this->testModel->setTranslation('name', 'en', 'Test value EN');
$this->testModel->setTranslation('name', 'nl', 'Test value NL');
Expand Down

0 comments on commit 0821563

Please sign in to comment.