This package allows any Eloquent model record to have a custom URL associated to it through a polymorphic one-to-one relationship.
The URL will be automatically saved inside the urls
table, along with the model using the created
and updated
Eloquent events.
When the model is force deleted, the corresponding URL will also be deleted by leveraging the deleted
Eloquent event.
Install the package via Composer:
composer require neurony/laravel-url
Publish the migration file with:
php artisan vendor:publish --provider="Neurony\Url\ServiceProvider" --tag="migrations"
After the migration has been published you can create the urls
table by running:
php artisan migrate
Insert the following as your last line in your routes/web.php
file:
Route::customUrl();
This route will catch any URL and will compare it against the urls
table.
If the URL matches a record in that table, it will dispatch the request to the designated controller and action specified in step 2.
Your Eloquent models should use the Neurony\Url\Traits\HasUrl
trait and the Neurony\Url\Options\UrlOptions
class.
The trait contains an abstract method getUrlOptions()
that you must implement yourself.
Here's an example of how to implement the trait:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Neurony\Url\Options\UrlOptions;
use Neurony\Url\Traits\HasUrl;
class YourModel extends Model
{
use HasUrl;
/**
* Get the options for generating the url.
*
* @return UrlOptions
*/
public function getUrlOptions() : UrlOptions
{
return UrlOptions::instance()
->routeUrlTo(YourController::class, 'show') // mandatory --- a controller and action specifying where to dispatch the request when the custom URL is accessed in the browser
->generateUrlSlugFrom('name') // mandatory --- a field present in your model's table from which to generate the slug that will be later used to generate the URL
->saveUrlSlugTo('slug'); // mandatory --- a field present in your model's table that will be used to store the slug
}
}
The getUrlOptions()
should always return a Neurony\Url\Options\UrlOptions
instance.
You can view all options and their methods of implementation inside the Neurony\Url\Options\UrlOptions
class.
Create the controller and action specified in the getUrlOptions()
method.
Once created, you can fetch your Eloquent model instance corresponding to the URL visited by using the getUrlable()
or getUrlableOrFail()
methods, present in the Neurony\Url\Models\Url
class.
<?php
namespace App\Controllers;
use Illuminate\Routing\Controller;
use Neurony\Url\Models\Url;
class PostsController extends Controller
{
public function show()
{
$post = Url::getUrlableOrFail();
return view('posts.show')->with([
'post' => $post
]);
}
}
The difference between
getUrlable()
andgetUrlableOrFail()
is that when no record is found, the first returnsnull
while the second throws aModelNotFoundException
.
You can define a prefix for your URLs using the prefixUrlWith()
method in your definition of the getUrlOptions()
method.
/**
* Get the options for generating the url.
*
* @return UrlOptions
*/
public function getUrlOptions() : UrlOptions
{
return UrlOptions::instance()
->routeUrlTo(YourController::class, 'show')
->generateUrlSlugFrom('name')
->saveUrlSlugTo('slug')
->prefixUrlWith(function ($prefix, $model) {
foreach ($model->parents as $parent) {
$prefix[] = $parent->slug;
}
return implode('/' , (array)$prefix);
});
}
The example above illustrates how to prefix an URL using a callable.
Please note that you can also pass a string or an array to the prefixUrlWith()
method:
...->prefixUrlWith('some-prefix'); // URL will be "some-prefix/your-model-slug"
...->prefixUrlWith(['some', 'prefix']); // URL will be "some/prefix/your-model-slug"
You can define a suffix for your URLs using the suffixUrlWith()
method in your definition of the getUrlOptions()
method.
/**
* Get the options for generating the url.
*
* @return UrlOptions
*/
public function getUrlOptions() : UrlOptions
{
return UrlOptions::instance()
->routeUrlTo(YourController::class, 'show')
->generateUrlSlugFrom('name')
->saveUrlSlugTo('slug')
->suffixUrlWith(function ($suffix, $model) {
foreach ($model->children as $child) {
$prefix[] = $child->slug;
}
return implode('/' , (array)$prefix);
});
}
The example above illustrates how to suffix an URL using a callable.
Please note that you can also pass a string or an array to the prefixUrlWith()
method:
...->suffixUrlWith('some-suffix'); // URL will be "your-model-slug/some-suffix"
...->suffixUrlWith(['some', 'suffix']); // URL will be "your-model-slug/some/suffix"
You can specify with what string to separate the different segments of an URL by using the glueUrlWith()
method in your definition of the getUrlOptions()
method.
The default glue value is /
.
/**
* Get the options for generating the url.
*
* @return UrlOptions
*/
public function getUrlOptions() : UrlOptions
{
return UrlOptions::instance()
->routeUrlTo(YourController::class, 'show')
->generateUrlSlugFrom('name')
->saveUrlSlugTo('slug')
->glueUrlWith('_');
}
Let's say that in your urls
table you have the following 2 URLs: posts/my-posts
and posts/my-posts/post-one
.
By default, when updating an URL, all its "children" URLs will be updated also.
So if you update the posts/my-posts
URL to become posts/my-latest-posts
, the trait will automatically update all the underlying URLs, meaning that the URL posts/my-posts/post-one
will become posts/my-latest-posts/post-one
.
To disable this automation, you can use the doNotUpdateCascading()
method in your definition of the getUrlOptions()
method.
/**
* Get the options for generating the url.
*
* @return UrlOptions
*/
public function getUrlOptions() : UrlOptions
{
return UrlOptions::instance()
->routeUrlTo(YourController::class, 'show')
->generateUrlSlugFrom('name')
->saveUrlSlugTo('slug')
->doNotUpdateCascading();
}
If you want to save an Eloquent model that uses the HasUrl
trait, but you don't want to create or update its corresponding URL, you can do so by calling the doNotGenerateUrl()
method.
// create a model record without creating an URL for it
$model = (new YourModel)->doNotGenerateUrl()->create(...);
// update a model record without updating its URL
$model->doNotGenerateUrl()->update(...);
You can get the absolute URL of your Eloquent model record by using:
$model = YourModel::first();
$model->getUrl(); // returns "http://domain.tld/your-model-slug"
You can get the relative URL of your Eloquent model record by using:
$model = YourModel::first();
$model->getUri(); // returns "your-model-slug"
You can access the Neurony\Url\Models\Url
instance from your Eloquent models using the url
relationp:
$model = YourModel::first();
$url = $model->url; // returns a loaded instance of the Neurony\Url\Models\Url model
You can access your Eloquent model instance from the Neurony\Url\Models\Url
model using the urlable
relation:
use Neurony\Url\Models\Url;
$url = Url::first();
$model = $url->urlable; // returns a loaded instance of your Eloquent model
You can find a specific URL by using the following query scope:
use Neurony\Url\Models\Url;
$url = Url::whereUrl('some-url')->first();
If you discover any security related issues, please email andrei.badea@neurony.ro instead of using the issue tracker.
The MIT License (MIT). Please see LICENSE for more information.
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.