-
-
Notifications
You must be signed in to change notification settings - Fork 486
Expand file tree
/
Copy pathBroadcastingConfigBootstrapper.php
More file actions
137 lines (115 loc) · 5.84 KB
/
Copy pathBroadcastingConfigBootstrapper.php
File metadata and controls
137 lines (115 loc) · 5.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
<?php
declare(strict_types=1);
namespace Stancl\Tenancy\Bootstrappers;
use Illuminate\Broadcasting\BroadcastManager;
use Illuminate\Config\Repository;
use Illuminate\Contracts\Broadcasting\Broadcaster;
use Illuminate\Contracts\Broadcasting\Factory as BroadcastingFactory;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Broadcast;
use Stancl\Tenancy\Contracts\TenancyBootstrapper;
use Stancl\Tenancy\Contracts\Tenant;
use Stancl\Tenancy\Overrides\TenancyBroadcastManager;
/**
* Maps tenant properties to broadcasting config and overrides
* the BroadcastManager binding with TenancyBroadcastManager.
*
* @see TenancyBroadcastManager
*/
class BroadcastingConfigBootstrapper implements TenancyBootstrapper
{
/**
* Tenant properties to be mapped to config (similarly to the TenantConfigBootstrapper).
*
* For example:
* [
* 'config.key.name' => 'tenant_property',
* ]
*/
public static array $credentialsMap = [];
public static string|null $broadcaster = null;
protected array $originalConfig = [];
protected BroadcastManager|null $originalBroadcastManager = null;
protected Broadcaster|null $originalBroadcaster = null;
public static array $mapPresets = [
'pusher' => [
'broadcasting.connections.pusher.key' => 'pusher_key',
'broadcasting.connections.pusher.secret' => 'pusher_secret',
'broadcasting.connections.pusher.app_id' => 'pusher_app_id',
'broadcasting.connections.pusher.options.cluster' => 'pusher_cluster',
],
'reverb' => [
'broadcasting.connections.reverb.key' => 'reverb_key',
'broadcasting.connections.reverb.secret' => 'reverb_secret',
'broadcasting.connections.reverb.app_id' => 'reverb_app_id',
'broadcasting.connections.reverb.options.cluster' => 'reverb_cluster',
],
'ably' => [
'broadcasting.connections.ably.key' => 'ably_key',
'broadcasting.connections.ably.public' => 'ably_public',
],
];
public function __construct(
protected Repository $config,
protected Application $app
) {
static::$broadcaster ??= $config->get('broadcasting.default');
static::$credentialsMap = array_merge(static::$mapPresets[static::$broadcaster] ?? [], static::$credentialsMap);
}
public function bootstrap(Tenant $tenant): void
{
$this->originalBroadcastManager = $this->app->make(BroadcastManager::class);
$this->originalBroadcaster = $this->app->make(Broadcaster::class);
$this->setConfig($tenant);
// Make BroadcastManager resolve to TenancyBroadcastManager which always re-resolves the used broadcasters so that
// the credentials used by broadcasters are always up-to-date with the config when retrieving the broadcasters using
// the manager and gives the channels of the broadcaster from central context to the newly resolved broadcasters in tenant context.
$this->app->extend(BroadcastManager::class, function (BroadcastManager $broadcastManager) {
$originalCustomCreators = invade($broadcastManager)->customCreators;
$tenantBroadcastManager = new TenancyBroadcastManager($this->app);
// TenancyBroadcastManager inherits the custom driver creators registered in the central context so that
// custom drivers work in tenant context without having to re-register the creators manually.
foreach ($originalCustomCreators as $driver => $closure) {
$tenantBroadcastManager->extend($driver, $closure);
}
return $tenantBroadcastManager;
});
// Swap currently bound Broadcaster instance for one that's resolved through the tenant BroadcastManager.
// Note that updating broadcasting config (credentials) in tenant context doesn't update the credentials
// used by the bound Broadcaster instance. If you need to e.g. send a notification in response to
// updating tenant's broadcasting credentials in tenant context, it's recommended to
// reinitialize tenancy after updating the credentials.
$this->app->extend(Broadcaster::class, function () {
return $this->app->make(BroadcastManager::class)->connection();
});
// Clear the resolved Broadcast facade's Illuminate\Contracts\Broadcasting\Factory instance
// so that it gets re-resolved as TenancyBroadcastManager instead of the central BroadcastManager
// when used. E.g. the Broadcast::auth() call in BroadcastController::authenticate (/broadcasting/auth).
Broadcast::clearResolvedInstance(BroadcastingFactory::class);
}
public function revert(): void
{
// Change the BroadcastManager and Broadcaster singletons back to what they were before initializing tenancy
$this->app->singleton(BroadcastManager::class, fn (Application $app) => $this->originalBroadcastManager);
$this->app->singleton(Broadcaster::class, fn (Application $app) => $this->originalBroadcaster);
// Clear the resolved Broadcast facade instance so that it gets re-resolved as the central BroadcastManager
Broadcast::clearResolvedInstance(BroadcastingFactory::class);
$this->unsetConfig();
}
protected function setConfig(Tenant $tenant): void
{
foreach (static::$credentialsMap as $configKey => $storageKey) {
$override = $tenant->$storageKey;
if (array_key_exists($storageKey, $tenant->getAttributes())) {
$this->originalConfig[$configKey] ??= $this->config->get($configKey);
$this->config->set($configKey, $override);
}
}
}
protected function unsetConfig(): void
{
foreach ($this->originalConfig as $key => $value) {
$this->config->set($key, $value);
}
}
}