mirror of
https://github.com/archtechx/tenancy.git
synced 2025-12-13 02:54:05 +00:00
This adds support for tenancy aware Storage::url() method
This commit is contained in:
parent
20e1fa1959
commit
adf3daa022
7 changed files with 196 additions and 3 deletions
|
|
@ -116,6 +116,16 @@ return [
|
||||||
'public' => '%storage_path%/app/public/',
|
'public' => '%storage_path%/app/public/',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use this to support Storage url method on local driver disks.
|
||||||
|
* You should create a symbolic link which points to the public directory using command: artisan tenants:link
|
||||||
|
* Then you can use tenant aware Storage url: Storage::disk('public')->url('file.jpg');
|
||||||
|
*/
|
||||||
|
'url_override' => [
|
||||||
|
// The array key is local disk (must exist in root_override) and value is public directory (%tenant_id% will be replaced with actual tenant id).
|
||||||
|
'public' => 'storage-%tenant_id%',
|
||||||
|
],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should storage_path() be suffixed.
|
* Should storage_path() be suffixed.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ namespace Stancl\Tenancy\Bootstrappers;
|
||||||
use Illuminate\Contracts\Foundation\Application;
|
use Illuminate\Contracts\Foundation\Application;
|
||||||
use Illuminate\Filesystem\FilesystemAdapter;
|
use Illuminate\Filesystem\FilesystemAdapter;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use League\Flysystem\Adapter\Local as LocalAdapter;
|
||||||
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
|
||||||
use Stancl\Tenancy\Contracts\Tenant;
|
use Stancl\Tenancy\Contracts\Tenant;
|
||||||
|
|
||||||
|
|
@ -57,7 +58,7 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper
|
||||||
foreach ($this->app['config']['tenancy.filesystem.disks'] as $disk) {
|
foreach ($this->app['config']['tenancy.filesystem.disks'] as $disk) {
|
||||||
/** @var FilesystemAdapter $filesystemDisk */
|
/** @var FilesystemAdapter $filesystemDisk */
|
||||||
$filesystemDisk = Storage::disk($disk);
|
$filesystemDisk = Storage::disk($disk);
|
||||||
$this->originalPaths['disks'][$disk] = $filesystemDisk->getAdapter()->getPathPrefix();
|
$this->originalPaths['disks']['path'][$disk] = $filesystemDisk->getAdapter()->getPathPrefix();
|
||||||
|
|
||||||
if ($root = str_replace(
|
if ($root = str_replace(
|
||||||
'%storage_path%',
|
'%storage_path%',
|
||||||
|
|
@ -71,6 +72,22 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->app['config']["filesystems.disks.{$disk}.root"] = $finalPrefix;
|
$this->app['config']["filesystems.disks.{$disk}.root"] = $finalPrefix;
|
||||||
|
|
||||||
|
// Storage Url
|
||||||
|
if ($filesystemDisk->getAdapter() instanceof LocalAdapter) {
|
||||||
|
$config = $filesystemDisk->getDriver()->getConfig();
|
||||||
|
$this->originalPaths['disks']['url'][$disk] = $config->has('url')
|
||||||
|
? $config->get('url')
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if ($url = str_replace(
|
||||||
|
'%tenant_id%',
|
||||||
|
$tenant->getTenantKey(),
|
||||||
|
$this->app['config']["tenancy.filesystem.url_override.{$disk}"] ?? ''
|
||||||
|
)) {
|
||||||
|
$config->set('url', url($url));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,10 +105,16 @@ class FilesystemTenancyBootstrapper implements TenancyBootstrapper
|
||||||
/** @var FilesystemAdapter $filesystemDisk */
|
/** @var FilesystemAdapter $filesystemDisk */
|
||||||
$filesystemDisk = Storage::disk($disk);
|
$filesystemDisk = Storage::disk($disk);
|
||||||
|
|
||||||
$root = $this->originalPaths['disks'][$disk];
|
$root = $this->originalPaths['disks']['path'][$disk];
|
||||||
|
|
||||||
$filesystemDisk->getAdapter()->setPathPrefix($root);
|
$filesystemDisk->getAdapter()->setPathPrefix($root);
|
||||||
$this->app['config']["filesystems.disks.{$disk}.root"] = $root;
|
$this->app['config']["filesystems.disks.{$disk}.root"] = $root;
|
||||||
|
|
||||||
|
// Storage Url
|
||||||
|
if ($filesystemDisk->getAdapter() instanceof LocalAdapter && ! is_null($this->originalPaths['disks']['url'])) {
|
||||||
|
$config = $filesystemDisk->getDriver()->getConfig();
|
||||||
|
$config->set('url', $this->originalPaths['disks']['url']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
69
src/Commands/Link.php
Normal file
69
src/Commands/Link.php
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Stancl\Tenancy\Commands;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Console\StorageLinkCommand;
|
||||||
|
use Stancl\Tenancy\Concerns\HasATenantsOption;
|
||||||
|
use Stancl\Tenancy\Contracts\Tenant;
|
||||||
|
|
||||||
|
class Link extends StorageLinkCommand
|
||||||
|
{
|
||||||
|
use HasATenantsOption;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command signature.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'tenants:link
|
||||||
|
{--tenants=* : The tenant(s) to run the command for. Default: all}
|
||||||
|
{--relative : Create the symbolic link using relative paths}
|
||||||
|
{--force : Recreate existing symbolic links}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Create the symbolic links configured for the tenancy applications';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the symbolic links that are configured for the application.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function links()
|
||||||
|
{
|
||||||
|
$disk_urls = config('tenancy.filesystem.url_override');
|
||||||
|
$disks = config('tenancy.filesystem.root_override');
|
||||||
|
$suffix_base = config('tenancy.filesystem.suffix_base');
|
||||||
|
|
||||||
|
return $this->getTenants()
|
||||||
|
->map(function (Tenant $tenant) use ($suffix_base, $disk_urls, $disks) {
|
||||||
|
|
||||||
|
$map = [];
|
||||||
|
|
||||||
|
foreach ($disk_urls as $disk => $public_path) {
|
||||||
|
$storage_path = str_replace('%storage_path%', $suffix_base . $tenant['id'], $disks[$disk]);
|
||||||
|
$storage_path = storage_path($storage_path);
|
||||||
|
|
||||||
|
$public_path = str_replace('%tenant_id%', $tenant['id'], $public_path);
|
||||||
|
$public_path = public_path($public_path);
|
||||||
|
|
||||||
|
// make sure storage path exist before we create symlink
|
||||||
|
if (! is_dir($storage_path)) {
|
||||||
|
mkdir($storage_path, 0777, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$map[] = [$public_path => $storage_path];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $map;
|
||||||
|
|
||||||
|
})->flatten(1)
|
||||||
|
->mapWithKeys(fn ($item) => $item)
|
||||||
|
->all();
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/Jobs/CreateStorageSymlinks.php
Normal file
46
src/Jobs/CreateStorageSymlinks.php
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Stancl\Tenancy\Jobs;
|
||||||
|
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
use Stancl\Tenancy\Contracts\Tenant;
|
||||||
|
|
||||||
|
class CreateStorageSymlinks implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Stancl\Tenancy\Contracts\Tenant
|
||||||
|
*/
|
||||||
|
public Tenant $tenant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Tenant $tenant)
|
||||||
|
{
|
||||||
|
$this->tenant = $tenant;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
Artisan::call('tenants:link', [
|
||||||
|
'--tenants' => [$this->tenant->getTenantKey()],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -82,6 +82,7 @@ class TenancyServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
$this->commands([
|
$this->commands([
|
||||||
Commands\Run::class,
|
Commands\Run::class,
|
||||||
|
Commands\Link::class,
|
||||||
Commands\Seed::class,
|
Commands\Seed::class,
|
||||||
Commands\Install::class,
|
Commands\Install::class,
|
||||||
Commands\Migrate::class,
|
Commands\Migrate::class,
|
||||||
|
|
|
||||||
|
|
@ -210,5 +210,32 @@ class BootstrapperTest extends TestCase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function filesystem_local_storage_has_own_public_url()
|
||||||
|
{
|
||||||
|
config([
|
||||||
|
'tenancy.bootstrappers' => [
|
||||||
|
FilesystemTenancyBootstrapper::class,
|
||||||
|
],
|
||||||
|
'tenancy.filesystem.root_override.public' => '%storage_path%/app/public/',
|
||||||
|
'tenancy.filesystem.url_override.public' => 'storage-%tenant_id%'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$tenant1 = Tenant::create();
|
||||||
|
$tenant2 = Tenant::create();
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant1);
|
||||||
|
$this->assertEquals(
|
||||||
|
'http://localhost/storage-'.$tenant1->getTenantKey().'/',
|
||||||
|
Storage::disk('public')->url('')
|
||||||
|
);
|
||||||
|
|
||||||
|
tenancy()->initialize($tenant2);
|
||||||
|
$this->assertEquals(
|
||||||
|
'http://localhost/storage-'.$tenant2->getTenantKey().'/',
|
||||||
|
Storage::disk('public')->url('')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// for queues see QueueTest
|
// for queues see QueueTest
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,11 @@ class CommandsTest extends TestCase
|
||||||
|
|
||||||
config(['tenancy.bootstrappers' => [
|
config(['tenancy.bootstrappers' => [
|
||||||
DatabaseTenancyBootstrapper::class,
|
DatabaseTenancyBootstrapper::class,
|
||||||
]]);
|
],
|
||||||
|
'tenancy.filesystem.suffix_base' => 'tenant-',
|
||||||
|
'tenancy.filesystem.root_override.public' => '%storage_path%/app/public/',
|
||||||
|
'tenancy.filesystem.url_override.public' => 'storage-%tenant_id%'
|
||||||
|
]);
|
||||||
|
|
||||||
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
||||||
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
|
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
|
||||||
|
|
@ -202,4 +206,17 @@ class CommandsTest extends TestCase
|
||||||
->expectsOutput('Tenant: ' . $tenantId1)
|
->expectsOutput('Tenant: ' . $tenantId1)
|
||||||
->expectsOutput('Tenant: ' . $tenantId2);
|
->expectsOutput('Tenant: ' . $tenantId2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @test */
|
||||||
|
public function link_command_works()
|
||||||
|
{
|
||||||
|
$tenantId1 = Tenant::create()->getTenantKey();
|
||||||
|
$tenantId2 = Tenant::create()->getTenantKey();
|
||||||
|
Artisan::call('tenants:link');
|
||||||
|
|
||||||
|
$this->assertDirectoryExists(storage_path("tenant-$tenantId1/app/public"));
|
||||||
|
$this->assertDirectoryExists(public_path("storage-$tenantId1"));
|
||||||
|
$this->assertDirectoryExists(storage_path("tenant-$tenantId2/app/public"));
|
||||||
|
$this->assertDirectoryExists(public_path("storage-$tenantId2"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue