Skip to content

Commit a15bdb3

Browse files
committed
v0.2.0
- Update README.md - Remove routes - Add Livewire full-page components as handler - Fix bug when triggering manually generateUrl() multiple times - Fix bug when related model is null - Fix bug in tests
1 parent 15edb25 commit a15bdb3

File tree

10 files changed

+126
-64
lines changed

10 files changed

+126
-64
lines changed

README.md

Lines changed: 83 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@
77

88
A package for using and generating unique urls for each Eloquent model in Laravel. This package is inspired by [spatie/laravel-sluggable](https://github.com/spatie/laravel-sluggable) but making the urls unique.
99

10+
- [Installation](#installation)
11+
- [Usage](#usage)
12+
- [Configuration](#configuration)
13+
- [Routes]()
14+
- [Prepare your model](#prepare-your-model)
15+
- [Disable auto creating urls](#batch-import)
16+
- [Livewire](#livewire)
17+
- [Contributing](#contributing)
18+
1019
### Goals:
1120
- When create or update a model to generate a unique url based on urlStrategy() function inside each model
1221
- If the url exists to create a new url with suffix _1, _2, etc.
@@ -28,35 +37,47 @@ php artisan vendor:publish --tag="laravel-unique-urls-migrations"
2837
php artisan migrate
2938
```
3039

31-
You can publish the config file with:
3240

41+
## Usage
42+
43+
### Configuration
44+
You can publish the config file with:
3345
```bash
3446
php artisan vendor:publish --tag="laravel-unique-urls-config"
3547
```
36-
37-
## Usage
38-
Add to the model:
48+
There will create `unique-urls.php` with:
3949
```php
40-
use Vlados\LaravelUniqueUrls\HasUniqueUrlTrait;
50+
return [
51+
// Language versions of the urls
52+
//
53+
'languages' => ['bg','en'],
54+
'redirect_http_code' => 301,
55+
];
4156
```
42-
In your method add these methods:
43-
```php
44-
public function urlStrategy(): string
45-
{
46-
return Str::slug($this->getAttribute('name'));
47-
}
4857

49-
public function getUrlHandler(): array
58+
### Prepare your model
59+
In your Model add these methods:
60+
```php
61+
class MyModel extends Model
5062
{
51-
return [
52-
// The controller used to handle the request
53-
'controller' => CategoryController::class,
54-
// The method
55-
'method' => 'view',
56-
// additional arguments sent to this method
57-
'arguments' => [],
58-
];
59-
}
63+
use Vlados\LaravelUniqueUrls\HasUniqueUrlTrait;
64+
65+
public function urlStrategy(): string
66+
{
67+
return Str::slug($this->getAttribute('name'));
68+
}
69+
70+
public function urlHandler(): array
71+
{
72+
return [
73+
// The controller used to handle the request
74+
'controller' => CategoryController::class,
75+
// The method
76+
'method' => 'view',
77+
// additional arguments sent to this method
78+
'arguments' => [],
79+
];
80+
}
6081
```
6182

6283
The method for handling the request:
@@ -67,6 +88,12 @@ public function view(Request $request, $arguments = [])
6788
}
6889
```
6990

91+
And last, add this line at the end of your `routes/web.php`
92+
```php
93+
Route::get('{urlObj}', [\Vlados\LaravelUniqueUrls\LaravelUniqueUrls::class, 'handleRequest'])->where('urlObj', '.*');
94+
```
95+
### Batch import
96+
If for example you have category tree and you need to import all the data before creating the urls, you can disable the automatic generation of the url on model creation
7097
To disable automatically generating the urls on create or update overwrite the method `isAutoGenerateUrls` in the model:
7198
```php
7299
public function isAutoGenerateUrls(): bool
@@ -79,6 +106,41 @@ and call `generateUrl()` later like this:
79106
YourModel::all()->each(function (YourModel $model) {
80107
$model->generateUrl();
81108
});
109+
```
110+
### Livewire
111+
To use [Livewire full-page component](https://laravel-livewire.com/docs/2.x/rendering-components#page-components) to handle the request, first set in `urlHandler()` function in your model:
112+
```php
113+
public function urlHandler(): array
114+
{
115+
return [
116+
// The Livewire controller
117+
'controller' => CategoryController::class,
118+
// The method should be empty
119+
'method' => '',
120+
// additional arguments sent to the mount() function
121+
'arguments' => [],
122+
];
123+
}
124+
```
125+
Example livewire component:
126+
```php
127+
class LivewireComponentExample extends Component
128+
{
129+
private Url $urlModel;
130+
private array $url_arguments;
131+
132+
public function mount(Url $urlObj, $arguments = [])
133+
{
134+
$this->urlModel = $urlObj;
135+
$this->url_arguments = $arguments;
136+
}
137+
138+
public function render()
139+
{
140+
return view('livewire.view-category');
141+
}
142+
}
143+
82144
```
83145

84146
## Testing

config/unique-urls.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
<?php
2-
3-
// config for Vlados/LaravelUniqueUrls
42
return [
53
'languages' => ['bg','en'],
64
'redirect_http_code' => 301,

routes/web.php

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/HasUniqueUrlInterface.php

Lines changed: 0 additions & 17 deletions
This file was deleted.

src/HasUniqueUrlTrait.php

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*/
1212
trait HasUniqueUrlTrait
1313
{
14-
abstract public function getUrlHandler();
14+
abstract public function urlHandler();
1515

1616
private bool $autoGenerateUrls = true;
1717

@@ -38,16 +38,29 @@ protected static function bootHasUniqueUrlTrait(): void
3838
public function generateUrl(): void
3939
{
4040
$unique_url = Url::makeSlug($this->urlStrategy(), $this);
41-
$urls = [];
41+
$createRecords = [];
42+
43+
$existing_languages = is_null($this->url) ? collect() : $this->url()->get()->keyBy('language');
4244
foreach (config('unique-urls.languages') as $lang) {
4345
$prefix = (config('app.fallback_locale') == $lang) ? '' : $lang . '/';
44-
$new_url = $this->getUrlHandler();
46+
$new_url = $this->urlHandler();
47+
48+
if (in_array($lang, $existing_languages->keys()->toArray())) {
49+
// the url is existing for this model
50+
if ($existing_languages[$lang]->slug !== $prefix.$unique_url) {
51+
// update the existing record if the url slug is different
52+
$existing_languages[$lang]["slug"] = $prefix.$unique_url;
53+
$existing_languages[$lang]->save();
54+
}
55+
56+
continue;
57+
}
4558
$new_url['language'] = $lang;
4659
$new_url['slug'] = $prefix . $unique_url;
47-
$urls[] = $new_url;
60+
$createRecords[] = $new_url;
4861
}
49-
if (count($urls)) {
50-
$this->url()->createMany($urls);
62+
if (count($createRecords)) {
63+
$this->url()->createMany($createRecords);
5164
}
5265
}
5366

@@ -79,9 +92,7 @@ public function url(): \Illuminate\Database\Eloquent\Relations\MorphOne
7992
*/
8093
public function getUrl($absolute = true): string
8194
{
82-
throw_if(is_null($this->url), 'The model has no generated url');
83-
84-
$url = $this->url()->where('language', app()->getLocale())->first()->slug;
95+
$url = $this->url()->where('language', app()->getLocale())->first()->slug ?? '';
8596

8697
return $absolute ? url($url) : $url;
8798
}

src/LaravelUniqueUrls.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ public function handleRequest(Url $urlObj, Request $request)
1515

1616
$slugController = new $urlObj->controller();
1717
$arguments = $urlObj->getAttribute('arguments');
18-
$arguments['related'] = $urlObj->getRelation('related');
18+
if (method_exists($slugController, '__invoke') && $urlObj->getAttribute('method') === '') {
19+
// if it is livewire
20+
$request->route()->setParameter('arguments', $arguments);
21+
return \App::call([$slugController, '__invoke']);
22+
}
23+
$arguments['related'] = $urlObj->related;
1924
if (isset($urlObj->method, $arguments) && method_exists($urlObj->controller, $urlObj->method)) {
2025
$called = $slugController->{$urlObj->method}($request, $arguments);
2126
} elseif (isset($urlObj->method) && ! isset($arguments) && method_exists($urlObj->controller, $urlObj->method)) {
@@ -28,7 +33,6 @@ public function handleRequest(Url $urlObj, Request $request)
2833
if (isset($called) && false !== $called) {
2934
return $called;
3035
}
31-
3236
abort('404');
3337
}
3438

src/LaravelUniqueUrlsServiceProvider.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ public function configurePackage(Package $package): void
1212
$package
1313
->name('laravel-unique-urls')
1414
->hasConfigFile()
15-
->hasRoute('web')
1615
->hasMigration('create_unique_urls_table');
1716
}
1817
}

src/Models/Url.php

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
* @property Carbon|null $created_at
2323
* @property Carbon|null $updated_at
2424
*/
25-
2625
class Url extends Model
2726
{
2827
use HasFactory;
@@ -36,7 +35,7 @@ class Url extends Model
3635
protected static function booted()
3736
{
3837
static::updated(callback: function (Url $url) {
39-
if (! $url->isDirty('slug')) {
38+
if (!$url->isDirty('slug')) {
4039
return;
4140
}
4241
Url::create([
@@ -70,7 +69,7 @@ public function related(): MorphTo
7069
*/
7170
public static function makeSlug(string $slug, Model $model): string
7271
{
73-
if (! $slug) {
72+
if (!$slug) {
7473
throw new Exception('Slug cannot be empty');
7574
}
7675
$where = $model->only(['id', 'type']);
@@ -97,12 +96,17 @@ private static function makeUniqueSlug($slug, $where)
9796

9897
private static function otherRecordExistsWithSlug(string $path, $whereModel): bool
9998
{
100-
$query = self::whereNot(function ($query) use ($whereModel) {
101-
$query->where('related_id', $whereModel['id'])
102-
->where('related_type', $whereModel['type']);
99+
$query = self::where(function ($query) use ($whereModel) {
100+
$query->whereNot(function ($query) use ($whereModel) {
101+
$query->where('related_id', $whereModel['id'])
102+
->where('related_type', $whereModel['type']);
103+
})
104+
->orWhere(function ($query) use ($whereModel): void {
105+
$query->whereNull('related_id')
106+
->whereNull('related_type');
107+
});
103108
})
104-
->where('slug', $path)
105-
->withoutGlobalScopes();
109+
->where('slug', $path);
106110

107111
return $query->exists();
108112
}

tests/HasUniqueUrlTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77

88
//uses(RefreshDatabase::class);
99

10+
beforeEach(function() {
11+
app()->setLocale('en');
12+
});
13+
1014
test('Check if it creates correct url', closure: function () {
1115
$model = TestModel::create(['name' => 'this is a test']);
1216
expect($model->getUrl())->toEqual(url('test-this-is-a-test'));

tests/Models/TestModel.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public function urlStrategy(): string
2424
return 'test-' . Str::slug($this->getAttribute('name'));
2525
}
2626

27-
public function getUrlHandler(): array
27+
public function urlHandler(): array
2828
{
2929
return [
3030
'controller' => TestUrlHandler::class,

0 commit comments

Comments
 (0)