diff --git a/src/Core/Console/VendorPublishCommand.php b/src/Core/Console/VendorPublishCommand.php
index 048346b4..f32a4aa5 100644
--- a/src/Core/Console/VendorPublishCommand.php
+++ b/src/Core/Console/VendorPublishCommand.php
@@ -6,13 +6,21 @@
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Arr;
use Illuminate\Support\ServiceProvider;
-use League\Flysystem\Adapter\Local;
+use Illuminate\Support\Str;
+use League\Flysystem\Filesystem as Flysystem;
+use League\Flysystem\Local\LocalFilesystemAdapter as LocalAdapter;
use League\Flysystem\MountManager;
+use League\Flysystem\UnixVisibility\PortableVisibilityConverter;
+use League\Flysystem\Visibility;
+use Symfony\Component\Console\Attribute\AsCommand;
+#[AsCommand(name: 'vendor:publish')]
class VendorPublishCommand extends Command
{
/**
- * @var Filesystem
+ * The filesystem instance.
+ *
+ * @var \Illuminate\Filesystem\Filesystem
*/
protected $files;
@@ -21,28 +29,51 @@ class VendorPublishCommand extends Command
*
* @var string
*/
- protected $provider;
+ protected ?string $provider = null;
/**
* The tags to publish.
*
* @var array
*/
- protected $tags = [];
+ protected array $tags = [];
/**
+ * The console command signature.
+ *
* @var string
*/
- protected $signature = 'vendor:publish {--force : Overwrite any existing files.}
- {--all : Publish assets for all service providers without prompt.}
- {--provider= : The service provider that has assets you want to publish.}
- {--tag=* : One or many tags that have assets you want to publish.}';
+ protected $signature = 'vendor:publish
+ {--existing : Publish and overwrite only the files that have already been published}
+ {--force : Overwrite any existing files}
+ {--all : Publish assets for all service providers without prompt}
+ {--provider= : The service provider that has assets you want to publish}
+ {--tag=* : One or many tags that have assets you want to publish}';
/**
+ * The name of the console command.
+ *
+ * This name is used to identify the command during lazy loading.
+ *
+ * @var string|null
+ *
+ * @deprecated
+ */
+ protected static $defaultName = 'vendor:publish';
+
+ /**
+ * The console command description.
+ *
* @var string
*/
protected $description = 'Publish any publishable assets from vendor packages';
+ /**
+ * Create a new command instance.
+ *
+ * @param \Illuminate\Filesystem\Filesystem $files
+ * @return void
+ */
public function __construct(Filesystem $files)
{
parent::__construct();
@@ -52,30 +83,31 @@ public function __construct(Filesystem $files)
/**
* Execute the console command.
+ *
+ * @return void
*/
- public function handle()
+ public function handle(): void
{
$this->determineWhatShouldBePublished();
- foreach ($this->tags ?: [null] as $tag) {
+ foreach ($this->tags as $tag) {
$this->publishTag($tag);
}
-
- $this->info('Publishing complete.');
}
/**
- * Determine the provider of tag(s) to publish.
+ * Determine the provider or tag(s) to publish.
+ *
+ * @return void
*/
- protected function determineWhatShouldBePublished()
+ protected function determineWhatShouldBePublished(): void
{
if ($this->option('all')) {
return;
}
- list($this->provider, $this->tags) = [
- $this->option('provider'),
- (array) $this->option('tag'),
+ [$this->provider, $this->tags] = [
+ $this->option('provider'), (array) $this->option('tag'),
];
if (! $this->provider && ! $this->tags) {
@@ -85,12 +117,14 @@ protected function determineWhatShouldBePublished()
/**
* Prompt for which provider or tag to publish.
+ *
+ * @return void
*/
- protected function promptForProviderOrTag()
+ protected function promptForProviderOrTag(): void
{
- $choice = $this->choice(
+ $choice = $this->components->choice(
"Which provider or tag's files would you like to publish?",
- $choices = $this->publishableChoices(),
+ $choices = $this->publishableChoices()
);
if ($choice == $choices[0] || is_null($choice)) {
@@ -101,16 +135,16 @@ protected function promptForProviderOrTag()
}
/**
- * return the choices available via the prompt.
+ * The choices available via the prompt.
*
* @return array
*/
- protected function publishableChoices()
+ protected function publishableChoices(): array
{
return array_merge(
['Publish files from all providers and tags listed below'],
- preg_filter('/^/', 'Provider: ', Arr::sort(ServiceProvider::publishableProviders())),
- preg_filter('/^/', 'Tag: ', Arr::sort(ServiceProvider::publishableGroups())),
+ preg_filter('/^/', 'Provider:> ', Arr::sort(ServiceProvider::publishableProviders())),
+ preg_filter('/^/', 'Tag:> ', Arr::sort(ServiceProvider::publishableGroups()))
);
}
@@ -118,10 +152,11 @@ protected function publishableChoices()
* Parse the answer that was given via the prompt.
*
* @param string $choice
+ * @return void
*/
- protected function parseChoice($choice)
+ protected function parseChoice(string $choice): void
{
- list($type, $value) = explode(': ', strip_tags($choice));
+ [$type, $value] = explode(': ', strip_tags($choice));
if ($type === 'Provider') {
$this->provider = $value;
@@ -131,27 +166,46 @@ protected function parseChoice($choice)
}
/**
- * Publish the assets for a tag.
+ * Publishes the assets for a tag.
*
* @param string $tag
+ * @return void
*/
- protected function publishTag($tag)
+ protected function publishTag(string $tag): void
{
- foreach ($this->pathsToPublish($tag) as $from => $to) {
+ $published = false;
+
+ $pathsToPublish = $this->pathsToPublish($tag);
+
+ if ($publishing = count($pathsToPublish) > 0) {
+ $this->components->info(sprintf(
+ 'Publishing %sassets',
+ $tag ? "[$tag] " : '',
+ ));
+ }
+
+ foreach ($pathsToPublish as $from => $to) {
$this->publishItem($from, $to);
}
+
+ if ($publishing === false) {
+ $this->components->info('No publishable resources for tag ['.$tag.'].');
+ } else {
+ $this->newLine();
+ }
}
/**
- * Get all the paths to publish.
+ * Get all of the paths to publish.
*
* @param string $tag
- *
* @return array
*/
- protected function pathsToPublish($tag)
+ protected function pathsToPublish(string $tag): array
{
- return ServiceProvider::pathsToPublish($this->provider, $tag);
+ return ServiceProvider::pathsToPublish(
+ $this->provider, $tag
+ );
}
/**
@@ -159,20 +213,19 @@ protected function pathsToPublish($tag)
*
* @param string $from
* @param string $to
+ * @return void
*/
- protected function publishItem($from, $to)
+ protected function publishItem(string $from, string $to): void
{
if ($this->files->isFile($from)) {
$this->publishFile($from, $to);
-
return;
} elseif ($this->files->isDirectory($from)) {
$this->publishDirectory($from, $to);
-
return;
}
- $this->error("Can't locate path: <{$from}>");
+ $this->components->error("Can't locate path: <{$from}>");
}
/**
@@ -180,46 +233,70 @@ protected function publishItem($from, $to)
*
* @param string $from
* @param string $to
+ * @return void
*/
- protected function publishFile($from, $to)
+ protected function publishFile(string $from, string $to): void
{
- if (! $this->files->exists($to) || $this->option('force')) {
+ if ((! $this->option('existing') && (! $this->files->exists($to) || $this->option('force')))
+ || ($this->option('existing') && $this->files->exists($to))) {
$this->createParentDirectory(dirname($to));
+
$this->files->copy($from, $to);
- $this->status($from, $to, 'File');
+
+ $this->status($from, $to, 'file');
+ } else {
+ if ($this->option('existing')) {
+ $this->components->twoColumnDetail(sprintf(
+ 'File [%s] does not exist',
+ str_replace(base_path().'/', '', $to),
+ ), 'SKIPPED>');
+ } else {
+ $this->components->twoColumnDetail(sprintf(
+ 'File [%s] already exists',
+ str_replace(base_path().'/', '', realpath($to)),
+ ), 'SKIPPED>');
+ }
}
}
/**
* Publish the directory to the given directory.
*
- * @param string $from
- * @param string $to
- *
- * @throws \League\Flysystem\FileNotFoundException
+ * @param string $from
+ * @param string $to
+ * @return void
*/
- protected function publishDirectory($from, $to)
+ protected function publishDirectory($from, $to): void
{
+ $visibility = PortableVisibilityConverter::fromArray([], Visibility::PUBLIC);
+
$this->moveManagedFiles(new MountManager([
- 'from' => new \League\Flysystem\Filesystem(new Local($from)),
- 'to' => new \League\Flysystem\Filesystem(new Local($to)),
+ 'from' => new Flysystem(new LocalAdapter($from)),
+ 'to' => new Flysystem(new LocalAdapter($to, $visibility)),
]));
- $this->status($from, $to, 'Directory');
+ $this->status($from, $to, 'directory');
}
/**
* Move all the files in the given MountManager.
*
* @param \League\Flysystem\MountManager $manager
- *
- * @throws \League\Flysystem\FileNotFoundException
+ * @return void
*/
- protected function moveManagedFiles($manager)
+ protected function moveManagedFiles(MountManager $manager): void
{
foreach ($manager->listContents('from://', true) as $file) {
- if ($file['type'] === 'file' && (! $manager->has('to://' . $file['path']) || $this->option('force'))) {
- $manager->put('to://' . $file['path'], $manager->read('from://' . $file['path']));
+ $path = Str::after($file['path'], 'from://');
+
+ if (
+ $file['type'] === 'file'
+ && (
+ (! $this->option('existing') && (! $manager->fileExists('to://'.$path) || $this->option('force')))
+ || ($this->option('existing') && $manager->fileExists('to://'.$path))
+ )
+ ) {
+ $manager->write('to://'.$path, $manager->read($file['path']));
}
}
}
@@ -228,8 +305,9 @@ protected function moveManagedFiles($manager)
* Create the directory to house the published files if needed.
*
* @param string $directory
+ * @return void
*/
- protected function createParentDirectory($directory)
+ protected function createParentDirectory(string $directory): void
{
if (! $this->files->isDirectory($directory)) {
$this->files->makeDirectory($directory, 0755, true);
@@ -242,14 +320,19 @@ protected function createParentDirectory($directory)
* @param string $from
* @param string $to
* @param string $type
+ * @return void
*/
- protected function status($from, $to, $type)
+ protected function status(string $from, string $to, string $type): void
{
- $from = str_replace(base_path(), '', realpath($from));
- $to = str_replace(base_path(), '', realpath($to));
+ $from = str_replace(base_path().'/', '', realpath($from));
- $this->line(
- "Copied {$type} [{$from}] To [{$to}]",
- );
+ $to = str_replace(base_path().'/', '', realpath($to));
+
+ $this->components->task(sprintf(
+ 'Copying %s [%s] to [%s]',
+ $type,
+ $from,
+ $to,
+ ));
}
}