Skip to content

Instantly share code, notes, and snippets.

@emeka-osuagwu
Last active March 12, 2024 13:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save emeka-osuagwu/42b072c50e80dc1a024355b51da6beba to your computer and use it in GitHub Desktop.
Save emeka-osuagwu/42b072c50e80dc1a024355b51da6beba to your computer and use it in GitHub Desktop.
User Service implimentation
version: '3.7'
services:
localman_service_server:
container_name: localman_service_server
build:
context: ./docker/local
dockerfile: ./nginx/Dockerfile
restart: unless-stopped
ports:
- ${ENV_SERVICE_SERVER_PORT}:80
volumes:
- ./:/var/www/html
- ./docker/local/nginx/nginx.conf:/etc/nginx/conf.d/default.conf
networks:
- localman_service_network
localman_redis:
container_name: localman_redis
image: redis:6
restart: always
ports:
- ${REDIS_PORT}:${REDIS_PORT}
command: --port ${REDIS_PORT}
expose:
- ${REDIS_PORT}
volumes:
- ./data:/data
networks:
- localman_service_network
redis-commander:
image: rediscommander/redis-commander:latest
environment:
- REDIS_HOSTS=localman_service_network:localman_redis:${REDIS_PORT}
ports:
- ${REDIS_UI_PORT}:8081
networks:
- localman_service_network
depends_on:
- localman_redis
localman_service_api:
container_name: localman_service_api
build:
context: ./
dockerfile: ./docker/local/php/Dockerfile
ports:
- ${ENV_SERVICE_API_PORT}:${ENV_SERVICE_API_PORT}
volumes:
- ./:/var/www/html
networks:
- localman_service_network
depends_on:
- localman_redis
- localman_service_server
networks:
localman_service_network:
name: localman_service_network
cache:
driver: local
<?php
namespace App\Http\Controllers\V1\Auth;
/*
|--------------------------------------------------------------------------
| Illuminate Namespace
|--------------------------------------------------------------------------
*/
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Validator;
/*
|--------------------------------------------------------------------------
| Traits Namespace
|--------------------------------------------------------------------------
*/
use App\Traits\ResponseTrait;
/*
|--------------------------------------------------------------------------
| Exception Namespace
|--------------------------------------------------------------------------
*/
use App\Exceptions\AppException;
/*
|--------------------------------------------------------------------------
| Services Namespace
|--------------------------------------------------------------------------
*/
use App\Services\UserService;
use App\Services\AuthService;
use App\Enums\ResponseCode\AuthResponseCode;
/*
|--------------------------------------------------------------------------
| Event Namespace
|--------------------------------------------------------------------------
*/
use App\Jobs\Auth\LoginJob;
class Login extends Controller
{
use ResponseTrait;
public function __construct
(
protected AuthService $authService,
protected UserService $userService,
){}
/*
|--------------------------------------------------------------------------
| Request Validation
|--------------------------------------------------------------------------
*/
public function requestValidation($data)
{
/*
|--------------------------------------------------------------------------
| add comment
|--------------------------------------------------------------------------
*/
$rules = [
'password' => 'required|string',
'account_type' => 'required|string|in:' . implode(',', accountTypes()),
];
/*
|--------------------------------------------------------------------------
| add comment
|--------------------------------------------------------------------------
*/
if($data['account_type'] == 'business'){
$rules['email'] = 'required|string|email';
}
/*
|--------------------------------------------------------------------------
| add comment
|--------------------------------------------------------------------------
*/
if($data['account_type'] == 'user'){
$rules['phone_number'] = 'required|string|min:11|max:11';
}
/*
|--------------------------------------------------------------------------
| add comment
|--------------------------------------------------------------------------
*/
return Validator::make($data, $rules);
}
/*
|--------------------------------------------------------------------------
| loan application
|--------------------------------------------------------------------------
*/
public function __invoke(Request $request)
{
/*
|--------------------------------------------------------------------------
| check request Validation
|--------------------------------------------------------------------------
*/
$validator = $this->requestValidation($request->all());
if ($validator->fails()) {
return $this->sendResponse($validator->errors(), AuthResponseCode::AUTH_REQUEST_VALIDATION_ERROR);
}
/*
|--------------------------------------------------------------------------
| find business on big query
|--------------------------------------------------------------------------
*/
$find_user_payload = ['phone_number' => $request->phone_number];
if($request->account_type == "business"){
$find_user_payload = ['email' => $request->email];
}
/*
|--------------------------------------------------------------------------
| find user on big query
|--------------------------------------------------------------------------
*/
$find_user = $this->userService->findWhere($find_user_payload);
/*
|--------------------------------------------------------------------------
| check if request failed
|--------------------------------------------------------------------------
*/
if(!$find_user['is_successful']){
return $this->sendResponse([], AuthResponseCode::INVALID_AUTHORIZATION);
}
/*
|--------------------------------------------------------------------------
| check if user exist
|--------------------------------------------------------------------------
*/
if(!count($find_user['response'])){
return $this->sendResponse([], AuthResponseCode::INVALID_AUTHORIZATION);
}
/*
|--------------------------------------------------------------------------
| check if password match
|--------------------------------------------------------------------------
*/
if(!hashCheck($request->password, $find_user['response'][0]['password'])){
return $this->sendResponse($validator->errors(), AuthResponseCode::INVALID_AUTHORIZATION);
}
/*
|--------------------------------------------------------------------------
| check if user profile is active
|--------------------------------------------------------------------------
*/
if($find_user['response'][0]['profile_status'] != 'active'){
return $this->sendResponse([], AuthResponseCode::ACCOUNT_PENDING_ACTIVATION);
}
/*
|--------------------------------------------------------------------------
| set variable
|--------------------------------------------------------------------------
*/
$user = $find_user['response'][0];
/*
|--------------------------------------------------------------------------
| unset password
|--------------------------------------------------------------------------
*/
unset($user['password']);
/*
|--------------------------------------------------------------------------
| set variable
|--------------------------------------------------------------------------
*/
$payload = [
...$user,
'time' => now()->format('l, F j, H:i:s'),
'device' => "{$request->header('device-name')} {$request->header('app-os')} $request->header('device-id')",
'location' => $request->header('location') ?? null,
'account_type' => $request->account_type,
];
/*
|--------------------------------------------------------------------------
| generate token
|--------------------------------------------------------------------------
*/
$token = $this->authService->createAuthentication($payload);
/*
|--------------------------------------------------------------------------
| email job
|--------------------------------------------------------------------------
*/
LoginJob::dispatch($payload)->delay(now()->addMinutes(1));
/*
|--------------------------------------------------------------------------
| response data
|--------------------------------------------------------------------------
*/
return $this->sendResponse(['token' => $token], AuthResponseCode::AUTH_REQUEST_SUCCESSFUL);
}
}
<?php
namespace App\Http\Controllers\V1\Auth;
/*
|--------------------------------------------------------------------------
| Illuminate Namespace
|--------------------------------------------------------------------------
*/
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Validator;
/*
|--------------------------------------------------------------------------
| Traits Namespace
|--------------------------------------------------------------------------
*/
use App\Traits\ResponseTrait;
/*
|--------------------------------------------------------------------------
| Exception Namespace
|--------------------------------------------------------------------------
*/
use App\Exceptions\AppException;
/*
|--------------------------------------------------------------------------
| Services Namespace
|--------------------------------------------------------------------------
*/
use App\Services\UserService;
use App\Services\WalletService;
use App\Enums\ResponseCode\AuthResponseCode;
use App\Enums\ResponseCode\UserResponseCode;
/*
|--------------------------------------------------------------------------
| Jobs Namespace
|--------------------------------------------------------------------------
*/
use App\Jobs\Auth\CreateAccountJob;
use App\Jobs\Wallet\CreateWalletJob;
class Register extends Controller
{
use ResponseTrait;
public function __construct
(
protected UserService $userService,
protected WalletService $walletService,
){}
/*
|--------------------------------------------------------------------------
| Request Validation
|--------------------------------------------------------------------------
*/
public function requestValidation($data)
{
/*
|--------------------------------------------------------------------------
| add comment
|--------------------------------------------------------------------------
*/
$rules = [
'email' => 'required|string|email',
'password' => 'required|alpha_num|min:6|max:10',
'full_name' => 'required|string|min:3',
'phone_number' => 'required|string|min:11|max:11',
'account_type' => 'required|string|in:' . implode(',', accountTypes()),
];
/*
|--------------------------------------------------------------------------
| add comment
|--------------------------------------------------------------------------
*/
return Validator::make($data, $rules);
}
/*
|--------------------------------------------------------------------------
| loan application
|--------------------------------------------------------------------------
*/
public function __invoke(Request $request)
{
/*
|--------------------------------------------------------------------------
| check request Validation
|--------------------------------------------------------------------------
*/
$validator = $this->requestValidation($request->all());
if ($validator->fails()) {
return $this->sendResponse($validator->errors(), AuthResponseCode::AUTH_REQUEST_VALIDATION_ERROR);
}
/*
|--------------------------------------------------------------------------
| set variable
|--------------------------------------------------------------------------
*/
$register_payload = [
'id' => generateUUID(),
'roles' => json_encode(['user']),
'email' => $request->email,
'password' => hashValue($request->password),
'full_name' => $request->full_name,
'created_at' => now()->toDateTimeString(),
'updated_at' => now()->toDateTimeString(),
'phone_number' => $request->phone_number,
'account_type' => $request->account_type,
'profile_status' => 'pending',
];
/*
|--------------------------------------------------------------------------
| create user
|--------------------------------------------------------------------------
*/
$create_user_response = $this->userService->create($register_payload);
/*
|--------------------------------------------------------------------------
| check if request failed
|--------------------------------------------------------------------------
*/
if(!$create_user_response['is_successful']){
return $this->sendResponse([], AuthResponseCode::AUTH_REQUEST_FAILED);
}
/*
|--------------------------------------------------------------------------
| set variable
|--------------------------------------------------------------------------
*/
$wallet_payload = [
'id' => generateUUID(),
'balance' => '0',
'provider' => 'paystack',
'user_id' => $register_payload['id'],
'account_type' => $request->account_type,
];
/*
|--------------------------------------------------------------------------
| dispatch job
|--------------------------------------------------------------------------
*/
CreateAccountJob::dispatch($register_payload)->delay(now()->addMinutes(1));
CreateWalletJob::dispatch($wallet_payload)->delay(now()->addMinutes(1));
/*
|--------------------------------------------------------------------------
| response data
|--------------------------------------------------------------------------
*/
return $this->sendResponse([], AuthResponseCode::AUTH_REQUEST_SUCCESSFUL);
}
}
<?php
namespace App\Traits;
use Exception;
use App\Jobs\System\ProcessLogJob;
trait ResponseTrait
{
function sendResponse($data, $response_code_object)
{
$payload = $response_code_object->toString();
$payload['data'] = $data;
$log_payload = [
'request' => request()->all(),
'response' => $payload,
];
return response()->json($payload, 200);
}
}
<?php
namespace App\Services;
use Throwable;
use App\Exceptions\AppException;
use App\Contracts\BigQueryProviderContract;
use App\Enums\RequestResponseTypeEnums;
use App\Services\Providers\BigQueryQueryBuilder;
use Illuminate\Support\Facades\Validator;
/*
|--------------------------------------------------------------------------
| Exception Namespace
|--------------------------------------------------------------------------
*/
class UserService
{
use BigQueryQueryBuilder;
/*
|--------------------------------------------------------------------------
| Add Comment
|--------------------------------------------------------------------------
*/
public function __construct
(
protected BigQueryProviderContract $bigQueryProvider
)
{}
/*
|--------------------------------------------------------------------------
| add comment
|--------------------------------------------------------------------------
*/
public function create(array $payload): array | object {/*
|--------------------------------------------------------------------------
| validate payload
|--------------------------------------------------------------------------
*/
$validator = Validator::make($payload, [
'id' => 'required|string',
'bvn' => 'nullable|string|min:11|max:11',
'roles' => 'nullable|string',
'email' => 'required|string|email',
'password' => 'required|string',
'full_name' => 'required|string',
'created_at' => 'required|string',
'updated_at' => 'required|string',
'origination' => 'nullable|string',
'phone_number' => 'required|string|min:11|max:11',
'account_type' => 'required|in:' . implode(',', accountTypes()),
'profile_status' => 'required|in:pending',
]);
/*
|--------------------------------------------------------------------------
| check Validation
|--------------------------------------------------------------------------
*/
if ($validator->fails()) {
return [
'status' => RequestResponseTypeEnums::SERVICE_REQUEST_VALIDATION_ERROR->value,
'response' => $validator->errors(),
'is_successful' => false
];
}
/*
|--------------------------------------------------------------------------
| check if user id do not exist
|--------------------------------------------------------------------------
*/
$find_user_response = $this->findWhereOr([
'id' => $payload['id'],
'email' => $payload['email'],
'phone_number' => $payload['phone_number']
]);
/*
|--------------------------------------------------------------------------
| check if user exist
|--------------------------------------------------------------------------
*/
if(count($find_user_response['response'])){
return [
'status' => RequestResponseTypeEnums::SERVICE_REQUEST_DUPLICATE_RECORD_ERROR->value,
'response' => [],
'is_successful' => false
];
}
/*
|--------------------------------------------------------------------------
| make request
|--------------------------------------------------------------------------
*/
try {
$query = $this->insertNewRecordInTable('users', $payload);
$this->bigQueryProvider->query($query);
} catch (Throwable $exception){
report($exception);
return [
'status' => RequestResponseTypeEnums::SERVICE_REQUEST_ERROR->value,
'response' => $exception->getMessage(),
'is_successful' => false
];
}
/*
|--------------------------------------------------------------------------
| return successful response
|--------------------------------------------------------------------------
*/
return [
'status' => RequestResponseTypeEnums::SERVICE_REQUEST_SUCCESSFUL->value,
'response' => $payload,
'is_successful' => true
];
}
/*
|--------------------------------------------------------------------------
| Add Comment
|--------------------------------------------------------------------------
*/
public function findWhereOr(array $payload): array {
/*
|--------------------------------------------------------------------------
| check if payload is empty
|--------------------------------------------------------------------------
*/
if(!count($payload)){
return [
'status' => RequestResponseTypeEnums::SERVICE_REQUEST_EMPTY_PAYLOAD_ERROR->value,
'response' => [],
'is_successful' => false
];
}
/*
|--------------------------------------------------------------------------
| validate payload
|--------------------------------------------------------------------------
*/
$validator = Validator::make($payload, [
'bvn' => 'required_if:section,bvn|digits:11',
'email' => 'required_if:section,email',
'phone_number' => 'required_if:section,phone_number|digits:11',
]);
/*
|--------------------------------------------------------------------------
| check Validation
|--------------------------------------------------------------------------
*/
if($validator->fails()) {
return [
'status' => RequestResponseTypeEnums::SERVICE_REQUEST_VALIDATION_ERROR->value,
'response' => $validator->errors(),
'is_successful' => false
];
}
/*
|--------------------------------------------------------------------------
| make request
|--------------------------------------------------------------------------
*/
try {
$query = $this->findInTableWhereOr('users', $payload);
$payload = $this->bigQueryProvider->query($query)->toJson();
} catch (Throwable $exception){
report($exception);
return [
'status' => RequestResponseTypeEnums::SERVICE_REQUEST_ERROR->value,
'response' => $exception->getMessage(),
'is_successful' => false
];
}
/*
|--------------------------------------------------------------------------
| return successful response
|--------------------------------------------------------------------------
*/
return [
'status' => RequestResponseTypeEnums::SERVICE_REQUEST_SUCCESSFUL->value,
'response' => $payload,
'is_successful' => true
];
}
/*
|--------------------------------------------------------------------------
| Add Comment
|--------------------------------------------------------------------------
*/
public function findWhere(array $payload): array | object {
/*
|--------------------------------------------------------------------------
| check if payload is empty
|--------------------------------------------------------------------------
*/
if (!count($payload)){
return [
'status' => RequestResponseTypeEnums::SERVICE_REQUEST_EMPTY_PAYLOAD_ERROR->value,
'response' => [],
'is_successful' => false
];
}
/*
|--------------------------------------------------------------------------
| validate payload
|--------------------------------------------------------------------------
*/
$validator = Validator::make($payload, [
'id' => 'nullable|string',
'bvn' => 'nullable|string|min:11|max:11',
'email' => 'nullable|string',
'phone_number' => 'nullable|string|min:11|max:11',
]);
/*
|--------------------------------------------------------------------------
| check Validation
|--------------------------------------------------------------------------
*/
if ($validator->fails()) {
return [
'status' => RequestResponseTypeEnums::SERVICE_REQUEST_VALIDATION_ERROR->value,
'response' => [],
'is_successful' => false
];
}
/*
|--------------------------------------------------------------------------
| make request
|--------------------------------------------------------------------------
*/
try {
$query = $this->findInTableWhere('users', $payload);
$payload = $this->bigQueryProvider->query($query)->toJson();
} catch (Throwable $exception){
report($exception);
return [
'status' => RequestResponseTypeEnums::SERVICE_REQUEST_ERROR->value,
'response' => $exception->getMessage(),
'is_successful' => false
];
}
/*
|--------------------------------------------------------------------------
| return successful response
|--------------------------------------------------------------------------
*/
return [
'status' => RequestResponseTypeEnums::SERVICE_REQUEST_SUCCESSFUL->value,
'response' => $payload,
'is_successful' => true
];
}
/*
|--------------------------------------------------------------------------
| Add Comment
|--------------------------------------------------------------------------
*/
public function getAll(): array {
try {
$query = $this->findAllInTable('users');
$payload = $this->bigQueryProvider->query($query)->toJson();
} catch (AppException $exception) {
report($exception);
return [
'status' => RequestResponseTypeEnums::SERVICE_REQUEST_ERROR->value,
'response' => $exception->getMessage(),
'is_successful' => false
];
}
/*
|--------------------------------------------------------------------------
| return successful response
|--------------------------------------------------------------------------
*/
return [
'status' => RequestResponseTypeEnums::SERVICE_REQUEST_SUCCESSFUL->value,
'response' => $payload,
'is_successful' => true
];
}
/*
|--------------------------------------------------------------------------
| Add Comment
|--------------------------------------------------------------------------
*/
public function update(string $selector, array $payload): array
{
/*
|--------------------------------------------------------------------------
| validate payload
|--------------------------------------------------------------------------
*/
$validator = Validator::make($payload, [
'bvn' => 'nullable|string',
'image' => 'nullable|string',
'password' => 'nullable|string',
'updated_at' => 'nullable|string',
'is_deleted' => 'nullable|string',
'profile_status' => 'nullable|string|in:active,inactive',
'transaction_pin' => 'nullable|string',
]);
/*
|--------------------------------------------------------------------------
| check Validation
|--------------------------------------------------------------------------
*/
if ($validator->fails()) {
return [
'status' => RequestResponseTypeEnums::SERVICE_REQUEST_VALIDATION_ERROR->value,
'response' => [],
'is_successful' => false
];
}
/*
|--------------------------------------------------------------------------
| make request
|--------------------------------------------------------------------------
*/
try {
$query = $this->updateTableRecordWhereIn('users', $selector, $payload);
$this->bigQueryProvider->query($query);
} catch (Throwable $exception){
report($exception);
return [
'status' => RequestResponseTypeEnums::SERVICE_REQUEST_ERROR->value,
'response' => $exception->getMessage(),
'is_successful' => false
];
}
/*
|--------------------------------------------------------------------------
| return successful response
|--------------------------------------------------------------------------
*/
return [
'status' => RequestResponseTypeEnums::SERVICE_REQUEST_SUCCESSFUL->value,
'response' => $payload,
'is_successful' => true
];
}
}
@emeka-osuagwu
Copy link
Author

the snippets above examples from a side project

Context

  • docker file = this gives an idea the development tools
  • Login file = this is a invokeble controller that contains the user logic, validation etc
  • Register file = this is a invokeble controller that contains user register, validation etc
  • UserService file = this is contains user modal logic, validation etc

What is existing about the sample

1

In the examples, you would see the user validation on the service level. This ensures that developers follow a strict role when they interact with the User Model, and it also gives you an API-like response to understand the state of your request to the model. This is a big improvement to the Laravel way of doing this.

2

I took PHP's recent update (Enums) to make sure we have a constant service type across the entire codebase. This would help avoid backward compatibility issues for constant value changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment