programado / komando
Laravel utilities by programado, including database synchronization and reusable GraphQL file attachments.
Package info
github.com/programado-kun-pasio/komando
Type:laravel-package
pkg:composer/programado/komando
Requires
- php: >=8.4
- laravel/framework: ^11.0|^12.0|^13.0
- nuwave/lighthouse: ^6.45
- spatie/ssh: ^1.13
Requires (Dev)
- laravel/pint: ^1.0
- orchestra/testbench: ^9.0|^10.0|^11.0
- phpunit/phpunit: ^10.5|^11.0|^12.0
README
GraphQL file attachments
Komando provides reusable named file slots and unassigned file attachments for Eloquent models and Lighthouse mutations. The package includes a ready-to-use file model, file and attachment migrations, a download route and the complete attachment lifecycle. Applications only keep their slot enum, authorization and legacy data migrations.
Requirements
- Configure Lighthouse with
transactional_mutationsenabled so the parent mutation and its files share one transaction. - Enable the file module before running migrations.
- Custom file models must contain
name,mime_type,extension,sizeandmetadatacolumns.
The package migrations create files and file_attachments. Laravel derives file_id as an integer, UUID or ULID
through the configured file model. attachable_id follows Laravel's global morph key type.
Schema::morphUsingUuids(); // Or Schema::morphUsingUlids() when attachment owners use ULIDs.
Existing applications keep project-specific data migrations, for example renaming an old polymorphic table or mapping
legacy tags to slots. Set migrate_file_table to false when the application already owns the files table. Keep this
setting stable so migration rollback uses the same table ownership decision. Set migrations to false only when the
application deliberately owns all package tables.
Configuration
Publish the configuration and configure the application's file model:
php artisan vendor:publish --provider="Programado\Komando\Providers\KomandoServiceProvider" --tag="config"
'files' => [ 'enabled' => true, 'migrations' => true, 'migrate_file_table' => true, 'file_model' => Programado\Komando\Files\Models\File::class, 'attachment_model' => Programado\Komando\Files\Models\FileAttachment::class, 'factory' => Programado\Komando\Files\Services\DefaultStoredFileFactory::class, 'disk' => 'files', 'attachment_table' => 'file_attachments', 'graphql_slot_type' => 'FileSlot', 'download' => [ 'enabled' => true, 'path' => 'api/files/{file}/download', 'middleware' => [], ], ],
The default file model uses ULIDs and exposes storageName(), name(), path() and url(). Its download route is
named komando.files.download. The path and middleware can be configured without changing the generated URLs.
Applications can replace it with a custom model implementing StoredFileContract and using IsStoredFile:
use Programado\Komando\Files\Contracts\StoredFileContract; use Programado\Komando\Files\Traits\IsStoredFile; class File extends Model implements StoredFileContract { use HasUlids, IsStoredFile; }
Models that own files implement HasFileAttachmentsContract:
use Programado\Komando\Files\Contracts\HasFileAttachmentsContract; use Programado\Komando\Files\Traits\HasFileAttachments; class Workspace extends Model implements HasFileAttachmentsContract { use HasFileAttachments; }
The application owns and registers its concrete backed FileSlot enum. Alternatively, keep the default
graphql_slot_type value String and quote slot names in the schema.
Publish the shared attachment input:
php artisan vendor:publish --provider="Programado\Komando\Providers\KomandoServiceProvider" --tag="komando-files-graphql"
The directives are discovered automatically and work on output and input fields:
type Workspace { logo_light: File @fileSlot(slot: WORKSPACE_LOGO_LIGHT) files: [File!]! @fileAttachments } input UpsertWorkspaceInput { id: ID logo_light: Upload @fileSlot(slot: WORKSPACE_LOGO_LIGHT) files: FileAttachmentChangesInput @fileAttachments }
For @fileSlot, omitted input keeps the slot unchanged, an Upload replaces it and null removes it.
@fileAttachments accepts add: [Upload!] and remove: [ID!]; removal is restricted to unassigned files related to
the mutated owner. New physical files are deleted after rollback, while replaced files are only deleted after commit
and only when no attachment references remain.
Applications with additional required file columns can configure a custom implementation of
StoredFileFactoryContract.
After the database record and physical file are stored successfully, Komando dispatches StoredFileStored. The event
implements ShouldDispatchAfterCommit, so listeners only run after the complete Lighthouse transaction commits.
Database sync
Description
This Artisan command enables the synchronization of databases between a remote system and your local development environment. It creates a dump of the remote database, compresses it, transfers it to your local system, and imports it into your local database.
Requirements
Local Requirements
scp- for secure file transfer7z- for compressing/decompressing database dumpsmysql- for importing the database
Remote Requirements
mysqldump- for creating database dumps7z- for compressing database dumps
Installation
- Install the package via Composer:
composer require programado/komando
- Publish the configuration file:
php artisan vendor:publish --provider="Programado\Komando\Providers\KomandoServiceProvider" --tag="config"
- Configure your environment variables in
.env:
KOMANDO_SSH_HOST=your-remote-host.com KOMANDO_SSH_USER=app KOMANDO_SSH_PORT=22 KOMANDO_REMOTE_DB_HOST=127.0.0.1 KOMANDO_REMOTE_DB_USER=default KOMANDO_REMOTE_DB_PASSWORD=your-password
Configuration
The package uses a configuration file config/komando.php with the following structure:
return [ 'database_sync' => [ 'default_connection' => 'mysql', 'connections' => ['mysql'], // Database connections to sync 'ssh' => [ 'host' => env('KOMANDO_SSH_HOST'), 'user' => env('KOMANDO_SSH_USER', 'app'), 'port' => env('KOMANDO_SSH_PORT', 22), ], 'remote_database' => [ 'host' => env('KOMANDO_REMOTE_DB_HOST', '127.0.0.1'), 'user' => env('KOMANDO_REMOTE_DB_USER', 'default'), 'password' => env('KOMANDO_REMOTE_DB_PASSWORD'), ], 'commands' => [ 'local' => ['scp', '7z', 'mysql'], 'remote' => ['mysqldump', '7z'], ], 'compression' => [ 'level' => 9, // 7z compression level (1-9) ], 'mysqldump' => [ 'options' => ['--skip-lock-tables'], ], 'safety' => [ 'allow_production_wipe' => false, ], ], ];
Usage
php artisan komando:sync:database
The command now reads all configuration from the config file and environment variables. No command-line parameters are needed.
Configuration Options
connections: Array of database connection names to syncssh.host: SSH hostname (required)ssh.user: SSH username (default: 'app')ssh.port: SSH port (default: 22)remote_database.*: Remote database connection settingscommands.*: Required commands for local and remote systemscompression.level: 7z compression level (1-9)mysqldump.options: Additional mysqldump optionssafety.allow_production_wipe: Allow database wipe in production (default: false)
Process
The command performs the following actions for each specified database connection:
- Checks if all required commands are available on both local and remote systems
- Creates a dump of the remote database
- Compresses the dump on the remote system
- Transfers the compressed dump to your local system
- Extracts the dump locally
- Wipes your local database (with safeguards for production environments)
- Imports the dump into your local database
- Runs migrations
Security Notes
- This command wipes your local database before import! In production environments, additional confirmation is requested.
- Ensure your SSH credentials are secure.
- Avoid using production systems as the target destination.
Troubleshooting
If the command fails, check the following points:
- Are all required commands available on both local and remote systems?
- Do you have access to the remote server via SSH?
- Does the SSH user have sufficient permissions for database access?
- Are the database connection settings in your
.envfile correctly configured?