skip to Main Content

I am developing a package but somehow I am not able to make Laravel to discover it.

Package is on the root of the app and follows the next structure:

Package structure

Files:

StatusManager.php

<?php

namespace TroyerxStatusManager;

trait StatusManager {
  //Code
}

Composer.json in packages/troyerx/statusmanager/

{
    "name": "troyerx/statusmanager",
    "description": "Simple model status manager",
    "license": "MIT",
    "keywords": [
        "troyerx",
        "php",
        "statusmanager"
    ],
    "authors": [
        {
            "name": "Sergio",
            "email": "[email protected]"
        }
    ],
    "autoload": {
        "psr-4": {
            "Troyerx\Statusmanager\": "src/"
        }
    },
    "require": {}
}

Mi app composer.json looks like:

{
    "name": "laravel/laravel",
    "type": "project",
    "description": "The Laravel Framework.",
    "keywords": ["framework", "laravel"],
    "license": "MIT",
    "require": {
        "php": "^8.1",
        "guzzlehttp/guzzle": "^7.2",
        "laravel/framework": "^10.0",
        "laravel/sanctum": "^3.2",
        "laravel/tinker": "^2.8"
    },
    "require-dev": {
        "fakerphp/faker": "^1.9.1",
        "laravel/pint": "^1.0",
        "laravel/sail": "^1.18",
        "mockery/mockery": "^1.4.4",
        "nunomaduro/collision": "^7.0",
        "phpunit/phpunit": "^10.0",
        "spatie/laravel-ignition": "^2.0"
    },
    "autoload": {
        "psr-4": {
            "App\": "app/",
            "Troyerx\Statusmanager\": "packages/troyerx/statusmanager/src/",
            "Database\Factories\": "database/factories/",
            "Database\Seeders\": "database/seeders/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Troyerx\Statusmanager\": "packages/troyerx/statusmanager/src/",
            "Tests\": "tests/"
        }
    },
    "scripts": {
        "post-autoload-dump": [
            "Illuminate\Foundation\ComposerScripts::postAutoloadDump",
            "@php artisan package:discover --ansi"
        ],
        "post-update-cmd": [
            "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
        ],
        "post-root-package-install": [
            "@php -r "file_exists('.env') || copy('.env.example', '.env');""
        ],
        "post-create-project-cmd": [
            "@php artisan key:generate --ansi"
        ]
    },
    "extra": {
        "laravel": {
            "dont-discover": []
        }
    },
    "config": {
        "optimize-autoloader": true,
        "preferred-install": "dist",
        "sort-packages": true,
        "allow-plugins": {
            "pestphp/pest-plugin": true,
            "php-http/discovery": true
        }
    },
    "minimum-stability": "stable",
    "prefer-stable": true
}

Since the package is marked to be discover on the autoload, I should expect to get it included when I run:

composer dump-autoload

But I get the following prompt:

Generating optimized autoload files
> IlluminateFoundationComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi

   INFO  Discovering packages.  

  laravel/sail ................................................................................................................................ DONE
  laravel/sanctum ............................................................................................................................. DONE
  laravel/tinker .............................................................................................................................. DONE
  nesbot/carbon ............................................................................................................................... DONE
  nunomaduro/collision ........................................................................................................................ DONE
  nunomaduro/termwind ......................................................................................................................... DONE
  spatie/laravel-ignition ..................................................................................................................... DONE

Generated optimized autoload files containing 5729 classes

If I call for the trait in a model its not autoloaded

<?php

namespace AppModels;
use IlluminateDatabaseEloquentModel;

use TroyerxStatusManagerStatusManager as StatusManager;

class Order extends Model
{
    use StatusManager;
}

Gives next error:

Trait "TroyerxStatusManagerStatusManager" not found

Why Laravel its not loading my package? Which step I am missing or where is the problem?

2

Answers


  1. Chosen as BEST ANSWER

    I have found the solution, to make Laravel to discover/install my package when I run composer dump-autoload.

    In the app composer.json I added:

    "repositories": [
        {
            "type": "path",
            "url": "packages/troyerx/statusmanager",
            "options": {
                "symlink": true
            }
        }
    ],
    

    And added in the require-dev the next line:

    "require-dev": {
        ...
        "troyerx/statusmanager": "@dev"
    },
    

    After running composer update the package is discover:

    INFO  Discovering packages.  
    
    laravel/sail ................................................................................................................................ DONE
    laravel/sanctum ............................................................................................................................. DONE
    laravel/tinker .............................................................................................................................. DONE
    nesbot/carbon ............................................................................................................................... DONE
    nunomaduro/collision ........................................................................................................................ DONE
    nunomaduro/termwind ......................................................................................................................... DONE
    spatie/laravel-ignition ..................................................................................................................... DONE
    troyerx/statusmanager ....................................................................................................................... DONE
    

  2. So, your package’s composer.json should be like this:

    {
        "name": "troyerx/statusmanager",
        "description": "Simple model status manager",
        "license": "MIT",
        "version": "1.0.0",
        "keywords": [
            "troyerx",
            "php",
            "statusmanager"
        ],
        "authors": [
            {
                "name": "Sergio",
                "email": "[email protected]"
            }
        ],
        "require": {
            "laravel/framework": "^10.0"
        },
        "autoload": {
            "psr-4": {
                "Troyerx\Statusmanager\": "src/"
            }
        }
    }
    

    See that I have:

    • Added a require (Laravel Framework), so it depends on Laravel (if it is pure PHP, remove it)
    • I have also added version, so you can keep track of version and it is easier

    Your main composer.json should be like this (I will list the sections I have changed or added):

    {
        "name": "laravel/laravel",
        "type": "project",
        "description": "The Laravel Framework.",
        "keywords": ["framework", "laravel"],
        "license": "MIT",
        "require": {
            "php": "^8.1",
            "guzzlehttp/guzzle": "^7.2",
            "laravel/framework": "^10.0",
            "laravel/sanctum": "^3.2",
            "laravel/tinker": "^2.8",
            "troyerx/statusmanager": "^1.0"
        },
        "require-dev": {
            "fakerphp/faker": "^1.9.1",
            "laravel/pint": "^1.0",
            "laravel/sail": "^1.18",
            "mockery/mockery": "^1.4.4",
            "nunomaduro/collision": "^7.0",
            "phpunit/phpunit": "^10.0",
            "spatie/laravel-ignition": "^2.0"
        },
        "autoload": {
            "psr-4": {
                "App\": "app/",
                "Database\Factories\": "database/factories/",
                "Database\Seeders\": "database/seeders/"
            }
        },
        "autoload-dev": {
            "psr-4": {
                "Tests\": "tests/"
            }
        },
        "repositories": [
            {
                "type": "path",
                "url": "packages/"
            },
        ],
        "scripts": {
            "post-autoload-dump": [
                "Illuminate\Foundation\ComposerScripts::postAutoloadDump",
                "@php artisan package:discover --ansi"
            ],
            "post-update-cmd": [
                "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
            ],
            "post-root-package-install": [
                "@php -r "file_exists('.env') || copy('.env.example', '.env');""
            ],
            "post-create-project-cmd": [
                "@php artisan key:generate --ansi"
            ]
        },
        "extra": {
            "laravel": {
                "dont-discover": []
            }
        },
        "config": {
            "optimize-autoloader": true,
            "preferred-install": "dist",
            "sort-packages": true,
            "allow-plugins": {
                "pestphp/pest-plugin": true,
                "php-http/discovery": true
            }
        },
        "minimum-stability": "stable",
        "prefer-stable": true
    }
    

    I have:

    • Removed your custom namespace from autoload and autoload-dev
    • Added a normal require with a version constraint
    • Added repositories section in the file linking to what folder it should look it up

    So, first edit your package’s composer file, then add the repositories to your main composer file, and then use the command line to do composer require troyerx/statusmanager:^1.0 and done, it will automatically be picked.

    Remember to bump the package’s composer version (version section) to a new version, and then just run composer update troyerx/statusmanager in your main composer and done!

    This is the composer documentation I have used: https://getcomposer.org/doc/05-repositories.md#path


    One thing to have in mind: if you are developing a Laravel package, you should not have database and factories inside src, but outside, and then have that package’s composer.json like this:

    {
        "name": "troyerx/statusmanager",
        "description": "Simple model status manager",
        "license": "MIT",
        "version": "1.0.0",
        "keywords": [
            "troyerx",
            "php",
            "statusmanager"
        ],
        "authors": [
            {
                "name": "Sergio",
                "email": "[email protected]"
            }
        ],
        "require": {
            "laravel/framework": "^10.0"
        },
        "autoload": {
            "psr-4": {
                "Troyerx\Statusmanager\": "src/",
                "Troyerx\Statusmanager\Database\Factories\": "database/factories/",
                "Troyerx\Statusmanager\Database\Seeders\": "database/seeders/"
            }
        }
    }
    

    You need to have your own ServiceProvider and add it to the package’s composer extra -> laravel section, like this: https://github.com/barryvdh/laravel-ide-helper/blob/4dc20b028adaeadf603860f179f09da5aa21f5b5/composer.json#L70-L74

    Use that development package as guide on how to implement your own package, it should look like very similar (at least with your files, but structure wise).

    Use Laravel Jetstream (for example), to see how to load the migrations. Do follow it as an example.

    Have in mind that all Laravel packages are like those 2, so if you know a package, go to github and see the structure is has and what it has in composer.json, etc. Follow that!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search