This package allows you to add a dynamic link to a Laravel model. This dynamic link may be a reference to another model or could also be an external URL. In essence, this package is just a Laravel relationship with some extra utility methods.
You can install the package via composer:
composer require esign/laravel-linkable
To make a model have a dynamic link you may add the HasDynamicLink
trait.
use Esign\Linkable\Concerns\HasDynamicLink;
use Illuminate\Database\Eloquent\Model;
class MenuItem extends Model
{
use HasDynamicLink;
}
Your database structure should look like the following:
Schema::create('menu_items', function (Blueprint $table) {
$table->id();
$table->string('dynamic_link_type')->nullable();
$table->string('dynamic_link_url')->nullable();
$table->string('dynamic_link_linkable_model')->nullable();
});
In case you set the dynamic_link_type
as internal
the dynamic_link_linkable_model
field will be used.
In order to know where a internal dynamic link should direct to you may implement LinkableUrlContract
on the related model:
use Esign\Linkable\Contracts\LinkableUrlContract;
class Post extends Model implements LinkableUrlContract
{
public function linkableUrl(): ?string
{
return "http://localhost/posts/{$this->id}";
}
}
In case you set the dynamic_link_type
as external
the dynamic_link_url
field will be used.
The value of this field should be a valid URL, e.g. https://www.example.com
.
Instead of using a regular MorphTo
relation, this package ships with a SingleColumnMorphTo
relation.
Some CMS, including our own, do not allow for morphable relations based on two columns, e.g. dynamic_link_linkable_type
and dynamic_link_linkable_id
.
The SingleColumnMorphTo
combines both the type and id fields into a single column, e.g. dynamic_link_linkable_model
.
The value for this single column is stored in the {model}:{id}
format, e.g. post:1
.
You're able to use both a fully qualified class name or a value from your application's morph map, just like a regular morphTo relation.
Note This approach is not ideal and more complex queries using this relationship may not work as expected. In case you're able to you may overwrite the
dynamicLinkLinkable
relation to use Laravel's defaultMorphTo
relationship.
To create an overview of all possible linkables you can create a MySQL view that creates a union of all possible models that can be linked to:
DB::statement('
CREATE OR REPLACE VIEW linkables AS
SELECT
CONCAT("post:", id) AS id,
"post" AS linkable_type,
id AS linkable_id,
CONCAT("Post - ", title) AS label
FROM posts
UNION
SELECT
CONCAT("comment:", id) AS id,
"comment" AS linkable_type,
id AS linkable_id,
CONCAT("Comment - ", title) AS label
FROM comments
');
This would create the following output:
id | linkable_type | linkable_id | label |
---|---|---|---|
post:1 | post | 1 | My First Post |
comment:1 | comment | 1 | My First Comment |
This package ships with a view component that will help you render both internal and external links:
use Esign\Linkable\Concerns\HasDynamicLink;
use App\Models\Post;
use App\Models\MenuItem;
$post = Post::create();
$menuItemInternal = MenuItem::create([
'dynamic_link_type' => HasDynamicLink::$linkTypeInternal,
'dynamic_link_url' => null,
'dynamic_link_linkable_model' => "post:{$post->id}",
]);
$menuItemExternal = MenuItem::create([
'dynamic_link_type' => HasDynamicLink::$linkTypeExternal,
'dynamic_link_url' => 'https://www.esign.eu',
'dynamic_link_linkable_model' => null,
]);
The view component will render an <a>
tag, when a model of the type external
is given, the target="_blank"
and rel="noopener"
attributes will be applied.
<x-linkable-dynamic-link :model="$menuItemInternal">Esign</x-linkable-dynamic-link>
<a href="https://www.esign.eu/posts/1">Esign</a>
<x-linkable-dynamic-link :model="$menuItemExternal">Esign</x-linkable-dynamic-link>
<a href="https://www.esign.eu" target="_blank" rel="noopener">Esign</a>
This Laravel package offers built-in support for internal and external links by default. However, in certain scenarios, you might want to introduce custom link types, such as referencing an anchor tag. To achieve this, you can extend the package's functionality.
- Extend the
HasDynamicLink
Trait
namespace App\Models\Concerns;
use Esign\Linkable\Concerns\HasDynamicLink as BaseHasDynamicLink;
trait HasDynamicLink
{
use BaseHasDynamicLink {
dynamicLink as baseDynamicLink;
}
public static string $linkTypeAnchor = 'anchor';
public function dynamicLink(): ?string
{
return match ($this->dynamicLinkType()) {
static::$linkTypeAnchor => $this->dynamicLinkUrl(),
default => $this->baseDynamicLink(),
};
}
}
- Create Your Own View Component
Next, you need to create your own view component that extends the DynamicLink
component provided by the package.
Here's how you can do that:
namespace App\View\Components;
use App\Models\Concerns\HasDynamicLink;
use Esign\Linkable\View\Components\DynamicLink as BaseDynamicLink;
class DynamicLink extends BaseDynamicLink
{
public function render(): ?View
{
return match ($this->model->dynamicLinkType()) {
HasDynamicLink::$linkTypeAnchor => view('linkable.dynamic-link-anchor'),
default => parent::render(),
};
}
}
<a {{ $attributes->merge(['href' => $model->dynamicLink()]) }}>{{ $slot }}</a>
- Use Your Custom View Component
With your custom view component in place, you can now use it in your Blade templates instead of the one provided by the package:
<x-dynamic-link :model="$modelReferencingAnchorTag">
My anchor tag
</x-dynamic-link>
composer test
The MIT License (MIT). Please see License File for more information.