Issues (364)

app/Tenant/Manager.php (4 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Tenant;
6
7
use App\Models\Tenant;
8
use App\Models\User;
9
use Illuminate\Config\Repository;
10
use Illuminate\Contracts\Filesystem\Filesystem;
11
use Illuminate\Database\ConnectionInterface;
12
use Illuminate\Database\DatabaseManager;
13
use Illuminate\Database\Eloquent\Model;
14
use Illuminate\Support\Facades\Artisan;
15
use Illuminate\Support\Facades\Storage;
16
17
class Manager
18
{
19
    protected User $user;
20
    protected string|null $partition = null;
21
    protected string $connectionName = 'tenantdb';
22
    protected string $defaultDatabase = 'tenant';
23
    protected string $defaultConnection = 'mysql';
24
25
    public function __construct(
26
        protected string|int $tenant_id,
27
        string|int $user_id,
28
        string|int|null $database_name,
29
        protected Repository $config,
30
        protected DatabaseManager $database,
31
    ) {
32
        $this->partition = $database_name ?? "tenant{$tenant_id}";
33
    }
34
35
    public static function fromModel(Model $model, User $user): self
36
    {
37
        return app(static::class, [
0 ignored issues
show
Bug Best Practice introduced by
The expression return app(static::class...nancy_db_name ?? null)) could return the type Illuminate\Contracts\Fou...\Foundation\Application which is incompatible with the type-hinted return App\Tenant\Manager. Consider adding an additional type-check to rule them out.
Loading history...
38
            'tenant_id' => $model->getKey(),
39
            'user_id' => $user->getKey(),
40
            'database_name' => tenancy()->find($model->getKey())->tenancy_db_name ?? null,
0 ignored issues
show
Accessing tenancy_db_name on the interface Stancl\Tenancy\Contracts\Tenant suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
41
        ]);
42
    }
43
44
    public static function tenant(string $company_id, string $user_id): self
45
    {
46
        return app(static::class, [
0 ignored issues
show
Bug Best Practice introduced by
The expression return app(static::class...'user_id' => $user_id)) could return the type Illuminate\Contracts\Fou...\Foundation\Application which is incompatible with the type-hinted return App\Tenant\Manager. Consider adding an additional type-check to rule them out.
Loading history...
47
            'tenant_id' => $company_id,
48
            'user_id' => $user_id,
49
        ]);
50
    }
51
52
    public function connect(bool $default = false): self
53
    {
54
        // $this->config->set('database.connections.tenant.database', $this->partition);
55
        // $this->database->reconnect($this->connectionName);
56
        if ($default) {
57
            $this->config->set('database.default', $this->connectionName);
58
            $this->database->reconnect($this->defaultConnection);
59
        } else {
60
            $tenants = Tenant::find($this->tenant_id);
61
            $this->config->set('database.connections.tenantdb.database', $tenants->tenancy_db_name);
62
            $this->database->reconnect($this->connectionName);
63
            tenancy()->initialize($tenants);
64
        }
65
66
        return $this;
67
    }
68
69
    public function disconnect(): self
70
    {
71
        $this->config->set('database.default', $this->defaultConnection);
72
        // $this->config->set('database.connections.tenant.database', $this->defaultDatabase);
73
74
        $this->database->reconnect($this->connectionName);
75
        $this->database->reconnect($this->defaultConnection);
76
77
        $defaultDatabase = $this->config->get('database.connections.'.$this->defaultConnection.'.database');
78
79
        $this->database->statement("USE {$defaultDatabase};");
80
81
        return $this;
82
    }
83
84
    public function database(): ConnectionInterface
85
    {
86
        return $this->database->connection($this->connectionName);
87
    }
88
89
    public function databaseExists(): bool
90
    {
91
        try {
92
            if ($this->config->get('database.default') === 'sqlite') {
93
                // $this->connect(true);
94
                $this->config->set('database.default', 'tenant');
95
            }
96
97
            return $this->database->select(<<<SQL
98
        SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '{$this->partition}';
99
        SQL) !== [];
100
        } catch (\PDOException) {
101
            return false;
102
        }
103
    }
104
105
    public function createDatabase(): self
106
    {
107
        $charset = $this->config->get('database.connections.mysql.charset', 'utf8');
108
        $collate = $this->config->get('database.connections.mysql.collation', 'utf8_unicode_ci');
109
        $this->database->statement("CREATE DATABASE {$this->partition} CHARACTER SET {$charset} COLLATE {$collate};");
110
111
        // $this->database->statement("USE {$this->partition};");
112
113
        return $this;
114
    }
115
116
    public function dropDatabase(): self
117
    {
118
        $this->database->statement("DROP DATABASE {$this->partition};");
119
120
        return $this;
121
    }
122
123
    public function migrateDatabase(): self
124
    {
125
        Artisan::call('migrate:fresh', [
126
            '--realpath' => database_path('migrations/tenant'),
127
            '--database' => $this->connectionName,
128
            '--force' => true,
129
        ]);
130
131
        return $this;
132
    }
133
134
    public function hasStoragePartition(): bool
135
    {
136
        return $this->rootStorage()->exists($this->partition);
137
    }
138
139
    public function makeStoragePartition(): bool
140
    {
141
        return $this->rootStorage()->makeDirectory($this->partition);
142
    }
143
144
    public function deleteStoragePartition(): bool
145
    {
146
        return $this->rootStorage()->deleteDirectory($this->partition);
147
    }
148
149
    public function storage(): Filesystem
150
    {
151
        if (! $this->hasStoragePartition()) {
152
            $this->makeStoragePartition();
153
        }
154
155
        return Storage::build([
156
            'driver' => 'local',
157
            'root' => storage_path("tenants/{$this->partition}"),
158
        ]);
159
    }
160
161
    public function rootStorage(): Filesystem
162
    {
163
        return Storage::build([
164
            'driver' => 'local',
165
            'root' => storage_path('tenants'),
166
        ]);
167
    }
168
169
    public function storagePath(string $path): string
170
    {
171
        return storage_path("tenants/{$this->partition}/{$path}");
172
    }
173
174
    public function connectionName(): string
175
    {
176
        return $this->connectionName;
177
    }
178
179
    public function partitionName(): string
180
    {
181
        return $this->partition;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->partition could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
182
    }
183
184
    public function tenantId(): string|int
185
    {
186
        return $this->tenant_id;
187
    }
188
}
189