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 Markable Laravel Package

maize-tech/laravel-markable

Add likes, bookmarks, favorites, reactions and more to Laravel models with a simple “markable” system. Includes install command, configurable user model and table prefix, and optional publishable migrations per mark type for quick setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require maize-tech/laravel-markable
    php artisan markable:install
    

    Publish only the migrations you need (e.g., bookmark, like, reaction):

    php artisan vendor:publish --tag="markable-migration-like"
    php artisan migrate
    
  2. Configure User Model: Update config/markable.php to specify your user model:

    'user_model' => App\Models\User::class,
    
  3. First Use Case: Add the Markable trait to a model (e.g., Post) and define supported marks:

    use Maize\Markable\Markable;
    use Maize\Markable\Models\Like;
    
    class Post extends Model
    {
        use Markable;
    
        protected static $marks = [
            Like::class,
        ];
    }
    
  4. Basic Usage:

    $post = Post::first();
    $user = auth()->user();
    
    Like::add($post, $user);       // Add like
    Like::toggle($post, $user);    // Toggle like
    Like::has($post, $user);       // Check if liked
    Like::count($post);            // Count likes
    

Implementation Patterns

Core Workflows

  1. Model Integration:

    • Use the Markable trait in Eloquent models to enable marks.
    • Define supported marks in $marks (e.g., Like, Bookmark, custom marks).
  2. Mark Management:

    • Add/Remove/Toggle: Use static methods (Like::add(), Like::remove(), Like::toggle()).
    • Metadata: Pass custom metadata (e.g., Like::add($post, $user, ['topic' => 'Laravel'])).
  3. Querying Marks:

    • Count Marks: Like::count($post).
    • Check Existence: Like::has($post, $user).
    • Dynamic Scopes: Use Eloquent scopes like Post::whereHasLike($user).
  4. Custom Marks:

    • Extend Maize\Markable\Mark and override markableRelationName()/markRelationName().
    • Example:
      class Bookmark extends Mark {
          public static function markableRelationName(): string { return 'bookmarkers'; }
      }
      
  5. Value-Based Marks (e.g., Reactions):

    • Configure allowed values in config/markable.php:
      'allowed_values' => [
          'reaction' => ['heart', 'thumbs-up'],
      ],
      
    • Use methods like Reaction::add($post, $user, 'heart').
  6. BackedEnum Support:

    • Define an enum (e.g., ReactionType) and set it in config:
      'allowed_values' => [
          'reaction' => \App\Enums\ReactionType::class,
      ],
      
    • Pass enum cases directly:
      Reaction::add($post, $user, ReactionType::Heart);
      

Integration Tips

  • API Endpoints: Create routes for mark actions (e.g., POST /posts/{post}/like). Example controller:

    public function like(Post $post) {
        Like::toggle($post, auth()->user());
        return response()->json(['status' => 'success']);
    }
    
  • Frontend Integration: Use JavaScript to toggle marks (e.g., fetch /posts/{post}/like). Example Blade:

    <button onclick="toggleLike({{ $post->id }})">
        {{ Like::has($post, auth()->user()) ? 'Unlike' : 'Like' }}
    </button>
    
  • Eager Loading: Optimize queries with with():

    Post::with(['likes', 'reacters'])->get();
    
  • Caching: Cache mark counts/metadata (e.g., Cache::remember()) for performance.


Gotchas and Tips

Pitfalls

  1. Migration Conflicts:

    • Ensure table names match the table_prefix in config (default: markable_).
    • Avoid naming conflicts with existing tables.
  2. Null User Checks:

    • Always validate $user before calling mark methods (e.g., auth()->user() may return null).
  3. Value Validation:

    • If allowed_values is configured, passing invalid values (e.g., Reaction::add($post, $user, 'invalid')) will throw an exception.
    • Wildcard (*) allows any value but bypasses validation.
  4. Enum Type Safety:

    • When using BackedEnum, ensure the enum’s backing type matches the database column (e.g., string for value column).
  5. Relation Naming:

    • Custom markableRelationName() must match the pivot table’s relation name in the model.
    • Default relation names are pluralized (e.g., Likelikes).
  6. Mass Assignment:

    • Avoid mass-assigning marks directly to models. Use the package’s static methods.

Debugging

  • Query Logs: Enable Laravel’s query logging to debug pivot table queries:

    DB::enableQueryLog();
    Like::add($post, $user);
    dd(DB::getQueryLog());
    
  • Common Errors:

    • ClassNotFound: Ensure mark classes (e.g., Like) are autoloaded.
    • TableNotFound: Run migrations or check table_prefix.
    • CallToUndefinedMethod: Verify relation names in custom marks.

Extension Points

  1. Custom Mark Logic: Override methods in Maize\Markable\Mark (e.g., boot() for model events).

  2. Event Listeners: Listen for mark events (e.g., MarkAdded, MarkRemoved) via Laravel’s event system.

  3. Policy Integration: Use Laravel’s policies to authorize mark actions:

    public function toggleLike(User $user, Post $post) {
        $this->authorize('like', $post);
        Like::toggle($post, $user);
    }
    
  4. Testing: Use Markable trait in tests:

    public function test_like_toggle() {
        $post = new Post();
        $user = new User();
        Like::toggle($post, $user);
        $this->assertTrue(Like::has($post, $user));
    }
    
  5. Performance:

    • Batch Operations: Use chunk() for bulk mark updates.
    • Indexing: Add indexes to user_id and markable_id in pivot tables.

Configuration Quirks

  • Table Prefix: Changing table_prefix after initial setup requires updating all migrations and existing data.

  • Allowed Values: Empty arrays ([]) disable values entirely. Use * for wildcards.

  • User Model: Ensure the configured user_model implements Illuminate\Contracts\Auth\Authenticatable.

Pro Tips

  1. Dynamic Mark Registration: Register marks dynamically in a service provider:

    public function boot() {
        if (!app()->runningInConsole()) {
            Mark::extend('DynamicMark', function () {
                return new class extends Mark { /* ... */ };
            });
        }
    }
    
  2. Soft Deletes: Extend marks to support soft deletes by adding SoftDeletes trait to the Mark model.

  3. API Resources: Create custom resources for marks (e.g., LikeResource) to shape responses:

    public function toArray($request) {
        return [
            'user' => $this->user,
            'metadata' => $this->metadata,
        ];
    }
    
  4. Validation: Validate mark values in Form Requests:

    public function rules() {
        return [
            'reaction' => ['required', Rule::in(['heart', 'thumbs-up'])],
        ];
    }
    
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.
codraw/framework-extra-bundle
codraw/messenger
codraw/security
codraw/mailer
codraw/contracts
codraw/profiling
codraw/dependency-injection
codraw/tester
codraw/core
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony