Basics
The foundation of MoonShine
is the Laravel
packages. If you are new to Laravel
package development, here are some resources to help you understand the core concepts:
- The chapter on Package Development in the
Laravel
documentation serves as an excellent reference guide. - Package Development Course by CutCode
- Free Guide to Package Development by CutCode
ServiceProvider
Through your package's ServiceProvider
, you can automatically add resources, pages, create menus, and authorization rules, among other things.
namespace Author\MoonShineMyPackage; use Illuminate\Support\ServiceProvider;use MoonShine\Contracts\Core\DependencyInjection\CoreContract;use MoonShine\Laravel\DependencyInjection\MoonShine; class MyPackageServiceProvider extends ServiceProvider{ /** @param MoonShine $core */ public function boot(CoreContract $core): void { $core ->resources([ MyPackageResource::class ]) ->page([ MyPackagePage::class ]) ; }}
You can also interact with the MenuManager
.
namespace Author\MoonShineMyPackage; use Illuminate\Support\ServiceProvider;use MoonShine\Contracts\Core\DependencyInjection\CoreContract;use MoonShine\Laravel\DependencyInjection\MoonShine;use MoonShine\Contracts\MenuManager\MenuManagerContract; class MyPackageServiceProvider extends ServiceProvider{ /** @param MoonShine $core */ public function boot(CoreContract $core, MenuManagerContract $menu): void { $menu->add([ MenuItem::make('MyPackagePage', MyPackagePage::class) ]); }}
You can also interact with the AssetManager
or ColorManager
.
use MoonShine\Contracts\AssetManager\AssetManagerContract; // .. public function boot(CoreContract $core, AssetManagerContract $assets): void{ $assets->add([ InlineCss::make('body {background: red;}') ]);}
use MoonShine\Contracts\ColorManager\ColorManagerContract; // .. public function boot(CoreContract $core, ColorManagerContract $colors): void{ $colors ->background('#A3C3D9') ->content('#A3C3D9') ->tableRow('#AE76A6') ->dividers('#AE76A6') ->borders('#AE76A6') ->buttons('#AE76A6') ->primary('#CCD6EB') ->secondary('#AE76A6');}
If you need to add additional authorization logic to the application or an external package, use the defineAuthorization
method.
use MoonShine\Contracts\Core\DependencyInjection\ConfiguratorContract;use MoonShine\Laravel\DependencyInjection\MoonShineConfigurator; //.. /** * @param MoonShineConfigurator $configurator */public function boot(ConfiguratorContract $configurator): void{ $configurator->authorizationRules( static function (ResourceContract $resource, Model $user, Ability $ability): bool { return true; } );}
You can also directly add components to the pages from the ServiceProvider
.
public function boot(): void{ ProfilePage::pushComponent(fn() => MyPackageComponent::make());}
Don’t forget to automatically include your ServiceProvider
in composer.json
.
"extra": { "laravel": { "providers": [ "Author\\MoonShineMyPackage\\MyPackageServiceProvider" ] }}
Traits
You can also include traits for resources or pages in your package and change the logic using the load{TraitName}
/boot{TraitName}
magic methods.
trait HasMyPackageTrait{ public function loadHasMyPackageTrait(): void { $this->getFormPage()->addAssets([ Js::make('vendor/my-package/js/app.js'), Css::make('vendor/my-package/css/app.css'), ]); } public function modifyFormComponent(ComponentContract $component): ComponentContract { return parent::modifyFormComponent($component)->fields([ Modal::make( 'This is my package modal.', '' ), ...$component->getFields()->toArray(), ]); }}
Custom Field Example
Let's quickly look at creating a custom field! This will be a visual editor based on the Quill.js
plugin.
We will create a field using the moonshine:field
command and choose to extend Textarea
.
php artisan moonshine:field Quill
Remove the unnecessary methods and add css/js.
declare(strict_types=1); namespace App\MoonShine\Fields; use MoonShine\UI\Fields\Textarea;use MoonShine\AssetManager\Css;use MoonShine\AssetManager\Js; final class Quill extends Textarea{ protected string $view = 'moonshine-quill::fields.quill'; public function assets(): array { return [ Css::make('/css/moonshine/quill/quill.snow.css'), // theme Js::make('/js/moonshine/quill/quill.js'), // library Js::make('/js/moonshine/quill/quill-init.js'), // initialization ]; }}
We will also change the field view.
<div x-data="quill"> <div class="ql-editor" :id="$id('quill')" style="height: auto;">{!! $value ?? '' !!}</div> <x-moonshine::form.textarea :attributes="$attributes->merge([ 'class' => 'ql-textarea', 'style' => 'display: none;' ])->except('x-bind:id')" >{!! $value ?? '' !!}</x-moonshine::form.textarea></div>
We took quill.snow.css
and quill.js
from the library, and the js initialization using Alpine.js
is provided below.
document.addEventListener('alpine:init', () => { Alpine.data('quill', () => ({ textarea: null, editor: null, init() { this.textarea = this.$root.querySelector('.ql-textarea') this.editor = this.$root.querySelector('.ql-editor') const t = this this.$nextTick(function() { let quill = new Quill(`#${t.editor.id}`, { theme: 'snow' }); quill.on('text-change', () => { t.textarea.value = t.editor.innerHTML || ''; t.textarea.dispatchEvent(new Event('change')); }); }) }, }))})
You can find the code example of this field in the repository.