Browse Source

feat: :sparkles: crud de enderecos dentro de client e provider

crud de enderecos dentro de client e provider
Gustavo Zanatta 1 month ago
parent
commit
06bd8e6471

+ 67 - 0
app/Http/Controllers/AddressController.php

@@ -0,0 +1,67 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use App\Http\Requests\AddressRequest;
+use App\Http\Resources\AddressResource;
+use App\Services\AddressService;
+use Illuminate\Http\JsonResponse;
+use Illuminate\Support\Facades\Log;
+
+class AddressController extends Controller
+{
+    protected AddressService $addressService;
+
+    public function __construct(AddressService $addressService)
+    {
+        $this->addressService = $addressService;
+    }
+    
+    public function showBySource(AddressRequest $request): JsonResponse
+    {
+        $address = $this->addressService->findBySource($request->validated());
+
+
+        return $this->successResponse(
+            payload: AddressResource::collection($address),
+        );
+    }
+
+    public function show($id): JsonResponse
+    {
+        $address = $this->addressService->one($id);
+        return $this->successResponse(
+            payload: new AddressResource($address),
+        );
+    }
+
+    public function store(AddressRequest $request): JsonResponse
+    {
+        $address = $this->addressService->create($request->validated());
+        return $this->successResponse(
+            payload: new AddressResource($address),
+            message: __("messages.created"),
+            code: 201,
+        );
+    }
+
+    public function update(AddressRequest $request, string $id): JsonResponse
+    {
+        $address = $this->addressService->update($request->validated(), $id);
+        Log::info($address);
+        return $this->successResponse(
+            payload: new AddressResource($address),
+            message: __("messages.updated"),
+        );
+    }
+
+    public function destroy(string $id): JsonResponse
+    {
+        $this->addressService->delete($id);
+
+        return $this->successResponse(
+            message: __("messages.deleted"),
+            code: 204,
+        );
+    }
+}

+ 13 - 0
app/Http/Controllers/CityController.php

@@ -6,6 +6,7 @@ use App\Services\CityService;
 use App\Http\Requests\CityRequest;
 use App\Http\Resources\CityResource;
 use Illuminate\Http\JsonResponse;
+use Illuminate\Http\Request;
 
 class CityController extends Controller
 {
@@ -52,4 +53,16 @@ class CityController extends Controller
             code: 204,
         );
     }
+
+    public function searchStateCityByDescription(Request $request): JsonResponse
+    {
+        $state = $request->query('stateUf');
+        $city = $request->query('cityName');
+
+        $result = $this->service->findStateCityByDescription($state, $city);
+
+        return $this->successResponse(
+            payload: $result,
+        );
+    }
 }

+ 56 - 0
app/Http/Requests/AddressRequest.php

@@ -0,0 +1,56 @@
+<?php
+
+namespace App\Http\Requests;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class AddressRequest extends FormRequest
+{
+    public function rules(): array
+    {
+        $rules = [
+            'source' => 'sometimes|string|in:provider,client',
+            'source_id' => 'sometimes|integer|min:1',
+            'zip_code' => [
+                'sometimes',
+                'string',
+                'regex:/^[0-9]{8}$/',
+            ],
+            'address' => 'sometimes|string',
+            'has_complement' => 'sometimes|boolean',
+            'complement' => 'nullable|string|max:255',
+            'nickname' => 'nullable|string|max:255',
+            'instructions' => 'nullable|string',
+            'city_id' => 'nullable|integer|exists:cities,id',
+            'state_id' => 'nullable|integer|exists:states,id',
+            'address_type' => 'sometimes|in:home,commercial,other',
+        ];
+
+        if ($this->isMethod('post')) {
+            $rules['source'] = 'required|string|in:provider,client';
+            $rules['source_id'] = [
+                'required',
+                'integer',
+                'min:1',
+            ];
+            $rules['zip_code'] = [
+                'required',
+                'string',
+                'regex:/^[0-9]{8}$/',
+            ];
+            $rules['address'] = 'required|string';
+            $rules['address_type'] = 'required|in:home,commercial,other';
+        }
+
+        return $rules;
+    }
+
+    public function messages(): array
+    {
+        return [
+            'zip_code.regex' => __('validation.custom.zip_code.invalid'),
+            'source.in' => __('validation.custom.source.invalid'),
+            'address_type.in' => __('validation.custom.address_type.invalid'),
+        ];
+    }
+}

+ 37 - 0
app/Http/Resources/AddressResource.php

@@ -0,0 +1,37 @@
+<?php
+
+namespace App\Http\Resources;
+
+use Illuminate\Http\Request;
+use Illuminate\Http\Resources\Json\JsonResource;
+
+class AddressResource extends JsonResource
+{
+    /**
+     * Transform the resource into an array.
+     *
+     * @return array<string, mixed>
+     */
+    public function toArray(Request $request): array
+    {
+        return [
+            'id' => $this->id,
+            'source' => $this->source,
+            'source_id' => $this->source_id,
+            'zip_code' => $this->zip_code,
+            'address' => $this->address,
+            'has_complement' => $this->has_complement,
+            'complement' => $this->complement,
+            'nickname' => $this->nickname,
+            'instructions' => $this->instructions,
+            'city_id' => $this->city_id,
+            'state_id' => $this->state_id,
+            'city' => $this->whenLoaded('city'),
+            'state' => $this->whenLoaded('state'),
+            'address_type' => $this->address_type,
+            'created_at' => $this->created_at,
+            'updated_at' => $this->updated_at,
+            'deleted_at' => $this->deleted_at,
+        ];
+    }
+}

+ 43 - 0
app/Models/Address.php

@@ -0,0 +1,43 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\SoftDeletes;
+
+class Address extends Model
+{
+    use HasFactory, SoftDeletes;
+
+    protected $fillable = [
+        'source',
+        'source_id',
+        'zip_code',
+        'address',
+        'has_complement',
+        'complement',
+        'nickname',
+        'instructions',
+        'city_id',
+        'state_id',
+        'address_type',
+    ];
+
+    protected $casts = [
+        'has_complement' => 'boolean',
+        'created_at' => 'datetime',
+        'updated_at' => 'datetime',
+        'deleted_at' => 'datetime',
+    ];
+
+    public function city()
+    {
+        return $this->belongsTo(City::class);
+    }
+
+    public function state()
+    {
+        return $this->belongsTo(State::class);
+    }
+}

+ 45 - 0
app/Services/AddressService.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace App\Services;
+
+use App\Http\Resources\AddressResource;
+use App\Models\Address;
+use Illuminate\Database\Eloquent\Collection;
+
+class AddressService
+{
+    public function findBySource(array $data): ?Collection
+    {
+        $allAddresses = Address::where('source', $data['source'])
+            ->where('source_id', $data['source_id'])
+            ->with(['city', 'state'])
+            ->get();
+
+        return $allAddresses;
+    }
+
+    public function one($id): Address
+    {
+        return Address::find($id);
+    }
+
+
+    public function create(array $data): Address
+    {
+        return Address::create($data);
+    }
+
+    public function update(array $data, int $id): Address
+    {
+        $address = Address::findOrFail($id);
+        $address->fill($data);
+        $address->save();
+        return $address->fresh();
+    }
+
+    public function delete(int $id): bool
+    {
+        $address = Address::findOrFail($id);
+        return $address->delete();
+    }
+}

+ 27 - 2
app/Services/CityService.php

@@ -3,13 +3,14 @@
 namespace App\Services;
 
 use App\Models\City;
+use App\Models\State;
 use Illuminate\Database\Eloquent\Collection;
 
 class CityService
 {
     public function getAll(): Collection
     {
-        return City::query()->orderBy("created_at", "desc")->get();
+        return City::query()->orderBy("name", "asc")->get();
     }
 
     public function findById(int $id): ?City
@@ -45,5 +46,29 @@ class CityService
         return $model->delete();
     }
 
-    // Add custom business logic methods here
+    public function findStateCityByDescription(string $state, string $city): ?array
+    {
+        $state = State::query()
+            ->where('code', $state)
+            ->select(
+                'name as label',
+                'id as value',
+            )
+            ->first();
+
+        $city = City::query()
+            ->where('state_id', $state->value)
+            ->where('name', $city)
+            ->select(
+                'name as label',
+                'id as value',
+                'state_id',
+            )
+            ->first();
+
+        return [
+            'state' => $state,
+            'city' => $city
+        ];
+    }
 }

+ 1 - 1
app/Services/StateService.php

@@ -9,7 +9,7 @@ class StateService
 {
     public function getAll(): Collection
     {
-        return State::query()->orderBy("created_at", "desc")->get();
+        return State::query()->orderBy("name", "asc")->get();
     }
 
     public function findById(int $id): ?State

+ 39 - 0
database/migrations/2026_02_05_000003_create_addresses_table.php

@@ -0,0 +1,39 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    public function up(): void
+    {
+        Schema::create('addresses', function (Blueprint $table) {
+            $table->id();
+            $table->string('source');
+            $table->unsignedBigInteger('source_id');
+            $table->string('zip_code');
+            $table->text('address');
+            $table->boolean('has_complement')->default(false);
+            $table->string('complement')->nullable();
+            $table->string('nickname')->nullable();
+            $table->text('instructions')->nullable();
+            $table->unsignedBigInteger('city_id')->nullable();
+            $table->foreign('city_id')->references('id')->on('cities')->onDelete('set null');
+            $table->unsignedBigInteger('state_id')->nullable();
+            $table->foreign('state_id')->references('id')->on('states')->onDelete('set null');
+            $table->enum('address_type', ['home', 'commercial', 'other']);
+            $table->timestamps();
+            $table->softDeletes();
+
+            // Indexes
+            $table->index(['source', 'source_id']);
+            $table->index('zip_code');
+        });
+    }
+
+    public function down(): void
+    {
+        Schema::dropIfExists('addresses');
+    }
+};

+ 6 - 0
database/seeders/PermissionSeeder.php

@@ -52,6 +52,12 @@ class PermissionSeeder extends Seeder
                         "bits" => 271,
                         "children" => [],
                     ],
+                    [
+                        "scope" => "config.address",
+                        "description" => "Configurações de Endereços",
+                        "bits" => 271,
+                        "children" => [],
+                    ],
                     [
                         "scope" => "config.city",
                         "description" => "Configurações de Cidades",

+ 1 - 0
database/seeders/UserTypePermissionSeeder.php

@@ -31,6 +31,7 @@ class UserTypePermissionSeeder extends Seeder
                     $userPermissions = [
                         ['scope' => 'dashboard', 'bits' => 1],
                         ['scope' => 'config.user', 'bits' => 5],
+                        ['scope' => 'config.address', 'bits' => 271],
                         ['scope' => 'config.city', 'bits' => 1],
                         ['scope' => 'config.client', 'bits' => 271],
                         ['scope' => 'config.country', 'bits' => 1],

+ 12 - 0
lang/en/validation.php

@@ -187,6 +187,18 @@ return [
             'already_linked_to_client' => 'This user is already linked to a client.',
             'already_linked_to_provider' => 'This user is already linked to a provider.',
         ],
+        'zip_code' => [
+            'invalid' => 'The zip code provided is invalid. It must contain 8 digits.',
+        ],
+        'source' => [
+            'invalid' => 'The source type is invalid. It must be Provider or Client.',
+        ],
+        'source_id' => [
+            'not_found' => 'The source record was not found.',
+        ],
+        'address_type' => [
+            'invalid' => 'The address type is invalid.',
+        ],
     ],
 
     /*

+ 12 - 0
lang/es/validation.php

@@ -187,6 +187,18 @@ return [
             'already_linked_to_client' => 'Este usuario ya está vinculado a un cliente.',
             'already_linked_to_provider' => 'Este usuario ya está vinculado a un proveedor.',
         ],
+        'zip_code' => [
+            'invalid' => 'El código postal proporcionado no es válido. Debe contener 8 dígitos.',
+        ],
+        'source' => [
+            'invalid' => 'El tipo de origen no es válido. Debe ser Provider o Client.',
+        ],
+        'source_id' => [
+            'not_found' => 'El registro de origen no fue encontrado.',
+        ],
+        'address_type' => [
+            'invalid' => 'El tipo de dirección no es válido.',
+        ],
     ],
 
     /*

+ 12 - 0
lang/pt/validation.php

@@ -188,6 +188,18 @@ return [
             'already_linked_to_client' => 'Este usuário já está vinculado a um cliente.',
             'already_linked_to_provider' => 'Este usuário já está vinculado a um prestador.',
         ],
+        'zip_code' => [
+            'invalid' => 'O CEP informado é inválido. Deve conter 8 dígitos.',
+        ],
+        'source' => [
+            'invalid' => 'O tipo de origem é inválido. Deve ser Provider ou Client.',
+        ],
+        'source_id' => [
+            'not_found' => 'O registro de origem não foi encontrado.',
+        ],
+        'address_type' => [
+            'invalid' => 'O tipo de endereço é inválido.',
+        ],
     ],
 
     /*

+ 10 - 0
routes/authRoutes/address.php

@@ -0,0 +1,10 @@
+<?php
+
+use App\Http\Controllers\AddressController;
+use Illuminate\Support\Facades\Route;
+
+Route::get('/addresses', [AddressController::class, 'showBySource'])->middleware('permission:config.address,view');
+Route::get('/addresses/{id}', [AddressController::class, 'show'])->middleware('permission:config.address,view');
+Route::post('/addresses', [AddressController::class, 'store'])->middleware('permission:config.address,add');
+Route::put('/addresses/{id}', [AddressController::class, 'update'])->middleware('permission:config.address,edit');
+Route::delete('/addresses/{id}', [AddressController::class, 'destroy'])->middleware('permission:config.address,delete');

+ 2 - 0
routes/authRoutes/city.php

@@ -16,3 +16,5 @@ Route::delete('/city/{id}', [CityController::class, 'destroy'])->middleware('per
 Route::get('/city-state/{id}', [CityController::class, 'allByStateId'])->middleware('permission:config.city,view');
 
 Route::get('/city-country/{id}', [CityController::class, 'allByCountryId'])->middleware('permission:config.city,view');
+
+Route::get('/busca-estado-cidade-por-descricao', [CityController::class, 'searchStateCityByDescription'])->middleware('permission:config.city,view');