glastree_on_gitea
This commit is contained in:
+21
@@ -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.
|
||||
+678
@@ -0,0 +1,678 @@
|
||||
<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>
|
||||
|
||||
[](https://packagist.org/packages/spatie/laravel-package-tools)
|
||||

|
||||
[](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;
|
||||
use Spatie\LaravelPackageTools\Commands\InstallCommand;
|
||||
|
||||
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)
|
||||
* [Getting Started](#getting-started)
|
||||
* [Assets](#assets)
|
||||
* [Blade Components](#blade-view-components)
|
||||
* [Commands - Callable and Console](#commands---callable-and-console)
|
||||
* [Config Files](#config-files)
|
||||
* [Inertia Components](#inertia-components)
|
||||
* [Working with migrations](#working-with-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...
|
||||
|
||||
assigning 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.1",
|
||||
"illuminate/contracts": "^10.0|^11.0|^12.0|^13.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.5",
|
||||
"orchestra/testbench": "^8.0|^9.2|^10.0|^11.0",
|
||||
"pestphp/pest": "^2.1|^3.1|^4.0",
|
||||
"phpunit/php-code-coverage": "^10.0|^11.0|^12.0",
|
||||
"phpunit/phpunit": "^10.5|^11.5|^12.5",
|
||||
"spatie/pest-plugin-test-time": "^2.2|^3.0"
|
||||
},
|
||||
"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
|
||||
}
|
||||
+28
@@ -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;
|
||||
}
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace Spatie\LaravelPackageTools\Commands\Concerns;
|
||||
|
||||
trait AskToStarRepoOnGitHub
|
||||
{
|
||||
protected ?string $starRepo = null;
|
||||
|
||||
protected bool $defaultStarAnswer = true;
|
||||
|
||||
public function askToStarRepoOnGitHub($vendorSlashRepoName, bool $defaultAnswer = false): self
|
||||
{
|
||||
$this->starRepo = $vendorSlashRepoName;
|
||||
$this->defaultStarAnswer = $defaultAnswer;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function processStarRepo(): self
|
||||
{
|
||||
if ($this->starRepo) {
|
||||
if ($this->confirm('Would you like to star our repo on GitHub?', $this->defaultStarAnswer)) {
|
||||
$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;
|
||||
}
|
||||
}
|
||||
+49
@@ -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;
|
||||
}
|
||||
}
|
||||
+75
@@ -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;
|
||||
}
|
||||
}
|
||||
+43
@@ -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;
|
||||
}
|
||||
}
|
||||
+24
@@ -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;
|
||||
}
|
||||
}
|
||||
+19
@@ -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;
|
||||
}
|
||||
}
|
||||
+15
@@ -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;
|
||||
}
|
||||
}
|
||||
+21
@@ -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;
|
||||
}
|
||||
}
|
||||
+15
@@ -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();
|
||||
}
|
||||
}
|
||||
+20
@@ -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;
|
||||
}
|
||||
}
|
||||
Vendored
+26
@@ -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;
|
||||
}
|
||||
}
|
||||
Vendored
+28
@@ -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;
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
<?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) {
|
||||
$configFilePath = $this->normalizeConfigPath($configFileName);
|
||||
$vendorConfig = $this->package->basePath("/../config/{$configFilePath}.php");
|
||||
|
||||
// Only mergeConfigFile if a .php file and not if a stub file
|
||||
if (! is_file($vendorConfig)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$normalizedConfigKey = $this->normalizeConfigKey($configFileName);
|
||||
$this->mergeConfigFrom($vendorConfig, $normalizedConfigKey);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function bootPackageConfigs(): self
|
||||
{
|
||||
if (empty($this->package->configFileNames) || ! $this->app->runningInConsole()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
foreach ($this->package->configFileNames as $configFileName) {
|
||||
$configFilePath = $this->normalizeConfigPath($configFileName);
|
||||
|
||||
$vendorConfig ;
|
||||
if (
|
||||
! is_file($vendorConfig = $this->package->basePath("/../config/{$configFilePath}.php"))
|
||||
&&
|
||||
! is_file($vendorConfig = $this->package->basePath("/../config/{$configFilePath}.php.stub"))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->publishes(
|
||||
[$vendorConfig => config_path("{$configFilePath}.php")],
|
||||
"{$this->package->shortName()}-config"
|
||||
);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function normalizeConfigKey(string $configFileName): string
|
||||
{
|
||||
return str_replace(['/', '\\'], '.', $configFileName);
|
||||
}
|
||||
|
||||
protected function normalizeConfigPath(string $configFileName): string
|
||||
{
|
||||
return str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $configFileName);
|
||||
}
|
||||
}
|
||||
+29
@@ -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;
|
||||
}
|
||||
}
|
||||
Vendored
+108
@@ -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);
|
||||
}
|
||||
}
|
||||
+19
@@ -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;
|
||||
}
|
||||
}
|
||||
Vendored
+21
@@ -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;
|
||||
}
|
||||
}
|
||||
Vendored
+32
@@ -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;
|
||||
}
|
||||
}
|
||||
Vendored
+21
@@ -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;
|
||||
}
|
||||
}
|
||||
Vendored
+21
@@ -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;
|
||||
}
|
||||
}
|
||||
+26
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user