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

Constrained Morph To For Laravel Laravel Package

pindab0ter/constrained-morph-to-for-laravel

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require pindab0ter/constrained-morph-to-for-laravel
    

    No publisher or service provider required—just use the trait directly.

  2. First Use Case: Define a constrained polymorphic relationship in a model:

    use Pindab0ter\ConstrainedMorphTo\HasConstrainedMorphTo;
    
    class Comment extends Model
    {
        use HasConstrainedMorphTo;
    
        public function commentable(): HasConstrainedMorphTo
        {
            return $this->constrainedMorphTo(
                'commentable',
                [Post::class, User::class] // Only allow Post or User
            );
        }
    }
    
  3. Key Files to Review:

    • src/HasConstrainedMorphTo.php (Core trait)
    • src/ConstrainedMorphTo.php (Relationship definition)
    • tests/ (Usage examples and edge cases)

Implementation Patterns

Core Workflow

  1. Define Constraints:

    // Allow only specific models
    $this->constrainedMorphTo('polymorphic_name', [ModelA::class, ModelB::class]);
    
  2. Accessing Relationships:

    $comment = Comment::find(1);
    $commentable = $comment->commentable; // Returns Post or User, or null if invalid
    
  3. Dynamic Constraints:

    // Use a closure for dynamic constraints (e.g., based on user role)
    $this->constrainedMorphTo('polymorphic_name', function () {
        return auth()->user()->canAccess() ? [Post::class, Video::class] : [Post::class];
    });
    

Integration Tips

  • Migrations: Ensure your morphs table columns (polymorphic_type, polymorphic_id) exist.
  • Casting: Use morphMap in AppServiceProvider if you need custom type aliases:
    public function boot()
    {
        $this->app['router']->morphMap = [
            'post' => Post::class,
            'user' => User::class,
        ];
    }
    
  • API Responses: Serialize constrained relationships in JSON:
    return CommentResource::make($comment)->additional([
        'commentable' => $comment->commentable?->toArray()
    ]);
    

Common Use Cases

  1. Strict Polymorphic Relations:
    // Only allow 'Post' or 'Video' as commentable
    $this->constrainedMorphTo('commentable', [Post::class, Video::class]);
    
  2. Conditional Constraints:
    // Admin users can attach to more types
    $constraints = auth()->user()->isAdmin()
        ? [Post::class, User::class, Media::class]
        : [Post::class, User::class];
    $this->constrainedMorphTo('attachable', $constraints);
    
  3. Fallback Behavior:
    // Set a default model if the constraint fails
    $this->constrainedMorphTo('fallback', [Post::class], Post::class);
    

Gotchas and Tips

Pitfalls

  1. Runtime Errors:

    • If a saved polymorphic relationship violates constraints, the package throws an exception (not silently fails).
    • Fix: Validate constraints before saving or use try-catch:
      try {
          $comment->commentable()->associate($invalidModel);
          $comment->save(); // Throws Pindab0ter\ConstrainedMorphTo\Exceptions\InvalidMorphType
      } catch (\Exception $e) {
          // Handle error (e.g., log or redirect)
      }
      
  2. Caching Issues:

    • Constraints defined as closures are evaluated per request. Avoid expensive logic inside them.
    • Tip: Cache results if constraints depend on external data:
      $constraints = Cache::remember("user_{$user->id}_constraints", now()->addHours(1), function () use ($user) {
          return $user->getAllowedModels();
      });
      
  3. Morph Map Conflicts:

    • If using Laravel’s morphMap, ensure your constrained models are included in the map.
    • Tip: Override getMorphClass() in constrained models if needed:
      class Post extends Model
      {
          public function getMorphClass()
          {
              return 'post'; // Custom alias
          }
      }
      

Debugging

  1. Check Constraints:
    • Log constraints during development:
      $this->constrainedMorphTo('polymorphic_name', [Post::class], function () {
          \Log::debug('Constraints:', [Post::class, User::class]);
          return [Post::class, User::class];
      });
      
  2. Validate Saved Data:
    • Use dd($model->commentable) to inspect the loaded relationship and verify its type.

Extension Points

  1. Custom Exceptions:

    • Extend the base exception for your app’s needs:
      use Pindab0ter\ConstrainedMorphTo\Exceptions\InvalidMorphType;
      
      class CustomInvalidMorphException extends InvalidMorphType
      {
          // Add custom logic
      }
      
    • Override the exception class in the trait:
      use HasConstrainedMorphTo {
          HasConstrainedMorphTo::save as traitSave;
      }
      
      public function save(array $options = [])
      {
          try {
              $this->traitSave($options);
          } catch (InvalidMorphType $e) {
              throw new CustomInvalidMorphException($e->getMessage());
          }
      }
      
  2. Add Metadata:

    • Store constraint metadata in the model:
      protected $constraintMetadata = [
          'commentable' => ['allowed' => [Post::class, User::class], 'last_validated' => null],
      ];
      
    • Validate constraints in boot():
      public static function boot()
      {
          parent::boot();
          static::saved(function ($model) {
              foreach ($model->constraintMetadata as $relation => $meta) {
                  if (!$model->{$relation} instanceof $meta['allowed']) {
                      // Handle violation
                  }
              }
          });
      }
      
  3. Performance:

    • For large-scale apps, preload constraints:
      // In a service or repository
      public function getConstraintsFor($model)
      {
          return Cache::remember("constraints_{$model->id}", now()->addMinutes(60), function () use ($model) {
              return $model->getAllowedConstraints();
          });
      }
      
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.
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
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope