🆙 Add cms i using 🆙

This commit is contained in:
Remco
2025-11-25 22:42:56 +01:00
parent 94704e0925
commit d44196149e
35591 changed files with 3601123 additions and 0 deletions
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) spatie <freek@spatie.be>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@@ -0,0 +1,684 @@
<div align="left">
<a href="https://spatie.be/open-source?utm_source=github&utm_medium=banner&utm_campaign=laravel-package-tools">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://spatie.be/packages/header/laravel-package-tools/html/dark.webp">
<img alt="Logo for laravel-package-tools" src="https://spatie.be/packages/header/laravel-package-tools/html/light.webp">
</picture>
</a>
<h1>Tools for creating Laravel packages</h1>
[![Latest Version on Packagist](https://img.shields.io/packagist/v/spatie/laravel-package-tools.svg?style=flat-square)](https://packagist.org/packages/spatie/laravel-package-tools)
![Tests](https://github.com/spatie/laravel-package-tools/workflows/Tests/badge.svg)
[![Total Downloads](https://img.shields.io/packagist/dt/spatie/laravel-package-tools.svg?style=flat-square)](https://packagist.org/packages/spatie/laravel-package-tools)
</div>
This package contains a `PackageServiceProvider` that you can use in your packages to easily register config files,
migrations, and more.
Here's an example of how it can be used.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
use MyPackage\ViewComponents\Alert;
use Spatie\LaravelPackageTools\Commands\Concerns;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasConfigFile()
->hasViews()
->hasViewComponent('spatie', Alert::class)
->hasViewComposer('*', MyViewComposer::class)
->sharesDataWithAllViews('downloads', 3)
->hasTranslations()
->hasAssets()
->publishesServiceProvider('MyProviderName')
->hasRoute('web')
->hasMigration('create_package_tables')
->hasCommand(YourCoolPackageCommand::class)
->hasInstallCommand(function(InstallCommand $command) {
$command
->publishConfigFile()
->publishAssets()
->publishMigrations()
->copyAndRegisterServiceProviderInApp()
->askToStarRepoOnGitHub();
});
}
}
```
Under the hood it will do the necessary work to register the necessary things and make all sorts of files publishable.
## Support us
[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/laravel-package-tools.jpg?t=1" width="419px" />](https://spatie.be/github-ad-click/laravel-package-tools)
We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can
support us by [buying one of our paid products](https://spatie.be/open-source/support-us).
We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.
You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards
on [our virtual postcard wall](https://spatie.be/open-source/postcards).
## Getting started
This package is opinionated on how you should structure your package. To get started easily, consider
using [our package-skeleton repo](https://github.com/spatie/package-skeleton-laravel) to start your package. The
skeleton is structured perfectly to work perfectly with the `PackageServiceProvider` in this package.
## Usage
To avoid needing to scroll through to find the right usage section, here is a Table of Contents:
* [Directory Structure](#directory-structure)
* [Making your functionality publishable](#making-your-functionality-publishable)
* [Getting Started](#getting-started)
* [Assets](#assets)
* [Blade Components](#blade-view-components)
* [Blade Anonymous Components](#blade-anonymous-components)
* [Blade Custom Directives](#blade-custom-directives)
* [Blade Custom Echo Handlers](#blade-custom-echo-handlers)
* [Blade Custom Conditionals](#blade-custom-conditionals)
* [Commands - Callable and Console](#commands-callable-and-console)
* [Optimize Commands (Laravel v11+)](#optimize-commands)
* [Config Files](#config-files)
* [Inertia Components](#inertia-components)
* [Livewire Views and Components](#livewire-views-and-components)
* [Database Migrations](#database-migrations)
* [Routes](#routes)
* [Publishable Service Providers](#publishable-service-providers)
* [Translations](#translations)
* [Views](#views)
* [View Composers](#view-composers)
* [Views Global Shared Data](#views-global-shared-data)
* [Creating and Install Command](#creating-an-install-command)
* [Lifecycle Hooks](#lifecycle-hooks)
### Directory Structure
This package is opinionated on how you should structure your package,
and by default expects a structure based on
[our package-skeleton repo](https://github.com/spatie/package-skeleton-laravel),
and to get started easily you should consider using this to start your package.
The structure for a package expected by default looks like this:
```
<root>
<package root>/src/ Default location for PackageServiceProvider extended class
<package root>/src/Commands/ Commands (callable and console-only)
<package root>/src/Components/ Blade components
<package root>/src/Providers/ Other Service Providers
<package root>/config/ Mergeable and publishable config files
<package root>/database/factories/ Database factories
<package root>/database/migrations/ Publishable stubs and loadable migrations
<package root>/resources/dist/ Publishable assets
<package root>/resources/js/pages/ Inertia views
<package root>/resources/lang/ International translations
<package root>/resources/views/ Views
<package root>/routes/ Routes
```
Note: When using paths in any Package method except `discoversMigrations()`,
the path given is relative to the location of your primary Service Provider
i.e. relative to `<package root>/src`
so e.g. `<package root>/ConfigFiles` would be specified as `../ConfigFiles`.
### Getting Started
In your package you should let your service provider extend `Spatie\LaravelPackageTools\PackageServiceProvider`.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package) : void
{
$package->name('your-package-name');
}
}
```
Defining your package name with a call to `name()` is mandatory.
**Note:** If your package name starts with `laravel-` then this prefix will be omitted
and the remainder of the name used as a short-name instead when publishing files etc.
And now let's look at all the different Laravel functions this supports...
assing the package name to `name` is mandatory.
### Assets
Any assets your package provides, should be placed in the `<package root>/resources/dist/` directory.
You can make these assets publishable the `hasAssets` method.
```php
$package
->name('your-package-name')
->hasAssets();
```
Users of your package will be able to publish the assets with this command:
```bash
php artisan vendor:publish --tag=your-package-name-assets
```
This will copy over the assets to the `public/vendor/<your-package-name>` directory in the app where your package is
installed in.
### Blade view components
Any Blade view components that your package provides should be placed in the `<package root>/src/Components` directory.
You can register these views with the `hasViewComponents` command.
```php
$package
->name('your-package-name')
->hasViewComponents('spatie', Alert::class);
```
This will register your view components with Laravel. In the case of `Alert::class`, it can be referenced in views
as `<x-spatie-alert />`, where `spatie` is the prefix you provided during registration.
Calling `hasViewComponents` will also make view components publishable, and will be published
to `app/Views/Components/vendor/<package name>`.
Users of your package will be able to publish the view components with this command:
```bash
php artisan vendor:publish --tag=your-package-name-components
```
### Commands - Callable and Console
You can register any command you package provides with the `hasCommand` function.
```php
$package
->name('your-package-name')
->hasCommand(YourCoolPackageCommand::class);
````
If your package provides multiple commands, you can either use `hasCommand` multiple times, or pass an array
to `hasCommands`
```php
$package
->name('your-package-name')
->hasCommands([
YourCoolPackageCommand::class,
YourOtherCoolPackageCommand::class,
]);
```
### Config Files
To register a config file, you should create a php file with your package name in the `config` directory of your
package. In this example it should be at `<package root>/config/your-package-name.php`.
If your package name starts with `laravel-`, we expect that your config file does not contain that prefix. So if your
package name is `laravel-cool-package`, the config file should be named `cool-package.php`.
To register that config file, call `hasConfigFile()` on `$package` in the `configurePackage` method.
```php
$package
->name('your-package-name')
->hasConfigFile();
```
The `hasConfigFile` method will also make the config file publishable. Users of your package will be able to publish the
config file with this command.
```bash
php artisan vendor:publish --tag=your-package-name-config
```
Should your package have multiple config files, you can pass their names as an array to `hasConfigFile`
```php
$package
->name('your-package-name')
->hasConfigFile(['my-config-file', 'another-config-file']);
```
### Inertia Components
Any `.vue` or `.jsx` files your package provides, should be placed in the `<package root>/resources/js/Pages` directory.
You can register these components with the `hasInertiaComponents` command.
```php
$package
->name('your-package-name')
->hasInertiaComponents();
```
This will register your components with Laravel.
The user should publish the inertia components manually or using the [installer-command](#adding-an-installer-command) in order to use them.
If you have an inertia component `<package root>/resources/js/Pages/myComponent.vue`, you can use it like
this: `Inertia::render('YourPackageName/myComponent')`. Of course, you can also use subdirectories to organise your components.
#### Publishing inertia components
Calling `hasInertiaComponents` will also make inertia components publishable. Users of your package will be able to publish the views with this
command:
```bash
php artisan vendor:publish --tag=your-package-name-inertia-components
```
Also, the inertia components are available in a convenient way with your package [installer-command](#adding-an-installer-command)
### Working with migrations
The `PackageServiceProvider` assumes that any migrations are placed in this
directory: `<package root>/database/migrations`. Inside that directory you can put any migrations.
To register your migration, you should pass its name without the extension to the `hasMigration` table.
If your migration file is called `create_my_package_tables.php.stub` you can register them like this:
```php
$package
->name('your-package-name')
->hasMigration('create_my_package_tables');
```
Should your package contain multiple migration files, you can just call `hasMigration` multiple times or
use `hasMigrations`.
```php
$package
->name('your-package-name')
->hasMigrations(['my_package_tables', 'some_other_migration']);
```
Alternatively, if you wish to publish all migrations in your package by default, you may call `discoversMigrations`.
```php
$package
->name('your-package-name')
->discoversMigrations();
```
Calling this method will look for migrations in the `./database/migrations` directory of your project. However, if you have defined your migrations
in a different folder, you may pass a value to the `$path` variable to instruct the app to discover migrations from that location.
```php
$package
->name('your-package-name')
->discoversMigrations(path: '/path/to/your/migrations/folder');
```
Calling either `hasMigration`, `hasMigration` or `discoversMigrations` will also make migrations publishable. Users of your package will be able to publish the
migrations with this command:
```bash
php artisan vendor:publish --tag=your-package-name-migrations
```
Like you might expect, published migration files will be prefixed with the current datetime.
You can also enable the migrations to be registered without needing the users of your package to publish them:
```php
$package
->name('your-package-name')
->hasMigrations(['my_package_tables', 'some_other_migration'])
->runsMigrations();
```
### Routes
The `PackageServiceProvider` assumes that any route files are placed in this directory: `<package root>/routes`. Inside
that directory you can put any route files.
To register your route, you should pass its name without the extension to the `hasRoute` method.
If your route file is called `web.php` you can register them like this:
```php
$package
->name('your-package-name')
->hasRoute('web');
```
Should your package contain multiple route files, you can just call `hasRoute` multiple times or use `hasRoutes`.
```php
$package
->name('your-package-name')
->hasRoutes(['web', 'admin']);
```
### Publishable Service Providers
Some packages need an example service provider to be copied into the `app\Providers` directory of the Laravel app. Think
of for instance, the `laravel/horizon` package that copies an `HorizonServiceProvider` into your app with some sensible
defaults.
```php
$package
->name('your-package-name')
->publishesServiceProvider($nameOfYourServiceProvider);
```
The file that will be copied to the app should be stored in your package
in `/resources/stubs/{$nameOfYourServiceProvider}.php.stub`.
When your package is installed into an app, running this command...
```bash
php artisan vendor:publish --tag=your-package-name-provider
```
... will copy `/resources/stubs/{$nameOfYourServiceProvider}.php.stub` in your package
to `app/Providers/{$nameOfYourServiceProvider}.php` in the app of the user.
### Translations
Any translations your package provides, should be placed in the `<package root>/resources/lang/<language-code>`
directory.
You can register these translations with the `hasTranslations` command.
```php
$package
->name('your-package-name')
->hasTranslations();
```
This will register the translations with Laravel.
Assuming you save this translation file at `<package root>/resources/lang/en/translations.php`...
```php
return [
'translatable' => 'translation',
];
```
... your package and users will be able to retrieve the translation with:
```php
trans('your-package-name::translations.translatable'); // returns 'translation'
```
If your package name starts with `laravel-` then you should leave that off in the example above.
Coding with translation strings as keys, you should create JSON files
in `<package root>/resources/lang/<language-code>.json`.
For example, creating `<package root>/resources/lang/it.json` file like so:
```json
{
"Hello!": "Ciao!"
}
```
...the output of...
```php
trans('Hello!');
```
...will be `Ciao!` if the application uses the Italian language.
Calling `hasTranslations` will also make translations publishable. Users of your package will be able to publish the
translations with this command:
```bash
php artisan vendor:publish --tag=your-package-name-translations
```
### Views
Any views your package provides, should be placed in the `<package root>/resources/views` directory.
You can register these views with the `hasViews` command.
```php
$package
->name('your-package-name')
->hasViews();
```
This will register your views with Laravel.
If you have a view `<package root>/resources/views/myView.blade.php`, you can use it like
this: `view('your-package-name::myView')`. Of course, you can also use subdirectories to organise your views. A view
located at `<package root>/resources/views/subdirectory/myOtherView.blade.php` can be used
with `view('your-package-name::subdirectory.myOtherView')`.
#### Using a custom view namespace
You can pass a custom view namespace to the `hasViews` method.
```php
$package
->name('your-package-name')
->hasViews('custom-view-namespace');
```
You can now use the views of the package like this:
```php
view('custom-view-namespace::myView');
```
#### Publishing the views
Calling `hasViews` will also make views publishable. Users of your package will be able to publish the views with this
command:
```bash
php artisan vendor:publish --tag=your-package-name-views
```
> **Note:**
>
> If you use custom view namespace then you should change your publish command like this:
```bash
php artisan vendor:publish --tag=custom-view-namespace-views
```
### View Composers
You can register any view composers that your project uses with the `hasViewComposers` method. You may also register a
callback that receives a `$view` argument instead of a classname.
To register a view composer with all views, use an asterisk as the view name `'*'`.
```php
$package
->name('your-package-name')
->hasViewComposer('viewName', MyViewComposer::class)
->hasViewComposer('*', function($view) {
$view->with('sharedVariable', 123);
});
```
### Views Global Shared Data
You can share data with all views using the `sharesDataWithAllViews` method. This will make the shared variable
available to all views.
```php
$package
->name('your-package-name')
->sharesDataWithAllViews('companyName', 'Spatie');
```
### Creating an Install Command
Instead of letting your users manually publishing config files, migrations, and other files manually, you could opt to
add an install command that does all this work in one go. Packages like Laravel Horizon and Livewire provide such
commands.
When using Laravel Package Tools, you don't have to write an `InstallCommand` yourself. Instead, you can simply
call, `hasInstallCommand` and configure it using a closure. Here's an example.
```php
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\LaravelPackageTools\Package;
use Spatie\LaravelPackageTools\Commands\Concerns;
class YourPackageServiceProvider extends PackageServiceProvider
{
public function configurePackage(Package $package): void
{
$package
->name('your-package-name')
->hasConfigFile()
->hasMigration('create_package_tables')
->publishesServiceProvider('MyServiceProviderName')
->hasInstallCommand(function(InstallCommand $command) {
$command
->publishConfigFile()
->publishAssets()
->publishMigrations()
->askToRunMigrations()
->copyAndRegisterServiceProviderInApp()
->askToStarRepoOnGitHub('your-vendor/your-repo-name')
});
}
}
```
With this in place, the package user can call this command:
```bash
php artisan your-package-name:install
```
Using the code above, that command will:
- publish the config file
- publish the assets
- publish the migrations
- copy the `/resources/stubs/MyProviderName.php.stub` from your package to `app/Providers/MyServiceProviderName.php`, and also register that
provider in `config/app.php`
- ask if migrations should be run now
- prompt the user to open up `https://github.com/'your-vendor/your-repo-name'` in the browser in order to star it
You can also call `startWith` and `endWith` on the `InstallCommand`. They will respectively be executed at the start and
end when running `php artisan your-package-name:install`. You can use this to perform extra work or display extra
output.
```php
use Spatie\LaravelPackageTools\Commands\Concerns;
public function configurePackage(Package $package): void
{
$package
// ... configure package
->hasInstallCommand(function(InstallCommand $command) {
$command
->startWith(function(InstallCommand $command) {
$command->info('Hello, and welcome to my great new package!');
})
->publishConfigFile()
->publishAssets()
->publishMigrations()
->askToRunMigrations()
->copyAndRegisterServiceProviderInApp()
->askToStarRepoOnGitHub('your-vendor/your-repo-name')
->endWith(function(InstallCommand $command) {
$command->info('Have a great day!');
})
});
}
```
### Lifecycle Hooks
You can put any custom logic your package needs while starting up in one of these methods:
- `registeringPackage`: will be called at the start of the `register` method of `PackageServiceProvider`
- `packageRegistered`: will be called at the end of the `register` method of `PackageServiceProvider`
- `bootingPackage`: will be called at the start of the `boot` method of `PackageServiceProvider`
- `packageBooted`: will be called at the end of the `boot` method of `PackageServiceProvider`
## Testing
```bash
composer test
```
This package now supports test groups as follows:
```bash
composer test -- --group=blade
```
The current groups suported are:
* base
* assets
* blade
* commands
* config
* inertia
* migrations
* provider
* routes
* shareddata
* translations
* viewcomposer
* views
* installer
Additionally, if you wish to test only backwards compatibility you can use:
* legacy
**Note:** `InvalidPackage` exceptions thrown during Laravel application bootup are reported by Pest,
but because the occur before the start of a test case
Pest by default does not allow you intentionally to test for them being thrown.
The tests in this package now include checks for intentional `InvalidPackage` exceptions being thrown
by catching and saving such exceptions in the TestServiceProvider,
and then rethrowing the exception at the very start of a Pest test case,
and this is achieved by loading a modified version of the Pest `test()` function
before anything else is loaded.
Whilst this is done for you if you run `composer test`,
if you want to run `vendor/bin/pest` directly you now need to run it like this:
```bash
php -d auto_prepend_file=tests/Prepend.php vendor/bin/pest
```
## Changelog
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
## Contributing
Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details.
## Security Vulnerabilities
Please review [our security policy](../../security/policy) on how to report security vulnerabilities.
## Credits
- [Freek Van der Herten](https://github.com/freekmurze)
- [All Contributors](../../contributors)
## License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
@@ -0,0 +1,52 @@
{
"name": "spatie/laravel-package-tools",
"description": "Tools for creating Laravel packages",
"keywords": [
"spatie",
"laravel-package-tools"
],
"homepage": "https://github.com/spatie/laravel-package-tools",
"license": "MIT",
"authors": [
{
"name": "Freek Van der Herten",
"email": "freek@spatie.be",
"role": "Developer"
}
],
"require": {
"php": "^8.0",
"illuminate/contracts": "^9.28|^10.0|^11.0|^12.0"
},
"require-dev": {
"mockery/mockery": "^1.5",
"orchestra/testbench": "^7.7|^8.0|^9.0|^10.0",
"pestphp/pest": "^1.23|^2.1|^3.1",
"phpunit/php-code-coverage": "^9.0|^10.0|^11.0",
"phpunit/phpunit": "^9.5.24|^10.5|^11.5",
"spatie/pest-plugin-test-time": "^1.1|^2.2"
},
"autoload": {
"psr-4": {
"Spatie\\LaravelPackageTools\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"Spatie\\LaravelPackageTools\\Tests\\": "tests"
}
},
"scripts": {
"test": "php -d auto_prepend_file=tests/Prepend.php vendor/bin/pest --colors=always",
"test-coverage": "@test --coverage",
"test-dirty": "@test --dirty --compact"
},
"config": {
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true
}
},
"minimum-stability": "dev",
"prefer-stable": true
}
@@ -0,0 +1,28 @@
<?php
namespace Spatie\LaravelPackageTools\Commands\Concerns;
trait AskToRunMigrations
{
protected bool $askToRunMigrations = false;
public function askToRunMigrations(): self
{
$this->askToRunMigrations = true;
return $this;
}
protected function processAskToRunMigrations(): self
{
if ($this->askToRunMigrations) {
if ($this->confirm('Would you like to run the migrations now?')) {
$this->comment('Running migrations...');
$this->call('migrate');
}
}
return $this;
}
}
@@ -0,0 +1,36 @@
<?php
namespace Spatie\LaravelPackageTools\Commands\Concerns;
trait AskToStarRepoOnGitHub
{
protected ?string $starRepo = null;
public function askToStarRepoOnGitHub($vendorSlashRepoName): self
{
$this->starRepo = $vendorSlashRepoName;
return $this;
}
protected function processStarRepo(): self
{
if ($this->starRepo) {
if ($this->confirm('Would you like to star our repo on GitHub?')) {
$repoUrl = "https://github.com/{$this->starRepo}";
if (PHP_OS_FAMILY == 'Darwin') {
exec("open {$repoUrl}");
}
if (PHP_OS_FAMILY == 'Windows') {
exec("start {$repoUrl}");
}
if (PHP_OS_FAMILY == 'Linux') {
exec("xdg-open {$repoUrl}");
}
}
}
return $this;
}
}
@@ -0,0 +1,49 @@
<?php
namespace Spatie\LaravelPackageTools\Commands\Concerns;
trait PublishesResources
{
protected array $publishes = [];
public function publish(string ...$tag): self
{
$this->publishes = array_merge($this->publishes, $tag);
return $this;
}
public function publishAssets(): self
{
return $this->publish('assets');
}
public function publishConfigFile(): self
{
return $this->publish('config');
}
public function publishInertiaComponents(): self
{
return $this->publish('inertia-components');
}
public function publishMigrations(): self
{
return $this->publish('migrations');
}
protected function processPublishes(): self
{
foreach ($this->publishes as $tag) {
$name = str_replace('-', ' ', $tag);
$this->comment("Publishing {$name}...");
$this->callSilently("vendor:publish", [
'--tag' => "{$this->package->shortName()}-{$tag}",
]);
}
return $this;
}
}
@@ -0,0 +1,75 @@
<?php
namespace Spatie\LaravelPackageTools\Commands\Concerns;
use Illuminate\Support\Str;
trait SupportsServiceProviderInApp
{
protected bool $copyServiceProviderInApp = false;
public function copyAndRegisterServiceProviderInApp(): self
{
$this->copyServiceProviderInApp = true;
return $this;
}
protected function processCopyServiceProviderInApp(): self
{
if ($this->copyServiceProviderInApp) {
$this->comment('Publishing service provider...');
$this->copyServiceProviderInApp();
}
return $this;
}
protected function copyServiceProviderInApp(): self
{
$providerName = $this->package->publishableProviderName;
if (! $providerName) {
return $this;
}
$this->callSilent('vendor:publish', ['--tag' => $this->package->shortName() . '-provider']);
$namespace = Str::replaceLast('\\', '', $this->laravel->getNamespace());
if (intval(app()->version()) < 11 || ! file_exists(base_path('bootstrap/providers.php'))) {
$appConfig = file_get_contents(config_path('app.php'));
} else {
$appConfig = file_get_contents(base_path('bootstrap/providers.php'));
}
$class = '\\Providers\\' . Str::replace('/', '\\', $providerName) . '::class';
if (Str::contains($appConfig, $namespace . $class)) {
return $this;
}
if (intval(app()->version()) < 11 || ! file_exists(base_path('bootstrap/providers.php'))) {
file_put_contents(config_path('app.php'), str_replace(
"{$namespace}\\Providers\\BroadcastServiceProvider::class,",
"{$namespace}\\Providers\\BroadcastServiceProvider::class," . PHP_EOL . " {$namespace}{$class},",
$appConfig
));
} else {
file_put_contents(base_path('bootstrap/providers.php'), str_replace(
"{$namespace}\\Providers\\AppServiceProvider::class,",
"{$namespace}\\Providers\\AppServiceProvider::class," . PHP_EOL . " {$namespace}{$class},",
$appConfig
));
}
file_put_contents(app_path('Providers/' . $providerName . '.php'), str_replace(
"namespace App\Providers;",
"namespace {$namespace}\Providers;",
file_get_contents(app_path('Providers/' . $providerName . '.php'))
));
return $this;
}
}
@@ -0,0 +1,43 @@
<?php
namespace Spatie\LaravelPackageTools\Commands\Concerns;
use Closure;
trait SupportsStartWithEndWith
{
public ?Closure $startWith = null;
public ?Closure $endWith = null;
public function startWith(callable $callable): self
{
$this->startWith = $callable;
return $this;
}
public function endWith(callable $callable): self
{
$this->endWith = $callable;
return $this;
}
protected function processStartWith(): self
{
if ($this->startWith) {
($this->startWith)($this);
}
return $this;
}
protected function processEndWith(): self
{
if ($this->endWith) {
($this->endWith)($this);
}
return $this;
}
}
@@ -0,0 +1,48 @@
<?php
namespace Spatie\LaravelPackageTools\Commands;
use Illuminate\Console\Command;
use Spatie\LaravelPackageTools\Commands\Concerns\AskToRunMigrations;
use Spatie\LaravelPackageTools\Commands\Concerns\AskToStarRepoOnGitHub;
use Spatie\LaravelPackageTools\Commands\Concerns\PublishesResources;
use Spatie\LaravelPackageTools\Commands\Concerns\SupportsServiceProviderInApp;
use Spatie\LaravelPackageTools\Commands\Concerns\SupportsStartWithEndWith;
use Spatie\LaravelPackageTools\Package;
class InstallCommand extends Command
{
use AskToRunMigrations;
use AskToStarRepoOnGitHub;
use PublishesResources;
use SupportsServiceProviderInApp;
use SupportsStartWithEndWith;
protected Package $package;
public function __construct(Package $package)
{
$this->signature = $package->shortName() . ':install';
$this->description = 'Install ' . $package->name;
$this->package = $package;
$this->hidden = true;
parent::__construct();
}
public function handle()
{
$this
->processStartWith()
->processPublishes()
->processAskToRunMigrations()
->processCopyServiceProviderInApp()
->processStarRepo()
->processEndWith();
$this->info("{$this->package->shortName()} has been installed!");
}
}
@@ -0,0 +1,15 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\Package;
trait HasAssets
{
public bool $hasAssets = false;
public function hasAssets(): static
{
$this->hasAssets = true;
return $this;
}
}
@@ -0,0 +1,24 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\Package;
trait HasBladeComponents
{
public array $viewComponents = [];
public function hasViewComponent(string $prefix, string $viewComponentName): static
{
$this->viewComponents[$viewComponentName] = $prefix;
return $this;
}
public function hasViewComponents(string $prefix, ...$viewComponentNames): static
{
foreach ($viewComponentNames as $componentName) {
$this->viewComponents[$componentName] = $prefix;
}
return $this;
}
}
@@ -0,0 +1,43 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\Package;
trait HasCommands
{
public array $commands = [];
public array $consoleCommands = [];
public function hasCommand(string $commandClassName): static
{
$this->commands[] = $commandClassName;
return $this;
}
public function hasCommands(...$commandClassNames): static
{
$this->commands = array_merge(
$this->commands,
collect($commandClassNames)->flatten()->toArray()
);
return $this;
}
public function hasConsoleCommand(string $commandClassName): static
{
$this->consoleCommands[] = $commandClassName;
return $this;
}
public function hasConsoleCommands(...$commandClassNames): static
{
$this->consoleCommands = array_merge(
$this->consoleCommands,
collect($commandClassNames)->flatten()->toArray()
);
return $this;
}
}
@@ -0,0 +1,21 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\Package;
trait HasConfigs
{
public array $configFileNames = [];
public function hasConfigFile($configFileName = null): static
{
$configFileName ??= $this->shortName();
if (! is_array($configFileName)) {
$configFileName = [$configFileName];
}
$this->configFileNames = $configFileName;
return $this;
}
}
@@ -0,0 +1,17 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\Package;
trait HasInertia
{
public bool $hasInertiaComponents = false;
public function hasInertiaComponents(?string $namespace = null): static
{
$this->hasInertiaComponents = true;
$this->viewNamespace = $namespace;
return $this;
}
}
@@ -0,0 +1,19 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\Package;
use Spatie\LaravelPackageTools\Commands\InstallCommand;
trait HasInstallCommand
{
public function hasInstallCommand($callable): static
{
$installCommand = new InstallCommand($this);
$callable($installCommand);
$this->consoleCommands[] = $installCommand;
return $this;
}
}
@@ -0,0 +1,46 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\Package;
trait HasMigrations
{
public bool $runsMigrations = false;
public bool $discoversMigrations = false;
public ?string $migrationsPath = null;
public array $migrationFileNames = [];
public function runsMigrations(bool $runsMigrations = true): static
{
$this->runsMigrations = $runsMigrations;
return $this;
}
public function hasMigration(string $migrationFileName): static
{
$this->migrationFileNames[] = $migrationFileName;
return $this;
}
public function hasMigrations(...$migrationFileNames): static
{
$this->migrationFileNames = array_merge(
$this->migrationFileNames,
collect($migrationFileNames)->flatten()->toArray()
);
return $this;
}
public function discoversMigrations(bool $discoversMigrations = true, string $path = '/database/migrations'): static
{
$this->discoversMigrations = $discoversMigrations;
$this->migrationsPath = $path;
return $this;
}
}
@@ -0,0 +1,25 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\Package;
trait HasRoutes
{
public array $routeFileNames = [];
public function hasRoute(string $routeFileName): static
{
$this->routeFileNames[] = $routeFileName;
return $this;
}
public function hasRoutes(...$routeFileNames): static
{
$this->routeFileNames = array_merge(
$this->routeFileNames,
collect($routeFileNames)->flatten()->toArray()
);
return $this;
}
}
@@ -0,0 +1,15 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\Package;
trait HasServiceProviders
{
public ?string $publishableProviderName = null;
public function publishesServiceProvider(string $providerName): static
{
$this->publishableProviderName = $providerName;
return $this;
}
}
@@ -0,0 +1,15 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\Package;
trait HasTranslations
{
public bool $hasTranslations = false;
public function hasTranslations(): static
{
$this->hasTranslations = true;
return $this;
}
}
@@ -0,0 +1,21 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\Package;
trait HasViewComposers
{
public array $viewComposers = [];
public function hasViewComposer($view, $viewComposer): static
{
if (! is_array($view)) {
$view = [$view];
}
foreach ($view as $viewName) {
$this->viewComposers[$viewName] = $viewComposer;
}
return $this;
}
}
@@ -0,0 +1,15 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\Package;
trait HasViewSharedData
{
public array $sharedViewData = [];
public function sharesDataWithAllViews(string $name, $value): static
{
$this->sharedViewData[$name] = $value;
return $this;
}
}
@@ -0,0 +1,24 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\Package;
trait HasViews
{
public bool $hasViews = false;
public ?string $viewNamespace = null;
public function hasViews(?string $namespace = null): static
{
$this->hasViews = true;
$this->viewNamespace = $namespace;
return $this;
}
public function viewNamespace(): string
{
return $this->viewNamespace ?? $this->shortName();
}
}
@@ -0,0 +1,20 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\PackageServiceProvider;
trait ProcessAssets
{
protected function bootPackageAssets(): static
{
if (! $this->package->hasAssets || ! $this->app->runningInConsole()) {
return $this;
}
$vendorAssets = $this->package->basePath('/../resources/dist');
$appAssets = public_path("vendor/{$this->package->shortName()}");
$this->publishes([$vendorAssets => $appAssets], "{$this->package->shortName()}-assets");
return $this;
}
}
@@ -0,0 +1,26 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\PackageServiceProvider;
trait ProcessBladeComponents
{
protected function bootPackageBladeComponents(): self
{
if (empty($this->package->viewComponents)) {
return $this;
}
foreach ($this->package->viewComponents as $componentClass => $prefix) {
$this->loadViewComponentsAs($prefix, [$componentClass]);
}
if ($this->app->runningInConsole()) {
$vendorComponents = $this->package->basePath('/Components');
$appComponents = base_path("app/View/Components/vendor/{$this->package->shortName()}");
$this->publishes([$vendorComponents => $appComponents], "{$this->package->name}-components");
}
return $this;
}
}
@@ -0,0 +1,28 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\PackageServiceProvider;
trait ProcessCommands
{
protected function bootPackageCommands(): self
{
if (empty($this->package->commands)) {
return $this;
}
$this->commands($this->package->commands);
return $this;
}
protected function bootPackageConsoleCommands(): self
{
if (empty($this->package->consoleCommands) || ! $this->app->runningInConsole()) {
return $this;
}
$this->commands($this->package->consoleCommands);
return $this;
}
}
@@ -0,0 +1,51 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\PackageServiceProvider;
trait ProcessConfigs
{
public function registerPackageConfigs(): self
{
if (empty($this->package->configFileNames)) {
return $this;
}
foreach ($this->package->configFileNames as $configFileName) {
$vendorConfig = $this->package->basePath("/../config/{$configFileName}.php");
// Only mergeConfigFile if a .php file and not if a stub file
if (! is_file($vendorConfig)) {
continue;
}
$this->mergeConfigFrom($vendorConfig, $configFileName);
}
return $this;
}
protected function bootPackageConfigs(): self
{
if (empty($this->package->configFileNames) || ! $this->app->runningInConsole()) {
return $this;
}
foreach ($this->package->configFileNames as $configFileName) {
$vendorConfig ;
if (
! is_file($vendorConfig = $this->package->basePath("/../config/{$configFileName}.php"))
&&
! is_file($vendorConfig = $this->package->basePath("/../config/{$configFileName}.php.stub"))
) {
continue;
}
$this->publishes(
[$vendorConfig => config_path("{$configFileName}.php")],
"{$this->package->shortName()}-config"
);
}
return $this;
}
}
@@ -0,0 +1,29 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\PackageServiceProvider;
use Illuminate\Support\Str;
trait ProcessInertia
{
protected function bootPackageInertia(): self
{
if (! $this->package->hasInertiaComponents) {
return $this;
}
$namespace = $this->package->viewNamespace;
$directoryName = Str::of($this->packageView($namespace))->studly()->remove('-')->value();
$vendorComponents = $this->package->basePath('/../resources/js/Pages');
$appComponents = base_path("resources/js/Pages/{$directoryName}");
if ($this->app->runningInConsole()) {
$this->publishes(
[$vendorComponents => $appComponents],
"{$this->packageView($namespace)}-inertia-components"
);
}
return $this;
}
}
@@ -0,0 +1,108 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\PackageServiceProvider;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Str;
trait ProcessMigrations
{
protected function bootPackageMigrations(): self
{
if ($this->package->discoversMigrations) {
$this->discoverPackageMigrations();
return $this;
}
$now = Carbon::now();
foreach ($this->package->migrationFileNames as $migrationFileName) {
$vendorMigration = $this->package->basePath("/../database/migrations/{$migrationFileName}.php");
// Support for the .stub file extension
if (! file_exists($vendorMigration)) {
$vendorMigration .= '.stub';
}
if ($this->app->runningInConsole()) {
$appMigration = $this->generateMigrationName($migrationFileName, $now->addSecond());
$this->publishes(
[$vendorMigration => $appMigration],
"{$this->package->shortName()}-migrations"
);
}
if ($this->package->runsMigrations) {
$this->loadMigrationsFrom($vendorMigration);
}
}
return $this;
}
protected function discoverPackageMigrations(): void
{
$now = Carbon::now();
$migrationsPath = trim($this->package->migrationsPath, '/');
$files = (new Filesystem())->files($this->package->basePath("/../{$migrationsPath}"));
foreach ($files as $file) {
$filePath = $file->getPathname();
$migrationFileName = Str::replace(['.stub', '.php'], '', $file->getFilename());
// Publish but do not add timestamp to non migration files
if (Str::endsWith($filePath, [".php", ".php.stub"])) {
$appMigration = $this->generateMigrationName($migrationFileName, $now->addSecond());
} else {
$appMigration = database_path("migrations/{$file->getFilename()}");
}
if ($this->app->runningInConsole()) {
$this->publishes(
[$filePath => $appMigration],
"{$this->package->shortName()}-migrations"
);
}
// Do not load non migration files
if ($this->package->runsMigrations && Str::endsWith($filePath, [".php", ".php.stub"])) {
$this->loadMigrationsFrom($filePath);
}
}
}
protected function generateMigrationName(string $migrationFileName, Carbon|CarbonImmutable $now): string
{
$migrationsPath = 'migrations/' . dirname($migrationFileName) . '/';
$migrationFileName = basename($migrationFileName);
$len = strlen($migrationFileName) + 4;
if (Str::contains($migrationFileName, '/')) {
$migrationsPath .= Str::of($migrationFileName)->beforeLast('/')->finish('/');
$migrationFileName = Str::of($migrationFileName)->afterLast('/');
}
foreach (glob(database_path("{$migrationsPath}*.php")) as $filename) {
if ((substr($filename, -$len) === $migrationFileName . '.php')) {
return $filename;
}
}
$migrationFileName = self::stripTimestampPrefix($migrationFileName);
$timestamp = $now->format('Y_m_d_His');
$formattedFileName = Str::of($migrationFileName)->snake()->finish('.php');
return database_path("{$migrationsPath}{$timestamp}_{$formattedFileName}");
}
private static function stripTimestampPrefix(string $filename): string
{
return preg_replace('/^\d{4}_\d{2}_\d{2}_\d{6}_/', '', $filename);
}
}
@@ -0,0 +1,19 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\PackageServiceProvider;
trait ProcessRoutes
{
protected function bootPackageRoutes(): self
{
if (empty($this->package->routeFileNames)) {
return $this;
}
foreach ($this->package->routeFileNames as $routeFileName) {
$this->loadRoutesFrom("{$this->package->basePath('/../routes/')}{$routeFileName}.php");
}
return $this;
}
}
@@ -0,0 +1,21 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\PackageServiceProvider;
trait ProcessServiceProviders
{
protected function bootPackageServiceProviders(): self
{
if (! $this->package->publishableProviderName || ! $this->app->runningInConsole()) {
return $this;
}
$providerName = $this->package->publishableProviderName;
$vendorProvider = $this->package->basePath("/../resources/stubs/{$providerName}.php.stub");
$appProvider = base_path("app/Providers/{$providerName}.php");
$this->publishes([$vendorProvider => $appProvider], "{$this->package->shortName()}-provider");
return $this;
}
}
@@ -0,0 +1,32 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\PackageServiceProvider;
trait ProcessTranslations
{
protected function bootPackageTranslations(): self
{
if (! $this->package->hasTranslations) {
return $this;
}
$vendorTranslations = $this->package->basePath('/../resources/lang');
$appTranslations = (function_exists('lang_path'))
? lang_path("vendor/{$this->package->shortName()}")
: resource_path("lang/vendor/{$this->package->shortName()}");
$this->loadTranslationsFrom($vendorTranslations, $this->package->shortName());
$this->loadJsonTranslationsFrom($vendorTranslations);
$this->loadJsonTranslationsFrom($appTranslations);
if ($this->app->runningInConsole()) {
$this->publishes(
[$vendorTranslations => $appTranslations],
"{$this->package->shortName()}-translations"
);
}
return $this;
}
}
@@ -0,0 +1,21 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\PackageServiceProvider;
use Illuminate\Support\Facades\View;
trait ProcessViewComposers
{
protected function bootPackageViewComposers(): self
{
if (empty($this->package->viewComposers)) {
return $this;
}
foreach ($this->package->viewComposers as $viewName => $viewComposer) {
View::composer($viewName, $viewComposer);
}
return $this;
}
}
@@ -0,0 +1,21 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\PackageServiceProvider;
use Illuminate\Support\Facades\View;
trait ProcessViewSharedData
{
protected function bootPackageViewSharedData(): self
{
if (empty($this->package->sharedViewData)) {
return $this;
}
foreach ($this->package->sharedViewData as $name => $value) {
View::share($name, $value);
}
return $this;
}
}
@@ -0,0 +1,26 @@
<?php
namespace Spatie\LaravelPackageTools\Concerns\PackageServiceProvider;
trait ProcessViews
{
protected function bootPackageViews(): self
{
if (! $this->package->hasViews) {
return $this;
}
$namespace = $this->package->viewNamespace;
$viewsPath = $this->package->basePath('/../resources/views');
$vendorViews = realpath($viewsPath) ?: $viewsPath;
$appViews = base_path("resources/views/vendor/{$this->packageView($namespace)}");
$this->loadViewsFrom($vendorViews, $this->package->viewNamespace());
if ($this->app->runningInConsole()) {
$this->publishes([$vendorViews => $appViews], "{$this->packageView($namespace)}-views");
}
return $this;
}
}
@@ -0,0 +1,13 @@
<?php
namespace Spatie\LaravelPackageTools\Exceptions;
use Exception;
class InvalidPackage extends Exception
{
public static function nameIsRequired(): self
{
return new static('This package does not have a name. You can set one with `$package->name("yourName")`');
}
}
@@ -0,0 +1,67 @@
<?php
namespace Spatie\LaravelPackageTools;
use Illuminate\Support\Str;
use Spatie\LaravelPackageTools\Concerns\Package\HasAssets;
use Spatie\LaravelPackageTools\Concerns\Package\HasBladeComponents;
use Spatie\LaravelPackageTools\Concerns\Package\HasCommands;
use Spatie\LaravelPackageTools\Concerns\Package\HasConfigs;
use Spatie\LaravelPackageTools\Concerns\Package\HasInertia;
use Spatie\LaravelPackageTools\Concerns\Package\HasInstallCommand;
use Spatie\LaravelPackageTools\Concerns\Package\HasMigrations;
use Spatie\LaravelPackageTools\Concerns\Package\HasRoutes;
use Spatie\LaravelPackageTools\Concerns\Package\HasServiceProviders;
use Spatie\LaravelPackageTools\Concerns\Package\HasTranslations;
use Spatie\LaravelPackageTools\Concerns\Package\HasViewComposers;
use Spatie\LaravelPackageTools\Concerns\Package\HasViews;
use Spatie\LaravelPackageTools\Concerns\Package\HasViewSharedData;
class Package
{
use HasAssets;
use HasBladeComponents;
use HasCommands;
use HasConfigs;
use HasInertia;
use HasInstallCommand;
use HasMigrations;
use HasRoutes;
use HasServiceProviders;
use HasTranslations;
use HasViewComposers;
use HasViews;
use HasViewSharedData;
public string $name;
public string $basePath;
public function name(string $name): static
{
$this->name = $name;
return $this;
}
public function shortName(): string
{
return Str::after($this->name, 'laravel-');
}
public function basePath(?string $directory = null): string
{
if ($directory === null) {
return $this->basePath;
}
return $this->basePath . DIRECTORY_SEPARATOR . ltrim($directory, DIRECTORY_SEPARATOR);
}
public function setBasePath(string $path): static
{
$this->basePath = $path;
return $this;
}
}
@@ -0,0 +1,126 @@
<?php
namespace Spatie\LaravelPackageTools;
use Illuminate\Support\ServiceProvider;
use ReflectionClass;
use Spatie\LaravelPackageTools\Concerns\PackageServiceProvider\ProcessAssets;
use Spatie\LaravelPackageTools\Concerns\PackageServiceProvider\ProcessBladeComponents;
use Spatie\LaravelPackageTools\Concerns\PackageServiceProvider\ProcessCommands;
use Spatie\LaravelPackageTools\Concerns\PackageServiceProvider\ProcessConfigs;
use Spatie\LaravelPackageTools\Concerns\PackageServiceProvider\ProcessInertia;
use Spatie\LaravelPackageTools\Concerns\PackageServiceProvider\ProcessMigrations;
use Spatie\LaravelPackageTools\Concerns\PackageServiceProvider\ProcessRoutes;
use Spatie\LaravelPackageTools\Concerns\PackageServiceProvider\ProcessServiceProviders;
use Spatie\LaravelPackageTools\Concerns\PackageServiceProvider\ProcessTranslations;
use Spatie\LaravelPackageTools\Concerns\PackageServiceProvider\ProcessViewComposers;
use Spatie\LaravelPackageTools\Concerns\PackageServiceProvider\ProcessViews;
use Spatie\LaravelPackageTools\Concerns\PackageServiceProvider\ProcessViewSharedData;
use Spatie\LaravelPackageTools\Exceptions\InvalidPackage;
abstract class PackageServiceProvider extends ServiceProvider
{
use ProcessAssets;
use ProcessBladeComponents;
use ProcessCommands;
use ProcessConfigs;
use ProcessInertia;
use ProcessMigrations;
use ProcessRoutes;
use ProcessServiceProviders;
use ProcessTranslations;
use ProcessViewComposers;
use ProcessViews;
use ProcessViewSharedData;
protected Package $package;
abstract public function configurePackage(Package $package): void;
/** @throws InvalidPackage */
public function register()
{
$this->registeringPackage();
$this->package = $this->newPackage();
$this->package->setBasePath($this->getPackageBaseDir());
$this->configurePackage($this->package);
if (empty($this->package->name)) {
throw InvalidPackage::nameIsRequired();
}
$this->registerPackageConfigs();
$this->packageRegistered();
return $this;
}
public function registeringPackage()
{
}
public function newPackage(): Package
{
return new Package();
}
public function packageRegistered()
{
}
public function boot()
{
$this->bootingPackage();
$this
->bootPackageAssets()
->bootPackageBladeComponents()
->bootPackageCommands()
->bootPackageConsoleCommands()
->bootPackageConfigs()
->bootPackageInertia()
->bootPackageMigrations()
->bootPackageRoutes()
->bootPackageServiceProviders()
->bootPackageTranslations()
->bootPackageViews()
->bootPackageViewComposers()
->bootPackageViewSharedData()
->packageBooted();
return $this;
}
public function bootingPackage()
{
}
public function packageBooted()
{
}
protected function getPackageBaseDir(): string
{
$reflector = new ReflectionClass(get_class($this));
$packageBaseDir = dirname($reflector->getFileName());
// Some packages like to keep Laravels directory structure and place
// the service providers in a Providers folder.
// move up a level when this is the case.
if (str_ends_with($packageBaseDir, DIRECTORY_SEPARATOR.'Providers')) {
$packageBaseDir = dirname($packageBaseDir);
}
return $packageBaseDir;
}
public function packageView(?string $namespace): ?string
{
return is_null($namespace)
? $this->package->shortName()
: $this->package->viewNamespace;
}
}