• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ICanBoogie / Module / 4270677071

pending completion
4270677071

push

github

Olivier Laviale
Tidy

2 of 2 new or added lines in 2 files covered. (100.0%)

73 of 415 relevant lines covered (17.59%)

0.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

19.35
/lib/Module.php
1
<?php
2

3
/*
4
 * This file is part of the ICanBoogie package.
5
 *
6
 * (c) Olivier Laviale <olivier.laviale@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
namespace ICanBoogie;
13

14
use ICanBoogie\ActiveRecord\Model;
15
use ICanBoogie\ActiveRecord\ModelNotDefined;
16
use ICanBoogie\Module\Descriptor;
17
use ICanBoogie\Module\ModuleProvider;
18
use RuntimeException;
19
use Throwable;
20

21
use function ICanBoogie\ActiveRecord\get_model;
22
use function is_string;
23

24
/**
25
 * A module of the framework.
26
 *
27
 * @property-read string $flat_id Underscored identifier.
28
 * @property-read string $id The identifier of the module, defined by {@link Descriptor::$id}.
29
 * @property-read Model $model The primary model of the module.
30
 * @property-read Module $parent The parent module, defined by {@link Descriptor::$parent}.
31
 * @property-read string $path The path to the module, defined by {@link Descriptor::$path}.
32
 * @property-read string $title The localized title of the module.
33
 * @property-read Application $app
34
 */
35
class Module extends Prototyped
36
{
37
    /*
38
     * PERMISSIONS:
39
     *
40
     * NONE: Well, you can't do anything
41
     *
42
     * ACCESS: You can access the module and view its records
43
     *
44
     * CREATE: You can create new records
45
     *
46
     * MAINTAIN: You can edit the records you created
47
     *
48
     * MANAGE: You can delete the records you created
49
     *
50
     * ADMINISTER: You have complete control over the module
51
     *
52
     */
53
    public const PERMISSION_NONE = 0;
54
    public const PERMISSION_ACCESS = 1;
55
    public const PERMISSION_CREATE = 2;
56
    public const PERMISSION_MAINTAIN = 3;
57
    public const PERMISSION_MANAGE = 4;
58
    public const PERMISSION_ADMINISTER = 5;
59

60
    /**
61
     * Defines the name of the operation used to save the records of the module.
62
     */
63
    public const OPERATION_SAVE = 'save';
64

65
    /**
66
     * Defines the name of the operation used to delete the records of the module.
67
     */
68
    public const OPERATION_DELETE = 'delete';
69

70
    /**
71
     * Returns the identifier of the module as defined by its descriptor.
72
     *
73
     * This method is the getter for the {@link $id} magic property.
74
     */
75
    protected function get_id(): string
76
    {
77
        return $this->descriptor->id;
10✔
78
    }
79

80
    /**
81
     * Returns the path of the module as defined by its descriptor.
82
     *
83
     * This method is the getter for the {@link $path} magic property.
84
     */
85
    protected function get_path(): string
86
    {
87
        /** @phpstan-ignore-next-line */
88
        return $this->descriptor->path;
×
89
    }
90

91
    public function __construct(
92
        public readonly Descriptor $descriptor,
93
        public readonly ModuleProvider $module_provider,
94
    ) {
95
    }
11✔
96

97
    /**
98
     * Returns the identifier of the module.
99
     */
100
    public function __toString(): string
101
    {
102
        return $this->id;
×
103
    }
104

105
    /**
106
     * Returns the _flat_ version of the module's identifier.
107
     *
108
     * This method is the getter for the {@link $flat_id} magic property.
109
     */
110
    protected function get_flat_id(): string
111
    {
112
        return strtr($this->id, [
1✔
113

114
            '.' => '_',
1✔
115
            '-' => '_'
1✔
116

117
        ]);
1✔
118
    }
119

120
    /**
121
     * Returns the primary model of the module.
122
     *
123
     * This is the getter for the {@link $model} magic property.
124
     */
125
    protected function get_model(): ActiveRecord\Model
126
    {
127
        return $this->model();
1✔
128
    }
129

130
    /**
131
     * Returns the module title, translated to the current language.
132
     *
133
     * @deprecated
134
     */
135
    protected function get_title(): string
136
    {
137
        $default = $this->descriptor->title ?? 'Undefined';
×
138

139
        /** @phpstan-ignore-next-line */
140
        return $this->app->translate($this->flat_id, [], [ 'scope' => 'module_title', 'default' => $default ]);
×
141
    }
142

143
    /**
144
     * Returns the parent module.
145
     */
146
    protected function get_parent(): ?Module
147
    {
148
        $parent = $this->descriptor->parent;
1✔
149

150
        return $parent ? $this->module_provider->module_for_id($parent) : null;
1✔
151
    }
152

153
    /**
154
     * Checks if the module is installed.
155
     *
156
     * @return bool|null `true` if the module is installed, `false` if the module (or parts of) is not installed, `null`
157
     * if the module has no installation.
158
     */
159
    public function is_installed(ErrorCollection $errors): ?bool
160
    {
161
        if (!$this->descriptor->models) {
×
162
            return null;
×
163
        }
164

165
        $rc = true;
×
166

167
        foreach ($this->descriptor->models as $id) {
×
168
            if (!$this->model($id)->is_installed()) {
×
169
                $errors->add($this->id, "The model %name is not installed.", [
×
170

171
                    'name' => $id
×
172

173
                ]);
×
174

175
                $rc = false;
×
176
            }
177
        }
178

179
        return $rc;
×
180
    }
181

182
    /**
183
     * Install the module.
184
     *
185
     * If the module has models they are installed.
186
     *
187
     * @return bool|null true if the module has successfully been installed, false if the
188
     * module (or parts of the module) fails to install or null if the module has
189
     * no installation process.
190
     */
191
    public function install(ErrorCollection $errors): ?bool
192
    {
193
        if (!$this->descriptor->models) {
×
194
            return null;
×
195
        }
196

197
        $rc = true;
×
198

199
        foreach ($this->descriptor->models as $id) {
×
200
            $model = $this->model($id);
×
201

202
            if ($model->is_installed()) {
×
203
                continue;
×
204
            }
205

206
            try {
207
                $model->install();
×
208
            } catch (Throwable $e) {
×
209
                $errors->add($this->id, "Unable to install model %model: !message", [
×
210

211
                    'model' => $id,
×
212
                    'message' => $e->getMessage()
×
213

214
                ]);
×
215

216
                $rc = false;
×
217
            }
218
        }
219

220
        return $rc;
×
221
    }
222

223
    /**
224
     * Uninstall the module.
225
     *
226
     * Basically it uninstall the models installed by the module.
227
     *
228
     * @return bool|null `true` if the module was successfully uninstalled. `false` if the module
229
     * (or parts of the module) failed to uninstall. `null` if there is no uninstall process.
230
     *
231
     * @throws Throwable
232
     */
233
    public function uninstall(): ?bool
234
    {
235
        if (!$this->descriptor->models) {
×
236
            return null;
×
237
        }
238

239
        $rc = true;
×
240

241
        foreach ($this->descriptor->models as $id) {
×
242
            $model = $this->model($id);
×
243

244
            if (!$model->is_installed()) {
×
245
                continue;
×
246
            }
247

248
            $model->uninstall();
×
249
        }
250

251
        return $rc;
×
252
    }
253

254
    /**
255
     * Get a model from the module.
256
     *
257
     * If the model has not been created yet, it is created on the fly.
258
     *
259
     * @throws ModelNotDefined when the model is not defined by the module.
260
     * @throws RuntimeException when the class of the model does not exist.
261
     */
262
    public function model(string $model_id = 'primary'): Model
263
    {
264
        if ($model_id === 'primary') {
1✔
265
            $model_id = current($this->descriptor->models);
1✔
266
        }
267

268
        assert(is_string($model_id));
269

270
        return get_model($model_id);
1✔
271
    }
272

273
    /**
274
     * Get a block.
275
     *
276
     * @return mixed Depends on the implementation. Should return a string or an object
277
     * implementing `__toString`.
278
     *
279
     * @throws RuntimeException if the block is not defined.
280
     */
281
    public function getBlock(string $name)
282
    {
283
        $args = func_get_args();
×
284

285
        array_shift($args);
×
286

287
        $callback = 'block_' . $name;
×
288

289
        if (!method_exists($this, $callback)) {
×
290
            throw new RuntimeException(
×
291
                format('The %method method is missing from the %module module to create block %type.', [
×
292

293
                    '%method' => $callback,
×
294
                    '%module' => $this->id,
×
295
                    '%type' => $name
×
296

297
                ])
×
298
            );
×
299
        }
300

301
        return $this->$callback(...$args);
×
302
    }
303
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc