diff --git a/Makefile b/Makefile
index b31f823..9a60730 100644
--- a/Makefile
+++ b/Makefile
@@ -1,12 +1,14 @@
# set user to "root" to run commands as root in docker
USER=$$(whoami)
# The docker command to execute commands directly in docker
-DOCKER=docker-compose exec --user=$(USER) backend-php
+DOCKER=docker-compose exec -T --user="$(USER)" backend-php
# The PHP binary to use, you may add arguments to PHP here
PHP=php
# directories writeable by webserver
WRITEABLE_DIRS=/app/runtime /app/logs /app/backend/web/assets
+TESTCASE=
+COMMAND=
# default target lists general usage information
default:
@@ -43,7 +45,10 @@ stop:
# run bash inside docker container
bash: cli
cli:
- $(DOCKER) bash
+ docker-compose exec --user="$(USER)" backend-php bash
+
+run:
+ $(DOCKER) sh -c '$(COMMAND)'
start-docker: docker-compose.override.yml runtime/build-docker config/components-dev.local.php backend/config/cookie-validation.key env.php stop
docker-compose up -d
@@ -79,7 +84,7 @@ backend/config/cookie-validation.key:
## Docker Runtime Tests ##
test: tests/_data/dump.sql
- $(DOCKER) vendor/bin/codecept run
+ $(DOCKER) vendor/bin/codecept run $(TESTCASE)
clean:
rm -rf tests/_data/dump.sql
diff --git a/README.md b/README.md
index 3828272..694f5a1 100644
--- a/README.md
+++ b/README.md
@@ -196,6 +196,18 @@ The following list explains the directory structure in more detail:
- `logs/` - log files
- `runtime/` - temporary runtime files
+
+# Development
+
+Below commands are helpful while developing this project:
+
+```bash
+./yii gii/api --openApiPath=/app/openapi/schema.yaml --generateMigrations=0 --generateControllers=0 --generateUrls=0
+
+./yii gii/api --openApiPath=/app/openapi/schema.yaml --generateMigrations=1 --generateControllers=0 --generateUrls=0 --generateModels=0 --generateModelFaker=0
+```
+
+
# Support
**Need help with your API project?**
diff --git a/api/config/xdebug.ini b/api/config/xdebug.ini
new file mode 100644
index 0000000..d34bbc7
--- /dev/null
+++ b/api/config/xdebug.ini
@@ -0,0 +1,9 @@
+;zend_extension=xdebug.so
+xdebug.mode=debug
+xdebug.start_with_request=yes
+;;; if you are on macOS, use host.docker.internal to identify the host machine, due to a network limitation on mac (https://docs.docker.com/docker-for-mac/networking/#port-mapping)
+;;; otherwise find host ip
+xdebug.discover_client_host=yes
+xdebug.client_host=host.docker.internal
+xdebug.client_port=9003
+xdebug.idekey=XDEBUG_API
diff --git a/backend/config/xdebug.ini b/backend/config/xdebug.ini
new file mode 100644
index 0000000..ec55ea6
--- /dev/null
+++ b/backend/config/xdebug.ini
@@ -0,0 +1,9 @@
+;zend_extension=xdebug.so
+xdebug.mode=debug
+xdebug.start_with_request=yes
+;;; if you are on macOS, use host.docker.internal to identify the host machine, due to a network limitation on mac (https://docs.docker.com/docker-for-mac/networking/#port-mapping)
+;;; otherwise find host ip
+xdebug.discover_client_host=yes
+xdebug.client_host=host.docker.internal
+xdebug.client_port=9003
+xdebug.idekey=XDEBUG_BACKEND
diff --git a/backend/views/site/error.php b/backend/views/site/error.php
new file mode 100644
index 0000000..4a37b25
--- /dev/null
+++ b/backend/views/site/error.php
@@ -0,0 +1,27 @@
+title = $name;
+?>
+
+
+
= Html::encode($this->title) ?>
+
+
+ = nl2br(Html::encode($message)) ?>
+
+
+
+ The above error occurred while the Web server was processing your request.
+
+
+ Please contact us if you think this is a server error. Thank you.
+
+
+
diff --git a/composer.json b/composer.json
index b2394fb..735bbdd 100644
--- a/composer.json
+++ b/composer.json
@@ -28,7 +28,7 @@
},
"config": {
"platform": {
- "php": "7.1.3"
+ "php": "7.4.26"
},
"fxp-asset": {
"enabled": false
diff --git a/config/gii-generators.php b/config/gii-generators.php
index a890af5..fe63a72 100644
--- a/config/gii-generators.php
+++ b/config/gii-generators.php
@@ -11,6 +11,5 @@
'modelNamespace' => 'common\\models',
'fakerNamespace' => 'common\\models\\faker',
'migrationPath' => '@common/migrations',
-
],
];
diff --git a/console/commands/FakerController.php b/console/commands/FakerController.php
index e384159..8cf8b65 100644
--- a/console/commands/FakerController.php
+++ b/console/commands/FakerController.php
@@ -4,7 +4,7 @@
use yii\console\Controller;
use yii\helpers\Console;
-use yii\helpers\FileHelper;
+use yii\helpers\{FileHelper, VarDumper};
use yii\helpers\StringHelper;
/**
@@ -16,11 +16,14 @@ public function actionIndex()
{
$fakers = FileHelper::findFiles(\Yii::getAlias('@common/models'), [
'only' => ['*Faker.php'],
+ 'except' => ['BaseModelFaker.php'],
]);
- foreach($fakers as $fakerFile) {
- $className = 'common\\models\\' . StringHelper::basename($fakerFile, '.php');
- $this->stdout('Generating fake data for ' . StringHelper::basename($fakerFile, 'Faker.php') . '...');
+ $sortedFakersModels = static::sortModels($fakers, '\\common\\models\\faker\\');
+
+ foreach($sortedFakersModels as $justModelClassName) {
+ $className = 'common\\models\\faker\\' . StringHelper::basename($justModelClassName, '.php').'Faker';
+ $this->stdout('Generating fake data for ' . StringHelper::basename($justModelClassName, 'Faker.php') . '...');
$faker = new $className;
for($i = 0; $i < 10; $i++) {
$model = $faker->generateModel();
@@ -32,4 +35,70 @@ public function actionIndex()
$this->stdout("done.\n", Console::BOLD, Console::FG_GREEN);
}
}
+
+ public static function sortModels(array $fakers, string $fakerNamespace = 'app\\models\\')
+ {
+ $modelsDependencies = [];
+ foreach($fakers as $fakerFile) {
+ $className = $fakerNamespace . StringHelper::basename($fakerFile, '.php');
+ $faker = new $className;
+
+ $modelClassName = str_replace(
+ 'Faker',
+ '',
+ StringHelper::basename($fakerFile, '.php')
+ );
+
+ if (!method_exists($className, 'dependentOn')) {
+ $modelsDependencies[$modelClassName] = null;
+ } else {
+ $modelsDependencies[$modelClassName] = $className::dependentOn();
+ }
+ }
+
+ $standalone = array_filter($modelsDependencies, function ($elm) {
+ return $elm === null;
+ });
+
+ $dependent = array_filter($modelsDependencies, function ($elm) {
+ return $elm !== null;
+ });
+
+ $justDepenentModels = array_keys($dependent);
+ $sortedDependentModels = $justDepenentModels;
+
+ foreach ($justDepenentModels as $model) {
+ if ($modelsDependencies[$model] !== null) {
+ foreach ($modelsDependencies[$model] as $dependentOn) {
+ if ($modelsDependencies[$dependentOn] !== null) {
+ // move $dependentOn before $model
+
+ // move model to sort/order
+ // in that function if it is already before (sorted) then avoid it
+ static::moveModel($sortedDependentModels, $dependentOn, $model);
+ }
+ }
+ }
+ }
+
+ $finalSortedModels = array_merge(array_keys($standalone), $sortedDependentModels);
+ return $finalSortedModels;
+ }
+
+ public static function moveModel(&$sortedDependentModels, $dependentOn, $model)
+ {
+ $modelKey = array_search($model, $sortedDependentModels);
+ $depKey = array_search($dependentOn, $sortedDependentModels);
+ if ($depKey < $modelKey) {
+ return;
+ }
+
+ unset($sortedDependentModels[$depKey]);
+
+ $restRight = array_slice($sortedDependentModels, $modelKey);
+ $theKey = (($modelKey) < 0) ? 0 : ($modelKey);
+ $restLeft = array_slice($sortedDependentModels, 0, $theKey);
+
+ $sortedDependentModels = array_merge($restLeft, [$dependentOn], $restRight);
+ }
}
diff --git a/console/config/components.php b/console/config/components.php
index 4ea56aa..ee095c4 100644
--- a/console/config/components.php
+++ b/console/config/components.php
@@ -7,15 +7,18 @@
return [
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
+ 'flushInterval' => 1,
'targets' => [
[
'class' => yii\log\FileTarget::class,
+ 'exportInterval' => 1,
'levels' => ['error', 'warning'],
'logFile' => '@logs/console-app.error.log',
'logVars' => [],
],
[
'class' => yii\log\FileTarget::class,
+ 'exportInterval' => 1,
'levels' => ['info'],
'logFile' => '@logs/console-app.info.log',
'logVars' => [],
diff --git a/docker-compose.override.dist.yml b/docker-compose.override.dist.yml
index 88a3c6c..2c9b4b4 100644
--- a/docker-compose.override.dist.yml
+++ b/docker-compose.override.dist.yml
@@ -5,15 +5,22 @@
version: '2'
services:
api-php:
+ environment:
+ PHP_ENABLE_XDEBUG: 1
ports:
- '8337:80'
+ volumes:
+ - ./api/config/xdebug.ini:/usr/local/etc/php/conf.d/xdebug.ini
backend-php:
+ environment:
+ PHP_ENABLE_XDEBUG: 1
ports:
- '8338:80'
volumes:
# Re-use local composer cache via host-volume
- ~/.composer-docker/cache:/root/.composer/cache:delegated
+ - ./backend/config/xdebug.ini:/usr/local/etc/php/conf.d/xdebug.ini
db:
volumes:
diff --git a/docs/backend-bootstrap4.md b/docs/backend-bootstrap4.md
index dc85056..f18584e 100644
--- a/docs/backend-bootstrap4.md
+++ b/docs/backend-bootstrap4.md
@@ -47,7 +47,7 @@ Steps:
```
-- Add a navigation to the mail layout:
+- Add a navigation to the main layout:
```php
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 6fe79af..ab7cd54 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -21,6 +21,7 @@ If you don't know how to create OpenAPI descriptions you can check out the follo
-
-
+ -
- ... (if you happen to know better resources to look at, please [let us know](https://github.com/cebe/yii2-app-api/issues/new)
Also check out the [OpenAPI Specification](https://spec.openapis.org/oas/v3.1.0) itself.
diff --git a/openapi/schema.yaml b/openapi/schema.yaml
new file mode 100644
index 0000000..d50402b
--- /dev/null
+++ b/openapi/schema.yaml
@@ -0,0 +1,203 @@
+openapi: 3.0.3
+# Edit this schema and start your project
+# This is sample schema
+# To generate code which is based on this schema
+# run commands mentioned Development section in README.md file
+info:
+ title: 'Proxy-Service'
+ description: ""
+ version: 1.0.0
+ contact:
+ name: '...'
+ email: you@example.com
+servers:
+ - url: 'http://localhost:8937'
+ description: 'Local Dev API'
+security:
+ - BasicAuth: []
+components:
+ securitySchemes:
+ BasicAuth:
+ type: http
+ scheme: basic
+ schemas:
+ Account:
+ description: user account
+ type: object
+ required:
+ - id
+ - name
+ properties:
+ id:
+ type: integer
+ name:
+ description: account name
+ type: string
+ maxLength: 40
+ x-faker: 'substr($faker->userName(), 0, 40)'
+
+ Domain:
+ description: domain
+ type: object
+ required:
+ - id
+ - name
+ - account
+ - created_at
+ properties:
+ id:
+ type: integer
+ name:
+ description: domain or sub-domain name, in DNS syntax, IDN are converted
+ type: string
+ maxLength: 128
+ x-faker: '$faker->domainName'
+ account:
+ $ref: '#/components/schemas/Account'
+
+ routings:
+ type: array
+ items:
+ $ref: '#/components/schemas/Routing'
+
+ created_at:
+ readOnly: true
+ type: string
+ format: datetime
+ x-db-type: datetime
+ nullable: false
+
+ Routing:
+ description: rounting specification
+ type: object
+ required:
+ - id
+ - domain
+ properties:
+ id:
+ type: integer
+ domain:
+ $ref: '#/components/schemas/Domain'
+ path:
+ type: string
+ maxLength: 255
+ x-faker: '$faker->randomElement(["/", "/", "/", "/", "/api", "/tools", "/assets/web"])'
+
+ ssl:
+ type: boolean
+ redirect_to_ssl:
+ type: boolean
+
+ service:
+ type: string
+ maxLength: 255
+ x-faker: '"http://tador.cebe.net/" . $faker->domainName'
+
+ created_at:
+ readOnly: true
+ type: string
+ format: datetime
+ x-db-type: datetime
+ nullable: true
+ d123:
+ $ref: '#/components/schemas/D123'
+ a123:
+ $ref: '#/components/schemas/A123'
+
+ D123:
+ description: desc
+ type: object
+ required:
+ - id
+ properties:
+ id:
+ type: integer
+ name:
+ type: string
+ A123:
+ description: desc
+ type: object
+ required:
+ - id
+ properties:
+ id:
+ type: integer
+ name:
+ type: string
+ b123:
+ $ref: '#/components/schemas/B123'
+ B123:
+ description: desc
+ type: object
+ required:
+ - id
+ properties:
+ id:
+ type: integer
+ name:
+ type: string
+ c123:
+ $ref: '#/components/schemas/C123'
+ C123:
+ description: desc
+ type: object
+ required:
+ - id
+ properties:
+ id:
+ type: integer
+ name:
+ type: string
+ E123:
+ description: desc
+ type: object
+ required:
+ - id
+ properties:
+ id:
+ type: integer
+ name:
+ type: string
+ b123:
+ $ref: '#/components/schemas/B123'
+
+
+paths:
+ /:
+ get:
+ responses: []
+ description: none
+
+
+
+# Dependencies:
+# 'E123' => [
+# 0 => 'B123'
+# ]
+# 'Account' => null
+# 'C123' => null
+# 'Domain' => [
+# 0 => 'Account'
+# ]
+# 'B123' => [
+# 0 => 'C123'
+# ]
+# 'Routing' => [
+# 0 => 'Domain'
+# 1 => 'D123'
+# 2 => 'A123'
+# ]
+# 'D123' => null
+# 'A123' => [
+# 0 => 'B123'
+# ]
+
+# Sorted:
+# 'Account',
+# 'C123',
+# 'D123',
+# 'B123',
+# 'E123',
+# 'Domain',
+# 'A123',
+# 'Routing',