Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Linkable Laravel Package

novius/laravel-linkable

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:
    composer require novius/laravel-linkable
    
  2. Publish optional configs (if needed for customization):
    php artisan vendor:publish --provider="Novius\LaravelLinkable\LaravelLinkableServiceProvider" --tag=config
    php artisan vendor:publish --provider="Novius\LaravelLinkable\LaravelLinkableServiceProvider" --tag=lang
    
  3. Apply the trait to a model (e.g., Post):
    use Novius\LaravelLinkable\Traits\Linkable;
    
    class Post extends Model {
        use Linkable;
    
        public function linkableConfig(): LinkableConfig {
            return new LinkableConfig(
                routeName: 'post.show',
                routeParameterName: 'post',
                optionLabel: 'title',
                optionGroup: 'Content'
            );
        }
    }
    
  4. Define the route in routes/web.php:
    Route::get('/posts/{post}', [PostController::class, 'show'])->name('post.show');
    
  5. Use the url() method in your code:
    $post = Post::first();
    echo $post->url(); // Generates `/posts/1`
    

First Use Case: Linking Posts to Categories

// In a Nova resource or controller:
$post = Post::find(1);
$post->linkable()->attach(Category::find(5)); // Links post to category

Now, $post->url() will include the category context if configured.


Implementation Patterns

Core Workflows

1. Model Linking

  • Attach/Detach Links:
    $model->linkable()->attach($linkedModel); // One-to-one or many
    $model->linkable()->detach($linkedModel);
    
  • Query Linked Models:
    $linkedModels = $model->linkable()->get(); // All linked models
    $linkedModels = $model->linkable()->where('category_id', 1)->get(); // Filtered
    

2. URL Generation

  • Dynamic URLs with Callbacks:
    public function linkableConfig(): LinkableConfig {
        return new LinkableConfig(
            getUrlCallback: fn (Model $model) => route('post.details', ['post' => $model, 'tab' => 'comments'])
        );
    }
    
  • Localized URLs (if using spatie/laravel-localization):
    $post->url('fr'); // Generates `/fr/posts/1`
    

3. Nova/Filament Integration

  • Nova Field:
    Linkable::make('Related Post')
        ->optionsClasses([Post::class, Category::class])
        ->rules(['required', 'exists:posts,id']);
    
  • Filament Form:
    Linkable::make('Featured Image')
        ->setLinkableClasses([Media::class])
        ->setLocale(request()->locale);
    

4. Custom Query Scoping

  • Filter Links in Nova:
    public function linkableConfig(): LinkableConfig {
        return new LinkableConfig(
            optionsQuery: fn (Builder $query) => $query->where('published', true)
        );
    }
    
  • Resolve Links via API:
    $model = Linkable::resolve($linkableId); // Resolves via `resolveQuery`
    

Integration Tips

  1. Multi-Tenancy: Override resolveQuery to scope by tenant:
    resolveQuery: fn (Builder $query) => $query->where('tenant_id', auth()->tenant()->id),
    
  2. Soft Deletes: Ensure optionsQuery includes withTrashed() if needed:
    optionsQuery: fn (Builder $query) => $query->withTrashed()->where('active', true),
    
  3. Caching: Cache linkableConfig() if models are static:
    protected static function boot() {
        parent::boot();
        static::addGlobalScope('cacheConfig', fn (Builder $builder) => $builder->cachedConfig());
    }
    
  4. Testing: Mock the Linkable facade for unit tests:
    $this->mock(Linkable::class)->shouldReceive('getLink')->andReturn('/mocked-url');
    

Gotchas and Tips

Pitfalls

  1. Route Name Mismatches:

    • Issue: routeName/routeParameterName in linkableConfig() must match your route definitions exactly.
    • Fix: Verify routes with php artisan route:list and use route('route.name') to test.
  2. Locale Conflicts:

    • Issue: If disable_localization: true in config but getUrlCallback uses $model->locale, URLs may break.
    • Fix: Ensure consistency between config and model methods:
      public function getLocale() {
          return $this->locale ?? app()->getLocale();
      }
      
  3. Circular References:

    • Issue: Linking ModelA to ModelB and vice versa can cause infinite loops in queries.
    • Fix: Add a depth parameter to queries:
      $model->linkable()->whereDepth('<', 3)->get();
      
  4. Nova Field Performance:

    • Issue: Large datasets in Linkable::make() cause slow admin panel loads.
    • Fix: Limit options with optionsQuery:
      Linkable::make('Author')
          ->optionsQuery(fn (Builder $query) => $query->where('is_active', true)->limit(100))
      
  5. AGPL License:

    • Issue: AGPL may require open-sourcing your entire project if you modify/distribute the package.
    • Fix: Fork the package and relicense if proprietary code is involved.

Debugging

  1. URL Generation Failures:

    • Check Linkable::getLink($model) directly to isolate issues:
      dd(Linkable::getLink($model)); // Debug raw output
      
    • Ensure routeName exists:
      php artisan route:list | grep 'post.show'
      
  2. Linkable Field Not Showing Options:

    • Verify optionLabel is a valid column or closure:
      optionLabel: fn (Model $model) => "{$model->title} (#{$model->id})",
      
    • Check optionGroup is unique across models.
  3. Preview Token Issues:

    • If previewUrl() fails, ensure:
      • preview_token field exists in the database.
      • The guard_preview config matches your auth guard.

Extension Points

  1. Custom Link Types: Add support for non-model links (e.g., external URLs):

    // In config/linkable.php
    'linkable_routes' => [
        'external' => 'https://example.com/{id}',
    ];
    

    Then use in Nova:

    Linkable::make('External Link')->optionsClasses(['external']),
    
  2. Validation Rules: Extend the trait to add validation:

    use Illuminate\Validation\Rule;
    
    public function linkableConfig(): LinkableConfig {
        return new LinkableConfig(
            validateCallback: fn (Model $model, $linkedModel) => [
                Rule::unique('post_linkables', 'linked_id')
                    ->where('linkable_id', $model->id)
                    ->ignore($model->linkable()->getKey()),
            ]
        );
    }
    
  3. GraphQL Integration: Create a custom resolver for Laravel GraphQL:

    yield GraphQL::type('Linkable')
        ->resolve(fn ($root, $args) => Linkable::getLink($root->link));
    
  4. Bulk Operations: Add a syncLinkable() method to your model:

    public function syncLinkable(array $ids) {
        $this->linkable()->sync($ids);
    }
    

Config Quirks

  1. Autoloading Models:

    • The autoload_models_in config scans for models using the Linkable trait. If a model is missed:
      • Add it manually to linkable_models in config.
      • Or move it to the directory specified in autoload_models_in.
  2. Route Callback Overrides:

    • If setRouteCallback in the service provider doesn’t work, ensure it’s called after the booted event:
      $this->app->booted(fn () => Linkable::setRouteCallback(...));
      
  3. Locale Fallbacks:

    • If disable_localization: false but routes lack locale support, URLs may fail. Test with:
      $post->url('en'); // Force locale
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle