159 lines
4.9 KiB
PHP
159 lines
4.9 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Jobs\PullPackageFromPaddle;
|
|
use App\Jobs\SyncPackageToPaddle;
|
|
use App\Models\Package;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Str;
|
|
|
|
class PaddleSyncPackages extends Command
|
|
{
|
|
protected $signature = 'paddle:sync-packages
|
|
{--package=* : Limit sync to the given package IDs or slugs}
|
|
{--dry-run : Generate payload snapshots without calling Paddle}
|
|
{--pull : Fetch remote Paddle state instead of pushing local changes}
|
|
{--allow-unmapped : Allow sync when packages are missing Paddle product/price IDs}
|
|
{--queue : Dispatch jobs onto the queue instead of running synchronously}';
|
|
|
|
protected $description = 'Synchronise local packages with Paddle products and prices.';
|
|
|
|
public function handle(): int
|
|
{
|
|
$packages = $this->resolvePackages();
|
|
|
|
if ($packages->isEmpty()) {
|
|
$this->warn('No packages matched the given filters.');
|
|
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$dryRun = (bool) $this->option('dry-run');
|
|
$pull = (bool) $this->option('pull');
|
|
$queue = (bool) $this->option('queue');
|
|
$allowUnmapped = (bool) $this->option('allow-unmapped');
|
|
|
|
if (! $pull && ! $allowUnmapped && ! $this->hasPackageFilter()) {
|
|
if (! $this->guardUnmappedPackages($packages)) {
|
|
return self::FAILURE;
|
|
}
|
|
}
|
|
|
|
$packages->each(function (Package $package) use ($dryRun, $pull, $queue) {
|
|
if ($pull) {
|
|
$this->dispatchPullJob($package, $queue);
|
|
|
|
return;
|
|
}
|
|
|
|
$this->dispatchSyncJob($package, $dryRun, $queue);
|
|
});
|
|
|
|
$this->info(sprintf(
|
|
'Queued %d package %s for Paddle %s.',
|
|
$packages->count(),
|
|
Str::plural('entry', $packages->count()),
|
|
$pull ? 'pull' : 'sync'
|
|
));
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
protected function resolvePackages(): Collection
|
|
{
|
|
$keys = collect((array) $this->option('package'))->filter();
|
|
|
|
$query = Package::query();
|
|
|
|
if ($keys->isNotEmpty()) {
|
|
$ids = $keys
|
|
->filter(fn ($value) => is_numeric($value))
|
|
->map(fn ($value) => (int) $value);
|
|
|
|
$slugs = $keys
|
|
->reject(fn ($value) => is_numeric($value))
|
|
->values();
|
|
|
|
$query->where(function ($builder) use ($ids, $slugs) {
|
|
if ($ids->isNotEmpty()) {
|
|
$builder->orWhereIn('id', $ids);
|
|
}
|
|
|
|
if ($slugs->isNotEmpty()) {
|
|
$builder->orWhereIn('slug', $slugs);
|
|
}
|
|
});
|
|
}
|
|
|
|
return $query->orderByDesc('id')->get();
|
|
}
|
|
|
|
protected function hasPackageFilter(): bool
|
|
{
|
|
return collect((array) $this->option('package'))->filter()->isNotEmpty();
|
|
}
|
|
|
|
protected function guardUnmappedPackages(Collection $packages): bool
|
|
{
|
|
$unmapped = $packages->filter(fn (Package $package) => blank($package->paddle_product_id) || blank($package->paddle_price_id));
|
|
|
|
if ($unmapped->isEmpty()) {
|
|
return true;
|
|
}
|
|
|
|
$this->error('Unmapped Paddle package IDs detected. Resolve legacy mappings or pass --allow-unmapped.');
|
|
$this->table(
|
|
['ID', 'Slug', 'Missing'],
|
|
$unmapped->map(function (Package $package): array {
|
|
$missing = [];
|
|
if (blank($package->paddle_product_id)) {
|
|
$missing[] = 'product_id';
|
|
}
|
|
if (blank($package->paddle_price_id)) {
|
|
$missing[] = 'price_id';
|
|
}
|
|
|
|
return [
|
|
$package->id,
|
|
$package->slug,
|
|
implode(', ', $missing),
|
|
];
|
|
})->values()->all()
|
|
);
|
|
|
|
return false;
|
|
}
|
|
|
|
protected function dispatchSyncJob(Package $package, bool $dryRun, bool $queue): void
|
|
{
|
|
$context = [
|
|
'dry_run' => $dryRun,
|
|
];
|
|
|
|
if ($queue) {
|
|
SyncPackageToPaddle::dispatch($package->id, $context);
|
|
$this->line(sprintf('> queued sync for package #%d (%s)', $package->id, $package->slug));
|
|
|
|
return;
|
|
}
|
|
|
|
SyncPackageToPaddle::dispatchSync($package->id, $context);
|
|
$this->line(sprintf('> synced package #%d (%s)', $package->id, $package->slug));
|
|
}
|
|
|
|
protected function dispatchPullJob(Package $package, bool $queue): void
|
|
{
|
|
if ($queue) {
|
|
PullPackageFromPaddle::dispatch($package->id);
|
|
$this->line(sprintf('> queued pull for package #%d (%s)', $package->id, $package->slug));
|
|
|
|
return;
|
|
}
|
|
|
|
PullPackageFromPaddle::dispatchSync($package->id);
|
|
$this->line(sprintf('> pulled package #%d (%s)', $package->id, $package->slug));
|
|
}
|
|
}
|