├── .env.example
├── .gitignore
├── App
├── Controllers
│ ├── AuthController.php
│ ├── ProductController.php
│ └── UserController.php
├── Core
│ ├── Blueprint.php
│ ├── Commands
│ │ ├── Concerns
│ │ │ └── MakeCommand.php
│ │ ├── CreateAuthCommand.php
│ │ ├── CreateControllerCommand.php
│ │ ├── CreateDotEnvCommand.php
│ │ ├── CreateMiddlewareCommand.php
│ │ ├── CreateMigrationCommand.php
│ │ ├── CreateModelCommand.php
│ │ ├── CreateRouteCommand.php
│ │ ├── CreateSeederCommand.php
│ │ ├── CreateUpdateCommand.php
│ │ ├── RunGlobalSeederCommand.php
│ │ ├── RunMigrateCommand.php
│ │ ├── RunMigrateDatabaseCommand.php
│ │ ├── RunMigrateRefreshCommand.php
│ │ ├── RunSeederCommand.php
│ │ ├── ServeCommand.php
│ │ └── Stubs
│ │ │ ├── Auth
│ │ │ ├── auth-controller.stub
│ │ │ ├── auth-env.stub
│ │ │ └── auth-package.stub
│ │ │ ├── controller.stub
│ │ │ ├── env.stub
│ │ │ ├── middleware.stub
│ │ │ ├── migration.stub
│ │ │ ├── model.stub
│ │ │ ├── routes
│ │ │ ├── route-group-method.stub
│ │ │ ├── route.stub
│ │ │ └── use-route.stub
│ │ │ ├── seeder.stub
│ │ │ └── update.stub
│ ├── Controller.php
│ ├── Database.php
│ ├── DotEnvKey.php
│ ├── HasTokens.php
│ ├── Input.php
│ ├── Kernel.php
│ ├── Middleware.php
│ ├── Migration.php
│ ├── Model.php
│ ├── QueryBuilder.php
│ ├── Request.php
│ ├── Responses.php
│ ├── Router.php
│ ├── Schema.php
│ ├── Seeder.php
│ ├── Upload.php
│ └── Validator.php
├── Database
│ ├── Migrations
│ │ ├── 2023_01_31_104005_create_table_users.php
│ │ └── 2023_01_31_160221_create_table_roles.php
│ └── Seeders
│ │ ├── GlobalSeeder.php
│ │ ├── RoleSeeder.php
│ │ └── UserSeeder.php
├── Helpers
│ └── TimeHelper.php
├── Middleware
│ ├── AdminMiddleware.php
│ └── AuthMiddleware.php
├── Models
│ └── User.php
├── Packages
│ └── Auth.php
└── Routes
│ └── Api.php
├── LICENSE.md
├── README.md
├── composer.json
├── mardira
└── public
├── index.php
└── logo.png
/.env.example:
--------------------------------------------------------------------------------
1 | ACCESS_TOKEN_SECRET_KEY=
2 | REFRESH_TOKEN_SECRET_KEY=
3 |
4 | # Database Connection
5 | DB_HOST=
6 | DB_USER=
7 | DB_PASS=
8 | DB_NAME=
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | vendor
3 | .env
4 | composer.lock
5 | .gitignore
6 |
--------------------------------------------------------------------------------
/App/Controllers/AuthController.php:
--------------------------------------------------------------------------------
1 | input->file('image');
14 |
15 | $validator = Validator::validate([
16 | 'image' => [
17 | 'required' => true,
18 | 'file' => true,
19 | ],
20 | ], [
21 | 'image' => $image
22 | ]);
23 |
24 | if ($validator->fails()) {
25 | $this->response(400, [
26 | 'status' => 'error',
27 | 'message' => 'Validation failed',
28 | 'data' => $validator->errors(),
29 | ]);
30 | }
31 |
32 | $upload = new Upload($image);
33 | $upload->setPath($_SERVER['DOCUMENT_ROOT'] . "/uploads/");
34 | $upload->setAllowedExtensions(['jpg','png','jpeg','JPG']);
35 | $upload->setAllowedMimeTypes(['image/jpeg', 'image/png']);
36 |
37 | $upload->setRandomName(true);
38 | $upload->validate();
39 | $upload->upload();
40 | if ($upload->isUploaded()) {
41 | $upload->move();
42 | $this->response(200, [
43 | 'status' => 'success',
44 | 'message' => 'File uploaded successfully',
45 | 'data' => [
46 | 'file' => $upload->getName(),
47 | 'path' => $upload->getPath(),
48 | ],
49 | ]);
50 | }
51 | $this->response(400, [
52 | 'status' => 'error',
53 | 'message' => 'File not uploaded',
54 | 'data' => [
55 | 'file' => $upload->getName(),
56 | 'path' => $upload->getPath(),
57 | ],
58 | ]);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/App/Controllers/UserController.php:
--------------------------------------------------------------------------------
1 | response(200, $users);
16 | }
17 |
18 | // example with query builder
19 | public function show($id)
20 | {
21 | $user = DB::table('users')->where('id', $id)->first();
22 | $this->response(200, $user);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/App/Core/Blueprint.php:
--------------------------------------------------------------------------------
1 | table = $table;
39 | $this->connection = Database::getConnection();
40 | }
41 |
42 | public function string(string $column, int $length = 255)
43 | {
44 | $this->columns[] = $column . ' VARCHAR(' . $length . ')';
45 | return $this;
46 | }
47 |
48 | public function integer(string $column, int $length = 11)
49 | {
50 | $this->columns[] = $column . ' INT (' . $length . ')';
51 | return $this;
52 | }
53 |
54 |
55 | public function mediumInteger(string $column, int $length = 8)
56 | {
57 | $this->columns[] = $column . ' MEDIUMINT(' . $length . ')';
58 | return $this;
59 | }
60 |
61 | public function smallInteger(string $column, int $length = 6)
62 | {
63 | $this->columns[] = $column . ' SMALLINT(' . $length . ')';
64 | return $this;
65 | }
66 |
67 | public function text(string $column)
68 | {
69 | $this->columns[] = $column . ' TEXT';
70 | return $this;
71 | }
72 |
73 | public function longText(string $column)
74 | {
75 | $this->columns[] = $column . ' LONGTEXT';
76 | return $this;
77 | }
78 |
79 | public function mediumText(string $column)
80 | {
81 | $this->columns[] = $column . ' MEDIUMTEXT';
82 | return $this;
83 | }
84 |
85 | public function tinyText(string $column)
86 | {
87 | $this->columns[] = $column . ' TINYTEXT';
88 | return $this;
89 | }
90 |
91 | public function char(string $column, int $length = 255)
92 | {
93 | $this->columns[] = $column . ' CHAR(' . $length . ')';
94 | return $this;
95 | }
96 |
97 | public function float(string $column, int $total = 8, int $places = 2)
98 | {
99 | $this->columns[] = $column . ' FLOAT(' . $total . ',' . $places . ')';
100 | return $this;
101 | }
102 |
103 | public function double(string $column, int $total = 8, int $places = 2)
104 | {
105 | $this->columns[] = $column . ' DOUBLE(' . $total . ',' . $places . ')';
106 | return $this;
107 | }
108 |
109 | public function decimal(string $column, int $total = 8, int $places = 2)
110 | {
111 | $this->columns[] = $column . ' DECIMAL(' . $total . ',' . $places . ')';
112 | return $this;
113 | }
114 |
115 |
116 | public function boolean(string $column)
117 | {
118 | $this->columns[] = $column . ' TINYINT(1)';
119 | return $this;
120 | }
121 |
122 | public function enum(string $column, array $values)
123 | {
124 | $this->columns[] = $column . ' ENUM(' . implode(',', $values) . ')';
125 | return $this;
126 | }
127 |
128 | public function date(string $column)
129 | {
130 | $this->columns[] = $column . ' DATE';
131 | return $this;
132 | }
133 |
134 | public function dateTime(string $column)
135 | {
136 | $this->columns[] = $column . ' DATETIME';
137 | return $this;
138 | }
139 |
140 | public function time(string $column)
141 | {
142 | $this->columns[] = $column . ' TIME';
143 | return $this;
144 | }
145 |
146 |
147 | public function timestamp(string $column)
148 | {
149 | $this->columns[] = $column . ' TIMESTAMP';
150 | return $this;
151 | }
152 |
153 | public function binary(string $column)
154 | {
155 | $this->columns[] = $column . ' BINARY';
156 | return $this;
157 | }
158 |
159 | public function blob(string $column)
160 | {
161 | $this->columns[] = $column . ' BLOB';
162 | return $this;
163 | }
164 |
165 | public function longBlob(string $column)
166 | {
167 | $this->columns[] = $column . ' LONGBLOB';
168 | return $this;
169 | }
170 |
171 | public function mediumBlob(string $column)
172 | {
173 | $this->columns[] = $column . ' MEDIUMBLOB';
174 | return $this;
175 | }
176 |
177 | public function tinyBlob(string $column)
178 | {
179 | $this->columns[] = $column . ' TINYBLOB';
180 | return $this;
181 | }
182 |
183 | public function json(string $column)
184 | {
185 | $this->columns[] = $column . ' JSON';
186 | return $this;
187 | }
188 |
189 | public function jsonb(string $column)
190 | {
191 | $this->columns[] = $column . ' JSONB';
192 | return $this;
193 | }
194 |
195 | public function year(string $column)
196 | {
197 | $this->columns[] = $column . ' YEAR';
198 | return $this;
199 | }
200 |
201 | public function timestamps()
202 | {
203 | $this->columns[] = 'created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP';
204 | $this->columns[] = 'updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP';
205 | return $this;
206 | }
207 |
208 | public function softDeletes()
209 | {
210 | $this->columns[] = 'deleted_at TIMESTAMP NULL';
211 | return $this;
212 | }
213 |
214 | public function primary(string $column): Blueprint
215 | {
216 | $this->primary = $column;
217 | return $this;
218 | }
219 |
220 | public function increment(string $column): Blueprint
221 | {
222 | $this->columns[] = $column . ' INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY';
223 | return $this;
224 | }
225 |
226 | public function smallIncrement(string $column): Blueprint
227 | {
228 | $this->columns[] = $column . ' SMALLINT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY';
229 | return $this;
230 | }
231 |
232 | public function tinyIncrement(string $column): Blueprint
233 | {
234 | $this->columns[] = $column . ' TINYINT(4) UNSIGNED AUTO_INCREMENT PRIMARY KEY';
235 | return $this;
236 | }
237 |
238 | public function foreign(string $column): Blueprint
239 | {
240 | $this->foreign = $column;
241 | return $this;
242 | }
243 |
244 | public function references(string $column): Blueprint
245 | {
246 | $this->references = $column;
247 | return $this;
248 | }
249 |
250 | public function on(string $table): Blueprint
251 | {
252 | $this->on = $table;
253 | return $this;
254 | }
255 |
256 | public function onDelete(string $action): Blueprint
257 | {
258 | $this->onDelete = $action;
259 | return $this;
260 | }
261 |
262 | public function onUpdate(string $action): Blueprint
263 | {
264 | $this->onUpdate = $action;
265 | return $this;
266 | }
267 |
268 | public function unique(string $column): Blueprint
269 | {
270 | $this->unique = $column;
271 | return $this;
272 | }
273 |
274 | public function index($column): Blueprint
275 | {
276 | // if multiple index requested
277 | if (is_array($column)) {
278 | $this->index = implode(',', $column);
279 | return $this;
280 | }
281 |
282 | $this->index = $column;
283 |
284 | return $this;
285 | }
286 |
287 | public function default(string $value): Blueprint
288 | {
289 | $this->default = $value;
290 | return $this;
291 | }
292 |
293 | public function nullable(): Blueprint
294 | {
295 | $this->nullable = true;
296 | return $this;
297 | }
298 |
299 | public function autoIncrement(): Blueprint
300 | {
301 | $this->autoIncrement = true;
302 | return $this;
303 | }
304 |
305 | public function unsigned(): Blueprint
306 | {
307 | $this->unsigned = true;
308 | return $this;
309 | }
310 |
311 | public function after(string $column): Blueprint
312 | {
313 | $this->after = $column;
314 | return $this;
315 | }
316 |
317 | public function comment(string $comment): Blueprint
318 | {
319 | $this->comment = $comment;
320 | return $this;
321 | }
322 |
323 | public function charset(string $charset): Blueprint
324 | {
325 | $this->charset = $charset;
326 | return $this;
327 | }
328 |
329 | public function collation(string $collation): Blueprint
330 | {
331 | $this->collation = $collation;
332 | return $this;
333 | }
334 |
335 | public function engine(string $engine): Blueprint
336 | {
337 | $this->engine = $engine;
338 | return $this;
339 | }
340 |
341 | public function getConnection(): PDO
342 | {
343 | return $this->connection;
344 | }
345 |
346 | public function setConnection(PDO $connection): void
347 | {
348 | $this->connection = $connection;
349 | }
350 |
351 | public function getTable(): string
352 | {
353 | return $this->table;
354 | }
355 |
356 | public function getColumns(): array
357 | {
358 | return $this->columns;
359 | }
360 |
361 | public function getPrimary()
362 | {
363 | return $this->primary;
364 | }
365 |
366 | public function getForeign()
367 | {
368 | return $this->foreign;
369 | }
370 |
371 | public function getReferences()
372 | {
373 | return $this->references;
374 | }
375 |
376 | public function getOn()
377 | {
378 | return $this->on;
379 | }
380 |
381 | public function getOnDelete()
382 | {
383 | return $this->onDelete;
384 | }
385 |
386 | public function getOnUpdate()
387 | {
388 | return $this->onUpdate;
389 | }
390 |
391 | public function getUnique()
392 | {
393 | return $this->unique;
394 | }
395 |
396 | public function getIndex()
397 | {
398 | return $this->index;
399 | }
400 |
401 | public function getDefault()
402 | {
403 | return $this->default;
404 | }
405 |
406 | public function getNullable()
407 | {
408 | return $this->nullable;
409 | }
410 |
411 | public function getAutoIncrement()
412 | {
413 | return $this->autoIncrement;
414 | }
415 |
416 | public function getUnsigned()
417 | {
418 | return $this->unsigned;
419 | }
420 |
421 | public function getAfter()
422 | {
423 | return $this->after;
424 | }
425 |
426 | public function getComment()
427 | {
428 | return $this->comment;
429 | }
430 |
431 | public function getCharset()
432 | {
433 | return $this->charset;
434 | }
435 |
436 | public function build()
437 | {
438 | $sql = 'CREATE TABLE ' . $this->table . ' (';
439 | $sql .= implode(', ', $this->columns);
440 | if ($this->primary) {
441 | $sql .= ', PRIMARY KEY (' . $this->primary . ')';
442 | }
443 | if ($this->foreign) {
444 | $sql .= ', FOREIGN KEY (' . $this->foreign . ') REFERENCES ' . $this->on . '(' . $this->references . ')';
445 | }
446 | if ($this->onDelete) {
447 | $sql .= ' ON DELETE ' . $this->onDelete;
448 | }
449 | if ($this->onUpdate) {
450 | $sql .= ' ON UPDATE ' . $this->onUpdate;
451 | }
452 | if ($this->unique) {
453 | $sql .= ', UNIQUE (' . $this->unique . ')';
454 | }
455 |
456 | if ($this->index) {
457 |
458 | $sql .= ', INDEX (' . $this->index . ')';
459 | }
460 | $sql .= ')';
461 |
462 | if ($this->charset) {
463 | $sql .= ' CHARSET=' . $this->charset;
464 | }
465 | if ($this->collation) {
466 | $sql .= ' COLLATE=' . $this->collation;
467 | }
468 | if ($this->engine) {
469 | $sql .= ' ENGINE=' . $this->engine;
470 | }
471 | if ($this->comment) {
472 | $sql .= ' COMMENT=' . $this->comment;
473 | }
474 |
475 | $sql .= ';';
476 |
477 | return $sql;
478 | }
479 |
480 | public function execute()
481 | {
482 | $sql = $this->build();
483 | // set connection from Database class
484 | $this->connection->exec($sql);
485 | }
486 |
487 | public function create()
488 | {
489 | $this->execute();
490 | }
491 |
492 | public function table(string $table): Blueprint
493 | {
494 | $this->table = $table;
495 | return $this;
496 | }
497 |
498 | public function drop()
499 | {
500 | $sql = 'DROP TABLE ' . $this->table . ';';
501 | $this->connection->exec($sql);
502 | }
503 |
504 | public function truncate()
505 | {
506 | $sql = 'TRUNCATE TABLE ' . $this->table . ';';
507 | $this->connection->exec($sql);
508 | }
509 |
510 | public function dropIfExists()
511 | {
512 | $sql = 'DROP TABLE IF EXISTS ' . $this->table . ';';
513 | $this->connection->exec($sql);
514 | }
515 |
516 | public function truncateIfExists()
517 | {
518 | $sql = 'TRUNCATE TABLE IF EXISTS ' . $this->table . ';';
519 | $this->connection->exec($sql);
520 | }
521 |
522 | public function dropColumn(string $column)
523 | {
524 | $sql = 'ALTER TABLE ' . $this->table . ' DROP COLUMN ' . $column . ';';
525 | $this->connection->exec($sql);
526 | }
527 |
528 | public function dropPrimary()
529 | {
530 | $sql = 'ALTER TABLE ' . $this->table . ' DROP PRIMARY KEY;';
531 | $this->connection->exec($sql);
532 | }
533 |
534 | public function dropForeign()
535 | {
536 | $sql = 'ALTER TABLE ' . $this->table . ' DROP FOREIGN KEY ' . $this->foreign . ';';
537 | $this->connection->exec($sql);
538 | }
539 |
540 | public function addColumn(string $column)
541 | {
542 | $sql = 'ALTER TABLE ' . $this->table . ' ADD COLUMN ' . $column . ';';
543 | $this->connection->exec($sql);
544 | }
545 |
546 | public function rename(string $table): Blueprint
547 | {
548 | $this->rename = $table;
549 | return $this;
550 | }
551 |
552 | public function renameColumn(string $column, string $newColumn): Blueprint
553 | {
554 | $this->renameColumn = $column;
555 | $this->newColumn = $newColumn;
556 | return $this;
557 | }
558 |
559 |
560 | public function __toString()
561 | {
562 | return $this->build();
563 | }
564 |
565 | public function __destruct()
566 | {
567 | $this->connection = null;
568 | }
569 |
570 | public function __clone()
571 | {
572 | $this->connection = null;
573 | }
574 | }
575 |
--------------------------------------------------------------------------------
/App/Core/Commands/Concerns/MakeCommand.php:
--------------------------------------------------------------------------------
1 | getStub());
10 |
11 | $replacements = $this->getReplacements($name, $model);
12 |
13 | $stub = str_replace(
14 | array_keys($replacements),
15 | array_values($replacements),
16 | $stub
17 | );
18 |
19 | $fileName = $this->getFileName($name);
20 |
21 | $filePath = $this->getFilePath($fileName);
22 |
23 | file_put_contents($filePath, $stub);
24 | }
25 |
26 | protected function getFilePath($fileName)
27 | {
28 | return $this->getNamespacePath() . '/' . $fileName;
29 | }
30 |
31 | protected function getNamespacePath()
32 | {
33 | $namespace = $this->getNamespace();
34 |
35 | $namespace = str_replace('\\', '/', $namespace);
36 |
37 | return $namespace;
38 | }
39 |
40 | protected function getReplacements($name, $model)
41 | {
42 | return [];
43 | }
44 |
45 | protected function getNamespace()
46 | {
47 | return '';
48 | }
49 |
50 | protected function getFileName($name)
51 | {
52 | return $name . '.php';
53 | }
54 |
55 | protected function getClassName($name)
56 | {
57 | return $name;
58 | }
59 |
60 | protected function getStub()
61 | {
62 | return '';
63 | }
64 |
65 | public function createFile($stub, $namespace, $fileName, $replacements)
66 | {
67 | $stub = file_get_contents($stub);
68 |
69 | $stub = str_replace(
70 | array_keys($replacements),
71 | array_values($replacements),
72 | $stub
73 | );
74 |
75 | $filePath = $this->getFilePath($namespace, $fileName);
76 |
77 | file_put_contents($filePath, $stub);
78 | }
79 | }
--------------------------------------------------------------------------------
/App/Core/Commands/CreateAuthCommand.php:
--------------------------------------------------------------------------------
1 | setName($this->commandName)
24 | ->setDescription($this->commandDescription)
25 | ->addOption(
26 | $this->commandOptionName,
27 | null,
28 | InputOption::VALUE_NONE,
29 | $this->commandOptionDescription
30 | );
31 | }
32 |
33 | protected function execute(InputInterface $input, OutputInterface $output)
34 | {
35 | // if refresh Auth
36 |
37 | if ($input->getOption($this->commandOptionName)) {
38 | $this->makeAuthPackages();
39 | $this->makeAuthController();
40 | $output->writeln("Auth refreshed successfully.");
41 | return;
42 | }
43 |
44 | // if packages created
45 | if ($this->authPackagesAlreadyExist()) {
46 | $output->writeln("Auth already exists!");
47 | return;
48 | }
49 | // if controller created
50 |
51 | if ($this->authControllerAlreadyExist()) {
52 | $output->writeln("Auth already exists!");
53 | return;
54 | }
55 |
56 | $this->makeAuthPackages();
57 | $this->makeAuthController();
58 | $this->makeAuthEnv();
59 | $output->writeln("Auth created successfully.");
60 | }
61 |
62 | protected function getStubPackage()
63 | {
64 | return file_get_contents(__DIR__ . '/Stubs/Auth/auth-package.stub');
65 | }
66 |
67 | protected function getFileNamePackges()
68 | {
69 | return 'Auth.php';
70 | }
71 |
72 | protected function getFilePathAuthPackages()
73 | {
74 | return __DIR__ . '/../../Packages/Auth.php';
75 | }
76 |
77 | protected function authPackagesAlreadyExist()
78 | {
79 | return file_exists($this->getFilePathAuthPackages());
80 | }
81 |
82 | protected function getStubController()
83 | {
84 | return file_get_contents(__DIR__ . '/Stubs/Auth/auth-controller.stub');
85 | }
86 |
87 | protected function getFileNameController()
88 | {
89 | return 'AuthController.php';
90 | }
91 |
92 | protected function getFilePathAuthController()
93 | {
94 | return __DIR__ . '/../../Controllers/AuthController.php';
95 | }
96 |
97 | protected function authControllerAlreadyExist()
98 | {
99 | return file_exists($this->getFilePathAuthController());
100 | }
101 |
102 | protected function getStubEnv()
103 | {
104 | return file_get_contents(__DIR__ . '/Stubs/Auth/auth-env.stub');
105 | }
106 |
107 | protected function getFileNameEnv()
108 | {
109 | return '.env';
110 | }
111 |
112 | protected function getFilePathAuthEnv()
113 | {
114 | return __DIR__ . '/../../../.env';
115 | }
116 |
117 | protected function makeAuthPackages()
118 | {
119 | $stub = $this->getStubPackage();
120 | $stub = str_replace(
121 | ['{{namespace}}'],
122 | ['App\Packages'],
123 | $stub
124 | );
125 | file_put_contents($this->getFilePathAuthPackages(), $stub);
126 | }
127 |
128 | protected function makeAuthController()
129 | {
130 | $stub = $this->getStubController();
131 | $stub = str_replace(
132 | ['{{namespace}}'],
133 | ['App\Http\Controllers'],
134 | $stub
135 | );
136 | file_put_contents($this->getFilePathAuthController(), $stub);
137 | }
138 |
139 | protected function makeAuthEnv()
140 | {
141 | $stub = $this->getStubEnv();
142 | file_put_contents($this->getFilePathAuthEnv(), $stub, FILE_APPEND);
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/App/Core/Commands/CreateControllerCommand.php:
--------------------------------------------------------------------------------
1 | setName($this->commandName)
31 | ->setDescription($this->commandDescription)
32 | ->addArgument(
33 | $this->commandArgumentName,
34 | InputArgument::OPTIONAL,
35 | $this->commandArgumentDescription
36 | )
37 | ->addOption(
38 | $this->commandOptionName,
39 | null,
40 | InputOption::VALUE_OPTIONAL,
41 | $this->commandOptionDescription
42 | )->addOption(
43 | $this->commandRouteName,
44 | null,
45 | InputOption::VALUE_NONE,
46 | $this->commandRouteDescription
47 | );
48 | }
49 |
50 | protected function execute(InputInterface $input, OutputInterface $output)
51 | {
52 | $name = $input->getArgument('name');
53 | $model = $input->getOption('model');
54 |
55 | if (!$name) {
56 | $infoText = "What is the name of the controller?";
57 | $blueText = "\033[34m" . $infoText . "\033[0m";
58 | $name = $this->ask($blueText);
59 | if (!$name) {
60 | $infoText = "Controller name is required!";
61 | $redText = "\033[31m" . $infoText . "\033[0m";
62 | $output->writeln("{$redText}");
63 | return;
64 | }
65 | }
66 |
67 | // check if controller already exists
68 | if ($this->alreadyExists($name)) {
69 | $infoText = "Controller {$name} already exists!";
70 | $yellowText = "\033[33m" . $infoText . "\033[0m";
71 | $output->writeln("{$yellowText}");
72 | return;
73 | }
74 |
75 | $infoText = "Controller {$name} created successfully.";
76 | $greenText = "\033[32m" . $infoText . "\033[0m";
77 | $this->make($name, $model);
78 | $output->writeln("{$greenText}");
79 | // if use model option run command CreateModelCommand
80 | if ($model) {
81 | $this->runCreateModelCommand($model);
82 | }
83 |
84 | $inputs = $input->getOptions();
85 |
86 | if ($inputs['route']) {
87 | $this->runCreateRouteCommand($name);
88 | }
89 | }
90 |
91 | public function ask($question)
92 | {
93 | $handle = fopen("php://stdin", "r");
94 | echo $question . " ";
95 | $line = fgets($handle);
96 | return trim($line);
97 | }
98 |
99 | public function runCreateRouteCommand($name)
100 | {
101 | $command = $this->getApplication()->find('make:route');
102 | $arguments = [
103 | 'command' => 'make:route',
104 | '--controller' => $name,
105 | ];
106 | $input = new \Symfony\Component\Console\Input\ArrayInput($arguments);
107 | $output = new \Symfony\Component\Console\Output\ConsoleOutput();
108 | $command->run($input, $output);
109 | }
110 |
111 | // method to run CreateModelCommand
112 | public function runCreateModelCommand($model)
113 | {
114 | $command = $this->getApplication()->find('make:model');
115 | $arguments = [
116 | 'command' => 'make:model',
117 | 'name' => $model,
118 | ];
119 | $input = new \Symfony\Component\Console\Input\ArrayInput($arguments);
120 | $output = new \Symfony\Component\Console\Output\ConsoleOutput();
121 | $command->run($input, $output);
122 | }
123 |
124 | protected function alreadyExists($name)
125 | {
126 | return file_exists($this->getFilePath($this->getFileName($name)));
127 | }
128 |
129 | protected function getStub()
130 | {
131 | if (!file_exists($this->getStubPath())) {
132 | throw new \Exception('Stub not found');
133 | }
134 |
135 | return $this->getStubPath();
136 | }
137 |
138 | protected function getStubPath()
139 | {
140 | return __DIR__ . '/Stubs/controller.stub';
141 | }
142 |
143 | protected function getNamespace()
144 | {
145 | return 'App\Controllers';
146 | }
147 |
148 | protected function getFileName($name)
149 | {
150 | return $name . '.php';
151 | }
152 |
153 | protected function getReplacements($name, $model)
154 | {
155 | $replacements = [
156 | 'DummyNamespace' => $this->getNamespace(),
157 | 'CoreController' => 'App\Core\Controller',
158 | 'DummyClass' => $name,
159 | 'DummyParentClass' => 'Controller',
160 | 'DummyModel' => $model,
161 | ];
162 | return $replacements;
163 | }
164 |
165 | protected function createSubFolder($name)
166 | {
167 | $folderName = explode('/', $name);
168 | $folderName = $folderName;
169 | $folderPath = '';
170 | foreach ($folderName as $key => $value) {
171 | if ($key == count($folderName) - 1) {
172 | break;
173 | }
174 |
175 | $folderPath .= $value . '/';
176 |
177 | if (!file_exists($this->getFilePath($folderPath))) {
178 | mkdir($this->getFilePath($folderPath));
179 | }
180 | }
181 | }
182 |
183 | protected function make($name, $model)
184 | {
185 | $stub = file_get_contents($this->getStub());
186 |
187 | $replacements = $this->getReplacements($name, $model);
188 |
189 | $stub = str_replace(
190 | array_keys($replacements),
191 | array_values($replacements),
192 | $stub
193 | );
194 |
195 | $fileName = $this->getFileName($name);
196 |
197 | $filePath = $this->getFilePath($fileName);
198 |
199 | // create sub folder if it doesn't exist
200 | if (strpos($name, '/') !== false) {
201 | $this->createSubFolder($name);
202 | $folderName = explode('/', $name);
203 | // replace namespace
204 |
205 | $namespace = $this->getNamespace();
206 | foreach ($folderName as $key => $value) {
207 | if ($key == count($folderName) - 1) {
208 | break;
209 | }
210 |
211 | $namespace .= '\\' . $value;
212 | }
213 |
214 | $stub = str_replace(
215 | $replacements['DummyNamespace'],
216 | $namespace,
217 | $stub
218 | );
219 |
220 | // replace class name
221 | $className = explode('/', $name);
222 | $stub = str_replace(
223 | $replacements['DummyClass'],
224 | end($className),
225 | $stub
226 | );
227 | }
228 |
229 | file_put_contents($filePath, $stub);
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/App/Core/Commands/CreateDotEnvCommand.php:
--------------------------------------------------------------------------------
1 | setName($this->commandName)
19 | ->setDescription($this->commandDescription);
20 | }
21 |
22 | protected function execute(InputInterface $input, OutputInterface $output)
23 | {
24 |
25 | // check if .env exist
26 | if ($this->alreadyExists()) {
27 | $output->writeln(".env already exists!");
28 | return;
29 | }
30 |
31 | $this->make();
32 | $output->writeln(".env created successfully.");
33 | }
34 |
35 | protected function alreadyExists()
36 | {
37 | return file_exists($this->getFilePath($this->getFileName('.env')));
38 | }
39 |
40 | protected function getStub()
41 | {
42 | if (!file_exists($this->getStubPath())) {
43 | throw new \Exception('Stub not found');
44 | }
45 |
46 | return $this->getStubPath();
47 | }
48 |
49 | protected function getStubPath()
50 | {
51 | return __DIR__ . '/Stubs/env.stub';
52 | }
53 |
54 | protected function getFileName($name)
55 | {
56 | return $name;
57 | }
58 |
59 | protected function getReplacements()
60 | {
61 | // string random 64 characters different from each other and generate
62 | $accessToken = bin2hex(random_bytes(32));
63 | $refreshToken = bin2hex(random_bytes(32));
64 | $replacements = [
65 | 'DummyAccessToken' => $accessToken,
66 | 'DummyRefreshToken' => $refreshToken,
67 | 'DummyLocalhost' => 'localhost',
68 | 'DummyUsername' => 'root',
69 | 'DummyPassword' => '',
70 | 'DummyDbName' => 'mardira',
71 | ];
72 | return $replacements;
73 | }
74 |
75 | protected function getFilePath($name)
76 | {
77 | return __DIR__ . '/../../../' . $name;
78 | }
79 |
80 | protected function make()
81 | {
82 | $stub = $this->getStub();
83 | $replacements = $this->getReplacements();
84 | $filePath = $this->getFilePath($this->getFileName('.env'));
85 |
86 | $file = file_get_contents($stub);
87 |
88 | foreach ($replacements as $key => $value) {
89 | $file = str_replace("{{ $key }}", $value, $file);
90 | }
91 |
92 | file_put_contents($filePath, $file);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/App/Core/Commands/CreateMiddlewareCommand.php:
--------------------------------------------------------------------------------
1 | setName($this->commandName)
26 | ->setDescription($this->commandDescription)
27 | ->addArgument(
28 | $this->commandArgumentName,
29 | InputOption::VALUE_REQUIRED,
30 | $this->commandArgumentDescription
31 | );
32 | }
33 |
34 | protected function execute(InputInterface $input, OutputInterface $output)
35 | {
36 | $name = $input->getArgument('name');
37 |
38 | // check if middleware already exists
39 | if ($this->alreadyExists($name)) {
40 | $output->writeln("Middleware already exists!");
41 | return;
42 | }
43 |
44 | $this->make($name);
45 | $output->writeln("Middleware created successfully.");
46 | }
47 |
48 | protected function alreadyExists($name)
49 | {
50 | return file_exists($this->getFilePath($this->getFileName($name)));
51 | }
52 |
53 | protected function getFileName($name)
54 | {
55 | return $name . '.php';
56 | }
57 |
58 | protected function getNamespace()
59 | {
60 | return 'App\Middleware';
61 | }
62 |
63 | protected function getStub()
64 | {
65 | return __DIR__ . '/Stubs/middleware.stub';
66 | }
67 |
68 | protected function getReplacements($name)
69 | {
70 | $replacements = [
71 | 'DummyNamespace' => $this->getNamespace(),
72 | 'CoreMiddleware' => 'App\Core\Middleware',
73 | 'DummyClass' => $name,
74 | 'DummyParentClass' => 'Middleware',
75 | ];
76 |
77 | return $replacements;
78 | }
79 |
80 | protected function make($name)
81 | {
82 | $stub = file_get_contents($this->getStub());
83 | $replacements = $this->getReplacements($name);
84 |
85 | $stub = str_replace(
86 | array_keys($replacements),
87 | array_values($replacements),
88 | $stub
89 | );
90 |
91 | $fileName = $this->getFileName($name);
92 | $filePath = $this->getFilePath($fileName);
93 |
94 |
95 | file_put_contents($filePath, $stub);
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/App/Core/Commands/CreateMigrationCommand.php:
--------------------------------------------------------------------------------
1 | setName($this->commandName)
28 | ->setDescription($this->commandDescription)
29 | ->addArgument(
30 | $this->commandArgumentName,
31 | InputArgument::REQUIRED,
32 | $this->commandArgumentDescription
33 | )->addOption(
34 | $this->commandOptionName,
35 | null,
36 | InputOption::VALUE_OPTIONAL,
37 | $this->commandOptionDescription
38 | );
39 | }
40 |
41 | protected function execute(InputInterface $input, OutputInterface $output): void
42 | {
43 | $name = $input->getArgument('name');
44 | // if table null
45 | $table = $input->getOption('table') ?? $name;
46 |
47 | $this->make($name, $table);
48 |
49 | $output->writeln("Migration created successfully.");
50 | }
51 |
52 | protected function getStub(): string
53 | {
54 | if (!file_exists($this->getStubPath())) {
55 | throw new \Exception('Stub not found');
56 | }
57 |
58 | return $this->getStubPath();
59 | }
60 |
61 | protected function getStubPath(): string
62 | {
63 | return __DIR__ . '/Stubs/migration.stub';
64 | }
65 |
66 | protected function getDestinationPath(): string
67 | {
68 | return __DIR__ . 'App/Database/Migrations';
69 | }
70 |
71 | protected function getDestinationFileName(string $name): string
72 | {
73 | return date('Y_m_d_His') . '_' . $name . '.php';
74 | }
75 |
76 | protected function getReplacements(string $name, string $table): array
77 | {
78 | return [
79 | 'DummyClass' => $name,
80 | 'DummyNamespace' => $this->getNamespace(),
81 | 'DummyParentClass' => 'Migration',
82 | 'DummyMigrationNamespace' => $this->getMigrationNamespace(),
83 | 'DummyParentClass' => $this->getMigrationClassName(),
84 | 'DummySchemaNameSpace' => $this->getSchemaNamespace(),
85 | 'DummySchemaClassName' => $this->getSchemaClassName(),
86 | 'DummyBlueprintNameSpace' => $this->getBlueprintNamespace(),
87 | 'DummyBlueprintClassName' => $this->getBlueprintClassName(),
88 | 'DummyMigrationCoreNamespace' => $this->getMigrationCoreNamespace(),
89 | 'DummyTable' => $this->getTable($table)
90 | ];
91 | }
92 |
93 | protected function getTable(string $table)
94 | {
95 | $table = strtolower($table);
96 | $table = explode('_', $table);
97 | $table = end($table);
98 | return $table;
99 | }
100 |
101 | protected function getMigrationNamespace(): string
102 | {
103 | return 'App\Database\Migration';
104 | }
105 |
106 | protected function getMigrationClassName(): string
107 | {
108 | return 'Migration';
109 | }
110 |
111 | protected function getMigrationCoreNamespace(): string
112 | {
113 | return 'App\Core\Migration';
114 | }
115 |
116 | protected function getSchemaNamespace(): string
117 | {
118 | return 'App\Core\Schema';
119 | }
120 |
121 | protected function getSchemaClassName(): string
122 | {
123 | return 'Schema';
124 | }
125 |
126 | protected function getBlueprintNamespace(): string
127 | {
128 | return 'App\Core\Blueprint';
129 | }
130 |
131 | protected function getBlueprintClassName(): string
132 | {
133 | return 'Blueprint';
134 | }
135 |
136 | protected function getNamespace(): string
137 | {
138 | return 'App\Database\Migrations';
139 | }
140 |
141 | protected function getClassName(string $name): string
142 | {
143 | return $name;
144 | }
145 |
146 | protected function getFileName(string $name): string
147 | {
148 | return $name;
149 | }
150 |
151 | protected function make(string $name, string $table): void
152 | {
153 | $stub = file_get_contents($this->getStub());
154 |
155 | $replacements = $this->getReplacements($name, $table);
156 |
157 | $stub = str_replace(
158 | array_keys($replacements),
159 | array_values($replacements),
160 | $stub
161 | );
162 |
163 | $fileName = $this->getDestinationFileName($name);
164 |
165 | $filePath = $this->getFilePath($fileName);
166 |
167 | file_put_contents($filePath, $stub);
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/App/Core/Commands/CreateModelCommand.php:
--------------------------------------------------------------------------------
1 | setName($this->commandName)
29 | ->setDescription($this->commandDescription)
30 | ->addArgument(
31 | $this->commandArgumentName,
32 | InputArgument::REQUIRED,
33 | $this->commandArgumentDescription
34 | )->addOption(
35 | $this->commandOptionName,
36 | null,
37 | InputOption::VALUE_OPTIONAL,
38 | $this->commandOptionDescription
39 | );
40 | }
41 |
42 | protected function execute(InputInterface $input, OutputInterface $output): void
43 | {
44 |
45 | $name = $input->getArgument('name');
46 | $table = $input->getOption('table');
47 |
48 | // if model exist
49 |
50 | if (file_exists($this->getFilePath($this->getFileName($name)))) {
51 | $infoText = "Model {$name} already exists!";
52 | $yellowText = "\033[33m" . $infoText . "\033[0m";
53 | $output->writeln("{$yellowText}");
54 | return;
55 | }
56 |
57 | $this->make($name, $table);
58 |
59 | // text terminal green color
60 |
61 | $infoText = "Model created successfully.";
62 | $greenText = "\033[32m" . $infoText . "\033[0m";
63 |
64 | $output->writeln("{$greenText}");
65 | }
66 |
67 | protected function getStub(): string
68 | {
69 | if (!file_exists($this->getStubPath())) {
70 | throw new \Exception('Stub not found');
71 | }
72 |
73 | return $this->getStubPath();
74 | }
75 |
76 | protected function getStubPath(): string
77 | {
78 | return __DIR__ . '/Stubs/model.stub';
79 | }
80 |
81 | protected function getNamespace(): string
82 | {
83 | return 'App\Models';
84 | }
85 |
86 | protected function pluralize(string $name): string
87 | {
88 | $inflector = InflectorFactory::create()->build();
89 | return $inflector->pluralize($name);
90 | }
91 |
92 | protected function getFileName(string $name): string
93 | {
94 | return $name . '.php';
95 | }
96 |
97 | protected function getReplacements($name, $table): array
98 | {
99 | $replacements = [
100 | 'DummyNamespace' => $this->getNamespace(),
101 | 'CoreModel' => 'App\Core\Model',
102 | 'DummyClass' => $name,
103 | 'DummyParentClass' => 'Model',
104 | 'DummyTable' => $table ?? strtolower($this->pluralize($name)),
105 | 'DummyPrimaryKey' => 'id',
106 | ];
107 | return $replacements;
108 | }
109 |
110 | protected function make($name, $model): void
111 | {
112 | $stub = file_get_contents($this->getStub());
113 |
114 | $replacements = $this->getReplacements($name, $model);
115 |
116 | $stub = str_replace(
117 | array_keys($replacements),
118 | array_values($replacements),
119 | $stub
120 | );
121 |
122 | $fileName = $this->getFileName($name);
123 |
124 | $filePath = $this->getFilePath($fileName);
125 |
126 | file_put_contents($filePath, $stub);
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/App/Core/Commands/CreateRouteCommand.php:
--------------------------------------------------------------------------------
1 | [
34 | 'name' => 'controller',
35 | 'shortName' => 'c',
36 | 'input' => InputOption::VALUE_OPTIONAL,
37 | 'description' => 'Generate a resource controller for the given model',
38 | ],
39 | 'parameter' => [
40 | 'name' => 'parameter',
41 | 'shortName' => 'p',
42 | 'input' => InputOption::VALUE_OPTIONAL,
43 | 'description' => 'Parameter of the route',
44 | ],
45 | 'get' => [
46 | 'name' => 'get',
47 | 'shortName' => null,
48 | 'input' => InputOption::VALUE_NONE,
49 | 'description' => 'Create a new get route',
50 | ],
51 | 'post' => [
52 | 'name' => 'post',
53 | 'shortName' => null,
54 | 'input' => InputOption::VALUE_NONE,
55 | 'description' => 'Create a new post route',
56 | ],
57 | 'put' => [
58 | 'name' => 'put',
59 | 'shortName' => null,
60 | 'input' => InputOption::VALUE_NONE,
61 | 'description' => 'Create a new put route',
62 | ],
63 | 'patch' => [
64 | 'name' => 'patch',
65 | 'shortName' => null,
66 | 'input' => InputOption::VALUE_NONE,
67 | 'description' => 'Create a new patch route',
68 | ],
69 | 'delete' => [
70 | 'name' => 'delete',
71 | 'shortName' => null,
72 | 'input' => InputOption::VALUE_NONE,
73 | 'description' => 'Create a new delete route',
74 | ],
75 | 'options' => [
76 | 'name' => 'options',
77 | 'shortName' => null,
78 | 'input' => InputOption::VALUE_NONE,
79 | 'description' => 'Create a new options route',
80 | ],
81 | ];
82 |
83 | protected function configure()
84 | {
85 | $this->setName($this->commandName)
86 | ->setDescription($this->commandDescription)
87 | ->addArgument(
88 | $this->commandArgumentName,
89 | InputArgument::OPTIONAL,
90 | $this->commandArgumentDescription
91 | );
92 | foreach ($this->commandOptions as $option) {
93 | $this->addOption(
94 | $option['name'],
95 | $option['shortName'],
96 | $option['input'],
97 | $option['description']
98 | );
99 | }
100 | }
101 |
102 | protected function execute(InputInterface $input, OutputInterface $output)
103 | {
104 | $name = $input->getArgument('name');
105 | $controller = $input->getOption('controller');
106 | // remove any = from controller name
107 | $controller = str_replace('=', '', $controller);
108 | $parameter = $input->getOption('parameter');
109 |
110 | // remove any = from parameter name
111 | $parameter = str_replace('=', '', $parameter);
112 | // if method is not set, ask user to set it
113 | $methodName = strpos($name, ':') !== false ? '' : $name;
114 | if (!$name || strpos($name, ':') !== false) {
115 | $infoText = "Method is not set. Please type method name: ";
116 | $yellowText = "\033[33m" . $infoText . "\033[0m";
117 | $methodName = $this->ask($yellowText);
118 | if ($methodName == '') {
119 | $infoText = "Method is not set, route not created.";
120 | $blueText = "\033[34m" . $infoText . "\033[0m";
121 | $output->writeln($blueText);
122 | return;
123 | }
124 | }
125 |
126 | $inputs = $input->getOptions();
127 |
128 | $httpVerbs = '';
129 |
130 | foreach ($inputs as $key => $value) {
131 | if (in_array($key, $this->methodList)) {
132 | if ($value) {
133 | $httpVerbs = $key;
134 | }
135 | }
136 | }
137 |
138 | $httpVerbs = $httpVerbs ? $httpVerbs : 'get';
139 |
140 |
141 | // if controller is not set, ask user to set it
142 | if (!$controller) {
143 | $infoText = "Controller is not set. Please type controller name: ";
144 | $yellowText = "\033[33m" . $infoText . "\033[0m";
145 | $controller = $this->ask($yellowText);
146 | if ($controller == '') {
147 | $infoText = "Controller is not set, route not created.";
148 | $blueText = "\033[34m" . $infoText . "\033[0m";
149 | $output->writeln($blueText);
150 | return;
151 | }
152 | }
153 |
154 | // if controller does not exist from file, ask if user wants to create it with option y/n
155 | if (!(file_exists('App/Controllers/' . $controller . '.php'))) {
156 | $infoText = "Controller does not exist. Do you want to create it? (y/n)";
157 | $yellowText = "\033[33m" . $infoText . "\033[0m";
158 | $ask = $this->ask($yellowText);
159 | if ($ask == 'y' || $ask == 'Y') {
160 | $command = $this->getApplication()->find('make:controller');
161 | $arguments = [
162 | 'command' => 'make:controller',
163 | 'name' => $controller,
164 | ];
165 | $input = new \Symfony\Component\Console\Input\ArrayInput($arguments);
166 | $output = new \Symfony\Component\Console\Output\ConsoleOutput();
167 | $command->run($input, $output);
168 | } else {
169 | $infoText = "Controller not created.";
170 | $blueText = "\033[34m" . $infoText . "\033[0m";
171 | $output->writeln($blueText);
172 | return;
173 | }
174 | }
175 |
176 | $this->generateRoute($name, $methodName, $controller, $parameter, $httpVerbs);
177 |
178 | //
179 | $splitRoute = explode('/', $name);
180 | // filter splitRoute only has parameter
181 | if (strpos($name, ':') !== false) {
182 | $splitRoute = array_filter($splitRoute, function ($item) {
183 | return strpos($item, ':') !== false;
184 | });
185 | // remove : and implode with comma
186 | $parameter = implode(',', str_replace(':', '', $splitRoute));
187 | }
188 |
189 | // if method does not exist from controller automatically create it
190 | $findMethod = $this->findMethod($controller, $methodName, $parameter);
191 | if (!$findMethod) {
192 | $infoText = "Method {$methodName} from controller {$controller} does not exist!";
193 | $yellowText = "\033[33m" . $infoText . "\033[0m";
194 | // if its has parameter, create method with parameter method
195 | if ($parameter) {
196 | $infoText = "Creating method {$methodName} with parameter {$parameter} from controller {$controller}...";
197 | $blueText = "\033[34m" . $infoText . "\033[0m";
198 | } else {
199 | $infoText = "Creating method {$methodName} from controller {$controller}...";
200 | $blueText = "\033[34m" . $infoText . "\033[0m";
201 | }
202 | $infoText = "Method created successfully.";
203 | $greenText = "\033[32m" . $infoText . "\033[0m";
204 | $output->writeln($yellowText);
205 | $output->writeln($blueText);
206 | $output->writeln($greenText);
207 |
208 | $this->createMethod($controller, $methodName, $parameter);
209 | }
210 |
211 | $infoText = "Route created successfully.";
212 | $greenText = "\033[32m" . $infoText . "\033[0m";
213 | $output->writeln($greenText);
214 | }
215 |
216 | protected function createMethod($controller, $name, $parameter = null)
217 | {
218 | $controllerPath = $this->getControllerPath($controller);
219 | $controller = file_get_contents($controllerPath);
220 | $class = strrpos($controller, '}');
221 | $controller = substr($controller, 0, $class);
222 | // if parameter is not null, create method with parameter
223 | if ($parameter) {
224 | // if paramter is separated by comma, explode it
225 | if (strpos($parameter, ',') !== false) {
226 | $parameter = explode(',', $parameter);
227 | $parameter = array_map(function ($item) {
228 | return '$' . $item;
229 | }, $parameter);
230 | $parameter = implode(', ', $parameter);
231 | } else {
232 | $parameter = '$' . $parameter;
233 | }
234 | $controller .= "\tpublic function {$name}({$parameter})\n\t{\n\t\t\n\t}\n\n}";
235 | } else {
236 | $controller .= "\tpublic function {$name}()\n\t{\n\t\t\n\t}\n\n}";
237 | }
238 | file_put_contents($controllerPath, $controller);
239 | }
240 |
241 | protected function findMethod($controller, $name, $parameter = null)
242 | {
243 | $controller = $this->getControllerPath($controller);
244 | $controller = file_get_contents($controller);
245 | $controller = explode('public function', $controller);
246 |
247 | foreach ($controller as $key => $value) {
248 | // if parameter is not null, check if method has parameter
249 | if (strpos($value, $name) !== false) {
250 | return true;
251 | }
252 | }
253 | return false;
254 | }
255 |
256 | protected function ask($question)
257 | {
258 | $handle = fopen("php://stdin", "r");
259 | echo $question . " ";
260 | $line = fgets($handle);
261 | return trim($line);
262 | }
263 |
264 |
265 | protected function getUseRouteStub()
266 | {
267 | if (!file_exists($this->getUseRouteStubPath())) {
268 | throw new \Exception('Stub not found');
269 | }
270 |
271 | return $this->getUseRouteStubPath();
272 | }
273 |
274 | protected function getUseRouteStubPath()
275 | {
276 | return __DIR__ . '/Stubs/routes/use-route.stub';
277 | }
278 |
279 | protected function getRouteStub()
280 | {
281 | if (!file_exists($this->getRouteStubPath())) {
282 | throw new \Exception('Stub not found');
283 | }
284 |
285 | return $this->getRouteStubPath();
286 | }
287 |
288 | protected function getGroupStubMethodPath()
289 | {
290 | return __DIR__ . '/Stubs/routes/route-group-method.stub';
291 | }
292 |
293 | protected function getGroupStubMethod()
294 | {
295 | if (!file_exists($this->getGroupStubMethodPath())) {
296 | throw new \Exception('Stub not found');
297 | }
298 |
299 | return $this->getGroupStubMethodPath();
300 | }
301 |
302 | protected function getRouteStubPath()
303 | {
304 | return __DIR__ . '/Stubs/routes/route.stub';
305 | }
306 |
307 | protected function getRoutePath()
308 | {
309 | return 'App/Routes/Api.php';
310 | }
311 |
312 | protected function getReplacements($name, $methodName, $controller, $parameter = null, $httpVerbs)
313 | {
314 | // if method is index, create route without method
315 | $actionName = $name === NULL ? $methodName : $name;
316 | $action = $actionName == 'index' ? '' : '/' . $this->getAction($actionName);
317 | // change :parameter to {parameter}
318 | $splitRoute = explode('/', $action);
319 | // remove only :parameter and add {}
320 | $splitRoute = array_map(function ($item) {
321 | return strpos($item, ':') !== false ? '{' . str_replace(':', '', $item) . '}' : $item;
322 | }, $splitRoute);
323 | $action = implode('/', $splitRoute);
324 |
325 | $prefix = '/';
326 | if ($parameter) {
327 | // if parameter is separated by comma, explode it
328 | if (strpos($parameter, ',') !== false) {
329 | $parameter = explode(',', $parameter);
330 | $parameter = array_map(function ($item) {
331 | return '{' . $item . '}';
332 | }, $parameter);
333 | $parameter = implode('/', $parameter);
334 | } else {
335 | $parameter = '{' . $parameter . '}';
336 | }
337 | if (strpos($name, ':') !== false) {
338 | $prefix = '';
339 | }
340 |
341 | $dummyRoute = $prefix . $this->splitNameController($controller) . $action . '/' . $parameter;
342 | } else {
343 | $dummyRoute = $prefix . $this->splitNameController($controller) . $action;
344 | }
345 |
346 | return [
347 | 'DummyRoute' => $dummyRoute,
348 | 'DummyAction' => $methodName,
349 | 'DummyController' => $this->getControllerName($controller),
350 | 'DummyNameController' => $this->splitSlashController($controller),
351 | 'DummyHttpVerbs' => $httpVerbs,
352 | ];
353 | }
354 |
355 | protected function splitSlashController($controller)
356 | {
357 | $controller = $this->getControllerName($controller);
358 | // remove controller from last string
359 | $controller = str_replace('\\', '/', $controller);
360 | // split if there is /
361 | if (strpos($controller, '/') !== false) {
362 | $controller = explode('/', $controller);
363 | $controller = end($controller);
364 | }
365 |
366 | return $controller;
367 | }
368 |
369 | protected function splitNameController($controller)
370 | {
371 | $controller = $this->getControllerName($controller);
372 | // remove controller from last string
373 | $controller = substr($controller, 0, -10);
374 | // convert to lowercase
375 | $controller = strtolower($controller);
376 | // replace \ with /
377 | $controller = str_replace('\\', '/', $controller);
378 | // convert to plural
379 | $inflector = InflectorFactory::create()->build();
380 | $controller = $inflector->pluralize($controller);
381 | return $controller;
382 | }
383 |
384 | protected function getControllerPath($controller)
385 | {
386 | return 'App/Controllers/' . $controller . '.php';
387 | }
388 |
389 | protected function getAction($name)
390 | {
391 | return strtolower($name);
392 | }
393 |
394 | protected function getControllerName($name)
395 | {
396 | $name = str_replace('/', '\\', $name);
397 | return ucfirst($name);
398 | }
399 |
400 | protected function generateRoute($name, $methodName, $controller, $parameter = null, $httpVerbs)
401 | {
402 | $replacements = $this->getReplacements($name, $methodName, $controller, $parameter, $httpVerbs);
403 | $routeStub = $this->getRouteStub();
404 | $routePath = $this->getRoutePath();
405 |
406 | $useRouteStub = $this->getUseRouteStub();
407 | $useRouteContent = file_get_contents($useRouteStub);
408 |
409 | $useRouteContent = str_replace(
410 | array_keys($replacements),
411 | array_values($replacements),
412 | $useRouteContent
413 | );
414 |
415 | $routeStubContent = file_get_contents($routeStub);
416 | $routeStubContent = str_replace(
417 | array_keys($replacements),
418 | array_values($replacements),
419 | $routeStubContent
420 | );
421 |
422 |
423 | // get file content routes and check last use controller
424 | $routeContent = file_get_contents($routePath);
425 | $useControllers = strrpos($routeContent, 'use App\Controllers\\');
426 |
427 | // get last use controller
428 | $lastUseController = substr($routeContent, $useControllers);
429 | $lastUseController = substr($lastUseController, 0, strpos($lastUseController, ';') + 1);
430 |
431 | // check if use controller already exists
432 | if (strpos($routeContent, $useRouteContent) === false) {
433 | $routeContent = str_replace($lastUseController, $lastUseController . PHP_EOL . $useRouteContent, $routeContent);
434 | }
435 | file_put_contents($routePath, $routeContent);
436 |
437 | // get last Router::controller
438 | $routeContent = file_get_contents($routePath);
439 | $lastRoute = strrpos($routeContent, 'Router::controller');
440 | if (!$lastRoute) {
441 | $lastRoute = substr($lastUseController, 0, strpos($lastUseController, ';') + 1);
442 | }
443 | $controllerName = $this->splitSlashController($controller);
444 |
445 | $checkRoute = strrpos($routeContent, "Router::controller({$controllerName}::class)");
446 | if (!$checkRoute) {
447 | $useControllers = strrpos($routeContent, 'use App\Controllers\\');
448 |
449 | // get last use controller
450 | $lastUseController = substr($routeContent, $useControllers);
451 | $lastUseController = substr($lastUseController, 0, strpos($lastUseController, ';') + 1);
452 |
453 | $routeContent = str_replace($lastUseController, $lastUseController . PHP_EOL . PHP_EOL . $routeStubContent, $routeContent);
454 | file_put_contents($routePath, $routeContent);
455 | } else {
456 | $groupStubMethod = $this->getGroupStubMethod();
457 | $groupStubMethodContent = file_get_contents($groupStubMethod);
458 | $groupStubMethodContent = str_replace(
459 | array_keys($replacements),
460 | array_values($replacements),
461 | $groupStubMethodContent
462 | );
463 | $checkRoute = substr($routeContent, $checkRoute);
464 | $checkRoute = substr($checkRoute, 0, strpos($checkRoute, '});') + 3);
465 |
466 | // check action in Router::controller
467 | $checkMethod = strrpos($checkRoute, $this->getAction($methodName));
468 | if (!$checkMethod) {
469 | $lastMethod = strrchr($checkRoute, 'Router::');
470 | // check if result string end with ->group(function () {
471 | $checkGroupMethod = substr($checkRoute, -48, strpos($lastMethod, "group(function () {"));
472 | // if strlength is below to 43 then add string {
473 | if (strlen($checkGroupMethod) < 43) {
474 | $checkGroupMethod = substr($checkRoute, -45, strpos($lastMethod, "group(function () {"));
475 | }
476 |
477 | // check if method in group controller already exists
478 | if ($checkGroupMethod == "") {
479 | $lastMethod = substr($lastMethod, 0, strpos($lastMethod, ');') + 2);
480 | if (strpos($lastMethod, str_replace(' ', "\t", $groupStubMethodContent)) === false) {
481 | $routeContent = str_replace($lastMethod, $lastMethod . PHP_EOL . "\t" . $groupStubMethodContent, $routeContent);
482 | file_put_contents($routePath, $routeContent);
483 | }
484 | } else {
485 | // find stringlast
486 |
487 | $lastMethod = substr($checkGroupMethod, 0, strpos($checkGroupMethod, '{') + 1);
488 |
489 |
490 | $routeContent = str_replace($lastMethod, $lastMethod . PHP_EOL . "\t" . $groupStubMethodContent, $routeContent);
491 |
492 | file_put_contents($routePath, $routeContent);
493 | }
494 | }
495 | }
496 | }
497 | }
498 |
--------------------------------------------------------------------------------
/App/Core/Commands/CreateSeederCommand.php:
--------------------------------------------------------------------------------
1 | setName($this->commandName)
24 | ->setDescription($this->commandDescription)
25 | ->addArgument(
26 | $this->commandArgumentName,
27 | InputArgument::REQUIRED,
28 | $this->commandArgumentDescription
29 | );
30 | }
31 |
32 | protected function execute(InputInterface $input, OutputInterface $output): void
33 | {
34 | $name = $input->getArgument('name');
35 | $this->make($name);
36 |
37 | $output->writeln("Seeder created successfully.");
38 | }
39 |
40 | protected function getStub(): string
41 | {
42 | if (!file_exists($this->getStubPath())) {
43 | throw new \Exception('Stub not found');
44 | }
45 |
46 | return $this->getStubPath();
47 | }
48 |
49 | protected function getStubPath(): string
50 | {
51 | return __DIR__ . '/Stubs/seeder.stub';
52 | }
53 |
54 | protected function getDestinationPath(): string
55 | {
56 | return __DIR__ . 'App/Database/Migrations';
57 | }
58 |
59 | protected function getDestinationFileName(string $name): string
60 | {
61 | return $this->getClassName($name) . '.php';
62 | }
63 |
64 | protected function getClassName(string $name): string
65 | {
66 | return ucfirst($name);
67 | }
68 |
69 |
70 | protected function getNamespace(): string
71 | {
72 | return 'App\Database\Seeders';
73 | }
74 |
75 | protected function getReplacements($name): array
76 | {
77 | return [
78 | 'DummyClassName' => $this->getClassName($name),
79 | 'DummyParentClass' => $this->getParentClassName(),
80 | 'DummyNameSpace' => $this->getNamespace(),
81 | 'DummyCoreSeederNamespace' => $this->getCoreSeederNameSpace(),
82 | ];
83 | }
84 |
85 | protected function getParentClassName(): string
86 | {
87 | return 'Seeder';
88 | }
89 |
90 | protected function getCoreSeederNameSpace(): string
91 | {
92 | return 'App\Core\Seeder';
93 | }
94 |
95 | protected function getFileName(string $name): string
96 | {
97 | return $name;
98 | }
99 |
100 | protected function make(string $name): void
101 | {
102 | $stub = file_get_contents($this->getStub());
103 |
104 | $replacements = $this->getReplacements($name);
105 |
106 | $stub = str_replace(
107 | array_keys($replacements),
108 | array_values($replacements),
109 | $stub
110 | );
111 |
112 | $fileName = $this->getDestinationFileName($name);
113 | $filePath = $this->getFilePath($fileName);
114 |
115 | file_put_contents($filePath, $stub);
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/App/Core/Commands/CreateUpdateCommand.php:
--------------------------------------------------------------------------------
1 | setName($this->commandName)
24 | ->setDescription($this->commandDescription)
25 | ->addOption(
26 | $this->commandOptionName,
27 | null,
28 | InputOption::VALUE_OPTIONAL,
29 | $this->commandOptionDescription
30 | );
31 | }
32 |
33 | protected function execute(InputInterface $input, OutputInterface $output): void
34 | {
35 | $version = $input->getOption('version');
36 | $stub = $this->getStub();
37 | $stub = str_replace('{{version}}', $version, $stub);
38 | $file = file_get_contents('https://packagist.org/packages/mardira/mardira-framework');
39 | $version = $this->getVersion($file);
40 | $this->removeLockFile();
41 | $this->removeJsonFile();
42 | $this->initComposer();
43 | $this->requireCore($version);
44 |
45 | // get files from vendor folder app core
46 | foreach (glob("vendor/mardira/mardira-framework/App/Core/*") as $file) {
47 |
48 | // get command folder with if statement
49 | if (basename($file) == 'Commands') {
50 | // get files from command folder
51 | foreach (glob("vendor/mardira/mardira-framework/App/Core/Commands/*") as $file) {
52 | // if file changed from vendor folder and from App/Core folder show message
53 | if (file_exists($file) && file_exists("App/Core/Commands/" . basename($file))) {
54 | // check difference between files from vendor folder and App/Core folder with --dif
55 | $diff = shell_exec("diff {$file} App/Core/Commands/" . basename($file));
56 | if ($diff) {
57 | //split file name from path mardira-framework
58 | $file = explode('mardira-framework', $file)[1];
59 | // text terminal color yellow and name file
60 | $textYellow = "\033[33m" . $file . "\033[0m";
61 |
62 | $output->writeln("File {$textYellow} has been changed.");
63 | }
64 | }
65 |
66 | // if file added from vendor folder and not exist in App/Core folder show message
67 | else if (file_exists($file) && !file_exists("App/Core/Commands/" . basename($file))) {
68 | //split file name from path mardira-framework
69 | $file = explode('mardira-framework', $file)[1];
70 | // text terminal color green and name file
71 | $textGreen = "\033[32m" . $file . "\033[0m";
72 |
73 | $output->writeln("File {$textGreen} has been added.");
74 | }
75 |
76 | // if file deleted from vendor folder and exist in App/Core folder show message
77 | else if (!file_exists($file) && file_exists("App/Core/Commands/" . basename($file))) {
78 | $file = implode('/', ['/App/Core/Commands', basename($file)]);
79 | // text terminal color red and name file
80 | $textRed = "\033[31m" . $file . "\033[0m";
81 |
82 | $output->writeln("File {$textRed} has been deleted.");
83 | }
84 | }
85 | }
86 |
87 | // if file changed from vendor folder and from App/Core folder show message
88 | if (file_exists($file) && file_exists("App/Core/" . basename($file))) {
89 | // check difference between files from vendor folder and App/Core folder with --dif
90 | $diff = shell_exec("diff {$file} App/Core/" . basename($file));
91 | if ($diff) {
92 | //split file name from path mardira-framework
93 | $file = explode('mardira-framework', $file)[1];
94 | // text terminal color yellow and name file
95 | $textYellow = "\033[33m" . $file . "\033[0m";
96 |
97 | $output->writeln("File {$textYellow} has been changed.");
98 | }
99 | }
100 |
101 | // if file added from vendor folder and not exist in App/Core folder show message
102 | else if (file_exists($file) && !file_exists("App/Core/" . basename($file))) {
103 | //split file name from path mardira-framework
104 | $file = explode('mardira-framework', $file)[1];
105 | // text terminal color green and name file
106 | $textGreen = "\033[32m" . $file . "\033[0m";
107 |
108 | $output->writeln("File {$textGreen} has been added.");
109 | }
110 |
111 | // if file deleted from vendor folder and exist in App/Core folder show message
112 | else if (!file_exists($file) && file_exists("App/Core/" . basename($file))) {
113 |
114 | //implode basename($file) with path /App/Core/
115 | $file = implode('/', ['/App/Core', basename($file)]);
116 | $textRed = "\033[31m" . $file . "\033[0m";
117 |
118 | $output->writeln("File {$textRed} has been deleted.");
119 | }
120 | }
121 | $this->removeCoreFolder();
122 | $this->moveCoreFolder();
123 | $this->moveJsonFile();
124 | $this->removeVendorFolder();
125 | $this->composerUpdate();
126 |
127 | $output->writeln("Application updated successfully.");
128 | }
129 |
130 | protected function getVersion($file): string
131 | {
132 | $version = '';
133 | $pattern = '/(.*)<\/span>/';
134 | preg_match($pattern, $file, $matches);
135 | if (isset($matches[1])) {
136 | $version = $matches[1];
137 | }
138 | return $version;
139 | }
140 |
141 |
142 | protected function removeLockFile(): void
143 | {
144 | $command = "rm -rf composer.lock";
145 | exec($command);
146 | }
147 |
148 | protected function removeJsonFile(): void
149 | {
150 | $command = "rm -rf composer.json";
151 | exec($command);
152 | }
153 |
154 | protected function initComposer(): void
155 | {
156 | $command = "composer init --no-interaction";
157 | exec($command);
158 | }
159 |
160 | protected function requireCore($version): void
161 | {
162 | $version = explode('v', $version)[1];
163 | $command = "composer require mardira/mardira-framework:{$version}";
164 | exec($command);
165 | }
166 |
167 | protected function removeCoreFolder(): void
168 | {
169 | $command = "rm -rf App/Core";
170 | exec($command);
171 | }
172 |
173 | protected function moveCoreFolder(): void
174 | {
175 | $command = "mv vendor/mardira/mardira-framework/App/Core App";
176 | exec($command);
177 | }
178 |
179 | protected function moveJsonFile(): void
180 | {
181 | $command = "mv vendor/mardira/mardira-framework/composer.json .";
182 | exec($command);
183 | }
184 |
185 | protected function removeVendorFolder(): void
186 | {
187 | $command = "rm -rf vendor";
188 | exec($command);
189 | }
190 |
191 | protected function composerUpdate(): void
192 | {
193 | $command = "composer update";
194 | exec($command);
195 | }
196 |
197 | protected function getStub(): string
198 | {
199 | return file_get_contents(__DIR__ . '/stubs/update.stub');
200 | }
201 |
202 | protected function getFileName(): string
203 | {
204 | return 'update';
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/App/Core/Commands/RunGlobalSeederCommand.php:
--------------------------------------------------------------------------------
1 | setName($this->commandName)
21 | ->setDescription($this->commandDescription);
22 | }
23 |
24 | // execute seeding database
25 | protected function execute(InputInterface $input, OutputInterface $output): void
26 | {
27 | // get global seeder
28 | $globalSeeder = $this->getGlobalSeeder();
29 | // run seeding
30 | $globalSeeder->run();
31 |
32 | // count seeder called
33 |
34 | // looping seeder called
35 | foreach ($globalSeeder->getSeederCalled() as $seeder) {
36 | // write info file migration generate succesfully
37 | $time = round(microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"], 2);
38 |
39 | // count dot for info migration
40 | $dot = 80 - strlen($seeder);
41 |
42 | // text terminal color green and DONE
43 | $green = "\033[32m" . 'DONE' . "\033[0m";
44 |
45 | // text terminal color blue and name seeder
46 | $seeder = "\033[34m" . $seeder . "\033[0m";
47 |
48 | // text terminal colour yellow and RUNNING
49 | $yellow = "\033[33m" . 'RUNNING' . "\033[0m";
50 | $output->writeln("{$seeder} " . str_repeat('.', $dot) . " {$yellow}");
51 | $output->writeln("{$seeder} " . str_repeat('.', $dot) . " {$time} ms {$green}");
52 | }
53 |
54 | // write info file migration generate succesfully
55 | $output->writeln("Seeding run successfully.");
56 | }
57 |
58 |
59 |
60 | // get only global seeder
61 | protected function getGlobalSeeder()
62 | {
63 | // get Only GlobalSeeder
64 | $globalSeeder = $this->getSeederPath() . '/GlobalSeeder.php';
65 | // get object namespace
66 | $globalSeeder = new \App\Database\Seeders\GlobalSeeder();
67 | return $globalSeeder;
68 | }
69 |
70 | // get file name seeder
71 | protected function getFileSeederName($seeder): string
72 | {
73 | $seederName = explode('\\', get_class($seeder));
74 | return end($seederName);
75 | }
76 |
77 | // get path seeder
78 | protected function getSeederPath(): string
79 | {
80 | return __DIR__ . '/../../Database/Seeders';
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/App/Core/Commands/RunMigrateCommand.php:
--------------------------------------------------------------------------------
1 | setName($this->commandName)
22 | ->setDescription($this->commandDescription);
23 | }
24 |
25 | protected function execute(InputInterface $input, OutputInterface $output): void
26 | {
27 | // if database not exist show message and command click to create database
28 | $database = $this->getDatabaseName();
29 | if (!$this->isDatabaseExists()) {
30 | $output->writeln("Database {$database} not exists.");
31 | $output->writeln("Creating database {$database}...");
32 | $this->runCommand('migrate:database');
33 | }
34 |
35 | $migrations = $this->getMigrations();
36 | foreach ($migrations as $migration) {
37 | $this->runMigration($migration);
38 | // get file name migration
39 | $migrationName = $this->getFileMigrationName($migration);
40 |
41 | // get info time interval each migration run ms
42 | $time = round(microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"], 2);
43 |
44 | // count dot for info migration
45 | $dot = 80 - strlen($migrationName);
46 |
47 | // text terminal color green and DONE
48 | $green = "\033[32m" . 'DONE' . "\033[0m";
49 |
50 | // text terminal color blue and name file migration
51 | $textBlueMigrationFile = "\033[34m" . $migrationName . "\033[0m";
52 |
53 | $output->writeln("{$textBlueMigrationFile} " . str_repeat('.', $dot) . " {$time} ms {$green}");
54 | }
55 |
56 | // write info file migration generate succesfully
57 | $output->writeln("Migrations run successfully.");
58 | }
59 |
60 | protected function runCommand($command): void
61 | {
62 |
63 | $command = $this->getApplication()->find($command);
64 |
65 | $arguments = [
66 | 'command' => $command,
67 | ];
68 |
69 | $input = new \Symfony\Component\Console\Input\ArrayInput($arguments);
70 | $output = new \Symfony\Component\Console\Output\ConsoleOutput();
71 | $command->run($input, $output);
72 | }
73 |
74 | protected function getMigrations(): array
75 | {
76 | $migrations = [];
77 |
78 | foreach (glob($this->getMigrationsPath() . '/*.php') as $file) {
79 | $migrations[] = require_once $file;
80 | }
81 |
82 | return $migrations;
83 | }
84 |
85 |
86 | protected function getFileMigrationName($migrationName): string
87 | {
88 | $migrationName = (new \ReflectionClass($migrationName))->getFileName();
89 | $migrationName = preg_split('/[\/\\\\]/', $migrationName);
90 | $migrationName = end($migrationName);
91 | return $migrationName;
92 | }
93 |
94 | protected function getMigrationsPath(): string
95 | {
96 | return __DIR__ . '/../../Database/Migrations';
97 | }
98 |
99 | protected function runMigration($migration): void
100 | {
101 | $migration->up();
102 | }
103 |
104 | protected function isDatabaseExists(): bool
105 | {
106 | $databaseName = $this->getDatabaseName();
107 |
108 | $statement = $this->getConnection()->prepare("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = :databaseName");
109 | $statement->bindParam(':databaseName', $databaseName);
110 | $statement->execute();
111 |
112 | return $statement->rowCount() > 0;
113 | }
114 |
115 | protected function getDatabaseName(): string
116 | {
117 | return DotEnvKey::get('DB_NAME');
118 | }
119 |
120 | // get connection pdo
121 | protected function getConnection()
122 | {
123 | // pdo run get connect server only unsername and password
124 | $pdo = new \PDO(
125 | 'mysql:host=' . DotEnvKey::get('DB_HOST'),
126 | DotEnvKey::get('DB_USER'),
127 | DotEnvKey::get('DB_PASS')
128 | );
129 |
130 | $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
131 |
132 | return $pdo;
133 | }
134 |
135 | protected function getStub(): string
136 | {
137 | if (!file_exists($this->getStubPath())) {
138 | throw new \Exception('Stub not found');
139 | }
140 |
141 | return $this->getStubPath();
142 | }
143 |
144 | protected function getStubPath(): string
145 | {
146 | return __DIR__ . '/Stubs/migration.stub';
147 | }
148 |
149 | protected function getMigrationName(string $name): string
150 | {
151 | return date('Y_m_d_His') . '_' . $name . '.php';
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/App/Core/Commands/RunMigrateDatabaseCommand.php:
--------------------------------------------------------------------------------
1 | setName($this->commandName)
22 | ->setDescription($this->commandDescription);
23 | }
24 |
25 | protected function execute(InputInterface $input, OutputInterface $output): void
26 | {
27 | $connection = $this->getConnection();
28 |
29 | $sql = "CREATE DATABASE IF NOT EXISTS " . DotEnvKey::get('DB_NAME');
30 |
31 | $connection->exec($sql);
32 |
33 | $output->writeln("Database created successfully.");
34 | }
35 |
36 | // get connection pdo
37 | protected function getConnection()
38 | {
39 | // pdo run get connect server only unsername and password
40 | $pdo = new \PDO(
41 | 'mysql:host=' . DotEnvKey::get('DB_HOST'),
42 | DotEnvKey::get('DB_USER'),
43 | DotEnvKey::get('DB_PASS')
44 | );
45 |
46 | $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
47 |
48 | return $pdo;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/App/Core/Commands/RunMigrateRefreshCommand.php:
--------------------------------------------------------------------------------
1 | setName($this->commandName)
26 | ->setDescription($this->commandDescription)
27 | ->addOption(
28 | $this->commandOptionName,
29 | null,
30 | InputOption::VALUE_NONE,
31 | $this->commandOptionDescription
32 | );
33 | }
34 |
35 | protected function execute(InputInterface $input, OutputInterface $output): void
36 | {
37 | $database = $this->getDatabaseName();
38 | if (!$this->isDatabaseExists()) {
39 | $output->writeln("Database {$database} not exists.");
40 | $output->writeln("Creating database {$database}...");
41 | $this->runCommand('migrate:database');
42 | }
43 |
44 | $migrations = $this->getMigrations();
45 |
46 | foreach ($migrations as $migration) {
47 | $this->runMigration($migration);
48 | // get file name migration
49 | $migrationName = $this->getFileMigrationName($migration);
50 |
51 | // get info time interval each migration run ms
52 | $time = round(microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"], 2);
53 |
54 | // count dot for info migration
55 | $dot = 80 - strlen($migrationName);
56 |
57 | // text terminal color green and DONE
58 | $green = "\033[32m" . 'DONE' . "\033[0m";
59 |
60 | // text terminal color blue and name file migration
61 | $textBlueMigrationFile = "\033[34m" . $migrationName . "\033[0m";
62 |
63 | $output->writeln("{$textBlueMigrationFile} " . str_repeat('.', $dot) . " {$time} ms {$green}");
64 | }
65 |
66 | // write info file migration generate succesfully
67 | $output->writeln("Migrations run successfully.");
68 |
69 | $inputs = $input->getOptions();
70 |
71 | if ($inputs['seed']) {
72 | $runSeeder = new RunSeederCommand();
73 | $runSeeder->runGlobalSeeder($output);
74 | }
75 | }
76 |
77 | protected function runCommand($command): void
78 | {
79 |
80 | $command = $this->getApplication()->find($command);
81 |
82 | $arguments = [
83 | 'command' => $command,
84 | ];
85 |
86 | $input = new \Symfony\Component\Console\Input\ArrayInput($arguments);
87 | $output = new \Symfony\Component\Console\Output\ConsoleOutput();
88 | $command->run($input, $output);
89 | }
90 |
91 | protected function isDatabaseExists(): bool
92 | {
93 | $databaseName = $this->getDatabaseName();
94 |
95 | $statement = $this->getConnection()->prepare("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = :databaseName");
96 | $statement->bindParam(':databaseName', $databaseName);
97 | $statement->execute();
98 |
99 | return $statement->rowCount() > 0;
100 | }
101 |
102 | protected function getDatabaseName(): string
103 | {
104 | return DotEnvKey::get('DB_NAME');
105 | }
106 |
107 | // get connection pdo
108 | protected function getConnection()
109 | {
110 | // pdo run get connect server only unsername and password
111 | $pdo = new \PDO(
112 | 'mysql:host=' . DotEnvKey::get('DB_HOST'),
113 | DotEnvKey::get('DB_USER'),
114 | DotEnvKey::get('DB_PASS')
115 | );
116 |
117 | $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
118 |
119 | return $pdo;
120 | }
121 |
122 | protected function getMigrations(): array
123 | {
124 | $migrations = [];
125 |
126 | foreach (glob($this->getMigrationsPath() . '/*.php') as $file) {
127 | $migrations[] = require_once $file;
128 | }
129 |
130 | return $migrations;
131 | }
132 |
133 | protected function getFileMigrationName($migrationName): string
134 | {
135 | $migrationName = (new \ReflectionClass($migrationName))->getFileName();
136 | $migrationName = preg_split('/[\/\\\\]/', $migrationName);
137 | $migrationName = end($migrationName);
138 | return $migrationName;
139 | }
140 |
141 | protected function getMigrationsPath(): string
142 | {
143 | return __DIR__ . '/../../Database/Migrations';
144 | }
145 |
146 | protected function runMigration($migration): void
147 | {
148 | $migration->down();
149 | $migration->up();
150 | }
151 |
152 | protected function getStub(): string
153 | {
154 | if (!file_exists($this->getStubPath())) {
155 | throw new \Exception('Stub not found');
156 | }
157 |
158 | return file_get_contents($this->getStubPath());
159 | }
160 |
161 | protected function getStubPath(): string
162 | {
163 | return __DIR__ . '/stubs/migration.stub';
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/App/Core/Commands/RunSeederCommand.php:
--------------------------------------------------------------------------------
1 | setName($this->commandName)
25 | ->setDescription($this->commandDescription)
26 | ->addOption(
27 | $this->commandOptionName,
28 | null,
29 | InputOption::VALUE_OPTIONAL,
30 | $this->commandOptionDescription
31 | );
32 | }
33 |
34 | // execute seeding database
35 | protected function execute(InputInterface $input, OutputInterface $output): void
36 | {
37 | // get class name seeder
38 | $class = $input->getOption('class');
39 |
40 | // check class name seeder is null
41 | if (is_null($class)) {
42 | // run global seeder
43 | $this->runGlobalSeeder($output);
44 | } else {
45 | // run seeder
46 | $this->runSeeder($class, $output);
47 | }
48 | }
49 |
50 |
51 | // run GlobalSeeder
52 | public function runGlobalSeeder(OutputInterface $output): void
53 | {
54 | // get global seeder
55 | $globalSeeder = $this->getGlobalSeeder();
56 | // run global seeder
57 | $globalSeeder->run();
58 | // count seeder called
59 | $count = count($globalSeeder->getSeederCalled());
60 |
61 | // looping seeder called
62 | foreach ($globalSeeder->getSeederCalled() as $seeder) {
63 | // write info file migration generate succesfully
64 | $time = round(microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"], 2);
65 |
66 | // count dot for info migration
67 | $dot = 80 - strlen($seeder);
68 |
69 | // text terminal color green and DONE
70 | $green = "\033[32m" . 'DONE' . "\033[0m";
71 |
72 | // text terminal color blue and name seeder
73 | $seeder = "\033[34m" . $seeder . "\033[0m";
74 |
75 | // text terminal colour yellow and RUNNING
76 | $yellow = "\033[33m" . 'RUNNING' . "\033[0m";
77 | $output->writeln("{$seeder} " . str_repeat('.', $dot) . " {$yellow}");
78 | $output->writeln("{$seeder} " . str_repeat('.', $dot) . " {$time} ms {$green}");
79 | }
80 |
81 | // write info seeder generate succesfully
82 | $output->writeln("Seeded: {$count} seeders");
83 | }
84 | // run seeder
85 |
86 | protected function runSeeder(string $class, OutputInterface $output): void
87 | {
88 | // get seeder path
89 | $seederPath = $this->getSeederPath();
90 | // get seeder file
91 | $seederFile = $seederPath . '/' . $class . '.php';
92 | // get namespace seeder
93 | $class = $this->getNamespaceSeeder($class);
94 |
95 | // check seeder file is exists
96 | if (!file_exists($seederFile)) {
97 | // write info file migration generate succesfully
98 | $output->writeln("Seeder {$class} not found.");
99 | exit;
100 | }
101 | $time = round(microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"], 2);
102 |
103 | // count dot for info migration
104 | $dot = 80 - strlen($class);
105 |
106 | // text terminal color green and DONE
107 | $green = "\033[32m" . 'DONE' . "\033[0m";
108 |
109 | // text terminal color blue and name seeder
110 |
111 | $class = "\033[34m" . $class . "\033[0m";
112 |
113 | // text terminal colour yellow and RUNNING
114 |
115 | $yellow = "\033[33m" . 'RUNNING' . "\033[0m";
116 |
117 | $output->writeln("{$class} " . str_repeat('.', $dot) . " {$yellow}");
118 |
119 | $output->writeln("{$class} " . str_repeat('.', $dot) . " {$time} ms {$green}");
120 |
121 | // write info seeder generate succesfully
122 | $output->writeln("Seeded: 1 seeder");
123 | }
124 |
125 | // get only global seeder
126 | protected function getGlobalSeeder()
127 | {
128 | // get Only GlobalSeeder
129 | $globalSeeder = $this->getSeederPath() . '/GlobalSeeder.php';
130 | // get object namespace
131 | $globalSeeder = new \App\Database\Seeders\GlobalSeeder();
132 | return $globalSeeder;
133 | }
134 |
135 | // get file name seeder
136 | protected function getFileSeederName($seeder): string
137 | {
138 | $seederName = explode('\\', get_class($seeder));
139 | return end($seederName);
140 | }
141 |
142 | // get namespace seeder
143 | protected function getNamespaceSeeder($class): string
144 | {
145 | return 'App\\Database\\Seeders\\' . $class;
146 | }
147 |
148 | // get path seeder
149 | protected function getSeederPath(): string
150 | {
151 | return __DIR__ . '/../../Database/Seeders';
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/App/Core/Commands/ServeCommand.php:
--------------------------------------------------------------------------------
1 | setName($this->commandName)
28 | ->setDescription($this->commandDescription)
29 | ->addArgument(
30 | $this->commandArgumentName,
31 | InputArgument::OPTIONAL,
32 | $this->commandArgumentDescription
33 | )->addOption(
34 | $this->commandOptionName,
35 | null,
36 | InputOption::VALUE_OPTIONAL,
37 | $this->commandOptionDescription
38 | );
39 | }
40 |
41 | protected function execute(InputInterface $input, OutputInterface $output): void
42 | {
43 | $host = $input->getArgument('host');
44 | $port = $input->getOption('port');
45 | // set default host if empty
46 | if (empty($host)) {
47 | $host = '127.0.0.1';
48 | }
49 |
50 | // set default port if empty
51 | if (empty($port)) {
52 | $port = 8000;
53 | }
54 | $this->serve($host, $port);
55 |
56 | $output->writeln("Server started successfully.");
57 | }
58 |
59 | protected function getStub(): string
60 | {
61 | if (!file_exists($this->getStubPath())) {
62 | throw new \Exception('Stub not found');
63 | }
64 |
65 | return $this->getStubPath();
66 | }
67 |
68 | protected function getStubPath(): string
69 | {
70 | return __DIR__ . '/Stubs/serve.stub';
71 | }
72 |
73 | protected function getTemplate(): string
74 | {
75 | if (!file_exists($this->getTemplatePath())) {
76 | throw new \Exception('Template not found');
77 | }
78 |
79 | return $this->getTemplatePath();
80 | }
81 |
82 | protected function getTemplatePath(): string
83 | {
84 | return __DIR__ . '/../../Templates/serve.template';
85 | }
86 |
87 | protected function getConfiguration(): string
88 | {
89 | if (!file_exists($this->getConfigurationPath())) {
90 | throw new \Exception('Configuration not found');
91 | }
92 |
93 | return $this->getConfigurationPath();
94 | }
95 |
96 | protected function getConfigurationPath(): string
97 | {
98 | return __DIR__ . '/../../Config/serve.yaml';
99 | }
100 |
101 | protected function getConfigurationKey(): string
102 | {
103 | return 'serve';
104 | }
105 |
106 | public function serve(string $host, string $port): void
107 | {
108 | $this->validateHost($host);
109 | $this->validatePort($port);
110 | // start host
111 | $this->startHost($host, $port);
112 | }
113 |
114 | protected function startHost(string $host, string $port): void
115 | {
116 | $this->info("Starting Mardira development server: http://{$host}:{$port}");
117 | $this->info("Quit the server with CTRL+C.");
118 | // start server
119 | $this->startServer($host, $port);
120 | }
121 |
122 | protected function startServer(string $host, string $port): void
123 | {
124 | $this->info("Starting server...");
125 | // run execute command with PHP built-in server
126 | $this->executeCommand("php -S {$host}:{$port} -t public");
127 | }
128 |
129 | protected function executeCommand(string $command): void
130 | {
131 | passthru($command);
132 | }
133 |
134 | protected function info(string $message): void
135 | {
136 | echo $message . PHP_EOL;
137 | }
138 |
139 | protected function validateHost(string $host): void
140 | {
141 | $this->validate($host, 'host');
142 | }
143 |
144 | protected function validatePort(string $port): void
145 | {
146 | $this->validate($port, 'port');
147 | }
148 |
149 | protected function validate(string $value, string $type): void
150 | {
151 | $this->validateRequired($value, $type);
152 | $this->validateType($value, $type);
153 | }
154 |
155 | protected function validateRequired(string $value, string $type): void
156 | {
157 | if (empty($value)) {
158 | throw new \Exception("The {$type} is required.");
159 | }
160 | }
161 |
162 | protected function validateType(string $value, string $type): void
163 | {
164 | if ($type === 'host') {
165 | $this->validateHostType($value);
166 | }
167 |
168 | if ($type === 'port') {
169 | $this->validatePortType($value);
170 | }
171 | }
172 |
173 | protected function validateHostType(string $host): void
174 | {
175 | if (!filter_var($host, FILTER_VALIDATE_IP)) {
176 | throw new \Exception("The host must be a valid IP address.");
177 | }
178 | }
179 |
180 | protected function validatePortType(string $port): void
181 | {
182 | if (!is_numeric($port)) {
183 | throw new \Exception("The port must be a number.");
184 | }
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/App/Core/Commands/Stubs/Auth/auth-controller.stub:
--------------------------------------------------------------------------------
1 | $this->input->post('email'),
15 | 'password' => $this->input->post('password')
16 | ]
17 | );
18 |
19 | $this->response(401, [
20 | 'message' => 'Invalid credentials'
21 | ]);
22 | }
23 |
24 | public function register()
25 | {
26 | $email = $this->input->post('email');
27 | $password = $this->input->post('password');
28 |
29 | Auth::register([
30 | 'email' => $email,
31 | 'password' => $password
32 | ]);
33 |
34 | $this->response(401, [
35 | 'message' => 'User already exists'
36 | ]);
37 | }
38 |
39 | public function logout() : void
40 | {
41 | Auth::logout();
42 |
43 | $this->response(401, [
44 | 'message' => 'Invalid credentials'
45 | ]);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/App/Core/Commands/Stubs/Auth/auth-env.stub:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Token Expired Configuration in minutes
4 | EXPIRED_TOKEN=60
--------------------------------------------------------------------------------
/App/Core/Commands/Stubs/Auth/auth-package.stub:
--------------------------------------------------------------------------------
1 | verifyToken($token, DotEnvKey::get('ACCESS_TOKEN_SECRET_KEY'));
23 | if (isset($user->data)) {
24 | return $user->data;
25 | }
26 | return false;
27 | }
28 |
29 | public static function check(): bool
30 | {
31 | $token = Request::bearerToken();
32 | $user = self::verify($token);
33 | if ($user) {
34 | $user = DB::table('users')->where('id', $user->id)->first();
35 | if ($user) {
36 | return true;
37 | }
38 | }
39 | return false;
40 | }
41 |
42 | public static function user()
43 | {
44 | $token = Request::bearerToken();
45 | $user = self::verify($token);
46 | if ($user) {
47 | $user = DB::table('users')->where('id', $user->id)->first();
48 | if ($user) {
49 | return $user;
50 | }
51 | }
52 | return false;
53 | }
54 |
55 | public static function attempt(array $credentials = [], $validator = null)
56 | {
57 | // if validation fails
58 | if ($validator) {
59 | if ($validator->fails()) {
60 | self::response(401, [
61 | 'message' => 'Invalid credentials',
62 | 'errors' => $validator->errors()
63 | ]);
64 | }
65 | }
66 |
67 | // validate form via validator
68 | if (self::validate($credentials)) {
69 | $user = DB::table('users');
70 | $field = isset($credentials['email']) ? 'email' : 'username';
71 | $user = $user->where($field, isset($credentials['email']) ? $credentials['email'] : $credentials['username']);
72 | $user = $user->first();
73 |
74 | if ($user) {
75 | if (password_verify($credentials['password'], $user->password)) {
76 | $token = (new self)->generateToken([
77 | 'data' => [
78 | 'id' => $user->id,
79 | 'username' => $user->username,
80 | 'email' => $user->email,
81 | 'created_at' => $user->created_at,
82 | 'updated_at' => $user->updated_at,
83 | 'expires_at' => TimeHelper::setMinutes(30)
84 | ]
85 | ], DotEnvKey::get('ACCESS_TOKEN_SECRET_KEY'));
86 | self::response(200, [
87 | 'message' => 'Login successful',
88 | 'token' => $token,
89 | 'expires_at' => date('Y-m-d H:i:s', TimeHelper::setMinutes(30))
90 | ]);
91 | return true;
92 | }
93 | }
94 | }
95 | return false;
96 | }
97 |
98 | public static function register(array $credentials = [])
99 | {
100 | // validate form via validator
101 | if (self::validate($credentials)) {
102 | $user = DB::table('users');
103 | $field = isset($credentials['email']) ? 'email' : 'username';
104 | $user = $user->where($field, isset($credentials['email']) ? $credentials['email'] : $credentials['username']);
105 | $user = $user->first();
106 |
107 | if (!$user) {
108 | $credentials['password'] = password_hash($credentials['password'], PASSWORD_DEFAULT);
109 | $insert = DB::table('users')->insert($credentials);
110 | $user = DB::table('users')->where('id', $insert)->first();
111 | if ($user) {
112 | $token = (new self)->generateToken([
113 | 'data' => [
114 | 'id' => $user->id,
115 | 'username' => $user->username,
116 | 'email' => $user->email,
117 | 'created_at' => $user->created_at,
118 | 'updated_at' => $user->updated_at,
119 | 'expires_at' => TimeHelper::setMinutes(30)
120 | ]
121 | ], DotEnvKey::get('ACCESS_TOKEN_SECRET_KEY'));
122 | self::response(200, [
123 | 'message' => 'Register successful',
124 | 'token' => $token,
125 | 'expires_at' => date('Y-m-d H:i:s', TimeHelper::setMinutes(30))
126 | ]);
127 | return true;
128 | }
129 | }
130 | }
131 | return false;
132 | }
133 |
134 |
135 | public static function validate(array $credentials = [])
136 | {
137 | // foreach validate required each credentials
138 | $fields = [];
139 |
140 | foreach ($credentials as $key => $value) {
141 |
142 | // if key email use validate email
143 | if ($key == 'email') {
144 | $fieldset = [
145 | 'required' => true,
146 | 'email' => true
147 | ];
148 | $fields[] = [
149 | $key => $fieldset
150 | ];
151 | continue;
152 | }
153 |
154 | $fieldset = [
155 | 'required' => true,
156 | ];
157 |
158 | $fields[] = [
159 | $key => $fieldset
160 | ];
161 | }
162 |
163 | $fields = array_merge(...$fields);
164 | $validator = Validator::validate($fields, $credentials);
165 |
166 | if ($validator->fails()) {
167 | self::response(400, [
168 | 'message' => 'Failed',
169 | 'errors' => $validator->errors()
170 | ]);
171 | return;
172 | }
173 | return true;
174 | }
175 |
176 | public static function logout()
177 | {
178 | $token = Request::bearerToken();
179 | $user = self::verify($token);
180 | if ($user) {
181 | $user = DB::table('users')->where('id', $user->id)->first();
182 | if ($user) {
183 | $token = (new self)->generateToken([
184 | 'data' => [
185 | 'id' => $user->id,
186 | 'username' => $user->username,
187 | 'email' => $user->email,
188 | 'created_at' => $user->created_at,
189 | 'updated_at' => $user->updated_at,
190 | 'expires_at' => TimeHelper::setMinutes(30)
191 | ]
192 | ], DotEnvKey::get('ACCESS_TOKEN_SECRET_KEY'));
193 | self::response(200, [
194 | 'message' => 'Logout successful',
195 | 'token' => $token,
196 | 'expires_at' => date('Y-m-d H:i:s', TimeHelper::setMinutes(30))
197 | ]);
198 | return true;
199 | }
200 | }
201 | return false;
202 | }
203 | }
204 |
--------------------------------------------------------------------------------
/App/Core/Commands/Stubs/controller.stub:
--------------------------------------------------------------------------------
1 | increment('id');
15 | $table->timestamps();
16 | });
17 | }
18 |
19 | public function down()
20 | {
21 | DummySchemaClassName::dropIfExists('DummyTable');
22 | }
23 | };
--------------------------------------------------------------------------------
/App/Core/Commands/Stubs/model.stub:
--------------------------------------------------------------------------------
1 | group(function () {
2 | Router::DummyHttpVerbs('DummyRoute', 'DummyAction');
3 | });
--------------------------------------------------------------------------------
/App/Core/Commands/Stubs/routes/use-route.stub:
--------------------------------------------------------------------------------
1 | use App\Controllers\DummyController;
--------------------------------------------------------------------------------
/App/Core/Commands/Stubs/seeder.stub:
--------------------------------------------------------------------------------
1 | input = new Input();
21 | $this->env = new DotEnvKey();
22 | }
23 |
24 | /**
25 | * Model method to load the model
26 | *
27 | * @param mixed $model
28 | * @return object
29 | */
30 | public function model(string $model): object
31 | {
32 | if (is_array($model)) {
33 | $model = array_map(function ($model) {
34 | $model = 'App\\Models\\' . $model;
35 | $model = $model . 'Models';
36 | return new $model;
37 | }, $model);
38 | return $model;
39 | }
40 | $model = 'App\\Models\\' . $model;
41 | return new $model;
42 | }
43 |
44 | /**
45 | * load middleware
46 | *
47 | * @param mixed $middleware
48 | * @return void
49 | */
50 |
51 | public function middleware(string $middleware)
52 | {
53 | $middleware = 'App\\Middlewares\\' . $middleware;
54 | return new $middleware;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/App/Core/Database.php:
--------------------------------------------------------------------------------
1 | connection = new PDO(
18 | "mysql:host=" . DotEnvKey::get('DB_HOST') . ";dbname=" . DotEnvKey::get('DB_NAME'),
19 | DotEnvKey::get('DB_USER'),
20 | DotEnvKey::get('DB_PASS')
21 | );
22 | $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
23 | } catch (PDOException $e) {
24 | http_response_code(500);
25 | echo $e->getMessage();
26 | }
27 | return $this->connection;
28 | }
29 |
30 | public static function getConnection(): object
31 | {
32 | return (new self)->connection;
33 | }
34 | }
35 |
36 | $database = new Database();
37 |
--------------------------------------------------------------------------------
/App/Core/DotEnvKey.php:
--------------------------------------------------------------------------------
1 | load();
13 | return $_ENV[$key];
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/App/Core/HasTokens.php:
--------------------------------------------------------------------------------
1 | encodeToken($payload, $key);
25 | return $token;
26 | }
27 |
28 | /**
29 | * Verify the token and return the payload if the token is valid
30 | *
31 | * @param mixed $token
32 | * @param mixed $key
33 | * @return object
34 | */
35 | public function verifyToken(string $token, string $key)
36 | {
37 | if (strpos($token, 'Bearer') !== false) {
38 | $token = explode(' ', $token)[1];
39 | }
40 | $decoded = $this->decodeToken($token, $key);
41 | return $decoded;
42 | }
43 |
44 | /**
45 | * Encode the token using the JWT library
46 | *
47 | * @param mixed $payload
48 | * @param mixed $key
49 | * @return string
50 | */
51 | private function encodeToken(array $payload, string $key): string
52 | {
53 | return JWT::encode($payload, $key, $this->hash);
54 | }
55 |
56 | /**
57 | * Decode the token using the JWT library
58 | *
59 | * @param mixed $token
60 | * @param mixed $key
61 | * @return object
62 | */
63 | private function decodeToken(string $token, string $key): object
64 | {
65 | try {
66 | return JWT::decode($token, new Key($key, $this->hash));
67 | } catch (\Exception $e) {
68 | return (object) [
69 | 'error' => $e->getMessage()
70 | ];
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/App/Core/Input.php:
--------------------------------------------------------------------------------
1 | [],
68 | 'body' => ''
69 | ];
70 | }
71 | if (strpos($line, ': ') !== false) {
72 | [$header, $value] = explode(': ', $line);
73 | $data['headers'][$header] = $value;
74 | } else {
75 | $data['body'] .= $line;
76 | }
77 | return $data;
78 | });
79 | }, $parts);
80 | $parts = array_map(function ($part) {
81 | $data = [];
82 | if (isset($part['headers']['Content-Disposition'])) {
83 | $filename = null;
84 | $tmp_name = null;
85 | preg_match('/name="([^"]+)"/', $part['headers']['Content-Disposition'], $match);
86 | $data['name'] = $match[1];
87 | if (isset($part['headers']['Content-Type'])) {
88 | $data['type'] = $part['headers']['Content-Type'];
89 | }
90 | if (isset($part['headers']['Content-Length'])) {
91 | $data['size'] = $part['headers']['Content-Length'];
92 | }
93 | if (isset($part['headers']['Content-Transfer-Encoding'])) {
94 | $data['error'] = $part['headers']['Content-Transfer-Encoding'];
95 | }
96 | if (preg_match('/filename="([^"]+)"/', $part['headers']['Content-Disposition'], $match)) {
97 | $filename = $match[1];
98 | $tmp_name = tempnam(ini_get('upload_tmp_dir'), 'php');
99 | file_put_contents($tmp_name, $part['body']);
100 | }
101 | $data['parameter'] = $data['name'];
102 | $data['tmp_name'] = $tmp_name;
103 | $data['error'] = $filename ? UPLOAD_ERR_OK : UPLOAD_ERR_NO_FILE;
104 | $data['name'] = $filename;
105 | $data['size'] = filesize($tmp_name);
106 | }
107 | return $data;
108 | }, $parts);
109 | $parts = array_filter($parts);
110 |
111 | // rebase array with name as index
112 |
113 | $parts = array_reduce($parts, function ($data, $part) {
114 | if (isset($part['parameter'])) {
115 | $data[$part['parameter']] = $part;
116 | }
117 | return $data;
118 | }, []);
119 |
120 | $parts = array_map(function ($part) {
121 | unset($part['parameter']);
122 | return $part;
123 | }, $parts);
124 |
125 | $_FILES = $parts;
126 | }
127 | }
128 |
129 |
130 | if ($key) {
131 | return $_FILES[$key] ?? $default;
132 | }
133 |
134 | return $_FILES;
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/App/Core/Kernel.php:
--------------------------------------------------------------------------------
1 | init();
12 | }
13 |
14 | protected function init()
15 | {
16 | $this->loadConsole();
17 | }
18 |
19 | public static function run()
20 | {
21 | new static();
22 | }
23 |
24 | public function loadConsole()
25 | {
26 | $console = $this->console();
27 | $application = new Application();
28 | foreach ($console as $command) {
29 | $application->add(new $command());
30 | }
31 | $application->run();
32 | }
33 |
34 | public function console()
35 | {
36 | $console = [
37 | 'App\Core\Commands\CreateControllerCommand',
38 | 'App\Core\Commands\CreateModelCommand',
39 | 'App\Core\Commands\CreateMigrationCommand',
40 | 'App\Core\Commands\CreateSeederCommand',
41 | 'App\Core\Commands\CreateDotEnvCommand',
42 | 'App\Core\Commands\CreateAuthCommand',
43 | 'App\Core\Commands\CreateMiddlewareCommand',
44 | 'App\Core\Commands\ServeCommand',
45 | 'App\Core\Commands\RunMigrateCommand',
46 | 'App\Core\Commands\RunMigrateRefreshCommand',
47 | 'App\Core\Commands\RunMigrateDatabaseCommand',
48 | 'App\Core\Commands\RunGlobalSeederCommand',
49 | 'App\Core\Commands\RunSeederCommand',
50 | 'App\Core\Commands\CreateUpdateCommand',
51 | 'App\Core\Commands\CreateRouteCommand',
52 | ];
53 | return $console;
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/App/Core/Middleware.php:
--------------------------------------------------------------------------------
1 | connection;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/App/Core/Model.php:
--------------------------------------------------------------------------------
1 | statement = new Database();
17 | $this->statement = $this->statement->connection;
18 | }
19 |
20 | public static function all(): array
21 | {
22 | $table = (new static)->table;
23 | $query = "SELECT * FROM {$table}";
24 | $statement = (new static)->statement->prepare($query);
25 | $statement->execute();
26 | return $statement->fetchAll(PDO::FETCH_OBJ);
27 | }
28 |
29 | public static function find(int $id)
30 | {
31 | $table = (new static)->table;
32 | $primaryKey = (new static)->primaryKey;
33 | $query = "SELECT * FROM {$table} WHERE {$primaryKey} = :id";
34 | $statement = (new static)->statement->prepare($query);
35 | $statement->bindParam(':id', $id);
36 | $statement->execute();
37 | return $statement->fetch(PDO::FETCH_OBJ);
38 | }
39 |
40 | public function findWhere(array $data)
41 | {
42 | $query = "SELECT * FROM {$this->table} WHERE ";
43 | $query .= implode(' AND ', array_map(function ($key) {
44 | return $key . ' = :' . $key;
45 | }, array_keys($data)));
46 | $statement = $this->statement->prepare($query);
47 | foreach ($data as $key => $value) {
48 | $statement->bindValue(':' . $key, $value);
49 | }
50 | $statement->execute();
51 | return $statement->fetch(PDO::FETCH_OBJ);
52 | }
53 |
54 | public function row(): int
55 | {
56 | $query = "SELECT * FROM {$this->table}";
57 | $statement = $this->statement->prepare($query);
58 | $statement->execute();
59 | return $statement->rowCount();
60 | }
61 |
62 | public function where(array $data): array
63 | {
64 | $query = "SELECT * FROM {$this->table} WHERE ";
65 | $query .= implode(' AND ', array_map(function ($key) {
66 | return $key . ' = :' . $key;
67 | }, array_keys($data)));
68 | $statement = $this->statement->prepare($query);
69 | foreach ($data as $key => $value) {
70 | $statement->bindValue(':' . $key, $value);
71 | }
72 | $statement->execute();
73 | return $statement->fetchAll(PDO::FETCH_OBJ);
74 | }
75 |
76 | public function whereIn(string $column, array $data): array
77 | {
78 | $query = "SELECT * FROM {$this->table} WHERE {$column} IN (";
79 | $query .= implode(', ', array_map(function ($key) {
80 | return ':' . $key;
81 | }, array_keys($data)));
82 | $query .= ")";
83 | $statement = $this->statement->prepare($query);
84 | foreach ($data as $key => $value) {
85 | $statement->bindValue(':' . $key, $value);
86 | }
87 | $statement->execute();
88 | return $statement->fetchAll(PDO::FETCH_OBJ);
89 | }
90 |
91 | public function whereNotIn(string $column, array $data): array
92 | {
93 | $query = "SELECT * FROM {$this->table} WHERE {$column} NOT IN (";
94 | $query .= implode(', ', array_map(function ($key) {
95 | return ':' . $key;
96 | }, array_keys($data)));
97 | $query .= ")";
98 | $statement = $this->statement->prepare($query);
99 | foreach ($data as $key => $value) {
100 | $statement->bindValue(':' . $key, $value);
101 | }
102 | $statement->execute();
103 | return $statement->fetchAll(PDO::FETCH_OBJ);
104 | }
105 |
106 | public function create(array $data): bool
107 | {
108 | $query = "INSERT INTO {$this->table} SET ";
109 | $query .= implode(', ', array_map(function ($key) {
110 | return $key . ' = :' . $key;
111 | }, array_keys($data)));
112 | $statement = $this->statement->prepare($query);
113 | foreach ($data as $key => $value) {
114 | $statement->bindValue(':' . $key, $value);
115 | }
116 | return $statement->execute();
117 | }
118 |
119 | public function update(int $id, array $data): bool
120 | {
121 | $query = "UPDATE {$this->table} SET ";
122 | $query .= implode(', ', array_map(function ($key) {
123 | return $key . ' = :' . $key;
124 | }, array_keys($data)));
125 | $query .= " WHERE {$this->primaryKey} = :id";
126 | $statement = $this->statement->prepare($query);
127 | foreach ($data as $key => $value) {
128 | $statement->bindValue(':' . $key, $value);
129 | }
130 | $statement->bindValue(':id', $id);
131 | return $statement->execute();
132 | }
133 |
134 | public function updateOrCreate(array $data): bool
135 | {
136 | $query = "INSERT INTO {$this->table} SET ";
137 | $query .= implode(', ', array_map(function ($key) {
138 | return $key . ' = :' . $key;
139 | }, array_keys($data)));
140 | $query .= " ON DUPLICATE KEY UPDATE ";
141 | $query .= implode(', ', array_map(function ($key) {
142 | return $key . ' = :' . $key;
143 | }, array_keys($data)));
144 | $statement = $this->statement->prepare($query);
145 | foreach ($data as $key => $value) {
146 | $statement->bindValue(':' . $key, $value);
147 | }
148 | return $statement->execute();
149 | }
150 |
151 |
152 | public function delete(int $id): bool
153 | {
154 | $query = "DELETE FROM {$this->table} WHERE {$this->primaryKey} = :id";
155 | $statement = $this->statement->prepare($query);
156 | $statement->bindValue(':id', $id);
157 | return $statement->execute();
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/App/Core/QueryBuilder.php:
--------------------------------------------------------------------------------
1 | connection = Database::getConnection();
26 | }
27 |
28 |
29 | public static function table(string $table): QueryBuilder
30 | {
31 | $queryBuilder = new QueryBuilder();
32 | $queryBuilder->table = $table;
33 | return $queryBuilder;
34 | }
35 |
36 | public function query(string $query): QueryBuilder
37 | {
38 | $this->query = $query;
39 | return $this;
40 | }
41 |
42 | public function select(array $columns = ['*']): QueryBuilder
43 | {
44 | $this->columns = $columns;
45 | return $this;
46 | }
47 |
48 | public function from(string $table): QueryBuilder
49 | {
50 | $this->table = $table;
51 | return $this;
52 | }
53 |
54 | public function where(string $column, $value, string $operator = '='): QueryBuilder
55 | {
56 | $this->where[] = [
57 | 'column' => $column,
58 | 'value' => $value,
59 | 'operator' => $operator,
60 | ];
61 | return $this;
62 | }
63 |
64 | public function orWhere(string $column, $value, string $operator = '='): QueryBuilder
65 | {
66 | $this->where[] = [
67 | 'column' => $column,
68 | 'value' => $value,
69 | 'operator' => $operator,
70 | 'or' => true,
71 | ];
72 | return $this;
73 | }
74 |
75 | public function whereIn(string $column, array $values): QueryBuilder
76 | {
77 | $this->where[] = [
78 | 'column' => $column,
79 | 'value' => $values,
80 | 'operator' => 'IN',
81 | ];
82 | return $this;
83 | }
84 |
85 | public function whereNotIn(string $column, array $values): QueryBuilder
86 | {
87 | $this->where[] = [
88 | 'column' => $column,
89 | 'value' => $values,
90 | 'operator' => 'NOT IN',
91 | ];
92 | return $this;
93 | }
94 |
95 | public function whereNull(string $column): QueryBuilder
96 | {
97 | $this->where[] = [
98 | 'column' => $column,
99 | 'value' => null,
100 | 'operator' => 'IS NULL',
101 | ];
102 | return $this;
103 | }
104 |
105 | public function whereNotNull(string $column): QueryBuilder
106 | {
107 | $this->where[] = [
108 | 'column' => $column,
109 | 'value' => null,
110 | 'operator' => 'IS NOT NULL',
111 | ];
112 | return $this;
113 | }
114 |
115 | public function orderBy(string $column, string $direction = 'ASC'): QueryBuilder
116 | {
117 | $this->order[] = [
118 | 'column' => $column,
119 | 'direction' => $direction,
120 | ];
121 | return $this;
122 | }
123 |
124 | public function orderByAsc(string $column): QueryBuilder
125 | {
126 | return $this->orderBy($column, 'ASC');
127 | }
128 |
129 | public function orderByDesc(string $column): QueryBuilder
130 | {
131 | return $this->orderBy($column, 'DESC');
132 | }
133 |
134 | public function groupBy(string $column): QueryBuilder
135 | {
136 | $this->groupBy[] = $column;
137 | return $this;
138 | }
139 |
140 | public function having(string $column, $value, string $operator = '='): QueryBuilder
141 | {
142 | $this->having[] = [
143 | 'column' => $column,
144 | 'value' => $value,
145 | 'operator' => $operator,
146 | ];
147 | return $this;
148 | }
149 |
150 | public function sum(string $column, string $alias): QueryBuilder
151 | {
152 | $this->columns[] = "SUM({$column}) AS {$alias}";
153 | return $this;
154 | }
155 |
156 | public function avg(string $column, string $alias): QueryBuilder
157 | {
158 | $this->columns[] = "AVG({$column}) AS {$alias}";
159 | return $this;
160 | }
161 |
162 | public function min(string $column, string $alias): QueryBuilder
163 | {
164 | $this->columns[] = "MIN({$column}) AS {$alias}";
165 | return $this;
166 | }
167 |
168 | public function max(string $column, string $alias): QueryBuilder
169 | {
170 | $this->columns[] = "MAX({$column}) AS {$alias}";
171 | return $this;
172 | }
173 |
174 | public function join(string $table, string $first, string $second, string $type = ''): QueryBuilder
175 | {
176 | $this->joins[] = [
177 | 'table' => $table,
178 | 'first' => $first,
179 | 'operator' => '=',
180 | 'second' => $second,
181 | 'type' => $type,
182 | ];
183 | return $this;
184 | }
185 |
186 | public function leftJoin(string $table, string $first, string $second): QueryBuilder
187 | {
188 | return $this->join($table, $first, $second, 'LEFT');
189 | }
190 |
191 | public function rightJoin(string $table, string $first, string $second): QueryBuilder
192 | {
193 | return $this->join($table, $first, $second, 'RIGHT');
194 | }
195 |
196 | public function fullJoin(string $table, string $first, string $second): QueryBuilder
197 | {
198 | return $this->join($table, $first, $second, 'FULL');
199 | }
200 |
201 | public function innerJoin(string $table, string $first, string $second): QueryBuilder
202 | {
203 | return $this->join($table, $first, $second, 'INNER');
204 | }
205 |
206 | public function limit(int $limit): QueryBuilder
207 | {
208 | $this->limit = $limit;
209 | return $this;
210 | }
211 |
212 | public function insert($data): bool
213 | {
214 | if (isset($data[0])) {
215 | $columns = implode(', ', array_keys($data[0]));
216 | $values = implode(', ', array_map(function ($column) {
217 | return ':' . $column;
218 | }, array_keys($data[0])));
219 | foreach ($data as $key => $value) {
220 | $query = "INSERT INTO {$this->table} ({$columns}) VALUES ({$values})";
221 | $statement = $this->connection->prepare($query);
222 | foreach ($value as $key => $value) {
223 | $statement->bindValue(':' . $key, $value);
224 | }
225 | $statement->execute();
226 | }
227 | return true;
228 | } else {
229 | $columns = implode(', ', array_keys($data));
230 | $values = implode(', ', array_map(function ($column) {
231 | return ':' . $column;
232 | }, array_keys($data)));
233 | $query = "INSERT INTO {$this->table} ({$columns}) VALUES ({$values})";
234 | $statement = $this->connection->prepare($query);
235 | foreach ($data as $key => $value) {
236 | $statement->bindValue(':' . $key, $value);
237 | }
238 | return $statement->execute();
239 | }
240 | }
241 |
242 | public function insertGetId(array $data): int
243 | {
244 | $columns = implode(', ', array_keys($data));
245 | $values = implode(', ', array_map(function ($column) {
246 | return ':' . $column;
247 | }, array_keys($data)));
248 | $query = "INSERT INTO {$this->table} ({$columns}) VALUES ({$values})";
249 | $statement = $this->connection->prepare($query);
250 | foreach ($data as $key => $value) {
251 | $statement->bindValue(':' . $key, $value);
252 | }
253 | $statement->execute();
254 | return (int)$this->connection->lastInsertId();
255 | }
256 |
257 | public function get(): array
258 | {
259 | $query = $this->buildQuery();
260 | $this->statement = $this->connection->prepare($query);
261 | $this->bindValues();
262 | $this->statement->execute();
263 | return $this->statement->fetchAll(PDO::FETCH_OBJ);
264 | }
265 |
266 | public function first()
267 | {
268 | $query = $this->buildQuery();
269 | $this->statement = $this->connection->prepare($query);
270 | $this->bindValues();
271 | $this->statement->execute();
272 | return $this->statement->fetch(PDO::FETCH_OBJ);
273 | }
274 |
275 | public function count(): int
276 | {
277 | $query = $this->buildQuery();
278 | $this->statement = $this->connection->prepare($query);
279 | $this->bindValues();
280 | $this->statement->execute();
281 | return $this->statement->rowCount();
282 | }
283 |
284 |
285 | public function update(array $data): bool
286 | {
287 |
288 | $query = "UPDATE {$this->table} SET ";
289 | $query .= implode(', ', array_map(function ($column) {
290 | return $column . ' = :' . $column;
291 | }, array_keys($data)));
292 | // split select query
293 | $sql = $this->buildQuery();
294 | $where = substr($sql, strpos($sql, 'WHERE'));
295 | $query .= ' ' . $where;
296 | $this->statement = $this->connection->prepare($query);
297 | foreach ($data as $key => $value) {
298 | $this->statement->bindValue(':' . $key, $value);
299 | }
300 | $this->bindValues();
301 | return $this->statement->execute();
302 | }
303 |
304 |
305 |
306 | public function buildQuery(): string
307 | {
308 | $query = "SELECT ";
309 |
310 | if (count($this->columns) > 0) {
311 | $query .= implode(', ', $this->columns);
312 | } else {
313 | $query .= '*';
314 | }
315 |
316 | $query .= " FROM {$this->table}";
317 |
318 | if (count($this->joins) > 0) {
319 | foreach ($this->joins as $join) {
320 | $query .= " {$join['type']} JOIN {$join['table']} ON {$join['first']} {$join['operator']} {$join['second']}";
321 | }
322 | }
323 |
324 | if (count($this->where) > 0) {
325 | $query .= ' WHERE ';
326 | foreach ($this->where as $key => $where) {
327 | if (is_array($where['value'])) {
328 | $query .= "{$where['column']} {$where['operator']} (";
329 | foreach ($where['value'] as $value) {
330 | //remove reference column name for binding a.id to id
331 | if (strpos($where['column'], '.') !== false) {
332 | // change to _
333 | $columns = str_replace('.', '_', $where['column']);
334 | } else {
335 | $columns = $where['column'];
336 | }
337 | $query .= ":{$columns}, ";
338 | }
339 | $query = rtrim($query, ', ');
340 | $query .= ')';
341 | } else {
342 | //remove reference column name for binding a.id to id
343 | if (strpos($where['column'], '.') !== false) {
344 | // change to _
345 | $columns = str_replace('.', '_', $where['column']);
346 | } else {
347 | $columns = $where['column'];
348 | }
349 |
350 | $query .= "{$where['column']} {$where['operator']} :{$columns}";
351 | }
352 | if ($key < count($this->where) - 1) {
353 | // if isset or
354 | $query .= ' AND ';
355 | }
356 | }
357 | }
358 |
359 | if (count($this->order) > 0) {
360 | $query .= ' ORDER BY ';
361 | foreach ($this->order as $key => $order) {
362 | $query .= "{$order['column']} {$order['direction']}";
363 | if ($key < count($this->order) - 1) {
364 | $query .= ', ';
365 | }
366 | }
367 | }
368 |
369 | if (count($this->groupBy) > 0) {
370 | $query .= " GROUP BY " . implode(', ', $this->groupBy);
371 | }
372 |
373 | if ($this->limit > 0) {
374 | $query .= " LIMIT {$this->limit}";
375 | }
376 |
377 | if (count($this->having) > 0) {
378 | $query .= ' HAVING ';
379 | foreach ($this->having as $key => $having) {
380 | $query .= "{$having['column']} {$having['operator']} :{$having['column']}";
381 | if ($key < count($this->having) - 1) {
382 | $query .= ' AND ';
383 | }
384 | }
385 | }
386 |
387 | return $query;
388 | }
389 |
390 | public function bindValues()
391 | {
392 |
393 | // fix Invalid bind parameter with alias column
394 |
395 | foreach ($this->where as $where) {
396 | if (is_array($where['value'])) {
397 | foreach ($where['value'] as $value) {
398 | //remove reference column name for binding a.id to id
399 | if (strpos($where['column'], '.') !== false) {
400 | // change to _
401 | $columns = str_replace('.', '_', $where['column']);
402 | } else {
403 | $columns = $where['column'];
404 | }
405 | $this->statement->bindValue(':' . $columns, $value);
406 | }
407 | } else {
408 | //remove reference column name for binding a.id to id
409 | if (strpos($where['column'], '.') !== false) {
410 | // change to _
411 | $columns = str_replace('.', '_', $where['column']);
412 | } else {
413 | $columns = $where['column'];
414 | }
415 | $this->statement->bindValue(':' . $columns, $where['value']);
416 | }
417 | }
418 | }
419 |
420 | public function delete(string $column, string $value): bool
421 | {
422 | $query = "DELETE FROM {$this->table} WHERE {$column} = :value";
423 | $statement = $this->connection->prepare($query);
424 | $statement->bindValue(':value', $value);
425 | return $statement->execute();
426 | }
427 |
428 |
429 | }
430 |
--------------------------------------------------------------------------------
/App/Core/Request.php:
--------------------------------------------------------------------------------
1 | method() === 'GET') {
22 | foreach ($_GET as $key => $value) {
23 | $body[$key] = filter_input(INPUT_GET, $key, FILTER_SANITIZE_SPECIAL_CHARS);
24 | }
25 | }
26 |
27 | if ($this->method() === 'POST') {
28 | foreach ($_POST as $key => $value) {
29 | $body[$key] = filter_input(INPUT_POST, $key, FILTER_SANITIZE_SPECIAL_CHARS);
30 | }
31 | }
32 |
33 | return $body;
34 | }
35 |
36 | public function file(): array
37 | {
38 | $file = [];
39 |
40 | if ($this->method() === 'POST') {
41 | foreach ($_FILES as $key => $value) {
42 | $file[$key] = $value;
43 | }
44 | }
45 |
46 | return $file;
47 | }
48 |
49 | public static function bearerToken(): string
50 | {
51 | $headers = getallheaders();
52 | $token = $headers['Authorization'] ?? '';
53 |
54 | if (preg_match('/Bearer\s(\S+)/', $token, $matches)) {
55 | return $matches[1];
56 | }
57 |
58 | return '';
59 | }
60 |
61 | // request make dynamic property when request post by key and value
62 | public function __get($key)
63 | {
64 | return $this->body()[$key] ?? '';
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/App/Core/Responses.php:
--------------------------------------------------------------------------------
1 | $method,
23 | 'path' => $path,
24 | 'controller' => $controller,
25 | 'function' => $function,
26 | 'middleware' => $middlewares
27 | ];
28 | }
29 |
30 | public static function get(string $path, $action = null, array $middlewares = []): void
31 | {
32 | if (is_array($action)) {
33 | $controller = $action[0];
34 | $function = $action[1];
35 | } else {
36 | $controller = self::$controller;
37 | $function = $action;
38 | }
39 |
40 | if (count(self::$middlewares) > 0) {
41 | $middlewares = array_merge(self::$middlewares, $middlewares);
42 | }
43 |
44 | self::add('GET', $path, $controller, $function, $middlewares);
45 | }
46 |
47 | public static function post(string $path, $action = null, array $middlewares = []): void
48 | {
49 | if (is_array($action)) {
50 | $controller = $action[0];
51 | $function = $action[1];
52 | } else {
53 | $controller = self::$controller;
54 | $function = $action;
55 | }
56 |
57 | if (count(self::$middlewares) > 0) {
58 | $middlewares = array_merge(self::$middlewares, $middlewares);
59 | }
60 |
61 | self::add('POST', $path, $controller, $function, $middlewares);
62 | }
63 |
64 | public static function put(string $path, $action = null, array $middlewares = []): void
65 | {
66 | if (is_array($action)) {
67 | $controller = $action[0];
68 | $function = $action[1];
69 | } else {
70 | $controller = self::$controller;
71 | $function = $action;
72 | }
73 |
74 | if (count(self::$middlewares) > 0) {
75 | $middlewares = array_merge(self::$middlewares, $middlewares);
76 | }
77 |
78 | self::add('PUT', $path, $controller, $function, $middlewares);
79 | }
80 |
81 |
82 | public static function patch(string $path, $action = null, array $middlewares = []): void
83 | {
84 | if (is_array($action)) {
85 | $controller = $action[0];
86 | $function = $action[1];
87 | } else {
88 | $controller = self::$controller;
89 | $function = $action;
90 | }
91 |
92 | if (count(self::$middlewares) > 0) {
93 | $middlewares = array_merge(self::$middlewares, $middlewares);
94 | }
95 |
96 | self::add('PATCH', $path, $controller, $function, $middlewares);
97 | }
98 |
99 | public static function delete(string $path, $action = null, array $middlewares = []): void
100 | {
101 | if (is_array($action)) {
102 | $controller = $action[0];
103 | $function = $action[1];
104 | } else {
105 | $controller = self::$controller;
106 | $function = $action;
107 | }
108 |
109 | if (count(self::$middlewares) > 0) {
110 | $middlewares = array_merge(self::$middlewares, $middlewares);
111 | }
112 |
113 | self::add('DELETE', $path, $controller, $function, $middlewares);
114 | }
115 |
116 |
117 |
118 | public static function controller(string $controller, array $middlewares = []): Router
119 | {
120 | self::$controller = $controller;
121 | self::$middlewares = $middlewares;
122 | return new self;
123 | }
124 |
125 | public function group(callable $callback): void
126 | {
127 | $callback();
128 | }
129 |
130 | public static function run(): void
131 | {
132 | $requestMethod = $_SERVER['REQUEST_METHOD'];
133 | $requestUri = $_SERVER['REQUEST_URI'];
134 | $requestUri = explode('?', $requestUri)[0];
135 |
136 | // if default routes response welcome message
137 | if ($requestUri === '/') {
138 | self::response(200, ['message' => 'Welcome to the Mardira Framework']);
139 | return;
140 | }
141 | foreach (self::$routes as $route) {
142 | // remove $requestUri from /users/ to /users if last character is /
143 | if (substr($requestUri, -1) === '/') {
144 | $requestUri = substr($requestUri, 0, -1);
145 | }
146 |
147 | if ($route['path'] === $requestUri) {
148 | if ($route['method'] === $requestMethod) {
149 | self::checkRoute($route['path'], $route['controller'], $route['function'], $requestUri, $route['middleware']);
150 | return;
151 | }
152 | }
153 | }
154 |
155 | self::response(404, ['message' => 'Not Found']);
156 | }
157 |
158 |
159 | private static function checkRoute($routePath, $controller, $function, $requestUri, $middlewares)
160 | {
161 | $path = $routePath;
162 | $path = str_replace('/', '\/', $path);
163 | $path = preg_replace('/\{[a-zA-Z0-9]+\}/', '([a-zA-Z0-9]+)', $path);
164 | $path = '/^' . $path . '$/';
165 | try {
166 | if (preg_match($path, $requestUri, $matches)) {
167 |
168 | // if route has middleware
169 | if (count($middlewares) > 0) {
170 | foreach ($middlewares as $middleware) {
171 | $middleware = new $middleware;
172 | $middleware->handle(function () {
173 | return;
174 | });
175 | }
176 | }
177 |
178 | $controller = $controller;
179 | $function = $function;
180 | $controller = new $controller;
181 | $controller->$function(...array_slice($matches, 1));
182 | return;
183 | }
184 | } catch (\Throwable $th) {
185 | self::response(500, [
186 | 'message' => $th->getMessage(),
187 | 'file' => $th->getFile(),
188 | 'line' => $th->getLine(),
189 | 'trace' => $th->getTrace(),
190 | ]);
191 | return;
192 | }
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/App/Core/Schema.php:
--------------------------------------------------------------------------------
1 | create();
14 | }
15 |
16 | public static function drop(string $table)
17 | {
18 | $blueprint = new Blueprint($table);
19 | $blueprint->drop();
20 | }
21 |
22 | public static function dropIfExists(string $table)
23 | {
24 | $blueprint = new Blueprint($table);
25 | $blueprint->dropIfExists();
26 | }
27 |
28 | public static function table(string $table, callable $callback)
29 | {
30 | $blueprint = new Blueprint($table);
31 | $callback($blueprint);
32 | $blueprint->execute();
33 | }
34 |
35 | public static function rename(string $table, string $newName)
36 | {
37 | $blueprint = new Blueprint($table);
38 | $blueprint->rename($newName);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/App/Core/Seeder.php:
--------------------------------------------------------------------------------
1 | call($s);
15 | }
16 | return;
17 | }
18 | $seeder = $this->getClassName($seeder);
19 | $this->addSeederCalled($seeder);
20 | $seeder = new $seeder();
21 | $seeder->run();
22 | }
23 |
24 | protected function getClassName(string $name): string
25 | {
26 | return ucfirst($name);
27 | }
28 |
29 | protected function getSeederPath(): string
30 | {
31 | return __DIR__ . '/../Database/Seeders';
32 | }
33 |
34 | // count seeder called
35 | public function countSeederCalled(): int
36 | {
37 | return count($this->seederCalled);
38 | }
39 |
40 | // get seeder called
41 | public function getSeederCalled(): array
42 | {
43 | return $this->seederCalled;
44 | }
45 |
46 | // add seeder called
47 | public function addSeederCalled(string $seeder): void
48 | {
49 | $this->seederCalled[] = $seeder;
50 | }
51 |
52 | // check seeder called
53 | public function checkSeederCalled(string $seeder): bool
54 | {
55 | return in_array($seeder, $this->seederCalled);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/App/Core/Upload.php:
--------------------------------------------------------------------------------
1 | file = $file;
34 | $this->path = __DIR__ . '/../../public/uploads/';
35 | $this->name = $file['name'] ?? '';
36 | $this->extension = pathinfo($this->name, PATHINFO_EXTENSION);
37 | $this->size = $file['size'] ?? 0;
38 | $this->mimeType = $file['type'] ?? '';
39 | $this->error = $file['error'] ?? 0;
40 | $this->allowedExtensions = [];
41 | $this->allowedMimeTypes = [];
42 | $this->maxSize = 0;
43 | $this->overwrite = false;
44 | $this->randomName = false;
45 | $this->newName = null;
46 | $this->isUploaded = false;
47 | $this->isMoved = false;
48 | $this->isImage = false;
49 | $this->imageWidth = 0;
50 | $this->imageHeight = 0;
51 | $this->imageType = 0;
52 | $this->imageSizeStr = '';
53 | $this->imageMime = '';
54 | $this->imageExtension = '';
55 | }
56 |
57 | public function setPath($path)
58 | {
59 | $this->path = $path;
60 | return $this;
61 | }
62 |
63 | public function setName($name)
64 | {
65 | $this->name = $name;
66 | return $this;
67 | }
68 |
69 | public function setExtension($extension)
70 | {
71 | $this->extension = $extension;
72 | return $this;
73 | }
74 |
75 | public function setSize($size)
76 | {
77 | $this->size = $size;
78 | return $this;
79 | }
80 |
81 | public function setMimeType($mimeType)
82 | {
83 | $this->mimeType = $mimeType;
84 | return $this;
85 | }
86 |
87 | public function setError($error)
88 | {
89 | $this->error = $error;
90 | return $this;
91 | }
92 |
93 | public function setAllowedExtensions($allowedExtensions)
94 | {
95 | $this->allowedExtensions = $allowedExtensions;
96 | return $this;
97 | }
98 |
99 | public function setAllowedMimeTypes($allowedMimeTypes)
100 | {
101 | $this->allowedMimeTypes = $allowedMimeTypes;
102 | return $this;
103 | }
104 |
105 | public function setMaxSize($maxSize)
106 | {
107 | $this->maxSize = $maxSize;
108 | return $this;
109 | }
110 |
111 | public function setOverwrite($overwrite)
112 | {
113 | $this->overwrite = $overwrite;
114 | return $this;
115 | }
116 |
117 | public function setRandomName($randomName)
118 | {
119 | $this->randomName = $randomName;
120 | return $this;
121 | }
122 |
123 | public function setNewName($newName)
124 | {
125 | $this->newName = $newName;
126 | return $this;
127 | }
128 |
129 | public function getPath()
130 | {
131 | return $this->path;
132 | }
133 |
134 | public function getName()
135 | {
136 | return $this->name;
137 | }
138 |
139 | public function getExtension()
140 | {
141 | return $this->extension;
142 | }
143 |
144 | public function getSize()
145 | {
146 | return $this->size;
147 | }
148 |
149 | public function getMimeType()
150 | {
151 | return $this->mimeType;
152 | }
153 |
154 | public function getError()
155 | {
156 | return $this->error;
157 | }
158 |
159 | public function getAllowedExtensions()
160 | {
161 | return $this->allowedExtensions;
162 | }
163 |
164 | public function getAllowedMimeTypes()
165 | {
166 | return $this->allowedMimeTypes;
167 | }
168 |
169 | public function getMaxSize()
170 | {
171 | return $this->maxSize;
172 | }
173 |
174 | public function getOverwrite()
175 | {
176 | return $this->overwrite;
177 | }
178 |
179 | public function getRandomName()
180 | {
181 | return $this->randomName;
182 | }
183 |
184 | public function getNewName()
185 | {
186 | return $this->newName;
187 | }
188 |
189 | public function isUploaded()
190 | {
191 | return $this->isUploaded;
192 | }
193 |
194 | public function isMoved()
195 | {
196 | return $this->isMoved;
197 | }
198 |
199 |
200 | public function getImageWidth()
201 | {
202 | return $this->imageWidth;
203 | }
204 |
205 | public function getImageHeight()
206 | {
207 | return $this->imageHeight;
208 | }
209 |
210 | public function getImageType()
211 | {
212 | return $this->imageType;
213 | }
214 |
215 | public function getImageSizeStr()
216 | {
217 | return $this->imageSizeStr;
218 | }
219 |
220 | public function getImageMime()
221 | {
222 | return $this->imageMime;
223 | }
224 |
225 | public function getImageExtension()
226 | {
227 | return $this->imageExtension;
228 | }
229 |
230 | public static function upload()
231 | {
232 | return new static($_FILES);
233 | }
234 |
235 | public function validate()
236 | {
237 | if ($this->error !== 0) {
238 | throw new \Exception('Error uploading file');
239 | }
240 |
241 | if (!empty($this->allowedExtensions) && !in_array($this->extension, $this->allowedExtensions)) {
242 | throw new \Exception('Extension not allowed');
243 | }
244 |
245 | if (!empty($this->allowedMimeTypes) && !in_array($this->mimeType, $this->allowedMimeTypes)) {
246 | throw new \Exception('Mime type not allowed');
247 | }
248 |
249 | if ($this->maxSize > 0 && $this->size > $this->maxSize) {
250 | throw new \Exception('File size exceeds limit');
251 | }
252 |
253 | if (!is_dir($this->path)) {
254 | throw new \Exception('Directory does not exist');
255 | }
256 |
257 | if (!is_writable($this->path)) {
258 | throw new \Exception('Directory is not writable');
259 | }
260 |
261 | if (file_exists($this->path . $this->name) && !$this->overwrite) {
262 | throw new \Exception('File already exists');
263 | }
264 |
265 | $this->isUploaded = true;
266 | return $this;
267 | }
268 |
269 | public function move()
270 | {
271 | if (!$this->isUploaded) {
272 | throw new \Exception('File not uploaded');
273 | }
274 |
275 | if ($this->randomName) {
276 | $this->name = uniqid() . '.' . $this->extension;
277 | }
278 |
279 | if ($this->newName) {
280 | $this->name = $this->newName . '.' . $this->extension;
281 | }
282 |
283 | // check if tmp_name directory exists
284 | if (!file_exists($this->file['tmp_name'])) {
285 | throw new \Exception('File not found');
286 | }
287 |
288 | // upload file from cross server
289 | if (is_uploaded_file($this->file['tmp_name'])) {
290 | if (!move_uploaded_file($this->file['tmp_name'], $this->path . $this->name)) {
291 | throw new \Exception('Error moving file');
292 | }
293 | } // upload file from same server
294 | else {
295 | if (!rename($this->file['tmp_name'], $this->path . $this->name)) {
296 | throw new \Exception('Error moving file');
297 | }
298 | }
299 |
300 | $this->isMoved = true;
301 | return $this;
302 | }
303 |
304 | public function getImageInfo()
305 | {
306 | if (!$this->isImage) {
307 | throw new \Exception('File is not an image');
308 | }
309 |
310 | $imageInfo = getimagesize($this->file['tmp_name']);
311 | $this->imageWidth = $imageInfo[0];
312 | $this->imageHeight = $imageInfo[1];
313 | $this->imageType = $imageInfo[2];
314 | $this->imageSizeStr = $imageInfo[3];
315 | $this->imageMime = $imageInfo['mime'];
316 | $this->imageExtension = image_type_to_extension($imageInfo[2], false);
317 | return $this;
318 | }
319 |
320 | public function isImage()
321 | {
322 | $this->isImage = getimagesize($this->file['tmp_name']) !== false;
323 | return $this;
324 | }
325 | }
326 |
--------------------------------------------------------------------------------
/App/Core/Validator.php:
--------------------------------------------------------------------------------
1 | $rules) {
18 | foreach ($rules as $rule => $value) {
19 | switch ($rule) {
20 | case 'required':
21 | self::required($field);
22 | break;
23 | case 'min':
24 | self::min($field, $value);
25 | break;
26 | case 'max':
27 | self::max($field, $value);
28 | break;
29 | case 'email':
30 | self::email($field);
31 | break;
32 | case 'file':
33 | self::file($field);
34 | break;
35 | case 'unique':
36 | self::unique($field);
37 | break;
38 | case 'json':
39 | self::json($field);
40 | break;
41 | }
42 | }
43 | }
44 | return new self();
45 | }
46 |
47 | private static function required(string $field): void
48 | {
49 | if (!isset(self::$data[$field]) || empty(self::$data[$field])) {
50 | self::addError($field, 'The ' . $field . ' field is required');
51 | }
52 | }
53 |
54 | private static function min(string $field, int $min): void
55 | {
56 | if (strlen(self::$data[$field]) < $min) {
57 | self::addError($field, 'The ' . $field . ' field must be at least ' . $min . ' characters');
58 | }
59 | }
60 |
61 | private static function max(string $field, int $max): void
62 | {
63 | if (strlen(self::$data[$field]) > $max) {
64 | self::addError($field, 'The ' . $field . ' field must be at most ' . $max . ' characters');
65 | }
66 | }
67 |
68 | private static function email(string $field): void
69 | {
70 | if (!filter_var(self::$data[$field], FILTER_VALIDATE_EMAIL)) {
71 | self::addError($field, 'The ' . $field . ' field must be a valid email');
72 | }
73 | }
74 |
75 | private static function file(string $field): void
76 | {
77 | if (!isset($_FILES[$field]) || !is_uploaded_file($_FILES[$field]['tmp_name'])) {
78 | self::addError($field, 'The ' . $field . ' field must be a valid file');
79 | return;
80 | }
81 | }
82 |
83 | private static function json(string $field): void
84 | {
85 | if (!is_array(json_decode(self::$data[$field], true))) {
86 | self::addError($field, 'The ' . $field . ' field must be a valid json');
87 | }
88 | }
89 |
90 | private static function unique(string $field): void
91 | {
92 | $table = self::$fields[$field]['table'];
93 | $column = self::$fields[$field]['column'];
94 | $query = "SELECT * FROM {$table} WHERE {$column} = :{$column}";
95 | $statement = (new Database())->connection->prepare($query);
96 | $statement->bindParam(':' . $column, self::$data[$field]);
97 | $statement->execute();
98 | $result = $statement->fetch(PDO::FETCH_OBJ);
99 | if ($result) {
100 | self::addError($field, 'The ' . $field . ' field must be unique');
101 | }
102 | }
103 |
104 | private static function addError(string $field, string $message): void
105 | {
106 | self::$errors[$field] = $message;
107 | }
108 |
109 | public static function fails(): bool
110 | {
111 | return count(self::$errors) > 0;
112 | }
113 |
114 | public function errors(): array
115 | {
116 | return self::$errors;
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/App/Database/Migrations/2023_01_31_104005_create_table_users.php:
--------------------------------------------------------------------------------
1 | increment('id');
15 | $table->string('name', 50);
16 | $table->string('username', 25);
17 | $table->string('email', 30);
18 | $table->string('password', 64);
19 | $table->integer('role_id');
20 | $table->index('role_id');
21 | $table->string('remember_token', 100)->nullable();
22 | $table->timestamps();
23 | });
24 | }
25 |
26 | public function down()
27 | {
28 | Schema::dropIfExists('users');
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/App/Database/Migrations/2023_01_31_160221_create_table_roles.php:
--------------------------------------------------------------------------------
1 | increment('id');
15 | $table->string('name', 50);
16 | $table->string('description', 100)->nullable();
17 | $table->timestamps();
18 | });
19 | }
20 |
21 | public function down()
22 | {
23 | Schema::dropIfExists('roles');
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/App/Database/Seeders/GlobalSeeder.php:
--------------------------------------------------------------------------------
1 | call(RoleSeeder::class);
12 | $this->call(UserSeeder::class);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/App/Database/Seeders/RoleSeeder.php:
--------------------------------------------------------------------------------
1 | insert(
13 | [
14 | [
15 | 'name' => 'admin',
16 | 'description' => 'Administrator'
17 | ],
18 | [
19 | 'name' => 'user',
20 | 'description' => 'User'
21 | ]
22 | ]
23 | );
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/App/Database/Seeders/UserSeeder.php:
--------------------------------------------------------------------------------
1 | 'Administrator',
14 | 'username' => 'admin',
15 | 'email' => 'admin@admin.com',
16 | 'password' => password_hash('password', PASSWORD_DEFAULT),
17 | 'role_id' => 1,
18 | ];
19 | DB::table('users')->insert($data);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/App/Helpers/TimeHelper.php:
--------------------------------------------------------------------------------
1 | role_id;
16 |
17 | if ($role == 1) {
18 | return $next();
19 | }
20 | return $this->response(401, ['message' => 'Only Admin can access this route']);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/App/Middleware/AuthMiddleware.php:
--------------------------------------------------------------------------------
1 | response(401, ['message' => 'Unauthorized']);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/App/Models/User.php:
--------------------------------------------------------------------------------
1 | verifyToken($token, DotEnvKey::get('ACCESS_TOKEN_SECRET_KEY'));
23 | if (isset($user->data)) {
24 | return $user->data;
25 | }
26 | return false;
27 | }
28 |
29 | public static function check(): bool
30 | {
31 | $token = Request::bearerToken();
32 | $user = self::verify($token);
33 | if ($user) {
34 | $user = DB::table('users')->where('id', $user->id)->first();
35 | if ($user) {
36 | return true;
37 | }
38 | }
39 | return false;
40 | }
41 |
42 | public static function user()
43 | {
44 | $token = Request::bearerToken();
45 | $user = self::verify($token);
46 | if ($user) {
47 | $user = DB::table('users')->where('id', $user->id)->first();
48 | if ($user) {
49 | return $user;
50 | }
51 | }
52 | return false;
53 | }
54 |
55 | public static function attempt(array $credentials = [], $validator = null) : bool
56 | {
57 | // if validation fails
58 | if ($validator) {
59 | if ($validator->fails()) {
60 | self::response(401, [
61 | 'message' => 'Invalid credentials',
62 | 'errors' => $validator->errors()
63 | ]);
64 | }
65 | }
66 |
67 | // validate form via validator
68 | if (self::validate($credentials)) {
69 | $user = DB::table('users');
70 | $field = isset($credentials['email']) ? 'email' : 'username';
71 | $user = $user->where($field, isset($credentials['email']) ? $credentials['email'] : $credentials['username']);
72 | $user = $user->first();
73 |
74 | if ($user) {
75 | if (password_verify($credentials['password'], $user->password)) {
76 | $token = (new self)->generateToken([
77 | 'data' => [
78 | 'id' => $user->id,
79 | 'username' => $user->username,
80 | 'email' => $user->email,
81 | 'created_at' => $user->created_at,
82 | 'updated_at' => $user->updated_at,
83 | 'expires_at' => TimeHelper::setMinutes(30)
84 | ]
85 | ], DotEnvKey::get('ACCESS_TOKEN_SECRET_KEY'));
86 | self::response(200, [
87 | 'message' => 'Login successful',
88 | 'token' => $token,
89 | 'expires_at' => date('Y-m-d H:i:s', TimeHelper::setMinutes(30))
90 | ]);
91 | return true;
92 | }
93 | }
94 | }
95 | return false;
96 | }
97 |
98 | public static function register(array $credentials = [])
99 | {
100 | // validate form via validator
101 | if (self::validate($credentials)) {
102 | $user = DB::table('users');
103 | $field = isset($credentials['email']) ? 'email' : 'username';
104 | $user = $user->where($field, isset($credentials['email']) ? $credentials['email'] : $credentials['username']);
105 | $user = $user->first();
106 |
107 | if (!$user) {
108 | $credentials['password'] = password_hash($credentials['password'], PASSWORD_DEFAULT);
109 | $insert = DB::table('users')->insert($credentials);
110 | $user = DB::table('users')->where('id', $insert)->first();
111 | if ($user) {
112 | $token = (new self)->generateToken([
113 | 'data' => [
114 | 'id' => $user->id,
115 | 'username' => $user->username,
116 | 'email' => $user->email,
117 | 'created_at' => $user->created_at,
118 | 'updated_at' => $user->updated_at,
119 | 'expires_at' => TimeHelper::setMinutes(30)
120 | ]
121 | ], DotEnvKey::get('ACCESS_TOKEN_SECRET_KEY'));
122 | self::response(200, [
123 | 'message' => 'Register successful',
124 | 'token' => $token,
125 | 'expires_at' => date('Y-m-d H:i:s', TimeHelper::setMinutes(30))
126 | ]);
127 | return true;
128 | }
129 | }
130 | }
131 | return false;
132 | }
133 |
134 |
135 | public static function validate(array $credentials = [])
136 | {
137 | // foreach validate required each credentials
138 | $fields = [];
139 |
140 | foreach ($credentials as $key => $value) {
141 |
142 | // if key email use validate email
143 | if ($key == 'email') {
144 | $fieldset = [
145 | 'required' => true,
146 | 'email' => true
147 | ];
148 | $fields[] = [
149 | $key => $fieldset
150 | ];
151 | continue;
152 | }
153 |
154 | $fieldset = [
155 | 'required' => true,
156 | ];
157 |
158 | $fields[] = [
159 | $key => $fieldset
160 | ];
161 | }
162 |
163 | $fields = array_merge(...$fields);
164 | $validator = Validator::validate($fields, $credentials);
165 |
166 | if ($validator->fails()) {
167 | self::response(400, [
168 | 'message' => 'Failed',
169 | 'errors' => $validator->errors()
170 | ]);
171 | return;
172 | }
173 | return true;
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/App/Routes/Api.php:
--------------------------------------------------------------------------------
1 | group(function () {
11 | Router::get('/users', 'index');
12 | Router::get('/users/{id}', 'show');
13 | });
14 |
15 | Router::controller(AuthController::class)->group(function () {
16 | Router::post('/auth/login', 'login');
17 | Router::post('/auth/register', 'register');
18 | });
19 |
20 |
21 | Router::run();
22 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023, STMIK Mardira Indonesia.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 |
16 | The Software is provided "as is", without warranty of any kind, express or
17 | implied, including but not limited to the warranties of merchantability,
18 | fitness for a particular purpose and noninfringement. In no event shall the
19 | authors or copyright holders be liable for any claim, damages or other
20 | liability, whether in an action of contract, tort or otherwise, arising from,
21 | out of or in connection with the software or the use or other dealings in the
22 | Software.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 
4 |
5 |
6 |
7 | Mardira Framework is a PHP framework Model Controller Based for building web applications and APIs. It is designed to be simple, and fast.
8 |
9 | 
10 | 
11 | 
12 | 
13 | 
14 |
15 | ## Table of Contents
16 |
17 | - [Requirements](#requirements)
18 | - [Structure Folders](#structure-folders)
19 | - [Installation](#installation)
20 | - [Usage](#usage)
21 | - [Start Server](#start-server)
22 | - [Create .env](#create-env)
23 | - [Create Controller](#create-controller)
24 | - [Create Model](#create-model)
25 | - [Create Route](#create-route)
26 | - [Create Migration](#create-migration)
27 | - [Run Migration](#run-migration)
28 | - [Refresh Migration](#refresh-migration)
29 | - [Refresh Migration With Seed](#refresh-migration-with-seed)
30 | - [Create Seeder](#create-seeder)
31 | - [Run Seeder](#run-seeder)
32 | - [Run Seeder Specific](#run-seeder-specific)
33 | - [Create Authetication](#create-authetication)
34 | - [Refresh Authetication](#refresh-authetication)
35 | - [Update Framework Version](#update-framework-version)
36 | - [Controller](#controller)
37 | - [Model](#model)
38 | - [Migration](#migration)
39 | - [Seeder](#seeder)
40 | - [Middleware](#middleware)
41 | - [Route](#route)
42 | - [Route Group](#route-group)
43 | - [Query Builder](#query-builder)
44 | - [Select](#select)
45 | - [Where](#where)
46 | - [Or Where](#or-where)
47 | - [Where In](#where-in)
48 | - [Where Not In](#where-not-in)
49 | - [Where Null](#where-null)
50 | - [Where Not Null](#where-not-null)
51 | - [Order By](#order-by)
52 | - [Group By](#group-by)
53 | - [Join](#join)
54 | - [Insert](#insert)
55 | - [Update](#update)
56 | - [Delete](#delete)
57 | - [Count](#count)
58 |
59 | ## Requirements
60 |
61 | - PHP = 7.4
62 | - MySQL >= 5.7.8
63 | - Apache >= 2.4.41
64 | - Composer >= 2.0.9
65 |
66 | ## Structure Folders
67 |
68 | ```shell
69 | mardira-framework
70 | ├── App
71 | │ ├── Controllers
72 | │ │ ├── AuthController.php
73 | │ ├── Core
74 | │ │ ├── Commands
75 | │ ├── Database
76 | │ │ ├── Migrations
77 | │ │ │ ├── 2023_01_31_xxxxxx_create_table_users.php
78 | │ │ │ ├── 2023_01_31_xxxxxx_create_table_roles.php
79 | │ │ ├── Seeders
80 | │ │ │ ├── GlobalSeeder.php
81 | │ ├── Helpers
82 | │ ├── Middleware
83 | │ ├── Models
84 | │ ├── Packages
85 | │ ├── Routes
86 | │ │ ├── Api.php
87 | ```
88 |
89 | ## Installation
90 |
91 | ### Setup
92 |
93 | > You can create a new project using composer
94 |
95 | ```shell
96 | composer create-project mardira/mardira-framework
97 | ```
98 |
99 | > or you can clone this project
100 |
101 |
102 |
103 | ### Clone
104 |
105 | - Clone this repo to your local machine using `git clone
106 |
107 | ```shell
108 | git clone https://github.com/Bootcamp-STMIK-Mardira-Indonesia/mardira-framework.git
109 | ```
110 |
111 | > Then, install the dependencies using composer
112 |
113 | ```shell
114 | composer install
115 | ```
116 |
117 | > or
118 |
119 | ```shell
120 | composer update
121 | ```
122 |
123 | ## Usage
124 |
125 | ### Start Server
126 |
127 | ```shell
128 | php mardira serve
129 | ```
130 |
131 | > or
132 |
133 | ```shell
134 | php mardira serve --port=
135 | ```
136 |
137 | ### Create .env
138 |
139 | > You can create .env file using command
140 |
141 | ```shell
142 | php mardira make:env
143 | ```
144 |
145 | ### Create Controller
146 |
147 | ```shell
148 | php mardira make:controller ControllerName
149 | ```
150 |
151 | ### Create Model
152 |
153 | ```shell
154 | php mardira make:model ModelName
155 | ```
156 |
157 | ### Create Route
158 |
159 | ```shell
160 | php mardira make:route route_name --controller=ControllerName
161 | ```
162 |
163 | ### Create Migration
164 |
165 | ```shell
166 |
167 | php mardira make:migration create_table_table_name
168 | ```
169 |
170 | ### Run Migration
171 |
172 | > If database not exist, will automatically create database from .env
173 |
174 | ```shell
175 | php mardira migrate
176 | ```
177 |
178 | ### Refresh Migration
179 |
180 | ```shell
181 | php mardira migrate:refresh
182 | ```
183 |
184 | ### Refresh Migration With Seed
185 |
186 | ```shell
187 | php mardira migrate:refresh --seed
188 | ```
189 |
190 | ### Create Seeder
191 |
192 | ```shell
193 | php mardira make:seeder SeederName
194 | ```
195 |
196 | ### Run Seeder
197 |
198 | ```shell
199 | php mardira db:seed
200 | ```
201 |
202 | ### Run Seeder Specific
203 |
204 | ```shell
205 | php mardira db:seed --class=SeederName
206 | ```
207 |
208 | ### Create Authetication
209 |
210 | ```shell
211 | php mardira make:auth
212 | ```
213 |
214 | ### Refresh Authetication
215 |
216 | ```shell
217 | php mardira make:auth --refresh
218 | ```
219 |
220 | ### Update Framework Version
221 |
222 | ```shell
223 | php mardira update
224 | ```
225 |
226 | ### Controller
227 |
228 | > Create controller use `php mardira make:controller ControllerName`, here is example controller
229 |
230 | ```php
231 | response(200,[
242 | 'message' => 'Hello World'
243 | ]);
244 | }
245 | }
246 | ```
247 |
248 | > to use controller, you can add route in `App/Routes/Api.php`
249 |
250 | ```php
251 | You can use response in controller
262 |
263 | ```php
264 | $this->response(200,[
265 | 'message' => 'Hello World'
266 | ]);
267 |
268 | ```
269 |
270 | > return json expected
271 |
272 | ```json
273 | {
274 | "message": "Hello World"
275 | }
276 | ```
277 |
278 | > another response example 409
279 |
280 | ```php
281 | $this->response->json(409,[
282 | 'message' => 'Conflict'
283 | ]);
284 | ```
285 |
286 | ### Model
287 |
288 | > Create model use `php mardira make:model ModelName`, here is example model
289 |
290 | ```php
291 | to use model, you can add model in `App/Controllers/ControllerName.php`
305 |
306 | ```php
307 | response(200,[
321 | 'message' => 'Hello World',
322 | 'data' => $user
323 | ]);
324 | }
325 | }
326 | ```
327 |
328 | ### Migration
329 |
330 | > Create migration use `php mardira make:migration create_table_table_name`, here is example migration
331 |
332 | ```php
333 | schema->create('users', function ($table) {
344 | $table->increment('id');
345 | $table->string('name', 50);
346 | $table->string('email',50)->unique();
347 | $table->string('password', 64);
348 | $table->timestamps();
349 | });
350 | }
351 |
352 | public function down()
353 | {
354 | $this->schema->dropIfExists('users');
355 | }
356 | }
357 | ```
358 |
359 | ### Seeder
360 |
361 | > Create seeder use `php mardira make:seeder SeederName`, here is example seeder
362 |
363 | ```php
364 | 'Administrator',
378 | 'username' => 'admin',
379 | 'email' => 'admin@admin.com',
380 | 'password' => password_hash('password', PASSWORD_DEFAULT),
381 | 'role_id' => 1,
382 | ],
383 | [
384 | 'name' => 'User',
385 | 'username' => 'user',
386 | 'email' => 'user@user.com',
387 | 'password' => password_hash('password', PASSWORD_DEFAULT),
388 | 'role_id' => 2,
389 | ]
390 | ];
391 | DB::table('users')->insert($data);
392 | }
393 | }
394 |
395 | ```
396 |
397 | ### Middleware
398 |
399 | > Create middleware use `php mardira make:middleware MiddlewareName`, here is example middleware
400 |
401 | ```php
402 | response(401, ['message' => 'Unauthorized']);
417 | }
418 | }
419 | ```
420 |
421 | > to use middleware, you can add middleware in route
422 |
423 | ```php
424 |
425 | Router::get('/schedules', [ScheduleController::class, 'index'], [AuthMiddleware::class]);
426 |
427 | ```
428 |
429 | ### Routing
430 |
431 | > You can add route in `App/Routes/Api.php`
432 |
433 | ```php
434 |
435 | You can add route group in `App/Routes/Api.php`
446 |
447 | ```php
448 |
449 | group(function () {
455 | Router::post('/products/store', 'store');
456 | });
457 |
458 | ```
459 |
460 | ### Query Builder
461 |
462 |
463 |
464 |
465 |
466 | ####
467 |
468 | ```php
469 |
470 | use App\Core\QueryBuilder as DB;
471 |
472 | ```
473 |
474 | #### Select
475 |
476 | ```php
477 | DB::table('users')->select('name', 'email')->get();
478 | ```
479 |
480 | #### Where
481 |
482 | ```php
483 |
484 | // equal
485 | DB::table('users')->where('id', 1)->get();
486 |
487 | DB::table('users')->where('id', 1, '>')->get();
488 |
489 | DB::table('users')->where('id', 1, '<')->get();
490 |
491 | DB::table('users')->where('id', 1, '>=')->get();
492 |
493 | DB::table('users')->where('id', 1, '<=')->get();
494 |
495 | DB::table('users')->where('id', 1, '!=')->get();
496 |
497 | DB::table('users')->where('id', 1, '<>')->get();
498 |
499 | // like
500 |
501 | DB::table('users')->where('name', 'admin', 'like')->get();
502 |
503 | DB::table('users')->where('name', 'admin', 'not like')->get();
504 |
505 | ```
506 |
507 | #### Or Where
508 |
509 | ```php
510 |
511 |
512 | DB::table('users')->orWhere('id', 1)->get();
513 |
514 | DB::table('users')->orWhere('id', 1, '>')->get();
515 |
516 | DB::table('users')->orWhere('id', 1, '<')->get();
517 |
518 | DB::table('users')->orWhere('id', 1, '>=')->get();
519 |
520 | DB::table('users')->orWhere('id', 1, '<=')->get();
521 |
522 | DB::table('users')->orWhere('id', 1, '!=')->get();
523 |
524 | DB::table('users')->orWhere('id', 1, '<>')->get();
525 |
526 | ```
527 |
528 | #### Where In
529 |
530 | ```php
531 |
532 | DB::table('users')->whereIn('id', [1,2,3])->get();
533 |
534 | DB::table('users')->whereNotIn('id', [1,2,3])->get();
535 |
536 | ```
537 |
538 | #### Where Not In
539 |
540 | ```php
541 |
542 | DB::table('users')->whereNotIn('id', [1,2,3])->get();
543 |
544 | ```
545 |
546 | #### Where Null
547 |
548 | ```php
549 |
550 | DB::table('users')->whereNull('id')->get();
551 | ```
552 |
553 | #### Where Not Null
554 |
555 | ```php
556 | DB::table('users')->whereNotNull('id')->get();
557 | ```
558 |
559 | #### Order By
560 |
561 | ```php
562 |
563 | DB::table('users')->orderBy('id', 'desc')->get();
564 |
565 | DB::table('users')->orderBy('id', 'asc')->get();
566 |
567 | ```
568 |
569 | #### Join Table
570 |
571 | ```php
572 |
573 | DB::table('users')
574 | ->join('roles', 'users.role_id', '=', 'roles.id')
575 | ->select('users.*', 'roles.name as role_name')
576 | ->get();
577 |
578 | ```
579 |
580 | #### Group By
581 |
582 | ```php
583 |
584 | DB::table('users')
585 | ->groupBy('role_id')
586 | ->get();
587 |
588 | ```
589 |
590 | #### Insert
591 |
592 | ```php
593 |
594 | DB::table('users')->insert([
595 | 'name' => 'user',
596 | 'email' => 'user@user.com',
597 | 'password' => password_hash('password', PASSWORD_DEFAULT),
598 | ]);
599 |
600 | ```
601 |
602 | #### Update
603 |
604 | ```php
605 |
606 | DB::table('users')->where('id', 1)->update([
607 | 'name' => 'user',
608 | 'email' => 'user@gmail.com',
609 | ]);
610 |
611 | ```
612 |
613 | #### Delete
614 |
615 | ```php
616 |
617 | DB::table('users')->where('id', 1)->delete();
618 |
619 | ```
620 |
621 | #### Count
622 |
623 | ```php
624 |
625 | DB::table('users')->count();
626 |
627 | ```
628 |
629 | ## Support
630 |
631 | Reach out to me at one of the following places!
632 |
633 | - Website at `demostmikmi.com`
634 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mardira/mardira-framework",
3 | "type": "framework",
4 | "license": "MIT",
5 | "keywords": [
6 | "framework",
7 | "mardira",
8 | "bootcamp"
9 | ],
10 | "description": "Mardira Framework",
11 | "authors": [
12 | {
13 | "name": "xietsunzao",
14 | "email": "jefri@stmik-mi.ac.id"
15 | }
16 | ],
17 | "require": {
18 | "php": "^7.4",
19 | "vlucas/phpdotenv": "^5.5",
20 | "firebase/php-jwt": "^6.3",
21 | "symfony/console": "2.6.7",
22 | "doctrine/inflector": "^2.0"
23 | },
24 | "autoload": {
25 | "psr-4": {
26 | "App\\": "App/"
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/mardira:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 |
3 |