diff --git a/.env.ci b/.env.ci index 7508f67..b599841 100644 --- a/.env.ci +++ b/.env.ci @@ -7,9 +7,9 @@ APP_URL=http://127.0.0.1 LOG_CHANNEL=stack LOG_LEVEL=debug -DB_CONNECTION=mysql +DB_CONNECTION=pgsql DB_HOST=127.0.0.1 -DB_PORT=3306 +DB_PORT=5432 DB_DATABASE=toby DB_USERNAME=toby DB_PASSWORD=password diff --git a/.env.dusk.local b/.env.dusk.local index 3f4837b..48c004f 100644 --- a/.env.dusk.local +++ b/.env.dusk.local @@ -10,17 +10,17 @@ DUSK_DRIVER_URL=http://toby-selenium:4444/wd/hub LOG_CHANNEL=stack LOG_LEVEL=debug -DB_CONNECTION=mysql +DB_CONNECTION=pgsql DB_HOST=toby-db-test -DB_PORT=3306 +DB_PORT=5432 DB_DATABASE=toby DB_USERNAME=toby DB_PASSWORD=password BROADCAST_DRIVER=log CACHE_DRIVER=array -QUEUE_CONNECTION=sync -SESSION_DRIVER=file +QUEUE_CONNECTION=redis +SESSION_DRIVER=redis SESSION_LIFETIME=120 FILESYSTEM_DISK=local MAIL_MAILER=array diff --git a/.env.example b/.env.example index 661c459..fe3a597 100644 --- a/.env.example +++ b/.env.example @@ -7,33 +7,37 @@ APP_URL=http://localhost LOG_CHANNEL=stack LOG_LEVEL=debug -DB_CONNECTION=mysql +DB_CONNECTION=pgsql DB_HOST=toby-db-dev -DB_PORT=3306 +DB_PORT=5432 DB_DATABASE=toby DB_USERNAME=toby DB_PASSWORD=password DB_ROOT_PASSWORD=example -DOCKER_DEV_DB_EXTERNAL_PORT=3306 +EXTERNAL_WEBSERVER_PORT=80 + +DOCKER_DEV_DB_EXTERNAL_PORT=5432 DOCKER_DEV_DB_DATABASE=${DB_DATABASE} DOCKER_DEV_DB_USERNAME=${DB_USERNAME} DOCKER_DEV_DB_PASSWORD=${DB_PASSWORD} DOCKER_DEV_DB_ROOT_PASSWORD=${DB_ROOT_PASSWORD} -DOCKER_TEST_DB_EXTERNAL_PORT=3307 +DOCKER_TEST_DB_EXTERNAL_PORT=5433 DOCKER_TEST_DB_DATABASE=${DB_DATABASE} DOCKER_TEST_DB_USERNAME=${DB_USERNAME} DOCKER_TEST_DB_PASSWORD=${DB_PASSWORD} DOCKER_TEST_DB_ROOT_PASSWORD=${DB_ROOT_PASSWORD} -EXTERNAL_WEBSERVER_PORT= +REDIS_PORT=6379 +REDIS_HOST=toby-redis + XDG_CONFIG_HOME=/tmp BROADCAST_DRIVER=log CACHE_DRIVER=file -QUEUE_CONNECTION=sync -SESSION_DRIVER=file +QUEUE_CONNECTION=redis +SESSION_DRIVER=redis SESSION_LIFETIME=120 FILESYSTEM_DISK=local @@ -46,15 +50,13 @@ MAIL_PORT=${MAILHOG_PORT} MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null -MAIL_FROM_ADDRESS=null +MAIL_FROM_ADDRESS=hello@example.com MAIL_FROM_NAME="${APP_NAME}" DOCKER_INSTALL_XDEBUG=false GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= -GOOGLE_REDIRECT= - +GOOGLE_REDIRECT=http://localhost/login/google/end GOOGLE_CALENDAR_ID= - LOCAL_EMAIL_FOR_LOGIN_VIA_GOOGLE= diff --git a/.github/workflows/test-and-lint-php.yml b/.github/workflows/test-and-lint-php.yml index d287df0..c4994a9 100644 --- a/.github/workflows/test-and-lint-php.yml +++ b/.github/workflows/test-and-lint-php.yml @@ -11,15 +11,15 @@ jobs: name: Test & lint PHP stuff runs-on: ubuntu-20.04 services: - mysql: - image: mysql:8.0 + pgsql: + image: postgres:13 env: - MYSQL_DATABASE: toby - MYSQL_USER: toby - MYSQL_PASSWORD: password - MYSQL_ALLOW_EMPTY_PASSWORD: 1 + POSTGRES_DB: toby + POSTGRES_USER: toby + POSTGRES_PASSWORD: password + PGPASSWORD: password ports: - - 3306:3306 + - 5432:5432 steps: - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index 113d45d..63cf7ed 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ /node_modules /public/hot /public/storage -/public/avatars /public/js/ /public/css/ /public/mix-manifest.json diff --git a/app/Eloquent/Helpers/UserAvatarGenerator.php b/app/Eloquent/Helpers/UserAvatarGenerator.php deleted file mode 100644 index f21f425..0000000 --- a/app/Eloquent/Helpers/UserAvatarGenerator.php +++ /dev/null @@ -1,49 +0,0 @@ -generateUuid()}.svg"; - - Storage::put($path, $this->generate($user)); - - return $path; - } - - protected function generate(User $user): SVG - { - return $this->generator->rounded() - ->background($this->getColor($user->fullName)) - ->color("#F4F8FD") - ->smooth() - ->fontSize(0.33) - ->generateSvg($user->fullName); - } - - protected function getColor(string $name): string - { - $colors = config("colors"); - - return $colors[strlen($name) % count($colors)]; - } - - protected function generateUuid(): string - { - return Str::uuid()->toString(); - } -} diff --git a/app/Eloquent/Models/User.php b/app/Eloquent/Models/User.php index eafa9aa..baf733b 100644 --- a/app/Eloquent/Models/User.php +++ b/app/Eloquent/Models/User.php @@ -13,6 +13,7 @@ use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Support\Carbon; use Illuminate\Support\Collection; +use Rackbeat\UIAvatars\HasAvatar; use Toby\Domain\Enums\EmploymentForm; use Toby\Domain\Enums\Role; @@ -35,6 +36,7 @@ class User extends Authenticatable use HasFactory; use Notifiable; use SoftDeletes; + use HasAvatar; protected $guarded = []; @@ -75,16 +77,18 @@ class User extends Authenticatable } return $query - ->where("first_name", "LIKE", "%{$text}%") - ->orWhere("last_name", "LIKE", "%{$text}%") - ->orWhere("email", "LIKE", "%{$text}%"); + ->where("first_name", "ILIKE", $text) + ->orWhere("last_name", "ILIKE", $text) + ->orWhere("email", "ILIKE", $text); } - public function saveAvatar(string $path): void + public function getAvatar(): string { - $this->avatar = $path; + $colors = config("colors"); - $this->save(); + return $this->getAvatarGenerator() + ->backgroundColor($colors[strlen($this->fullname) % count($colors)]) + ->image(); } public function getFullNameAttribute(): string @@ -97,6 +101,11 @@ class User extends Authenticatable return $this->role === $role; } + protected function getAvatarNameKey(): string + { + return "fullName"; + } + protected static function newFactory(): UserFactory { return UserFactory::new(); diff --git a/app/Eloquent/Observers/UserObserver.php b/app/Eloquent/Observers/UserObserver.php index 1d8d3a6..33a76e5 100644 --- a/app/Eloquent/Observers/UserObserver.php +++ b/app/Eloquent/Observers/UserObserver.php @@ -4,37 +4,19 @@ declare(strict_types=1); namespace Toby\Eloquent\Observers; -use Illuminate\Support\Facades\Storage; -use Toby\Eloquent\Helpers\UserAvatarGenerator; use Toby\Eloquent\Helpers\YearPeriodRetriever; use Toby\Eloquent\Models\User; class UserObserver { public function __construct( - protected UserAvatarGenerator $generator, protected YearPeriodRetriever $yearPeriodRetriever, ) {} public function created(User $user): void { - $user->saveAvatar($this->generator->generateFor($user)); - $user->vacationLimits()->create([ "year_period_id" => $this->yearPeriodRetriever->current()->id, ]); } - - public function updating(User $user): void - { - if ($user->isDirty(["first_name", "last_name"])) { - Storage::delete($user->avatar); - $user->avatar = $this->generator->generateFor($user); - } - } - - public function forceDeleted(User $user): void - { - Storage::delete($user->avatar); - } } diff --git a/app/Eloquent/Observers/YearPeriodObserver.php b/app/Eloquent/Observers/YearPeriodObserver.php index e74cc3c..b7f370b 100644 --- a/app/Eloquent/Observers/YearPeriodObserver.php +++ b/app/Eloquent/Observers/YearPeriodObserver.php @@ -5,14 +5,12 @@ declare(strict_types=1); namespace Toby\Eloquent\Observers; use Toby\Domain\PolishHolidaysRetriever; -use Toby\Eloquent\Helpers\UserAvatarGenerator; use Toby\Eloquent\Models\User; use Toby\Eloquent\Models\YearPeriod; class YearPeriodObserver { public function __construct( - protected UserAvatarGenerator $generator, protected PolishHolidaysRetriever $polishHolidaysRetriever, ) {} diff --git a/app/Infrastructure/Http/Resources/UserResource.php b/app/Infrastructure/Http/Resources/UserResource.php index 5e3107f..2428f7f 100644 --- a/app/Infrastructure/Http/Resources/UserResource.php +++ b/app/Infrastructure/Http/Resources/UserResource.php @@ -18,7 +18,7 @@ class UserResource extends JsonResource "email" => $this->email, "role" => $this->role->label(), "position" => $this->position, - "avatar" => asset($this->avatar), + "avatar" => $this->getAvatar(), "deleted" => $this->trashed(), "employmentForm" => $this->employment_form->label(), "employmentDate" => $this->employment_date->toDisplayString(), diff --git a/composer.json b/composer.json index 9a9131f..0238916 100644 --- a/composer.json +++ b/composer.json @@ -17,10 +17,10 @@ "laravel/socialite": "^5.2", "laravel/telescope": "^4.6", "laravel/tinker": "^2.5", - "lasserafn/php-initial-avatar-generator": "^4.2", + "maatwebsite/excel": "^3.1", + "rackbeat/laravel-ui-avatars": "^1.0", "spatie/laravel-google-calendar": "^3.5", - "spatie/laravel-model-states": "^2.1", - "maatwebsite/excel": "^3.1" + "spatie/laravel-model-states": "^2.1" }, "require-dev": { "blumilksoftware/codestyle": "^0.10.0", diff --git a/composer.lock b/composer.lock index e905cf2..3fbeb24 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d3f019c6e743a3249af78baefe7acb01", + "content-hash": "937eef6310bd4bb12f1d0ccb623946d1", "packages": [ { "name": "asm89/stack-cors", @@ -1731,16 +1731,16 @@ }, { "name": "laravel/framework", - "version": "v9.2.0", + "version": "v9.3.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "13372872bed31ae75df8709b9de5cde01d50646e" + "reference": "c1c4404511b83fbf90ccbcdf864d4a85537f35e4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/13372872bed31ae75df8709b9de5cde01d50646e", - "reference": "13372872bed31ae75df8709b9de5cde01d50646e", + "url": "https://api.github.com/repos/laravel/framework/zipball/c1c4404511b83fbf90ccbcdf864d4a85537f35e4", + "reference": "c1c4404511b83fbf90ccbcdf864d4a85537f35e4", "shasum": "" }, "require": { @@ -1826,7 +1826,7 @@ "mockery/mockery": "^1.4.4", "orchestra/testbench-core": "^7.1", "pda/pheanstalk": "^4.0", - "phpstan/phpstan": "^1.0", + "phpstan/phpstan": "^1.4.7", "phpunit/phpunit": "^9.5.8", "predis/predis": "^1.1.9", "symfony/cache": "^6.0" @@ -1906,7 +1906,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2022-02-22T15:30:23+00:00" + "time": "2022-03-03T15:15:10+00:00" }, { "name": "laravel/sanctum", @@ -2102,21 +2102,21 @@ }, { "name": "laravel/telescope", - "version": "v4.7.3", + "version": "v4.7.4", "source": { "type": "git", "url": "https://github.com/laravel/telescope.git", - "reference": "f91e7d068d3754575388b990332d0aad8d7ac7d1" + "reference": "6a7815103f9c35fb535f008dec47938352a98d34" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/telescope/zipball/f91e7d068d3754575388b990332d0aad8d7ac7d1", - "reference": "f91e7d068d3754575388b990332d0aad8d7ac7d1", + "url": "https://api.github.com/repos/laravel/telescope/zipball/6a7815103f9c35fb535f008dec47938352a98d34", + "reference": "6a7815103f9c35fb535f008dec47938352a98d34", "shasum": "" }, "require": { "ext-json": "*", - "laravel/framework": "^8.29|^9.0", + "laravel/framework": "^8.37|^9.0", "php": "^7.3|^8.0", "symfony/var-dumper": "^5.0|^6.0" }, @@ -2164,9 +2164,9 @@ ], "support": { "issues": "https://github.com/laravel/telescope/issues", - "source": "https://github.com/laravel/telescope/tree/v4.7.3" + "source": "https://github.com/laravel/telescope/tree/v4.7.4" }, - "time": "2022-02-14T17:29:12+00:00" + "time": "2022-03-03T12:58:33+00:00" }, { "name": "laravel/tinker", @@ -4628,6 +4628,70 @@ }, "time": "2022-02-28T15:28:54+00:00" }, + { + "name": "rackbeat/laravel-ui-avatars", + "version": "1.0", + "source": { + "type": "git", + "url": "https://github.com/Rackbeat/laravel-ui-avatars.git", + "reference": "aab0a601ceb1b8d236407e8c62a358c68920a12d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Rackbeat/laravel-ui-avatars/zipball/aab0a601ceb1b8d236407e8c62a358c68920a12d", + "reference": "aab0a601ceb1b8d236407e8c62a358c68920a12d", + "shasum": "" + }, + "require": { + "laravel/framework": "~5.5|~5.6|~5.7|~5.8|~5.9|~6.0|^7.0|^8.0|^9.0", + "lasserafn/php-initial-avatar-generator": "^4.0", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "^7.0", + "satooshi/php-coveralls": "^1.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Rackbeat\\UIAvatars\\UIAvatarsServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Rackbeat\\UIAvatars\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lasse Rafn", + "email": "lasserafn@gmail.com" + }, + { + "name": "Rackbeat", + "email": "open-source@rackbeat.com" + } + ], + "description": "Official Laravel wrapper around ui-avatars.com and LasseRafn/php-initial-avatar-generator", + "keywords": [ + "Initials", + "avatars", + "laravel", + "php", + "ui-avatars" + ], + "support": { + "issues": "https://github.com/Rackbeat/laravel-ui-avatars/issues", + "source": "https://github.com/Rackbeat/laravel-ui-avatars/tree/1.0" + }, + "time": "2022-02-14T16:36:21+00:00" + }, { "name": "ralouphie/getallheaders", "version": "3.0.3", @@ -4979,16 +5043,16 @@ }, { "name": "spatie/laravel-model-states", - "version": "2.1.4", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-model-states.git", - "reference": "c9c4865abd2b5ec534214aede784631366bed7d4" + "reference": "7b31a63c0bd8b33d7dc5e12e6b16d2535d9b31a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-model-states/zipball/c9c4865abd2b5ec534214aede784631366bed7d4", - "reference": "c9c4865abd2b5ec534214aede784631366bed7d4", + "url": "https://api.github.com/repos/spatie/laravel-model-states/zipball/7b31a63c0bd8b33d7dc5e12e6b16d2535d9b31a8", + "reference": "7b31a63c0bd8b33d7dc5e12e6b16d2535d9b31a8", "shasum": "" }, "require": { @@ -5037,7 +5101,7 @@ "state" ], "support": { - "source": "https://github.com/spatie/laravel-model-states/tree/2.1.4" + "source": "https://github.com/spatie/laravel-model-states/tree/2.2.0" }, "funding": [ { @@ -5049,7 +5113,7 @@ "type": "github" } ], - "time": "2022-01-19T19:57:35+00:00" + "time": "2022-03-03T11:22:16+00:00" }, { "name": "spatie/laravel-package-tools", @@ -7806,29 +7870,30 @@ }, { "name": "doctrine/instantiator", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b" + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b", - "reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/10dcfce151b967d20fde1b34ae6640712c3891bc", + "reference": "10dcfce151b967d20fde1b34ae6640712c3891bc", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^8.0", + "doctrine/coding-standard": "^9", "ext-pdo": "*", "ext-phar": "*", - "phpbench/phpbench": "^0.13 || 1.0.0-alpha2", - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpbench/phpbench": "^0.16 || ^1", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.22" }, "type": "library", "autoload": { @@ -7855,7 +7920,7 @@ ], "support": { "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.4.0" + "source": "https://github.com/doctrine/instantiator/tree/1.4.1" }, "funding": [ { @@ -7871,7 +7936,7 @@ "type": "tidelift" } ], - "time": "2020-11-10T18:47:58+00:00" + "time": "2022-03-03T08:28:38+00:00" }, { "name": "fakerphp/faker", @@ -8153,16 +8218,16 @@ }, { "name": "kubawerlos/php-cs-fixer-custom-fixers", - "version": "v3.8.1", + "version": "v3.9.0", "source": { "type": "git", "url": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers.git", - "reference": "cddb6d7a365ce95dd554c357ebc16318b5e6bd0d" + "reference": "f5148937829fdb1ea84cbcc78698f1f24cba3541" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/cddb6d7a365ce95dd554c357ebc16318b5e6bd0d", - "reference": "cddb6d7a365ce95dd554c357ebc16318b5e6bd0d", + "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/f5148937829fdb1ea84cbcc78698f1f24cba3541", + "reference": "f5148937829fdb1ea84cbcc78698f1f24cba3541", "shasum": "" }, "require": { @@ -8194,9 +8259,9 @@ "description": "A set of custom fixers for PHP CS Fixer", "support": { "issues": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/issues", - "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.8.1" + "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.9.0" }, - "time": "2022-02-15T17:06:25+00:00" + "time": "2022-03-02T18:12:33+00:00" }, { "name": "laravel/dusk", @@ -8345,28 +8410,29 @@ }, { "name": "myclabs/deep-copy", - "version": "1.10.2", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220" + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220", - "reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", + "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, - "replace": { - "myclabs/deep-copy": "self.version" + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" }, "require-dev": { - "doctrine/collections": "^1.0", - "doctrine/common": "^2.6", - "phpunit/phpunit": "^7.1" + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", "autoload": { @@ -8391,7 +8457,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.10.2" + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" }, "funding": [ { @@ -8399,7 +8465,7 @@ "type": "tidelift" } ], - "time": "2020-11-13T09:40:50+00:00" + "time": "2022-03-03T13:19:32+00:00" }, { "name": "nunomaduro/collision", @@ -10456,16 +10522,16 @@ }, { "name": "spatie/ignition", - "version": "1.1.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/spatie/ignition.git", - "reference": "8ecde033600064e3ffdbf804deec0dcb05004387" + "reference": "5c554067887b7080bc58977fa30a488624d85d20" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/ignition/zipball/8ecde033600064e3ffdbf804deec0dcb05004387", - "reference": "8ecde033600064e3ffdbf804deec0dcb05004387", + "url": "https://api.github.com/repos/spatie/ignition/zipball/5c554067887b7080bc58977fa30a488624d85d20", + "reference": "5c554067887b7080bc58977fa30a488624d85d20", "shasum": "" }, "require": { @@ -10523,7 +10589,7 @@ "type": "github" } ], - "time": "2022-03-01T17:01:33+00:00" + "time": "2022-03-02T10:51:55+00:00" }, { "name": "spatie/laravel-ignition", diff --git a/config/cache.php b/config/cache.php index d520005..39e2bf3 100644 --- a/config/cache.php +++ b/config/cache.php @@ -21,6 +21,11 @@ return [ "driver" => "file", "path" => storage_path("framework/cache/data"), ], + "redis" => [ + "driver" => "redis", + "connection" => "cache", + "lock_connection" => "default", + ], ], "prefix" => env("CACHE_PREFIX", Str::slug(env("APP_NAME", "laravel"), "_") . "_cache"), ]; diff --git a/config/database.php b/config/database.php index c3831fc..0ab09ab 100644 --- a/config/database.php +++ b/config/database.php @@ -2,28 +2,46 @@ declare(strict_types=1); +use Illuminate\Support\Str; + return [ "default" => env("DB_CONNECTION", "mysql"), "connections" => [ - "mysql" => [ - "driver" => "mysql", + "pgsql" => [ + "driver" => "pgsql", "url" => env("DATABASE_URL"), "host" => env("DB_HOST", "127.0.0.1"), - "port" => env("DB_PORT", "3306"), + "port" => env("DB_PORT", "5432"), "database" => env("DB_DATABASE", "forge"), "username" => env("DB_USERNAME", "forge"), "password" => env("DB_PASSWORD", ""), - "unix_socket" => env("DB_SOCKET", ""), - "charset" => "utf8mb4", - "collation" => "utf8mb4_unicode_ci", + "charset" => "utf8", "prefix" => "", "prefix_indexes" => true, - "strict" => true, - "engine" => null, - "options" => extension_loaded("pdo_mysql") ? array_filter([ - PDO::MYSQL_ATTR_SSL_CA => env("MYSQL_ATTR_SSL_CA"), - ]) : [], + "search_path" => "public", + "sslmode" => "prefer", ], ], "migrations" => "migrations", + "redis" => [ + "client" => env("REDIS_CLIENT", "phpredis"), + "options" => [ + "cluster" => env("REDIS_CLUSTER", "redis"), + "prefix" => env("REDIS_PREFIX", Str::slug(env("APP_NAME", "laravel"), "_") . "_database_"), + ], + "default" => [ + "url" => env("REDIS_URL"), + "host" => env("REDIS_HOST", "127.0.0.1"), + "password" => env("REDIS_PASSWORD"), + "port" => env("REDIS_PORT", "6379"), + "database" => env("REDIS_DB", "0"), + ], + "cache" => [ + "url" => env("REDIS_URL"), + "host" => env("REDIS_HOST", "127.0.0.1"), + "password" => env("REDIS_PASSWORD"), + "port" => env("REDIS_PORT", "6379"), + "database" => env("REDIS_CACHE_DB", "1"), + ], + ], ]; diff --git a/config/filesystems.php b/config/filesystems.php index af08bf5..80a15f7 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -17,6 +17,6 @@ return [ ], ], "links" => [ - public_path("avatars") => storage_path("app/avatars"), + public_path("storage") => storage_path("app/storage"), ], ]; diff --git a/config/queue.php b/config/queue.php index 27dbd2e..626d9db 100644 --- a/config/queue.php +++ b/config/queue.php @@ -8,6 +8,14 @@ return [ "sync" => [ "driver" => "sync", ], + "redis" => [ + "driver" => "redis", + "connection" => "default", + "queue" => env("REDIS_QUEUE", "default"), + "retry_after" => 90, + "block_for" => null, + "after_commit" => false, + ], ], "failed" => [ "driver" => env("QUEUE_FAILED_DRIVER", "database-uuids"), diff --git a/config/ui-avatars.php b/config/ui-avatars.php new file mode 100644 index 0000000..68cbd82 --- /dev/null +++ b/config/ui-avatars.php @@ -0,0 +1,21 @@ + "api", + "default_region" => "eu", + "length" => 2, + "image_size" => 48, + "font_size" => 0.33, + "rounded" => true, + "smooth_rounding" => true, + "uppercase" => true, + "background_color" => "#a0a0a0", + "font_color" => "#F4F8FD", + "font_bold" => true, + "providers" => [ + "api" => Rackbeat\UIAvatars\Generators\ApiGenerator::class, + "local" => Rackbeat\UIAvatars\Generators\LocalGenerator::class, + ], +]; diff --git a/database/factories/VacationRequestActivityFactory.php b/database/factories/VacationRequestActivityFactory.php index c4b8c50..d1de4a3 100644 --- a/database/factories/VacationRequestActivityFactory.php +++ b/database/factories/VacationRequestActivityFactory.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace Database\Factories; use Illuminate\Database\Eloquent\Factories\Factory; -use Toby\Domain\Enums\VacationRequestState; +use Toby\Domain\States\VacationRequest\VacationRequestState; use Toby\Eloquent\Models\VacationRequestActivity; class VacationRequestActivityFactory extends Factory @@ -15,8 +15,8 @@ class VacationRequestActivityFactory extends Factory public function definition(): array { return [ - "from" => $this->faker->randomElement(VacationRequestState::cases()), - "to" => $this->faker->randomElement(VacationRequestState::cases()), + "from" => $this->faker->randomElement(VacationRequestState::all()), + "to" => $this->faker->randomElement(VacationRequestState::all()), ]; } } diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 9cd93f7..8d58909 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -6,10 +6,8 @@ namespace Database\Seeders; use Illuminate\Database\Seeder; use Illuminate\Support\Carbon; -use Illuminate\Support\Collection; use Toby\Domain\PolishHolidaysRetriever; use Toby\Domain\VacationDaysCalculator; -use Toby\Eloquent\Helpers\UserAvatarGenerator; use Toby\Eloquent\Models\User; use Toby\Eloquent\Models\VacationLimit; use Toby\Eloquent\Models\VacationRequest; @@ -17,10 +15,6 @@ use Toby\Eloquent\Models\YearPeriod; class DatabaseSeeder extends Seeder { - public function __construct( - protected UserAvatarGenerator $avatarGenerator, - ) {} - public function run(): void { User::unsetEventDispatcher(); @@ -35,8 +29,6 @@ class DatabaseSeeder extends Seeder $users = User::all(); - $this->generateAvatarsForUsers($users); - YearPeriod::factory() ->count(3) ->sequence( @@ -98,11 +90,4 @@ class DatabaseSeeder extends Seeder ->create(); } } - - protected function generateAvatarsForUsers(Collection $users): void - { - foreach ($users as $user) { - $user->saveAvatar($this->avatarGenerator->generateFor($user)); - } - } } diff --git a/database/seeders/DemoSeeder.php b/database/seeders/DemoSeeder.php index bd4a7ec..6731a3a 100644 --- a/database/seeders/DemoSeeder.php +++ b/database/seeders/DemoSeeder.php @@ -6,15 +6,19 @@ namespace Database\Seeders; use Illuminate\Database\Seeder; use Illuminate\Support\Carbon; -use Illuminate\Support\Collection; use Illuminate\Support\Str; use Toby\Domain\Enums\EmploymentForm; use Toby\Domain\Enums\Role; -use Toby\Domain\Enums\VacationRequestState; use Toby\Domain\Enums\VacationType; use Toby\Domain\PolishHolidaysRetriever; +use Toby\Domain\States\VacationRequest\AcceptedByAdministrative; +use Toby\Domain\States\VacationRequest\AcceptedByTechnical; +use Toby\Domain\States\VacationRequest\Approved; +use Toby\Domain\States\VacationRequest\Created; +use Toby\Domain\States\VacationRequest\Rejected; +use Toby\Domain\States\VacationRequest\WaitingForAdministrative; +use Toby\Domain\States\VacationRequest\WaitingForTechnical; use Toby\Domain\VacationDaysCalculator; -use Toby\Eloquent\Helpers\UserAvatarGenerator; use Toby\Eloquent\Models\User; use Toby\Eloquent\Models\VacationLimit; use Toby\Eloquent\Models\VacationRequest; @@ -23,10 +27,6 @@ use Toby\Eloquent\Models\YearPeriod; class DemoSeeder extends Seeder { - public function __construct( - protected UserAvatarGenerator $avatarGenerator, - ) {} - public function run(): void { User::unsetEventDispatcher(); @@ -107,8 +107,6 @@ class DemoSeeder extends Seeder $users = User::all(); - $this->generateAvatarsForUsers($users); - $year = 2021; YearPeriod::factory() @@ -148,7 +146,7 @@ class DemoSeeder extends Seeder /** @var VacationRequest $vacationRequestApproved */ $vacationRequestApproved = VacationRequest::factory([ "type" => VacationType::Vacation->value, - "state" => VacationRequestState::Created, + "state" => Created::class, "from" => Carbon::create($currentYearPeriod->year, 1, 31)->toDateString(), "to" => Carbon::create($currentYearPeriod->year, 2, 4)->toDateString(), "comment" => "Komentarz do wniosku urlopowego.", @@ -175,49 +173,50 @@ class DemoSeeder extends Seeder VacationRequestActivity::factory([ "from" => null, - "to" => VacationRequestState::Created, + "to" => Created::class, ])->for($vacationRequestApproved) ->for($employee1) ->create(); VacationRequestActivity::factory([ - "from" => VacationRequestState::Created, - "to" => VacationRequestState::WaitingForTechnical, + "from" => Created::class, + "to" => WaitingForTechnical::class, ])->for($vacationRequestApproved) ->create(); VacationRequestActivity::factory([ - "from" => VacationRequestState::WaitingForTechnical, - "to" => VacationRequestState::AcceptedByTechnical, + "from" => WaitingForTechnical::class, + "to" => AcceptedByTechnical::class, ])->for($vacationRequestApproved) ->for($technicalApprover) ->create(); VacationRequestActivity::factory([ - "from" => VacationRequestState::AcceptedByTechnical, - "to" => VacationRequestState::WaitingForAdministrative, + "from" => AcceptedByTechnical::class, + "to" => WaitingForAdministrative::class, ])->for($vacationRequestApproved) ->create(); VacationRequestActivity::factory([ - "from" => VacationRequestState::WaitingForAdministrative, - "to" => VacationRequestState::AcceptedByAdministrative, + "from" => WaitingForAdministrative::class, + "to" => AcceptedByAdministrative::class, ])->for($vacationRequestApproved) ->for($administrativeApprover) ->create(); VacationRequestActivity::factory([ - "from" => VacationRequestState::AcceptedByAdministrative, - "to" => VacationRequestState::Approved, + "from" => AcceptedByAdministrative::class, + "to" => Approved::class, ])->for($vacationRequestApproved) ->create(); - $vacationRequestApproved->changeStateTo(VacationRequestState::Approved); + $vacationRequestApproved->state = new Approved($vacationRequestApproved); + $vacationRequestApproved->save(); /** @var VacationRequest $vacationRequestWaitsForAdminApproval */ $vacationRequestWaitsForAdminApproval = VacationRequest::factory([ "type" => VacationType::Vacation->value, - "state" => VacationRequestState::Created, + "state" => Created::class, "from" => Carbon::create($currentYearPeriod->year, 2, 14)->toDateString(), "to" => Carbon::create($currentYearPeriod->year, 2, 14)->toDateString(), "comment" => "Komentarz do wniosku urlopowego.", @@ -244,36 +243,37 @@ class DemoSeeder extends Seeder VacationRequestActivity::factory([ "from" => null, - "to" => VacationRequestState::Created, + "to" => Created::class, ])->for($vacationRequestWaitsForAdminApproval) ->for($employee1) ->create(); VacationRequestActivity::factory([ - "from" => VacationRequestState::Created, - "to" => VacationRequestState::WaitingForTechnical, + "from" => Created::class, + "to" => WaitingForTechnical::class, ])->for($vacationRequestWaitsForAdminApproval) ->create(); VacationRequestActivity::factory([ - "from" => VacationRequestState::WaitingForTechnical, - "to" => VacationRequestState::AcceptedByTechnical, + "from" => WaitingForTechnical::class, + "to" => AcceptedByTechnical::class, ])->for($vacationRequestWaitsForAdminApproval) ->for($technicalApprover) ->create(); VacationRequestActivity::factory([ - "from" => VacationRequestState::AcceptedByTechnical, - "to" => VacationRequestState::WaitingForAdministrative, + "from" => AcceptedByTechnical::class, + "to" => WaitingForAdministrative::class, ])->for($vacationRequestWaitsForAdminApproval) ->create(); - $vacationRequestWaitsForAdminApproval->changeStateTo(VacationRequestState::WaitingForAdministrative); + $vacationRequestWaitsForAdminApproval->state = new WaitingForAdministrative($vacationRequestWaitsForAdminApproval); + $vacationRequestWaitsForAdminApproval->save(); /** @var VacationRequest $vacationRequestRejected */ $vacationRequestRejected = VacationRequest::factory([ "type" => VacationType::Vacation->value, - "state" => VacationRequestState::Created, + "state" => Created::class, "from" => Carbon::create($currentYearPeriod->year, 2, 7)->toDateString(), "to" => Carbon::create($currentYearPeriod->year, 2, 7)->toDateString(), "comment" => "", @@ -300,31 +300,25 @@ class DemoSeeder extends Seeder VacationRequestActivity::factory([ "from" => null, - "to" => VacationRequestState::Created, + "to" => Created::class, ])->for($vacationRequestRejected) ->for($employee1) ->create(); VacationRequestActivity::factory([ - "from" => VacationRequestState::Created, - "to" => VacationRequestState::WaitingForTechnical, + "from" => Created::class, + "to" => WaitingForTechnical::class, ])->for($vacationRequestRejected) ->create(); VacationRequestActivity::factory([ - "from" => VacationRequestState::WaitingForTechnical, - "to" => VacationRequestState::Rejected, + "from" => WaitingForTechnical::class, + "to" => Rejected::class, ])->for($vacationRequestRejected) ->for($technicalApprover) ->create(); - $vacationRequestRejected->changeStateTo(VacationRequestState::Rejected); - } - - protected function generateAvatarsForUsers(Collection $users): void - { - foreach ($users as $user) { - $user->saveAvatar($this->avatarGenerator->generateFor($user)); - } + $vacationRequestRejected->state = new Rejected($vacationRequestRejected); + $vacationRequestRejected->save(); } } diff --git a/docker-compose.yml b/docker-compose.yml index 7fcec65..6a4231b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -35,31 +35,42 @@ services: restart: unless-stopped database: - image: mysql:8.0 + image: postgres:13 container_name: toby-db-dev environment: - - MYSQL_ROOT_PASSWORD=${DOCKER_DEV_DB_ROOT_PASSWORD} - - MYSQL_DATABASE=${DOCKER_DEV_DB_DATABASE} - - MYSQL_USER=${DOCKER_DEV_DB_USERNAME} - - MYSQL_PASSWORD=${DOCKER_DEV_DB_PASSWORD} + - PGPASSWORD=${DOCKER_DEV_DB_ROOT_PASSWORD} + - POSTGRES_DB=${DOCKER_DEV_DB_DATABASE} + - POSTGRES_USER=${DOCKER_DEV_DB_USERNAME} + - POSTGRES_PASSWORD=${DOCKER_DEV_DB_PASSWORD} ports: - - ${DOCKER_DEV_DB_EXTERNAL_PORT:-3306}:3306 + - ${DOCKER_DEV_DB_EXTERNAL_PORT:-5432}:5432 volumes: - - toby-mysql-data:/var/lib/mysql + - toby-pgsql-data:/var/lib/pgsql networks: - toby-dev restart: unless-stopped database-test: - image: mysql:8.0 + image: postgres:13 container_name: toby-db-test environment: - - MYSQL_ROOT_PASSWORD=${DOCKER_TEST_DB_ROOT_PASSWORD} - - MYSQL_DATABASE=${DOCKER_TEST_DB_DATABASE} - - MYSQL_USER=${DOCKER_TEST_DB_USERNAME} - - MYSQL_PASSWORD=${DOCKER_TEST_DB_PASSWORD} + - PGPASSWORD=${DOCKER_TEST_DB_ROOT_PASSWORD} + - POSTGRES_DB=${DOCKER_TEST_DB_DATABASE} + - POSTGRES_USER=${DOCKER_TEST_DB_USERNAME} + - POSTGRES_PASSWORD=${DOCKER_TEST_DB_PASSWORD} ports: - - ${DOCKER_TEST_DB_EXTERNAL_PORT:-3307}:3306 + - ${DOCKER_TEST_DB_EXTERNAL_PORT:-5433}:5432 + networks: + - toby-dev + restart: unless-stopped + + redis: + image: redis:6 + container_name: toby-redis + ports: + - ${FORWARD_REDIS_PORT:-6379}:6379 + volumes: + - toby-redis-data:/var/lib/redis networks: - toby-dev restart: unless-stopped @@ -91,11 +102,14 @@ services: - /dev/shm:/dev/shm networks: - toby-dev + restart: unless-stopped networks: toby-dev: driver: bridge volumes: - toby-mysql-data: - name: toby-mysql-data + toby-pgsql-data: + name: toby-pgsql-data + toby-redis-data: + name: toby-redis-data diff --git a/docker/dev/php/Dockerfile b/docker/dev/php/Dockerfile index b6cd37d..da0c855 100644 --- a/docker/dev/php/Dockerfile +++ b/docker/dev/php/Dockerfile @@ -9,12 +9,15 @@ RUN if [ ${INSTALL_XDEBUG} = true ]; then \ && docker-php-ext-enable xdebug \ ;fi -RUN apk --no-cache add \ +RUN pecl install redis \ + && apk --no-cache add \ + postgresql-dev \ zip \ libzip-dev \ - libpng-dev \ && docker-php-ext-install \ + pdo_pgsql \ zip \ - gd \ && docker-php-ext-configure \ - zip + zip \ + && docker-php-ext-enable \ + redis diff --git a/phpunit.xml b/phpunit.xml index c1b2a5c..0b25bbc 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -22,7 +22,7 @@ - + diff --git a/routes/web.php b/routes/web.php index 154523e..1436160 100644 --- a/routes/web.php +++ b/routes/web.php @@ -48,9 +48,15 @@ Route::middleware("auth")->group(function (): void { ->name("vacation.requests.reject"); Route::post("/vacation-requests/{vacationRequest}/cancel", [VacationRequestController::class, "cancel"]) ->name("vacation.requests.cancel"); - Route::post("/vacation-requests/{vacationRequest}/accept-as-technical", [VacationRequestController::class, "acceptAsTechnical"]) + Route::post( + "/vacation-requests/{vacationRequest}/accept-as-technical", + [VacationRequestController::class, "acceptAsTechnical"], + ) ->name("vacation.requests.accept-as-technical"); - Route::post("/vacation-requests/{vacationRequest}/accept-as-administrative", [VacationRequestController::class, "acceptAsAdministrative"]) + Route::post( + "/vacation-requests/{vacationRequest}/accept-as-administrative", + [VacationRequestController::class, "acceptAsAdministrative"], + ) ->name("vacation.requests.accept-as-administrative"); Route::post("year-periods/{yearPeriod}/select", SelectYearPeriodController::class) diff --git a/storage/app/avatars/.gitignore b/storage/app/avatars/.gitignore deleted file mode 100644 index d6b7ef3..0000000 --- a/storage/app/avatars/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore diff --git a/tests/Unit/AvatarTest.php b/tests/Unit/AvatarTest.php deleted file mode 100644 index 2c13d21..0000000 --- a/tests/Unit/AvatarTest.php +++ /dev/null @@ -1,73 +0,0 @@ -createCurrentYearPeriod(); - Storage::fake(); - } - - public function testAvatarIsGeneratedWhenUserIsCreated(): void - { - $user = User::factory()->create(); - - Storage::assertExists($user->avatar); - } - - public function testAvatarIsDeletedWhenUserIsForceDeleted(): void - { - $user = User::factory()->create(); - - Storage::assertExists($user->avatar); - - $user->forceDelete(); - - Storage::assertMissing($user->avatar); - } - - public function testAvatarIsReplacedWhenUserChangedTheirName(): void - { - $user = User::factory()->create(); - $oldAvatar = $user->avatar; - - Storage::assertExists($oldAvatar); - - $user->update([ - "first_name" => "John", - "last_name" => "Doe", - ]); - - Storage::assertMissing($oldAvatar); - Storage::assertExists($user->avatar); - } - - public function testAvatarIsNotReplacedWhenUserChangedOtherData(): void - { - $user = User::factory()->create(); - $avatar = $user->avatar; - - Storage::assertExists($avatar); - - $user->update([ - "email" => "john.doe@example.com", - ]); - - Storage::assertExists($avatar); - } -}