BUAT PROJEK BARU
laravel new zapnet-api-v3
none
0
mysql
yes
no
buat folder git baru
git init
git add .
git commit -m "master"
hapus seluruh perubahan dan penambahan
git reset --hard
git clean -fd
BUAT UUID.php
buat folder dan file app/Traits/UUID.php
# force artinya jika folder atau filenya sudah tidak ada ,tidak akan error dan di overwrite
mkdir app/Traits -Force; New-Item -ItemType File app/Traits/UUID.php -Force
<?php
namespace App\Traits;
use Illuminate\Support\Str;
trait UUID
{
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
if ($model->getKey() === null) {
$model->setAttribute($model->getKeyName(), Str::uuid()->toString());
}
});
}
public function getIncrementing()
{
return false;
}
public function getKeyType()
{
return 'String';
}
}
BUAT RESPONSEHELPER.PHP
# force artinya jika folder atau filenya sudah tidak ada ,tidak akan error dan di overwrite
mkdir app/Helpers -Force; New-Item -ItemType File app/Helpers/ResponseHelper.php -Force
Edit App\Helpers\ResponseHelper.php
<?php
/**
* supaya response dari api nya tidak berantakan
* agar sesuai dengan ini
*/
namespace App\Helpers;
use Illuminate\Http\JsonResponse;
class ResponseHelper
{
public static function jsonResponse($success, $message, $data, $statusCode): JsonResponse
{
return response()->json([
'success' => $success,
'message' => $message,
'data' => $data,
], $statusCode);
}
}
BUAT RESPONSE BAHASA INDONESIA
php artisan lang:publish
# force artinya jika folder atau filenya sudah tidak ada ,tidak akan error dan di overwrite
mkdir lang/id -Force; New-Item -ItemType File lang/id/auth.php -Force
New-Item -ItemType File lang/id/pagination.php -Force
New-Item -ItemType File lang/id/passwords.php -Force
New-Item -ItemType File lang/id/validation.php -Force
edit auth.php
<?php
return [
/*
|--------------------------------------------------------------------------
| Baris Bahasa Autentikasi
|--------------------------------------------------------------------------
|
| Baris bahasa berikut digunakan selama proses autentikasi untuk berbagai
| pesan yang perlu kita tampilkan kepada pengguna. Anda bebas untuk
| mengubah baris bahasa ini sesuai dengan kebutuhan aplikasi Anda.
|
*/
'failed' => 'Kredensial ini tidak cocok dengan catatan kami.',
'password' => 'Kata sandi yang diberikan salah.',
'throttle' => 'Terlalu banyak percobaan masuk. Silakan coba lagi dalam :seconds detik.',
];
edit pagination.php
<?php
return [
/*
|--------------------------------------------------------------------------
| Baris Bahasa untuk Pagination
|--------------------------------------------------------------------------
|
| Baris bahasa berikut digunakan oleh pustaka paginator untuk membuat
| tautan paginasi sederhana. Anda bebas mengubahnya ke apa pun yang Anda
| inginkan untuk menyesuaikan tampilan agar lebih sesuai dengan aplikasi Anda.
|
*/
'previous' => '« Sebelumnya',
'next' => 'Berikutnya »',
];
edit password.php
<?php
return [
/*
|--------------------------------------------------------------------------
| Baris Bahasa untuk Reset Kata Sandi
|--------------------------------------------------------------------------
|
| Baris bahasa berikut adalah baris default yang cocok dengan alasan
| yang diberikan oleh broker kata sandi untuk upaya pembaruan kata sandi
| yang gagal, seperti karena kata sandi atau token reset tidak valid.
|
*/
'reset' => 'Kata sandi Anda telah direset.',
'sent' => 'Kami telah mengirimkan tautan reset kata sandi ke email Anda.',
'throttled' => 'Harap tunggu sebelum mencoba lagi.',
'token' => 'Token reset kata sandi ini tidak valid.',
'user' => "Kami tidak dapat menemukan pengguna dengan alamat email tersebut.",
];
edit validation.php
<?php
return [
/*
|---------------------------------------------------------------------------------------
| Baris Bahasa untuk Validasi
|---------------------------------------------------------------------------------------
|
| Baris bahasa berikut ini berisi standar pesan kesalahan yang digunakan oleh
| kelas validasi. Beberapa aturan mempunyai banyak versi seperti aturan 'size'.
| Jangan ragu untuk mengoptimalkan setiap pesan yang ada di sini.
|
*/
'accepted' => ':attribute harus diterima.',
'active_url' => ':attribute bukan URL yang valid.',
'after' => ':attribute harus berisi tanggal setelah :date.',
'after_or_equal' => ':attribute harus berisi tanggal setelah atau sama dengan :date.',
'alpha' => ':attribute hanya boleh berisi huruf.',
'alpha_dash' => ':attribute hanya boleh berisi huruf, angka, strip, dan garis bawah.',
'alpha_num' => ':attribute hanya boleh berisi huruf dan angka.',
'array' => ':attribute harus berisi sebuah array.',
'before' => ':attribute harus berisi tanggal sebelum :date.',
'before_or_equal' => ':attribute harus berisi tanggal sebelum atau sama dengan :date.',
'between' => [
'numeric' => ':attribute harus bernilai antara :min sampai :max.',
'file' => ':attribute harus berukuran antara :min sampai :max kilobita.',
'string' => ':attribute harus berisi antara :min sampai :max karakter.',
'array' => ':attribute harus memiliki :min sampai :max anggota.',
],
'boolean' => ':attribute harus bernilai true atau false',
'confirmed' => 'Konfirmasi :attribute tidak cocok.',
'date' => ':attribute bukan tanggal yang valid.',
'date_equals' => ':attribute harus berisi tanggal yang sama dengan :date.',
'date_format' => ':attribute tidak cocok dengan format :format.',
'different' => ':attribute dan :other harus berbeda.',
'digits' => ':attribute harus terdiri dari :digits angka.',
'digits_between' => ':attribute harus terdiri dari :min sampai :max angka.',
'dimensions' => ':attribute tidak memiliki dimensi gambar yang valid.',
'distinct' => ':attribute memiliki nilai yang duplikat.',
'email' => ':attribute harus berupa alamat surel yang valid.',
'ends_with' => ':attribute harus diakhiri salah satu dari berikut: :values',
'exists' => ':attribute yang dipilih tidak valid.',
'file' => ':attribute harus berupa sebuah berkas.',
'filled' => ':attribute harus memiliki nilai.',
'gt' => [
'numeric' => ':attribute harus bernilai lebih besar dari :value.',
'file' => ':attribute harus berukuran lebih besar dari :value kilobita.',
'string' => ':attribute harus berisi lebih besar dari :value karakter.',
'array' => ':attribute harus memiliki lebih dari :value anggota.',
],
'gte' => [
'numeric' => ':attribute harus bernilai lebih besar dari atau sama dengan :value.',
'file' => ':attribute harus berukuran lebih besar dari atau sama dengan :value kilobita.',
'string' => ':attribute harus berisi lebih besar dari atau sama dengan :value karakter.',
'array' => ':attribute harus terdiri dari :value anggota atau lebih.',
],
'image' => ':attribute harus berupa gambar.',
'in' => ':attribute yang dipilih tidak valid.',
'in_array' => ':attribute tidak ada di dalam :other.',
'integer' => ':attribute harus berupa bilangan bulat.',
'ip' => ':attribute harus berupa alamat IP yang valid.',
'ipv4' => ':attribute harus berupa alamat IPv4 yang valid.',
'ipv6' => ':attribute harus berupa alamat IPv6 yang valid.',
'json' => ':attribute harus berupa JSON string yang valid.',
'lt' => [
'numeric' => ':attribute harus bernilai kurang dari :value.',
'file' => ':attribute harus berukuran kurang dari :value kilobita.',
'string' => ':attribute harus berisi kurang dari :value karakter.',
'array' => ':attribute harus memiliki kurang dari :value anggota.',
],
'lte' => [
'numeric' => ':attribute harus bernilai kurang dari atau sama dengan :value.',
'file' => ':attribute harus berukuran kurang dari atau sama dengan :value kilobita.',
'string' => ':attribute harus berisi kurang dari atau sama dengan :value karakter.',
'array' => ':attribute harus tidak lebih dari :value anggota.',
],
'max' => [
'numeric' => ':attribute maskimal bernilai :max.',
'file' => ':attribute maksimal berukuran :max kilobita.',
'string' => ':attribute maskimal berisi :max karakter.',
'array' => ':attribute maksimal terdiri dari :max anggota.',
],
'mimes' => ':attribute harus berupa berkas berjenis: :values.',
'mimetypes' => ':attribute harus berupa berkas berjenis: :values.',
'min' => [
'numeric' => ':attribute minimal bernilai :min.',
'file' => ':attribute minimal berukuran :min kilobita.',
'string' => ':attribute minimal berisi :min karakter.',
'array' => ':attribute minimal terdiri dari :min anggota.',
],
'not_in' => ':attribute yang dipilih tidak valid.',
'not_regex' => 'Format :attribute tidak valid.',
'numeric' => ':attribute harus berupa angka.',
'password' => 'Kata sandi salah.',
'present' => ':attribute wajib ada.',
'regex' => 'Format :attribute tidak valid.',
'required' => ':attribute wajib diisi.',
'required_if' => ':attribute wajib diisi bila :other adalah :value.',
'required_unless' => ':attribute wajib diisi kecuali :other memiliki nilai :values.',
'required_with' => ':attribute wajib diisi bila terdapat :values.',
'required_with_all' => ':attribute wajib diisi bila terdapat :values.',
'required_without' => ':attribute wajib diisi bila tidak terdapat :values.',
'required_without_all' => ':attribute wajib diisi bila sama sekali tidak terdapat :values.',
'same' => ':attribute dan :other harus sama.',
'size' => [
'numeric' => ':attribute harus berukuran :size.',
'file' => ':attribute harus berukuran :size kilobyte.',
'string' => ':attribute harus berukuran :size karakter.',
'array' => ':attribute harus mengandung :size anggota.',
],
'starts_with' => ':attribute harus diawali salah satu dari berikut: :values',
'string' => ':attribute harus berupa string.',
'timezone' => ':attribute harus berisi zona waktu yang valid.',
'unique' => ':attribute sudah ada sebelumnya.',
'uploaded' => ':attribute gagal diunggah.',
'url' => 'Format :attribute tidak valid.',
'uuid' => ':attribute harus merupakan UUID yang valid.',
/*
|---------------------------------------------------------------------------------------
| Baris Bahasa untuk Validasi Kustom
|---------------------------------------------------------------------------------------
|
| Di sini Anda dapat menentukan pesan validasi untuk atribut sesuai keinginan dengan menggunakan
| konvensi "attribute.rule" dalam penamaan barisnya. Hal ini mempercepat dalam menentukan
| baris bahasa kustom yang spesifik untuk aturan atribut yang diberikan.
|
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/*
|---------------------------------------------------------------------------------------
| Kustom Validasi Atribut
|---------------------------------------------------------------------------------------
|
| Baris bahasa berikut digunakan untuk menukar 'placeholder' atribut dengan sesuatu yang
| lebih mudah dimengerti oleh pembaca seperti "Alamat Surel" daripada "surel" saja.
| Hal ini membantu kita dalam membuat pesan menjadi lebih ekspresif.
|
*/
'attributes' => [],
];
BUAT PAGINATEDRESOURCE.PHP
php artisan make:resource PaginateResource
php artisan make:provider RepositoryServiceProvider
Edit App\Http\Resources\PaginateResource.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PaginateResource extends JsonResource
{
/**
* Create a new resource instance
*
* @param mixed $resource
* @return void
*/
public function __construct($resource, public $resourceClass = null)
{
parent::__construct($resource);
}
public function collect($resource)
{
return $this->resourceClass::collection($resource);
}
/**
* Transform the resource into an array
*
* @param Illuminate\Request $request
* @return array|\Illuminate|Contracts|Support|Arrayable|\JsonSerializable
*/
public function toArray($request): array
{
// return parent::toArray($request);
return [
'data' => $this->collect($this->items()),
'meta' => [
'current_page' => $this->currentPage(),
'from' => $this->firstItem(),
'last_page' => $this->lastPage(),
'path' => $this->path(),
'per_page' => $this->perPage(),
'to' => $this->lastItem(),
'total' => $this->total(),
],
];
}
}
PASANG LARAVEL SANCTUM
php artisan install:api
buat model PersonalAccessToken , untuk menimpa settingan model PersonalAccessToken yang default
php artisan make:model PersonalAccessToken
edit app/Models/PersonalAccessToken.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Support\Str;
use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken;
class PersonalAccessToken extends SanctumPersonalAccessToken
{
use HasFactory;
protected $primaryKey = 'uuid';
public $incrementing = false;
protected $keyType = 'string';
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
if (empty($model->uuid)) {
$model->uuid = (string) Str::uuid();
}
});
}
}
edit app/Providers/AppServiceProvider.php
<?php
namespace App\Providers;
use App\Models\PersonalAccessToken;
use Illuminate\Support\ServiceProvider;
use Laravel\Sanctum\Sanctum;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*/
public function register(): void
{
//
}
/**
* Bootstrap any application services.
*/
public function boot(): void
{
//
Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
}
}
edit database/migrations/2025_09_17_131012_create_personal_access_tokens_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('personal_access_tokens', function (Blueprint $table) {
$table->uuid()->primary(); // ubah menjadi uuid dan primary
$table->uuidMorphs('tokenable'); // ubah menjadi uuid
$table->text('name');
$table->string('token', 64)->unique();
$table->text('abilities')->nullable();
$table->timestamp('last_used_at')->nullable();
$table->timestamp('expires_at')->nullable()->index();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('personal_access_tokens');
}
};
PASANG SPATIE/LARAVEL-PERMISSION
composer require spatie/laravel-permission
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
edit bootstrap/providers.php
<?php
return [
App\Providers\AppServiceProvider::class,
Spatie\Permission\PermissionServiceProvider::class, // Tambahkan
];
edit bootstrap/app.php
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__ . '/../routes/web.php',
api: __DIR__ . '/../routes/api.php',
commands: __DIR__ . '/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
// Tambahkan ==================================
$middleware->alias([
'role' => \Spatie\Permission\Middleware\RoleMiddleware::class,
'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class,
'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class,
]);
// ===========================================
})
->withExceptions(function (Exceptions $exceptions): void {
//
})->create();
edit database/migrations/2025_09_04_105112_create_permission_tables.php , ganti semua supaya gampang ,jika menggunakan uuid ,jika tidak abaikan step ini
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
$teams = config('permission.teams');
$tableNames = config('permission.table_names');
$columnNames = config('permission.column_names');
$pivotRole = $columnNames['role_pivot_key'] ?? 'role_id';
$pivotPermission = $columnNames['permission_pivot_key'] ?? 'permission_id';
if (empty($tableNames)) {
throw new \Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');
}
if ($teams && empty($columnNames['team_foreign_key'] ?? null)) {
throw new \Exception('Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.');
}
Schema::create($tableNames['permissions'], function (Blueprint $table) {
$table->bigIncrements('id'); // permission id
$table->string('name'); // For MySQL 8.0 use string('name', 125);
$table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125);
$table->timestamps();
$table->unique(['name', 'guard_name']);
});
Schema::create($tableNames['roles'], function (Blueprint $table) use ($teams, $columnNames) {
$table->bigIncrements('id'); // role id
if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing
$table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable();
$table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');
}
$table->string('name'); // For MySQL 8.0 use string('name', 125);
$table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125);
$table->timestamps();
if ($teams || config('permission.testing')) {
$table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);
} else {
$table->unique(['name', 'guard_name']);
}
});
Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission, $teams) {
$table->unsignedBigInteger($pivotPermission);
$table->string('model_type');
$table->uuid($columnNames['model_morph_key']);
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index');
$table->foreign($pivotPermission)
->references('id') // permission id
->on($tableNames['permissions'])
->onDelete('cascade');
if ($teams) {
$table->unsignedBigInteger($columnNames['team_foreign_key']);
$table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');
$table->primary(
[$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'],
'model_has_permissions_permission_model_type_primary'
);
} else {
$table->primary(
[$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
'model_has_permissions_permission_model_type_primary'
);
}
});
Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole, $teams) {
$table->unsignedBigInteger($pivotRole);
$table->string('model_type');
$table->uuid($columnNames['model_morph_key']);
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index');
$table->foreign($pivotRole)
->references('id') // role id
->on($tableNames['roles'])
->onDelete('cascade');
if ($teams) {
$table->unsignedBigInteger($columnNames['team_foreign_key']);
$table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');
$table->primary(
[$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'],
'model_has_roles_role_model_type_primary'
);
} else {
$table->primary(
[$pivotRole, $columnNames['model_morph_key'], 'model_type'],
'model_has_roles_role_model_type_primary'
);
}
});
Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames, $pivotRole, $pivotPermission) {
$table->unsignedBigInteger($pivotPermission);
$table->unsignedBigInteger($pivotRole);
$table->foreign($pivotPermission)
->references('id') // permission id
->on($tableNames['permissions'])
->onDelete('cascade');
$table->foreign($pivotRole)
->references('id') // role id
->on($tableNames['roles'])
->onDelete('cascade');
$table->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary');
});
app('cache')
->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)
->forget(config('permission.cache.key'));
}
/**
* Reverse the migrations.
*/
public function down(): void
{
$tableNames = config('permission.table_names');
if (empty($tableNames)) {
throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.');
}
Schema::drop($tableNames['role_has_permissions']);
Schema::drop($tableNames['model_has_roles']);
Schema::drop($tableNames['model_has_permissions']);
Schema::drop($tableNames['roles']);
Schema::drop($tableNames['permissions']);
}
};
buat PermissionSeeder dan RoleSeeder
php artisan make:seeder PermissionSeeder
php artisan make:seeder RoleSeeder
Edit database/seeders/PermissionSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
class PermissionSeeder extends Seeder
{
private $permissions = [
'dashboard' => [
'menu'
],
'head-of-family' => [
'menu',
'list',
'create',
'edit',
'delete'
],
'family-member' => [
'menu',
'list',
'create',
'edit',
'delete'
],
'social-assistance' => [
'menu',
'list',
'create',
'edit',
'delete'
],
'social-assistance-recipient' => [
'menu',
'list',
'create',
'edit',
'delete'
],
'event' => [
'menu',
'list',
'create',
'edit',
'delete'
],
'event-participant' => [
'menu',
'list',
'create',
'edit',
'delete'
],
'development' => [
'menu',
'list',
'create',
'edit',
'delete'
],
'development-applicant' => [
'menu',
'list',
'create',
'edit',
'delete'
],
'profile' => [
'menu',
'create',
'edit',
],
];
/**
* Run the database seeds.
*/
public function run(): void
{
foreach ($this->permissions as $key => $value) {
foreach ($value as $permission) {
Permission::firstOrCreate([
'name' => $key . '-' . $permission,
'guard_name' => 'sanctum'
]);
}
}
}
}
Edit database/seeders/RoleSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
class RoleSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
Role::firstOrCreate([
'name' => 'admin',
'guard_name' => 'sanctum'
])->givePermissionTo(Permission::all());
Role::firstOrCreate([
'name' => 'head-of-family',
'guard_name' => 'sanctum'
])->givePermissionTo([
'dashboard-menu',
'family-member-menu',
'family-member-list',
'family-member-create',
'family-member-edit',
'family-member-delete',
'social-assistance-menu',
'social-assistance-list',
'social-assistance-recipient-menu',
'social-assistance-recipient-list',
'social-assistance-recipient-create',
'social-assistance-recipient-edit',
'social-assistance-recipient-delete',
'event-menu',
'event-list',
'event-participant-menu',
'event-participant-list',
'event-participant-create',
'event-participant-edit',
'event-participant-delete',
'development-menu',
'development-list',
'development-applicant-menu',
'development-applicant-list',
'development-applicant-create',
'development-applicant-edit',
'development-applicant-delete',
'profile-menu',
]);
}
}
BUAT AUTH
# force artinya jika folder atau filenya sudah tidak ada ,tidak akan error dan di overwrite
mkdir app/Interfaces -Force; New-Item -ItemType File app/Interfaces/AuthRepositoryInterface.php -Force
mkdir app/Repositories -Force; New-Item -ItemType File app/Repositories/AuthRepository.php -Force
php artisan make:controller AuthController
php artisan make:request LoginStoreRequest
Edit app/Interfaces/AuthRepositoryInterface.php
<?php
/**
* definisikan fungsi fungsi yang akan kita buat
*/
namespace App\Interfaces;
interface AuthRepositoryInterface
{
public function login(
array $data
);
public function logout();
public function me();
}
Edit app/Repositories/AuthRepository.php
<?php
namespace App\repositories;
use App\Http\Resources\HeadOfFamilyResource;
use App\Interfaces\AuthRepositoryInterface;
use App\Models\Event;
use Exception;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class AuthRepository implements AuthRepositoryInterface
{
public function login(array $data)
{
if (!Auth::guard('web')->attempt($data)) {
return response([
'success' => false,
'message' => 'Unauthorized',
], 401);
}
$user = Auth::user();
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json([
'success' => true,
'token' => $token,
'message' => "Login Success"
]);
}
public function logout()
{
$user = Auth::user();
$user->currentAccessToken()->delete();
$response = [
'success' => true,
'message' => "Logout Success"
];
return response($response, 200);
}
public function me()
{
if (Auth::check()) {
$user = Auth::user();
$user->load('roles.permissions');
$permissions = $user->roles->flatMap->permissions->pluck('name');
$role = $user->roles->first()->name;
return response()->json([
'message' => 'User data',
'data' => [
'id' => $user->id,
'name' => $user->name,
'email' => $user->email,
'permissions' => $permissions,
'role' => $role,
// 'head_of_family' => new HeadOfFamilyResource($user->headOfFamily)
// 'head_of_family' => $user->headOfFamily
// ? new HeadOfFamilyResource($user->headOfFamily)
// : null,
],
]);
}
return response()->json([
'message' => 'You are not logged in'
], 401);
}
}
Edit App\Providers\RepositoryServiceProvider.php
<?php
/**
* di gunakan untuk mendaftarkan Repositories dan interfaces
*/
namespace App\Providers;
use App\Interfaces\AuthRepositoryInterface;
use App\Repositories\AuthRepository;
use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
//
$this->app->bind(AuthRepositoryInterface::class, AuthRepository::class);
}
/**
* Bootstrap services.
*/
public function boot(): void
{
//
}
}
edit App\Http\Controllers\AuthController.php
<?php
namespace App\Http\Controllers;
use App\Http\Requests\LoginStoreRequest;
use App\Interfaces\AuthRepositoryInterface;
use Illuminate\Http\Request;
class AuthController extends Controller
{
private AuthRepositoryInterface $authRepository;
public function __construct(AuthRepositoryInterface $authRepository)
{
$this->authRepository = $authRepository;
}
public function login(LoginStoreRequest $request)
{
$request = $request->validated();
return $this->authRepository->login($request);
}
public function logout()
{
return $this->authRepository->logout();
}
public function me()
{
return $this->authRepository->me();
}
}
edit config/auth.php
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option defines the default authentication "guard" and password
| reset "broker" for your application. You may change these values
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => env('AUTH_GUARD', 'sanctum'), // edit dari web menjadi sanctum
'passwords' => env('AUTH_PASSWORD_BROKER', 'users'),
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| which utilizes session storage plus the Eloquent user provider.
|
| All authentication guards have a user provider, which defines how the
| users are actually retrieved out of your database or other storage
| system used by the application. Typically, Eloquent is utilized.
|
| Supported: "session"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
// Tambahan ======================
'api' => [
'driver' => 'sanctum',
'provider' => 'users',
'hash' => 'false',
],
'sanctum' => [
'driver' => 'sanctum',
'provider' => 'users',
'hash' => 'false',
],
// =================================
],
edit App\Http\Requests\LoginStoreRequest
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class LoginStoreRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'email' => 'required|email',
'password' => 'required'
];
}
public function attributes()
{
return [
'email' => 'Email',
'password' => 'Kata Sandi'
];
}
}
SETTINGS USER
# force artinya jika folder atau filenya sudah tidak ada ,tidak akan error dan di overwrite
# php artisan make:model User -m // user sudah punya model dan migration default
New-Item -ItemType File app/Interfaces/UserRepositoryInterface.php -Force
New-Item -ItemType File app/Repositories/UserRepository.php -Force
php artisan make:resource UserResource
php artisan make:controller UserController --api
php artisan make:request UserStoreRequest
php artisan make:request UserUpdateRequest
# php artisan make:factory UserFactory // user sudah punya factory default
php artisan make:seeder UserSeeder
Edit app/Models/User.php
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use App\Traits\UUID;
use Illuminate\Database\Eloquent\Factories\HasFactory;
// use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
/** @use HasFactory<\Database\Factories\UserFactory> */
// use HasFactory, Notifiable, UUID, SoftDeletes, HasRoles, HasApiTokens;
use HasFactory, Notifiable, UUID, HasRoles, HasApiTokens;
/**
* The attributes that are mass assignable.
*
* @var list<string>
*/
protected $fillable = ['name', 'email', 'password'];
/**
* The attributes that should be hidden for serialization.
*
* @var list<string>
*/
protected $hidden = ['password', 'remember_token'];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
public function scopeSearch($query, $search)
{
return $query->where('name', 'like', '%' . $search . '%')
->orWhere('email', 'like', '%' . $search . '%');
}
// satu user hanya memiliki 1 head family
// public function headOfFamily()
// {
// return $this->hasOne(HeadOfFamily::class);
// }
// public function seller()
// {
// return $this->hasOne(Seller::class);
// }
// public function owner()
// {
// return $this->hasOne(Owner::class);
// }
// public function customer()
// {
// return $this->hasOne(Customer::class);
// }
// public function investor()
// {
// return $this->hasOne(Investor::class);
// }
// public function expense()
// {
// return $this->hasOne(Expense::class);
// }
}
Edit Database\migrations\create_users_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
// $table->id();
$table->uuid('id')->primary();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
// $table->softDeletes();
$table->timestamps();
});
Schema::create('password_reset_tokens', function (Blueprint $table) {
$table->string('email')->primary();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
Schema::dropIfExists('password_reset_tokens');
Schema::dropIfExists('sessions');
}
};
Edit App\Interfaces\UserRepositoryInterface
<?php
/**
* definisikan fungsi fungsi yang akan kita buat
*/
namespace App\Interfaces;
interface UserRepositoryInterface
{
public function getAll(
?string $search, // ketika search langsung di terima di fungsi getall
?int $limit, // kita bisa melimit data yang ingin di tampilkan
bool $execute // akan exe langsung ,tapi di paginasi tidak
);
public function getAllPaginated(
?string $search, // ketika search langsung di terima di fungsi getall
?int $rowPerPage, // kita bisa melimit data yang ingin di tampilkan
);
public function getById(
string $id
);
public function create(
array $data
);
public function update(
string $id,
array $data
);
public function delete(
string $id
);
}
Edit App\repositories\UserRepository
<?php
namespace App\repositories;
use App\Interfaces\UserRepositoryInterface;
use App\Models\User;
use Exception;
use Illuminate\Support\Facades\DB;
class UserRepository implements UserRepositoryInterface
{
public function getAll(?string $search, ?int $limit, bool $execute)
{
$query = User::where(function ($query) use ($search) {
//jika ada parameter search dia akan melakukan search, yang kita definisikan pada model user
if ($search) {
$query->search($search);
}
});
if ($limit) {
// take adalah mengambil beberapa berdsarkan limit
$query->take($limit);
}
if ($execute) {
return $query->get();
}
return $query;
}
public function getAllPaginated(?string $search, ?int $rowPerPage)
{
$query = $this->getAll(
$search,
$rowPerPage,
false
);
return $query->paginate($rowPerPage);
}
public function getById(string $id)
{
$query = User::where('id', $id);
return $query->first();
}
public function create(array $data)
{
// jika ada data yang salah tidak akan langsung di dinput
DB::beginTransaction();
try {
$user = new User;
$user->name = $data['name'];
$user->email = $data['email'];
$user->password = bcrypt($data['password']);
$user->save();
DB::commit();
return $user;
} catch (\Exception $e) {
DB::rollBack();
throw new Exception($e->getMessage());
}
}
public function update(string $id, array $data)
{
// jika ada data yang salah tidak akan langsung di dinput
DB::beginTransaction();
try {
$user = User::find($id);
$user->name = $data['name'];
// $user->email = $data['email'];
if (isset($data['password'])) {
$user->password = bcrypt($data['password']);
}
$user->save();
DB::commit();
return $user;
} catch (\Exception $e) {
DB::rollBack();
throw new Exception($e->getMessage());
}
}
public function delete(string $id)
{
// jika ada data yang salah tidak akan langsung di dinput
DB::beginTransaction();
try {
$user = User::find($id);
$user->delete();
DB::commit();
return $user;
} catch (\Exception $e) {
DB::rollBack();
throw new Exception($e->getMessage());
}
}
}
Edit App\Providers\RepositoryServiceProvider.php
<?php
/**
* di gunakan untuk mendaftarkan Repositories dan interfaces
*/
namespace App\Providers;
use App\Interfaces\AuthRepositoryInterface;
use App\Interfaces\UserRepositoryInterface;
use App\Repositories\AuthRepository;
use App\Repositories\UserRepository;
use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
//
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
$this->app->bind(AuthRepositoryInterface::class, AuthRepository::class);
}
/**
* Bootstrap services.
*/
public function boot(): void
{
//
}
}
Edit App\Http\Resources\UserResource.php
<?php
/**
* menampilkan apa saja yang kita ingin kana agar json rapih
*/
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
// return parent::toArray($request);
return [
'id' => $this->id,
'name' => $this->name,
'email' => $this->email
];
}
}
Edit App\Http\Controllers\UserController.php
<?php
namespace App\Http\Controllers;
use App\Helpers\ResponseHelper;
use App\Http\Requests\UserStoreRequest;
use App\Http\Requests\UserUpdateRequest;
use App\Http\Resources\PaginateResource;
use App\Http\Resources\UserResource;
use App\Interfaces\UserRepositoryInterface;
use Illuminate\Http\Request;
class UserController extends Controller
{
private UserRepositoryInterface $userRepository;
public function __construct(UserRepositoryInterface $userRepository)
{
$this->userRepository = $userRepository;
}
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
//
try {
$users = $this->userRepository->getAll($request->search, $request->limit, true);
return ResponseHelper::jsonResponse(true, 'Data User Berhasil Diambil', UserResource::collection($users), 200);
} catch (\Exception $e) {
return ResponseHelper::jsonResponse(false, $e->getMessage(), null, 500);
}
}
public function getAllPaginated(Request $request)
{
$request = $request->validate([
'search' => 'nullable|string',
'row_per_page' => 'required|integer'
]);
try {
$users = $this->userRepository->getAllPaginated(
$request['search'] ?? null,
$request['row_per_page'],
);
return ResponseHelper::jsonResponse(true, 'Data User Berhasil Diambil', PaginateResource::make($users, UserResource::class), 200);
} catch (\Exception $e) {
return ResponseHelper::jsonResponse(false, $e->getMessage(), null, 500);
}
}
/**
* Store a newly created resource in storage.
*/
public function store(UserStoreRequest $request)
{
$request = $request->validated();
try {
$user = $this->userRepository->create($request);
return ResponseHelper::jsonResponse(true, 'User berhasil ditambahkan', new UserResource($user), 201);
} catch (\Exception $e) {
return ResponseHelper::jsonResponse(false, $e->getMessage(), null, 500);
}
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
try {
$user = $this->userRepository->getById($id);
if (!$user) {
return ResponseHelper::jsonResponse(false, 'User tidak ditemukan', null, 404);
}
return ResponseHelper::jsonResponse(true, 'Detail user berhasil di ambil', new UserResource($user), 200);
} catch (\Exception $e) {
return ResponseHelper::jsonResponse(false, $e->getMessage(), null, 500);
}
}
/**
* Update the specified resource in storage.
*/
public function update(UserUpdateRequest $request, string $id)
{
$request = $request->validated();
try {
$user = $this->userRepository->getById($id);
if (!$user) {
return ResponseHelper::jsonResponse(false, 'User tidak ditemukan', null, 404);
}
$user = $this->userRepository->update(
$id,
$request
);
return ResponseHelper::jsonResponse(true, 'Data user berhasil diupdate', new UserResource($user), 200);
} catch (\Exception $e) {
return ResponseHelper::jsonResponse(false, $e->getMessage(), null, 500);
}
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
try {
$user = $this->userRepository->getById($id);
if (!$user) {
return ResponseHelper::jsonResponse(false, 'User tidak ditemukan', null, 404);
}
$user = $this->userRepository->delete(
$id,
);
return ResponseHelper::jsonResponse(true, 'Data user berhasil dihapus', new UserResource($user), 200);
} catch (\Exception $e) {
return ResponseHelper::jsonResponse(false, $e->getMessage(), null, 500);
}
}
}
Edit App\Http\Requests\UserStoreRequest.php
<?php
/**
* untuk validasi
*/
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UserStoreRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:8'
];
}
public function attributes()
{
return [
'name' => 'Nama',
'email' => 'Email',
'password' => 'Kata Sandi'
];
}
// public function messages()
// {
// return [
// 'required' => ':attribute harus diisi',
// 'string' => ':attribute harus berupa string',
// 'max' => ':attribute maksimal :max karakter',
// 'min' => ':attribute minimal :min karakter',
// 'unique' => ':attribute sudah ada',
// 'email' => ':attribute harus berupa email',
// ];
// }
}
Edit App\Http\Requests\UserUpdateRequest.php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UserUpdateRequest extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => 'required|string|max:255',
'password' => 'nullable|string|min:8'
];
}
public function attributes()
{
return [
'name' => 'Nama',
'password' => 'Password'
];
}
// public function messages()
// {
// return [
// 'required' => ':attribute harus diisi',
// 'string' => ':attribute harus berupa string',
// 'max' => ':attribute maksimal :max karakter',
// 'min' => ':attribute minimal :min karakter',
// ];
// }
}
Edit Database\Seeders\UserSeeder.php
<?php
namespace Database\Seeders;
use App\Models\User;
use Database\Factories\UserFactory;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class UserSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
//
User::create([
'name' => 'Admin',
'email' => 'admin@app.com',
'password' => bcrypt('password')
])->assignRole('admin');
User::create([
'name' => 'Kepala Keluarga',
'email' => 'headoffamily@app.com',
'password' => bcrypt('password')
])->assignRole('head-of-family');
UserFactory::new()->count(15)->create();
}
}
edit Database\Seeders\DatabaseSeeder.php
<?php
namespace Database\Seeders;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
// User::factory(10)->create();
// User::factory()->create([
// 'name' => 'Test User',
// 'email' => 'test@example.com',
// ]);
$this->call([
PermissionSeeder::class,
RoleSeeder::class,
UserSeeder::class,
// HeadOfFamilySeeder::class,
// OwnerSeeder::class,
// ServerSeeder::class,
// ExpenseSeeder::class,
]);
}
}
edit routes/api.php
<?php
use App\Http\Controllers\AuthController;
use App\Http\Controllers\UserController;
use Illuminate\Support\Facades\Route;
//===========================================================
// routes/web.php, api.php or any other central route files you have
Route::middleware('auth:sanctum')->group(function () {
Route::apiResource('user', UserController::class);
Route::get('user/all/paginated', [UserController::class, 'getAllPaginated']);
});
Route::post('/login', [AuthController::class, 'login'])->name('login');
Route::post('/register', [AuthController::class, 'register'])->name('register');
Route::middleware('auth:sanctum')->post('/logout', [AuthController::class, 'logout'])->name('logout');
Route::middleware('auth:sanctum')->get('/me', [AuthController::class, 'me'])->name('me');
migrate ulang
php artisan migrate:fresh --seed
php artisan serve