Weave 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

Path Generator Laravel Package

da-vinci-studio/path-generator

Generate consistent file and directory paths in your Laravel app with configurable patterns and helpers. Useful for organizing uploads, storage, and assets by date, model, or custom rules, keeping paths predictable and easy to change later.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require da-vinci-studio/path-generator
    

    Add to composer.json if not auto-loaded:

    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "DavinciStudio\\PathGenerator\\": "vendor/da-vinci-studio/path-generator/src/"
        }
    }
    

    Run composer dump-autoload.

  2. Basic Usage:

    use DavinciStudio\PathGenerator\PathGenerator;
    
    $generator = new PathGenerator();
    $path = $generator->generate('app/storage/logs/{filename}.log');
    

    Replace {filename} with a dynamic value:

    $path = $generator->generate('app/storage/logs/{filename}.log', ['filename' => 'app']);
    // Output: "app/storage/logs/app.log"
    
  3. First Use Case: Generate filesystem paths dynamically for:

    • User uploads (app/storage/uploads/{user_id}/{filename}.{ext})
    • Log files (storage/logs/{service}.{date}.log)
    • Config overrides (config/{env}.php)

Implementation Patterns

Core Workflows

  1. Dynamic Path Generation:

    // Generate a path with placeholders
    $path = $generator->generate('app/storage/{type}/{id}/{filename}.{ext}', [
        'type' => 'uploads',
        'id' => 123,
        'filename' => 'profile',
        'ext' => 'png'
    ]);
    // Output: "app/storage/uploads/123/profile.png"
    
  2. Integration with Laravel:

    • Service Provider Binding:

      // app/Providers/AppServiceProvider.php
      public function register()
      {
          $this->app->singleton(PathGenerator::class, function ($app) {
              return new PathGenerator();
          });
      }
      

      Inject via constructor:

      public function __construct(private PathGenerator $pathGenerator) {}
      
    • Helper Function:

      // app/Helpers/path.php
      if (!function_exists('generate_path')) {
          function generate_path(string $template, array $data = []): string
          {
              return app(PathGenerator::class)->generate($template, $data);
          }
      }
      

      Usage:

      $path = generate_path('app/storage/{type}/{id}.json', ['type' => 'cache', 'id' => 1]);
      
  3. Path Validation:

    // Check if path is valid before generating files
    if ($generator->isValid('app/storage/{type}/{id}', ['type' => 'uploads', 'id' => 123])) {
        $path = $generator->generate('app/storage/{type}/{id}', ['type' => 'uploads', 'id' => 123]);
        Storage::put($path, $content);
    }
    
  4. Environment-Specific Paths:

    $path = $generator->generate(
        env('APP_ENV') === 'production'
            ? 'app/storage/prod/{filename}.log'
            : 'app/storage/dev/{filename}.log',
        ['filename' => 'debug']
    );
    

Advanced Patterns

  1. Path Templates as Config: Store templates in config/path_templates.php:

    return [
        'uploads' => 'app/storage/uploads/{user_id}/{filename}.{ext}',
        'logs' => 'storage/logs/{service}.{date}.log',
    ];
    

    Usage:

    $template = config('path_templates.uploads');
    $path = $generator->generate($template, ['user_id' => 1, 'filename' => 'avatar', 'ext' => 'jpg']);
    
  2. Path Generation in Controllers:

    public function store(Request $request)
    {
        $request->validate(['file' => 'required|file']);
        $file = $request->file('file');
    
        $path = $this->pathGenerator->generate(
            'app/storage/uploads/{user_id}/{filename}.{ext}',
            [
                'user_id' => auth()->id(),
                'filename' => Str::slug($file->getClientOriginalName()),
                'ext' => $file->getClientOriginalExtension(),
            ]
        );
    
        $file->storeAs('public', basename($path));
    }
    
  3. Path Generation in Models:

    class Upload extends Model
    {
        public function getPathAttribute(): string
        {
            return app(PathGenerator::class)->generate(
                'app/storage/uploads/{user_id}/{filename}.{ext}',
                [
                    'user_id' => $this->user_id,
                    'filename' => $this->filename,
                    'ext' => $this->extension,
                ]
            );
        }
    }
    
  4. Path Generation in Blade:

    // Add to Composer autoload (if not already)
    // composer.json: "autoload": { "files": ["app/Helpers/path.php"] }
    

    Blade usage:

    <img src="{{ generate_path('app/storage/uploads/{user_id}/{filename}.{ext}', [
        'user_id' => $user->id,
        'filename' => $upload->filename,
        'ext' => $upload->extension
    ]) }}">
    

Gotchas and Tips

Pitfalls

  1. Placeholder Case Sensitivity:

    • Templates are case-sensitive. {Filename} ≠ {filename}.
    • Fix: Standardize placeholder naming (e.g., snake_case).
  2. Missing Placeholders:

    • If a placeholder in the template isn’t provided in the data array, the path will include the literal placeholder (e.g., {filename}).
    • Fix: Validate placeholders exist in data:
      $requiredPlaceholders = ['filename', 'ext'];
      $missing = array_diff($requiredPlaceholders, array_keys($data));
      if (!empty($missing)) {
          throw new \InvalidArgumentException("Missing placeholders: " . implode(', ', $missing));
      }
      
  3. Path Traversal Risks:

    • Malicious input could inject ../ to escape directories.
    • Fix: Sanitize inputs or use realpath() to resolve paths:
      $path = $generator->generate($template, $data);
      $resolvedPath = realpath($path);
      if ($resolvedPath === false || strpos($resolvedPath, $path) !== 0) {
          throw new \RuntimeException("Invalid path resolution");
      }
      
  4. Deprecated Methods:

    • The package is outdated (last release 2016). Assume no active maintenance.
    • Fix: Fork and extend if critical features are missing.

Debugging Tips

  1. Enable Debug Output:

    $generator->setDebug(true);
    $path = $generator->generate('app/{type}/{id}', ['type' => 'storage', 'id' => 1]);
    // Outputs: "Replaced placeholders: ['type' => 'storage', 'id' => '1']"
    
  2. Log Generated Paths:

    $path = $generator->generate($template, $data);
    \Log::debug('Generated path', ['template' => $template, 'data' => $data, 'path' => $path]);
    
  3. Test Edge Cases:

    • Empty data array:
      $generator->generate('app/{type}', []); // Output: "app/{type}"
      
    • Nested placeholders:
      $generator->generate('app/{folder}/{subfolder}/{filename}', [
          'folder' => 'storage/{type}',
          'subfolder' => 'uploads',
          'filename' => 'file.txt',
          'type' => 'user'
      ]);
      // Output: "app/storage/user/uploads/file.txt"
      
      Note: Nested placeholders are resolved literally (not recursively).

Extension Points

  1. Custom Placeholder Handlers: Override placeholder logic by extending the class:

    class CustomPathGenerator extends \DavinciStudio\PathGenerator\PathGenerator
    {
        protected function replacePlaceholder(string $placeholder, string $value): string
        {
            // Custom logic (e.g., URL-encode values)
            return str_replace(' ', '-', $value);
        }
    }
    
  2. Add Path Validation Rules: Extend to validate paths against a whitelist:

    class ValidatedPathGenerator extends \DavinciStudio\PathGenerator\PathGenerator
    {
        private $allowedPaths = ['app/storage/uploads', 'app/storage/logs'];
    
        public function generate(string $template, array $data = []): string
        {
            $path = parent::generate($template, $data);
            $resolvedPath = realpath($path);
            $basePath = realpath(dirname($path));
    
            foreach ($this->allowedPaths as $allowed) {
                if (strpos($resolvedPath, $allowed) ===
    
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.
andydefer/laravel-notification
adachsoft/cli-process-tester
jhaskell13/laraflow
sebastienheyd/boilerplate-media-manager
ccast/tagixo-primix
pretendtrue/laravel-builder
sawmainek/laravel-scout-tntsearch-driver
tomshaw/laravel-calendar-table
dibakar/laravel-ownership
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc