This package is still in development and does not have a complete test suite.
Move makes it very easy to create your own Admin Panel using Laravel and Livewire. This package was heavily inspired bij Laravel Nova. And works practically the same, except for some missing features.
Here is an example of how you can use it:
<?php
namespace App\Move;
use Uteq\Move\Fields\Id;
use Uteq\Move\Fields\Text;
use Uteq\Move\Resource;
class User extends Resource
{
public static $model = \App\Models\User::class;
public static string $title = 'name';
public function fields()
{
return [
Id::make(),
Text::make('Name', 'name'),
];
}
}
And this is a basic example with a user:
- Package dependencies
- Tests
To best support is by improving this package. There is still a lot work to be done. For example:
- Documentation -- Setup -- Fields (Also: Panel and Step) -- Search -- Actions -- Permissions -- Customizations -- Reuseable (Move re-usable in many ways)
- Test coverage
- Fields extension (adding all sorts of fields)
- Stubs and Class generators
- Cards (like Laravel Nova cards)
You can install the package via composer:
composer require uteq/laravel-move
Laravel Move will add Jetstream to your vendor folder, but will not install it automatically. So, for your convenience we tailor made a command that will install Jetstream and bootstrap the Move Admin Panel. Because Move uses Livewire as the preferred stack you do not have to supply the stack. In addition, you may use the --teams switch to enable team support:
php artisan move:install --team
To finalize your installation run:
php artisan migrate
For more Jetstream related setup, please check: https://jetstream.laravel.com/2.x/installation.html#installing-jetstream
You can publish the config file with:
php artisan vendor:publish --provider="Uteq\Move\MoveServiceProvider" --tag="move-config"
You can publish the view files with:
php artisan vendor:publish --provider="Uteq\Move\MoveServiceProvider" --tag="move-views"
Start by creating your first Move Resource. You can generate it or use the example below:
php artisan move:resource User --model=User
<?php
namespace App\Move;
use Uteq\Move\Fields\Id;
use Uteq\Move\Fields\Text;
use Uteq\Move\Resource;
class User extends Resource
{
public static $model = \App\Models\User::class;
public static string $title = 'name';
public function fields()
{
return [
Id::make(),
Text::make('Naam', 'name')
->rules(['required', 'string', 'max:255'])
->required(),
Text::make('E-mailadres', 'email')
->hideWhenUpdating()
->requiredOnCreateOnly()
->creationRules(['required', 'string', 'email', 'max:255', 'unique:users']),
Status::make('E-mail bevestigd?', 'email_verified_at', fn ($value) => $value !== null)
->hideFromForm(),
Panel::make('Wachtwoord wijzigen', [
Password::make('Wachtwoord', 'password', null)
->creationRules($this->passwordRules())
->requiredOnCreateOnly(),
Password::make('Wachtwoord bevestigen', 'password_confirmation')
->hideFromIndex()
->hideFromDetail()
->onlyForValidation(fn ($value, $field, $model) => $model->id)
->requiredOnCreateOnly(),
])
->nameOnCreate('Wachtwoord')
->nameOnUpdate('Wachtwoord wijzigen'),
Panel::make('Rol kiezen', [
Role::make('Rol', 'role'),
]),
];
}
public function filters()
{
return [];
}
public function actions()
{
return [];
}
}
Move out of the box adds a prefix to your resources, that way it will never interfere with your own routes.
The default is move
.
You can change the default prefix by overwriting it at your local MoveServiceProvider at App\Providers\MoveServiceProvider
:
use Illuminate\Support\Facades\Route;
function register()
{
Route::move('my-prefix');
}
The default namespace for Move is App\Move
. You are also able to register the Move Resources wherever you like.
You can Bootstrap this namespace in the following way
use Uteq\Move\Facades\Move;
public function register()
{
Move::resourceNamespace('App\\Resources', 'resources');
}
This will automatically create the namespace for the routes. The default route for this namespace will be:
https://move.test/move/resources/your-resource
The resource default name will than be:
resources.your-resource
If you work a lot with data structures that cannot be clearly defined in a mysql database structure. You will probably opt for something like a json field.
First add your json field to your table.
$table->json('meta')->nullable();
Than make sure you cast it properly in you model
protected $casts ['meta' => 'json'];
At last, add a field to your resource
Text::make('My meta value', 'meta.my_value'),
These are the currently supported fields in Move:
- Country
- Date
- Files
- Id
- Number
- Password
- Select (with filter search)
- Status
- Text
- Textarea
- Editor
Move automatically registers resources to the sidebar. By default there are two ways to show your sidebar resources. Grouped and flat. The default is grouped and is published with the \App\Providers\MoveServiceProvider.
Move::useSidebarGroups(true);
To use the flat display, just change to
Move::useSidebarGroups(false);
When you use the sidebar groups you should also set the resource property:
class User extends \Uteq\Move\Resource
{
public static string $group = 'admin';
}
Currently, this is not a supported feature, feel free to create a PR. Or you can simply overwrite the components/sidebar-menu.blade.php file.
Whenever you would prefer to order your sidebar by the given namespace just use the grouped sidebar approach. If not you can always overwrite the components/sidebar-menu.blade.php file or create a PR.
<x-move-sidebar>
<x-slot name="logo">
<a class="text-center" href="/">
<h1 class="text-2xl text-white font-black">{{ config('app.name') }}</h1>
</a>
</x-slot>
</x-move-sidebar>
You can completely overwrite the sidebar items. And the logo.
<x-move-sidebar :keep-not-custom="false" :with-padding="false">
<!-- You can also add you own html + css here, move just has a link component -->
<x-move-sidebar.link href="{{ route('dashboard') }}" alt-active="admin/dashboard/*">
Dashboard
</x-move-sidebar.link>
</x-move-sidebar>
You can also keep the automatically generated sidebar items and add you own before the automatic created. This is the default behavior.
<x-move-sidebar>
<x-move-sidebar.link href="{{ route('dashboard') }}" alt-active="admin/dashboard/*">
Dashboard
</x-move-sidebar.link>
</x-move-sidebar>
Resolving a resource means loading the concrete implementation of your Resource class. You can do this by providing the name of your resource:
use \Uteq\Move\Facades\Move;
Move::resolveResource('resources.your-resource');
Action handlers are classes that make it possible to store and delete your resources the way you prefer. By default Move will have its own default logic.
Move provides two default action handlers Uteq\Move\DomainActions\StoreResource
and Uteq\Move\DomainActions\DeleteResource
.
You are able to overwrite these handlers from your Resource and by default.
class StoreHandler
{
public function __invoke(Model $model, array $input = [])
{
//... Basic validation
$model->fill($input)->save();
return $model;
}
}
You can also extend the default StoreResource, but this is not mandatory.
Overwrite the action handlers from a ServiceProvider.
use Uteq\Move\Resource;
public function register()
{
Resource::$defaultActionHandlers = [
'update' => StoreResource::class,
'create' => StoreResource::class,
'delete' => DeleteResource::class,
];
}
Simply add the $actionHandlers to your resource:
use Uteq\Move\Resource;
use App\Actions\CustomResource;
class CustomResource extends Resource
{
public array $actionHandlers = [
'create' => CustomResource\Create::class,
'update' => CustomResource\Update::class,
'delete' => CustomResource\Delete::class,
];
}
This will overwrite the resource specific action handlers.
The preferred way to hook into the before save is using the default Laravel events https://laravel.com/docs/eloquent#events. You can also hook into the Store action by adding a beforeSave method that provides callables
public function beforeSave()
{
return [
fn($resource, $model, $data) => $data['rand'] = rand(1, 99),
function($resource, $model, $data) {
return $data['rand'] = rand(1, 99);
},
new MyCustomBeforeSaveAction,
];
}
The preferred way to hook into the after save is using the default Laravel events https://laravel.com/docs/eloquent#events. You can also hook into the Store action by adding a afterSave method that provides callables
public function afterSave()
{
return [
fn($resource, $model, $data) => $data['rand'] = rand(1, 99),
function($resource, $model, $data) {
return $data['rand'] = rand(1, 99);
},
new MyCustomAfterSaveAction,
];
}
Whenever you need to change to way a field stores (creates or updates) it's data.
You can hook into the beforeStore
method.
Every field has this method.
Text::make('Name', 'name')
->beforeStore(function($value) {
// ... Mutate the fields value to any given format.
return $value;
});
composer test
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
Please review our security policy on how to report security vulnerabilities.
The MIT License (MIT). Please see License File for more information.