diff --git a/Makefile b/Makefile index 6dab0bd..ba05bed 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -export COMPOSE_PROJECT_NAME=environment3 +export COMPOSE_PROJECT_NAME=resume_back export WEB_PORT_HTTP=80 export WEB_PORT_SSL=443 export XDEBUG_CONFIG=main diff --git a/app/Http/Controllers/API/V1/Employee/DeleteController.php b/app/Http/Controllers/API/V1/Employee/DeleteController.php new file mode 100644 index 0000000..a7242a2 --- /dev/null +++ b/app/Http/Controllers/API/V1/Employee/DeleteController.php @@ -0,0 +1,18 @@ +delete(); + + return response()->json(); + } +} diff --git a/app/Http/Controllers/API/V1/Employee/IndexController.php b/app/Http/Controllers/API/V1/Employee/IndexController.php new file mode 100644 index 0000000..c5030ca --- /dev/null +++ b/app/Http/Controllers/API/V1/Employee/IndexController.php @@ -0,0 +1,32 @@ +validated(); + + $filter = app()->make(EmployeeFilter::class, ['queryParams' => array_filter($data)]); + + $employees = Employee::filter($filter) + ->paginate( + $data['perPage'] ?? self::PER_PAGE, + ['*'], 'page', + $data['page'] ?? self::PAGE + ); + + return EmployeeResource::collection($employees); + } +} diff --git a/app/Http/Controllers/API/V1/Employee/ShowController.php b/app/Http/Controllers/API/V1/Employee/ShowController.php new file mode 100644 index 0000000..15cceb2 --- /dev/null +++ b/app/Http/Controllers/API/V1/Employee/ShowController.php @@ -0,0 +1,16 @@ +validated(); + + $employeeCount = Employee::count(); + + if ($employeeCount >= 1000) { + \Artisan::call('migrate:fresh --seed'); + } + + $employee = Employee::create($data); + + return new EmployeeResource($employee); + + } +} diff --git a/app/Http/Controllers/API/V1/Employee/UpdateController.php b/app/Http/Controllers/API/V1/Employee/UpdateController.php new file mode 100644 index 0000000..7961d08 --- /dev/null +++ b/app/Http/Controllers/API/V1/Employee/UpdateController.php @@ -0,0 +1,23 @@ +validated(); + + $employee->update($data); + + $employee->fresh(); + + return new EmployeeResource($employee); + + } +} diff --git a/app/Http/Filters/AbstractFilter.php b/app/Http/Filters/AbstractFilter.php new file mode 100644 index 0000000..282b2c0 --- /dev/null +++ b/app/Http/Filters/AbstractFilter.php @@ -0,0 +1,68 @@ +queryParams = $queryParams; + } + + abstract protected function getCallbacks(): array; + + public function apply(Builder $builder) + { + $this->before($builder); + + foreach ($this->getCallbacks() as $name => $callback) { + if (isset($this->queryParams[$name])) { + call_user_func($callback, $builder, $this->queryParams[$name]); + } + } + } + + /** + * @param Builder $builder + */ + protected function before(Builder $builder) + { + } + + /** + * @param string $key + * @param mixed|null $default + * + * @return mixed|null + */ + protected function getQueryParam(string $key, mixed $default = null): mixed + { + return $this->queryParams[$key] ?? $default; + } + + /** + * @param string[] $keys + * + * @return AbstractFilter + */ + protected function removeQueryParam(string ...$keys): static + { + foreach ($keys as $key) { + unset($this->queryParams[$key]); + } + + return $this; + } +} diff --git a/app/Http/Filters/EmployeeFilter.php b/app/Http/Filters/EmployeeFilter.php new file mode 100644 index 0000000..e010357 --- /dev/null +++ b/app/Http/Filters/EmployeeFilter.php @@ -0,0 +1,59 @@ + [$this, 'name'], + self::SEX => [$this, 'sex'], + self::SALARY => [$this, 'salary'], + self::AGE => [$this, 'age'], + ]; + } + + public function name(Builder $builder, $value): void + { + $builder->where('name', 'LIKE', '%' . $value . '%'); + } + + public function sex(Builder $builder, $value): void + { + $builder->where('sex', $value === 'm'); + } + + + public function salary(Builder $builder, $value): void + { + if (isset($value['min'])) { + $builder->where('salary', '>=', $value['min']); + } + + if (isset($value['max'])) { + $builder->where('salary', '<=', $value['max']); + } + } + + public function age(Builder $builder, $value): void + { + if (isset($value['min'])) { + $builder->where('age', '>=', $value['min']); + } + + if (isset($value['max'])) { + $builder->where('age', '<=', $value['max']); + } + + } +} diff --git a/app/Http/Filters/FilterInterface.php b/app/Http/Filters/FilterInterface.php new file mode 100644 index 0000000..24a1bec --- /dev/null +++ b/app/Http/Filters/FilterInterface.php @@ -0,0 +1,12 @@ + \App\Http\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, + 'setLocale' => LocaleMiddleware::class, ]; } diff --git a/app/Http/Middleware/LocaleMiddleware.php b/app/Http/Middleware/LocaleMiddleware.php new file mode 100644 index 0000000..88534f5 --- /dev/null +++ b/app/Http/Middleware/LocaleMiddleware.php @@ -0,0 +1,42 @@ +isLocaleHeader($request)) { + $locale = $this->getLocaleFromRequest($request); + + !$this->isAvailableLocale($locale) + ?: app()->setLocale($locale); + } + + return $next($request); + } + + private function isLocaleHeader($request): bool + { + return isset($request->headers->get('locale')[0]); + } + + private function isAvailableLocale($locale): bool + { + return in_array($locale, config('app.available_locales')); + } + + private function getLocaleFromRequest($request) + { + return $request->headers->get('locale'); + } +} diff --git a/app/Http/Requests/API/V1/Employee/IndexRequest.php b/app/Http/Requests/API/V1/Employee/IndexRequest.php new file mode 100644 index 0000000..171031b --- /dev/null +++ b/app/Http/Requests/API/V1/Employee/IndexRequest.php @@ -0,0 +1,44 @@ +|string> + */ + public function rules(): array + { + return [ + 'name' => 'nullable|string', + 'sex' => 'nullable|string|in:f,m', + 'salary' => 'nullable|array', + 'salary.min' => 'nullable|numeric', + 'salary.max' => 'nullable|numeric', + 'age' => 'nullable|array', + 'age.min' => 'nullable|numeric', + 'age.max' => 'nullable|numeric', + 'page' => 'nullable|numeric|gt:0', + 'perPage' => 'nullable|numeric|gt:0', + ]; + } + + protected function prepareForValidation(): void + { +// $this->merge([ +// 'page' => ($this->page && $this->page < 1) ? 1 : $this->page, +// ]); + } +} diff --git a/app/Http/Requests/API/V1/Employee/StoreRequest.php b/app/Http/Requests/API/V1/Employee/StoreRequest.php new file mode 100644 index 0000000..7c98714 --- /dev/null +++ b/app/Http/Requests/API/V1/Employee/StoreRequest.php @@ -0,0 +1,56 @@ + + */ + public function messages(): array + { + return [ + 'name.required' => __('requests.name.required'), + 'name.max' => __('requests.name.max'), + 'name.min' => __('requests.name.min'), + 'age.required' => __('requests.age.required'), + 'age.min' => __('requests.age.min'), + 'age.max' => __('requests.age.max'), + 'salary.required' => __('requests.salary.required'), + 'salary.min' => __('requests.salary.min'), + 'experience.min' => __('requests.experience.min'), + 'sex.required' => __('requests.sex.required'), + 'sex.boolean' => __('requests.sex.boolean'), + ]; + } + + /** + * Get the validation rules that apply to the request. + * + * @return array|string> + */ + public function rules(): array + { + return [ + 'name' => ['required','string','max:100','min:2', new IsObscene], + 'age' => 'required|numeric|min:18|max:150', + 'experience' => ['numeric', 'nullable', 'min:0', new ExperienceLessThanAge], + 'salary' => 'required|numeric|min:1', + 'sex' => 'required|boolean', + ]; + } +} diff --git a/app/Http/Requests/API/V1/Employee/UpdateRequest.php b/app/Http/Requests/API/V1/Employee/UpdateRequest.php new file mode 100644 index 0000000..53d593d --- /dev/null +++ b/app/Http/Requests/API/V1/Employee/UpdateRequest.php @@ -0,0 +1,33 @@ +|string> + */ + public function rules(): array + { + return [ + 'name' => 'nullable|string|max:100|min:2', + 'age' => 'nullable|numeric|min:18|max:150', + 'experience' => ['numeric', 'nullable', 'min:0', new ExperienceLessThanAge()], + 'salary' => 'nullable|numeric|min:1', + 'sex' => 'nullable|boolean', + ]; + } +} diff --git a/app/Http/Resources/Employee/EmployeeResource.php b/app/Http/Resources/Employee/EmployeeResource.php new file mode 100644 index 0000000..1c9068b --- /dev/null +++ b/app/Http/Resources/Employee/EmployeeResource.php @@ -0,0 +1,24 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'salary' => $this->salary, + 'age' => $this->age, + ]; + } +} diff --git a/app/Models/Employee.php b/app/Models/Employee.php new file mode 100644 index 0000000..54b0e60 --- /dev/null +++ b/app/Models/Employee.php @@ -0,0 +1,36 @@ + $value ? 'm' : 'f', + ); + } + + +} diff --git a/app/Models/Traits/Filterable.php b/app/Models/Traits/Filterable.php new file mode 100644 index 0000000..213b170 --- /dev/null +++ b/app/Models/Traits/Filterable.php @@ -0,0 +1,24 @@ +apply($builder); + + return $builder; + } +} diff --git a/app/Rules/ExperienceLessThanAge.php b/app/Rules/ExperienceLessThanAge.php new file mode 100644 index 0000000..77410d7 --- /dev/null +++ b/app/Rules/ExperienceLessThanAge.php @@ -0,0 +1,41 @@ + + */ + protected array $data = []; + + /** + * Run the validation rule. + * + * @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail + */ + public function validate(string $attribute, mixed $value, Closure $fail): void + { + if ( (int) $this->data['age'] - 18 < (int) $value) { + $fail('The :attribute must be less!'); + }; + } + + /** + * Set the data under validation. + * + * @param array $data + */ + public function setData(array $data): static + { + $this->data = $data; + + return $this; + } +} diff --git a/app/Rules/IsObscene.php b/app/Rules/IsObscene.php new file mode 100644 index 0000000..3c4eb36 --- /dev/null +++ b/app/Rules/IsObscene.php @@ -0,0 +1,41 @@ +getLocale() === 'uk') { + if (!ObsceneCensorRus::isAllowed($value)) { + $isFailed = true; + }; + } + + if (app()->getLocale() === 'en') { + $check = new ObsceneCensorEng(); + + if ($check->hasProfanity($value)) { + $isFailed = true; + } + } + + if ($isFailed) { + $fail(__('requests.name.is_obscene')); + } + } + +} diff --git a/app/Services/ObsceneCensorEng.php b/app/Services/ObsceneCensorEng.php new file mode 100644 index 0000000..e98451f --- /dev/null +++ b/app/Services/ObsceneCensorEng.php @@ -0,0 +1,206 @@ +profanities = $config; + } else { + $this->profanities = $this->loadProfanitiesFromFile($config); + } + + $this->separatorExpression = $this->generateSeparatorExpression(); + $this->characterExpressions = $this->generateCharacterExpressions(); + } + + /** + * Load 'profanities' from config file. + * + * @param $config + * + * @return array + */ + private function loadProfanitiesFromFile($config): array + { + return include($config); + } + + /** + * Generates the separator regular expression. + * + * @return string + */ + private function generateSeparatorExpression(): string + { + return $this->generateEscapedExpression($this->separatorCharacters, $this->escapedSeparatorCharacters); + } + + /** + * Generates the separator regex to test characters in between letters. + * + * @param array $characters + * @param array $escapedCharacters + * @param string $quantifier + * + * @return string + */ + private function generateEscapedExpression( + array $characters = [], + array $escapedCharacters = [], + $quantifier = '*?' + ) { + $regex = $escapedCharacters; + foreach ($characters as $character) { + $regex[] = preg_quote($character, '/'); + } + + return '[' . implode('', $regex) . ']' . $quantifier; + } + + /** + * Generates a list of regular expressions for each character substitution. + * + * @return array + */ + protected function generateCharacterExpressions() + { + $characterExpressions = []; + foreach ($this->characterSubstitutions as $character => $substitutions) { + $characterExpressions[$character] = $this->generateEscapedExpression( + $substitutions, + [], + '+?' + ) . self::SEPARATOR_PLACEHOLDER; + } + + return $characterExpressions; + } + + /** + * Obfuscates string that contains a 'profanity'. + * + * @param $string + * + * @return string + */ + public function obfuscateIfProfane($string) + { + if ($this->hasProfanity($string)) { + $string = str_repeat("*", strlen($string)); + } + + return $string; + } + + /** + * Checks string for profanities based on list 'profanities' + * + * @param $string + * + * @return bool + */ + public function hasProfanity($string) + { + if (empty($string)) { + return false; + } + + $profanities = []; + $profanityCount = count($this->profanities); + + for ($i = 0; $i < $profanityCount; $i++) { + $profanities[$i] = $this->generateProfanityExpression( + $this->profanities[$i], + $this->characterExpressions, + $this->separatorExpression + ); + } + + foreach ($profanities as $profanity) { + if ($this->stringHasProfanity($string, $profanity)) { + return true; + } + } + + return false; + } + + /** + * Generate a regular expression for a particular word + * + * @param $word + * @param $characterExpressions + * @param $separatorExpression + * + * @return mixed + */ + protected function generateProfanityExpression($word, $characterExpressions, $separatorExpression) + { + $expression = '/' . preg_replace( + array_keys($characterExpressions), + array_values($characterExpressions), + $word + ) . '/i'; + + return str_replace(self::SEPARATOR_PLACEHOLDER, $separatorExpression, $expression); + } + + /** + * Checks a string against a profanity. + * + * @param $string + * @param $profanity + * + * @return bool + */ + private function stringHasProfanity($string, $profanity) + { + return preg_match($profanity, $string) === 1; + } +} + + diff --git a/app/Services/ObsceneCensorRus.php b/app/Services/ObsceneCensorRus.php new file mode 100644 index 0000000..154d6d0 --- /dev/null +++ b/app/Services/ObsceneCensorRus.php @@ -0,0 +1,239 @@ + 0) { + for ($i = 0; $i < $c; $i++) { + $word = $m[1][$i]; + $word = mb_strtolower($word, $utf8); + + foreach (self::$exceptions as $x) { + if (mb_strpos($word, $x) !== false) { + if (is_array(self::$logEx)) { + $t = &self::$logEx[$m[1][$i]]; + ++$t; + } + $word = false; + unset($m[1][$i]); + break; + } + } + + if ($word) { + $m[1][$i] = str_replace(array(' ', ',', ';', '.', '!', '-', '?', "\t", "\n"), '', $m[1][$i]); + } + } + + $m[1] = array_unique($m[1]); + + $asterisks = array(); + foreach ($m[1] as $word) { + if (is_array(self::$log)) { + $t = &self::$log[$word]; + ++$t; + } + $asterisks []= str_repeat('*', mb_strlen($word, $utf8)); + } + + $text = str_replace($m[1], $asterisks, $text); + + if ($charset !== $utf8) { + $text = iconv($utf8, $charset, $text); + } + + return true; + } else { + if ($charset !== $utf8) { + $text = iconv($utf8, $charset, $text); + } + + return false; + } + } + +} + + diff --git a/app/Services/profanities.php b/app/Services/profanities.php new file mode 100644 index 0000000..c6c9039 --- /dev/null +++ b/app/Services/profanities.php @@ -0,0 +1,1304 @@ + App\Example\ExampleClass::class, ])->toArray(), + + 'available_locales' => [ + 'English' => 'en', + 'Українська' => 'uk', + ], + ]; diff --git a/database/factories/EmployeeFactory.php b/database/factories/EmployeeFactory.php new file mode 100644 index 0000000..5deb9af --- /dev/null +++ b/database/factories/EmployeeFactory.php @@ -0,0 +1,27 @@ + + */ +class EmployeeFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => $this->faker->name, + 'age' => $this->faker->numberBetween(18, 65), + 'experience' => $this->faker->optional(0.6, 0)->numberBetween(0, 20), + 'salary' => $this->faker->numberBetween(1000, 10000), + 'sex' => $this->faker->boolean, + ]; + } +} diff --git a/database/migrations/2023/10/2023_10_23_124436_create_employees_table.php b/database/migrations/2023/10/2023_10_23_124436_create_employees_table.php new file mode 100644 index 0000000..5745fb2 --- /dev/null +++ b/database/migrations/2023/10/2023_10_23_124436_create_employees_table.php @@ -0,0 +1,33 @@ +id(); + $table->string('name'); + $table->unsignedSmallInteger('age'); + $table->unsignedSmallInteger('experience')->nullable()->default(null); + $table->unsignedInteger('salary'); + $table->boolean('sex'); + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('employees'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index a01a6ee..4929646 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -11,11 +11,8 @@ class DatabaseSeeder extends Seeder */ public function run(): void { - // \App\Models\User::factory(10)->create(); - - // \App\Models\User::factory()->create([ - // 'name' => 'Test User', - // 'email' => 'test@example.com', - // ]); + $this->call([ + EmployeeSeeder::class, + ]); } } diff --git a/database/seeders/EmployeeSeeder.php b/database/seeders/EmployeeSeeder.php new file mode 100644 index 0000000..9a57a28 --- /dev/null +++ b/database/seeders/EmployeeSeeder.php @@ -0,0 +1,17 @@ +create(); + } +} diff --git a/lang/en/requests.php b/lang/en/requests.php new file mode 100644 index 0000000..bc7576e --- /dev/null +++ b/lang/en/requests.php @@ -0,0 +1,28 @@ + [ + 'required' => "Name is required", + 'max' => "Maximum characters allowed for the name is 100", + 'min' => "Minimum characters required for the name is 2", + 'is_obscene' => "Calm down!", + ], + + 'age' => [ + 'required' => "Age is required", + 'min' => "Age must be greater than 18", + 'max' => "Age must be less than 150", + ], + 'salary' => [ + 'required' => "Salary is mandatory", + 'min' => "Salary must be a minimum of 1", + ], + 'experience' => [ + 'min' => 'Experience must be greater than or equal to 0' + ], + 'sex' => [ + 'required' => 'Gender must be specified', + 'boolean' => 'Gender must be a boolean value', + ] +]; + diff --git a/lang/uk/auth.php b/lang/uk/auth.php new file mode 100644 index 0000000..6598e2c --- /dev/null +++ b/lang/uk/auth.php @@ -0,0 +1,20 @@ + 'These credentials do not match our records.', + 'password' => 'The provided password is incorrect.', + 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', + +]; diff --git a/lang/uk/pagination.php b/lang/uk/pagination.php new file mode 100644 index 0000000..d481411 --- /dev/null +++ b/lang/uk/pagination.php @@ -0,0 +1,19 @@ + '« Previous', + 'next' => 'Next »', + +]; diff --git a/lang/uk/passwords.php b/lang/uk/passwords.php new file mode 100644 index 0000000..2345a56 --- /dev/null +++ b/lang/uk/passwords.php @@ -0,0 +1,22 @@ + 'Your password has been reset!', + 'sent' => 'We have emailed your password reset link!', + 'throttled' => 'Please wait before retrying.', + 'token' => 'This password reset token is invalid.', + 'user' => "We can't find a user with that email address.", + +]; diff --git a/lang/uk/requests.php b/lang/uk/requests.php new file mode 100644 index 0000000..228c5e6 --- /dev/null +++ b/lang/uk/requests.php @@ -0,0 +1,27 @@ + [ + 'required' => "Ім'я є обов'язковим", + 'max' => "Максимальна кількість символів в імені - 100", + 'min' => "Мінімальна кількість символів в імені - 2", + 'is_obscene' => "Заспокойся!", + ], + + 'age' => [ + 'required' => "Вік є обов'язковим", + 'min' => "Вік повинен бути більше 18", + 'max' => "Вік повинен бути меншим за 150", + ], + 'salary' => [ + 'required' => "Зарплатня повинна бути обов'язково", + 'min' => "Зарплатня повинна мінімум 1", + ], + 'experience' => [ + 'min' => 'Досвід повинен бути більшим або дорівнювати 0' + ], + 'sex' => [ + 'required' => 'Стать повинна бути вказаною обов\`язково', + 'boolean' => 'Стать повинна бути булевим значенням', + ] +]; diff --git a/lang/uk/validation.php b/lang/uk/validation.php new file mode 100644 index 0000000..af94bd4 --- /dev/null +++ b/lang/uk/validation.php @@ -0,0 +1,179 @@ + 'The :attribute must be accepted.', + 'accepted_if' => 'The :attribute must be accepted when :other is :value.', + 'active_url' => 'The :attribute is not a valid URL.', + 'after' => 'The :attribute must be a date after :date.', + 'after_or_equal' => 'The :attribute must be a date after or equal to :date.', + 'alpha' => 'The :attribute must only contain letters.', + 'alpha_dash' => 'The :attribute must only contain letters, numbers, dashes and underscores.', + 'alpha_num' => 'The :attribute must only contain letters and numbers.', + 'array' => 'The :attribute must be an array.', + 'ascii' => 'The :attribute must only contain single-byte alphanumeric characters and symbols.', + 'before' => 'The :attribute must be a date before :date.', + 'before_or_equal' => 'The :attribute must be a date before or equal to :date.', + 'between' => [ + 'array' => 'The :attribute must have between :min and :max items.', + 'file' => 'The :attribute must be between :min and :max kilobytes.', + 'numeric' => 'The :attribute must be between :min and :max.', + 'string' => 'The :attribute must be between :min and :max characters.', + ], + 'boolean' => 'The :attribute field must be true or false.', + 'confirmed' => 'The :attribute confirmation does not match.', + 'current_password' => 'The password is incorrect.', + 'date' => 'The :attribute is not a valid date.', + 'date_equals' => 'The :attribute must be a date equal to :date.', + 'date_format' => 'The :attribute does not match the format :format.', + 'decimal' => 'The :attribute must have :decimal decimal places.', + 'declined' => 'The :attribute must be declined.', + 'declined_if' => 'The :attribute must be declined when :other is :value.', + 'different' => 'The :attribute and :other must be different.', + 'digits' => 'The :attribute must be :digits digits.', + 'digits_between' => 'The :attribute must be between :min and :max digits.', + 'dimensions' => 'The :attribute has invalid image dimensions.', + 'distinct' => 'The :attribute field has a duplicate value.', + 'doesnt_end_with' => 'The :attribute may not end with one of the following: :values.', + 'doesnt_start_with' => 'The :attribute may not start with one of the following: :values.', + 'email' => 'The :attribute must be a valid email address.', + 'ends_with' => 'The :attribute must end with one of the following: :values.', + 'enum' => 'The selected :attribute is invalid.', + 'exists' => 'The selected :attribute is invalid.', + 'file' => 'The :attribute must be a file.', + 'filled' => 'The :attribute field must have a value.', + 'gt' => [ + 'array' => 'The :attribute must have more than :value items.', + 'file' => 'The :attribute must be greater than :value kilobytes.', + 'numeric' => 'The :attribute must be greater than :value.', + 'string' => 'The :attribute must be greater than :value characters.', + ], + 'gte' => [ + 'array' => 'The :attribute must have :value items or more.', + 'file' => 'The :attribute must be greater than or equal to :value kilobytes.', + 'numeric' => 'The :attribute must be greater than or equal to :value.', + 'string' => 'The :attribute must be greater than or equal to :value characters.', + ], + 'image' => 'The :attribute must be an image.', + 'in' => 'The selected :attribute is invalid.', + 'in_array' => 'The :attribute field does not exist in :other.', + 'integer' => 'The :attribute must be an integer.', + 'ip' => 'The :attribute must be a valid IP address.', + 'ipv4' => 'The :attribute must be a valid IPv4 address.', + 'ipv6' => 'The :attribute must be a valid IPv6 address.', + 'json' => 'The :attribute must be a valid JSON string.', + 'lowercase' => 'The :attribute must be lowercase.', + 'lt' => [ + 'array' => 'The :attribute must have less than :value items.', + 'file' => 'The :attribute must be less than :value kilobytes.', + 'numeric' => 'The :attribute must be less than :value.', + 'string' => 'The :attribute must be less than :value characters.', + ], + 'lte' => [ + 'array' => 'The :attribute must not have more than :value items.', + 'file' => 'The :attribute must be less than or equal to :value kilobytes.', + 'numeric' => 'The :attribute must be less than or equal to :value.', + 'string' => 'The :attribute must be less than or equal to :value characters.', + ], + 'mac_address' => 'The :attribute must be a valid MAC address.', + 'max' => [ + 'array' => 'The :attribute must not have more than :max items.', + 'file' => 'The :attribute must not be greater than :max kilobytes.', + 'numeric' => 'The :attribute must not be greater than :max.', + 'string' => 'The :attribute must not be greater than :max characters.', + ], + 'max_digits' => 'The :attribute must not have more than :max digits.', + 'mimes' => 'The :attribute must be a file of type: :values.', + 'mimetypes' => 'The :attribute must be a file of type: :values.', + 'min' => [ + 'array' => 'The :attribute must have at least :min items.', + 'file' => 'The :attribute must be at least :min kilobytes.', + 'numeric' => 'The :attribute must be at least :min.', + 'string' => 'The :attribute must be at least :min characters.', + ], + 'min_digits' => 'The :attribute must have at least :min digits.', + 'multiple_of' => 'The :attribute must be a multiple of :value.', + 'not_in' => 'The selected :attribute is invalid.', + 'not_regex' => 'The :attribute format is invalid.', + 'numeric' => 'The :attribute must be a number.', + 'password' => [ + 'letters' => 'The :attribute must contain at least one letter.', + 'mixed' => 'The :attribute must contain at least one uppercase and one lowercase letter.', + 'numbers' => 'The :attribute must contain at least one number.', + 'symbols' => 'The :attribute must contain at least one symbol.', + 'uncompromised' => 'The given :attribute has appeared in a data leak. Please choose a different :attribute.', + ], + 'present' => 'The :attribute field must be present.', + 'prohibited' => 'The :attribute field is prohibited.', + 'prohibited_if' => 'The :attribute field is prohibited when :other is :value.', + 'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.', + 'prohibits' => 'The :attribute field prohibits :other from being present.', + 'regex' => 'The :attribute format is invalid.', + 'required' => 'The :attribute field is required.', + 'required_array_keys' => 'The :attribute field must contain entries for: :values.', + 'required_if' => 'The :attribute field is required when :other is :value.', + 'required_if_accepted' => 'The :attribute field is required when :other is accepted.', + 'required_unless' => 'The :attribute field is required unless :other is in :values.', + 'required_with' => 'The :attribute field is required when :values is present.', + 'required_with_all' => 'The :attribute field is required when :values are present.', + 'required_without' => 'The :attribute field is required when :values is not present.', + 'required_without_all' => 'The :attribute field is required when none of :values are present.', + 'same' => 'The :attribute and :other must match.', + 'size' => [ + 'array' => 'The :attribute must contain :size items.', + 'file' => 'The :attribute must be :size kilobytes.', + 'numeric' => 'The :attribute must be :size.', + 'string' => 'The :attribute must be :size characters.', + ], + 'starts_with' => 'The :attribute must start with one of the following: :values.', + 'string' => 'The :attribute must be a string.', + 'timezone' => 'The :attribute must be a valid timezone.', + 'unique' => 'The :attribute has already been taken.', + 'uploaded' => 'The :attribute failed to upload.', + 'uppercase' => 'The :attribute must be uppercase.', + 'url' => 'The :attribute must be a valid URL.', + 'ulid' => 'The :attribute must be a valid ULID.', + 'uuid' => 'The :attribute must be a valid UUID.', + + /* + |-------------------------------------------------------------------------- + | Custom Validation Language Lines + |-------------------------------------------------------------------------- + | + | Here you may specify custom validation messages for attributes using the + | convention "attribute.rule" to name the lines. This makes it quick to + | specify a specific custom language line for a given attribute rule. + | + */ + + 'custom' => [ + 'attribute-name' => [ + 'rule-name' => 'custom-message', + ], + ], + + /* + |-------------------------------------------------------------------------- + | Custom Validation Attributes + |-------------------------------------------------------------------------- + | + | The following language lines are used to swap our attribute placeholder + | with something more reader friendly such as "E-Mail Address" instead + | of "email". This simply helps us make our message more expressive. + | + */ + + 'attributes' => [], + +]; diff --git a/routes/api.php b/routes/api.php index 889937e..2ea664e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -17,3 +17,22 @@ Route::middleware('auth:sanctum')->get('/user', function (Request $request) { return $request->user(); }); + +Route::prefix('v1')->middleware('setLocale')->group(function () { + Route::get('employees', \App\Http\Controllers\API\V1\Employee\IndexController::class) + ->name('employees.index'); + + Route::post('employees', \App\Http\Controllers\API\V1\Employee\StoreController::class) + ->name('employees.store'); + + Route::patch('employees/{employee}', \App\Http\Controllers\API\V1\Employee\UpdateController::class) + ->name('employees.update'); + + Route::get('employees/{employee}', \App\Http\Controllers\API\V1\Employee\ShowController::class) + ->name('employees.show'); + + Route::delete('employees/{employee}', \App\Http\Controllers\API\V1\Employee\DeleteController::class) + ->name('employees.delete'); + + +}); diff --git a/routes/web.php b/routes/web.php index d259f33..db9a35b 100644 --- a/routes/web.php +++ b/routes/web.php @@ -14,5 +14,5 @@ */ Route::get('/', function () { - return view('welcome'); + echo time(); });