Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F256622
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
13 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/app/Console/Command.php b/src/app/Console/Command.php
index ca6f1104..18cf47ae 100644
--- a/src/app/Console/Command.php
+++ b/src/app/Console/Command.php
@@ -1,262 +1,264 @@
<?php
namespace App\Console;
use Illuminate\Support\Facades\DB;
abstract class Command extends \Illuminate\Console\Command
{
/**
* This needs to be here to be used.
*
* @var string
*/
protected $commandPrefix = '';
/**
* Annotate this command as being dangerous for any potential unintended consequences.
*
* Commands are considered dangerous if;
*
* * observers are deliberately not triggered, meaning that the deletion of an object model that requires the
* associated observer to clean some things up, or charge a wallet or something, are deliberately not triggered,
*
* * deletion of objects and their relations rely on database foreign keys with obscure cascading,
*
* * a command will result in the permanent, irrecoverable loss of data.
*
* @var boolean
*/
protected $dangerous = false;
/**
* Shortcut to creating a progress bar of a particular format with a particular message.
*
* @param int $count Number of progress steps
* @param string $message The description
*
* @return \Symfony\Component\Console\Helper\ProgressBar
*/
protected function createProgressBar($count, $message = null)
{
$bar = $this->output->createProgressBar($count);
$bar->setFormat(
'%current:7s%/%max:7s% [%bar%] %percent:3s%% %elapsed:7s%/%estimated:-7s% %message% '
);
if ($message) {
$bar->setMessage("{$message}...");
}
$bar->start();
return $bar;
}
/**
* Find the domain.
*
* @param string $domain Domain ID or namespace
* @param bool $withDeleted Include deleted
*
* @return \App\Domain|null
*/
public function getDomain($domain, $withDeleted = false)
{
return $this->getObject(\App\Domain::class, $domain, 'namespace', $withDeleted);
}
/**
* Find a group.
*
* @param string $group Group ID or email
* @param bool $withDeleted Include deleted
*
* @return \App\Group|null
*/
public function getGroup($group, $withDeleted = false)
{
return $this->getObject(\App\Group::class, $group, 'email', $withDeleted);
}
/**
* Find an object.
*
- * @param string $objectClass The name of the class
- * @param string $objectIdOrTitle The name of a database field to match.
- * @param string|null $objectTitle An additional database field to match.
- * @param bool $withDeleted Act as if --with-deleted was used
+ * @param string $objectClass The name of the class
+ * @param string $objectIdOrTitle The name of a database field to match.
+ * @param string|null $objectTitle An additional database field to match.
+ * @param bool $withDeleted Act as if --with-deleted was used
*
* @return mixed
*/
public function getObject($objectClass, $objectIdOrTitle, $objectTitle = null, $withDeleted = false)
{
if (!$withDeleted) {
+ // @phpstan-ignore-next-line
$withDeleted = $this->hasOption('with-deleted') && $this->option('with-deleted');
}
$object = $this->getObjectModel($objectClass, $withDeleted)->find($objectIdOrTitle);
if (!$object && !empty($objectTitle)) {
$object = $this->getObjectModel($objectClass, $withDeleted)
->where($objectTitle, $objectIdOrTitle)->first();
}
return $object;
}
/**
* Returns a preconfigured Model object for a specified class.
*
* @param string $objectClass The name of the class
* @param bool $withDeleted Include withTrashed() query
*
* @return mixed
*/
protected function getObjectModel($objectClass, $withDeleted = false)
{
if ($withDeleted) {
$model = $objectClass::withTrashed();
} else {
$model = new $objectClass();
}
if ($this->commandPrefix == 'scalpel') {
return $model;
}
$modelsWithOwner = [
\App\Wallet::class,
];
$tenantId = \config('app.tenant_id');
// Add tenant filter
if (in_array(\App\Traits\BelongsToTenantTrait::class, class_uses($objectClass))) {
$model = $model->withEnvTenantContext();
} elseif (in_array($objectClass, $modelsWithOwner)) {
$model = $model->whereExists(function ($query) use ($tenantId) {
$query->select(DB::raw(1))
->from('users')
->whereRaw('wallets.user_id = users.id')
->whereRaw('users.tenant_id ' . ($tenantId ? "= $tenantId" : 'is null'));
});
}
return $model;
}
/**
* Find a resource.
*
* @param string $resource Resource ID or email
* @param bool $withDeleted Include deleted
*
* @return \App\Resource|null
*/
public function getResource($resource, $withDeleted = false)
{
return $this->getObject(\App\Resource::class, $resource, 'email', $withDeleted);
}
/**
* Find a shared folder.
*
* @param string $folder Folder ID or email
* @param bool $withDeleted Include deleted
*
* @return \App\SharedFolder|null
*/
public function getSharedFolder($folder, $withDeleted = false)
{
return $this->getObject(\App\SharedFolder::class, $folder, 'email', $withDeleted);
}
/**
* Find the user.
*
* @param string $user User ID or email
* @param bool $withDeleted Include deleted
*
* @return \App\User|null
*/
public function getUser($user, $withDeleted = false)
{
return $this->getObject(\App\User::class, $user, 'email', $withDeleted);
}
/**
* Find the wallet.
*
* @param string $wallet Wallet ID
*
* @return \App\Wallet|null
*/
public function getWallet($wallet)
{
return $this->getObject(\App\Wallet::class, $wallet, null);
}
public function handle()
{
if ($this->dangerous) {
$this->warn(
"This command is a dangerous scalpel command with potentially significant unintended consequences"
);
$confirmation = $this->confirm("Are you sure you understand what's about to happen?");
if (!$confirmation) {
$this->info("Better safe than sorry.");
return false;
}
$this->info("Vámonos!");
}
return true;
}
/**
* Return a string for output, with any additional attributes specified as well.
*
* @param mixed $entry An object
*
* @return string
*/
protected function toString($entry)
{
/**
* Haven't figured out yet, how to test if this command implements an option for additional
* attributes.
if (!in_array('attr', $this->options())) {
return $entry->{$entry->getKeyName()};
}
*/
$str = [
$entry->{$entry->getKeyName()}
];
+ // @phpstan-ignore-next-line
foreach ($this->option('attr') as $attr) {
if ($attr == $entry->getKeyName()) {
$this->warn("Specifying {$attr} is not useful.");
continue;
}
if (!array_key_exists($attr, $entry->toArray())) {
$this->error("Attribute {$attr} isn't available");
continue;
}
if (is_numeric($entry->{$attr})) {
$str[] = $entry->{$attr};
} else {
$str[] = !empty($entry->{$attr}) ? $entry->{$attr} : "null";
}
}
return implode(" ", $str);
}
}
diff --git a/src/app/Console/Commands/Discount/MergeCommand.php b/src/app/Console/Commands/Discount/MergeCommand.php
index 561fc658..8ca61560 100644
--- a/src/app/Console/Commands/Discount/MergeCommand.php
+++ b/src/app/Console/Commands/Discount/MergeCommand.php
@@ -1,85 +1,85 @@
<?php
namespace App\Console\Commands\Discount;
use App\Console\Command;
/**
* Merge one discount (source) with another discount (target), and delete the source discount.
*
* This command re-associates the wallets that are discounted with the source discount to become discounted with the
* target discount.
*
* Optionally, update the description of the target discount.
*
* You are not allowed to merge discounts that have different discount rates.
*
* This command makes it feasible to merge existing discounts like the ones that are 100% and described as
* "It's us..", "it's us", "This is us", etc.
*
* Example usage:
*
* ```
* $ ./artisan scalpel:discount:merge \
* > 158f660b-e992-4fb9-ac12-5173b5f33807 \
* > 62af659f-17d8-4527-87c1-c69eaa26653c \
* > --description="Employee discount"
* ```
*/
class MergeCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
- protected $signature = 'discount:merge {source} {target} {--description*}';
+ protected $signature = 'discount:merge {source} {target} {--description=}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Merge one discount in to another discount, ' .
'optionally set the description, and delete the source discount';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$source = $this->getObject(\App\Discount::class, $this->argument('source'));
if (!$source) {
$this->error("No such source discount: {$source}");
return 1;
}
$target = $this->getObject(\App\Discount::class, $this->argument('target'));
if (!$target) {
$this->error("No such target discount: {$target}");
return 1;
}
if ($source->discount !== $target->discount) {
$this->error("Can't merge two discounts that have different rates");
return 1;
}
foreach ($source->wallets as $wallet) {
$wallet->discount_id = $target->id;
$wallet->timestamps = false;
$wallet->save();
}
- if ($this->option('description')) {
- $target->description = $this->option('description');
+ if ($description = $this->option('description')) {
+ $target->description = $description;
$target->save();
}
$source->delete();
}
}
diff --git a/src/app/Console/ObjectListCommand.php b/src/app/Console/ObjectListCommand.php
index 8b02a86e..95a1d898 100644
--- a/src/app/Console/ObjectListCommand.php
+++ b/src/app/Console/ObjectListCommand.php
@@ -1,61 +1,62 @@
<?php
namespace App\Console;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* This abstract class provides a means to treat objects in our model using CRUD, with the exception that
* this particular abstract class lists objects.
*/
abstract class ObjectListCommand extends ObjectCommand
{
public function __construct()
{
$this->description = "List all {$this->objectName} objects";
$this->signature = $this->commandPrefix ? $this->commandPrefix . ":" : "";
if (!empty($this->objectNamePlural)) {
$this->signature .= "{$this->objectNamePlural}";
} else {
$this->signature .= "{$this->objectName}s";
}
$classes = class_uses_recursive($this->objectClass);
if (in_array(SoftDeletes::class, $classes)) {
$this->signature .= " {--with-deleted : Include deleted {$this->objectName}s}";
}
$this->signature .= " {--attr=* : Attributes other than the primary unique key to include}";
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$classes = class_uses_recursive($this->objectClass);
+ // @phpstan-ignore-next-line
if (in_array(SoftDeletes::class, $classes) && $this->option('with-deleted')) {
$objects = $this->objectClass::withTrashed();
} else {
$objects = new $this->objectClass();
}
$objects->each(
function ($object) {
if ($object->deleted_at) {
$this->info("{$this->toString($object)} (deleted at {$object->deleted_at}");
} else {
$this->info("{$this->toString($object)}");
}
}
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jun 8, 2:49 PM (19 h, 56 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
196700
Default Alt Text
(13 KB)
Attached To
Mode
R2 kolab
Attached
Detach File
Event Timeline
Log In to Comment