Use resources for API responses

This commit is contained in:
Matteo Gheza 2024-04-11 00:39:06 +02:00
parent 94c58daae3
commit aea4cf5ab2
No known key found for this signature in database
GPG Key ID: A7019AD593CEF319
20 changed files with 430 additions and 47 deletions

View File

@ -7,6 +7,7 @@ use Illuminate\Support\Str;
use Illuminate\Validation\Rules\File;
use App\Models\Document;
use App\Models\DocumentFile;
use App\Models\User;
use App\Utils\Logger;
class DocumentsController extends Controller
@ -36,7 +37,7 @@ class DocumentsController extends Controller
$document->uploadedBy()->associate(auth()->user());
$document->save();
Logger::log("Caricamento scansione patente", auth()->user()->id);
Logger::log("Caricamento scansione patente", auth()->user());
return response()->json([
"uuid" => $document->uuid
@ -94,7 +95,8 @@ class DocumentsController extends Controller
$document->date = $request->input('date');
$document->save();
Logger::log("Aggiunta corso di formazione", $request->input('user'));
$user = User::findOrFail($request->input('user'));
Logger::log("Aggiunta corso di formazione", $user);
return response()->json([
"id" => $document->id
@ -132,7 +134,7 @@ class DocumentsController extends Controller
$document = new Document();
$document->type = 'medical_examination';
$document->doc_certifier = $request->input('doctor');
$document->doc_certifier = $request->input('certifier');
$document->user = $request->input('user');
$document->added_by = auth()->user()->id;
if($request->hasFile('file')) {
@ -152,7 +154,8 @@ class DocumentsController extends Controller
$document->expiration_date = $request->input('expiration_date');
$document->save();
Logger::log("Aggiunta visita medica", $request->input('user'));
$user = User::findOrFail($request->input('user'));
Logger::log("Aggiunta visita medica", $user);
return response()->json([
"id" => $document->id

View File

@ -2,6 +2,8 @@
namespace App\Http\Controllers;
use App\Http\Resources\ServiceResource;
use App\Http\Resources\ServicesListResource;
use App\Models\Place;
use App\Models\PlaceMunicipality;
use App\Models\PlaceProvince;
@ -63,7 +65,7 @@ class ServiceController extends Controller
$p = $service->place;
unset($p->lat, $p->lon, $p->place_id, $p->osm_id, $p->osm_type, $p->licence, $p->addresstype, $p->country, $p->country_code, $p->display_name, $p->road, $p->house_number, $p->postcode, $p->state, $p->suburb, $p->city, $p->municipality_id);
}
return response()->json($result);
return ServicesListResource::collection($result);
}
/**
@ -74,7 +76,7 @@ class ServiceController extends Controller
if (!$request->user()->hasPermission("services-read")) abort(401);
User::where('id', $request->user()->id)->update(['last_access' => now()]);
return response()->json(
return ServiceResource::make(
Service::join('users', 'users.id', '=', 'chief_id')
->join('services_types', 'services_types.id', '=', 'type_id')
->select('services.*', DBTricks::nameSelect("chief", "users"), 'services_types.name as type')
@ -124,15 +126,15 @@ class ServiceController extends Controller
if ($is_map_picker) {
//Find Place by lat lon
$place = Place::where('lat', $request->place->lat)->where('lon', $request->place->lon)->first();
$place = Place::where('lat', $request->place["lat"])->where('lon', $request->place["lon"])->first();
if (!$place) {
$place = new Place();
$place->lat = $request->place->lat;
$place->lon = $request->place->lon;
$place->lat = $request->place["lat"];
$place->lon = $request->place["lon"];
$response = Http::withUrlParameters([
'lat' => $request->place->lat,
'lon' => $request->place->lon,
'lat' => $request->place["lat"],
'lon' => $request->place["lon"],
])->get('https://nominatim.openstreetmap.org/reverse?format=json&lat={lat}&lon={lon}');
if (!$response->ok()) abort(500);
@ -252,6 +254,8 @@ class ServiceController extends Controller
User::whereIn('id', $usersToIncrement)->increment('services');
Logger::log($adding ? "Intervento aggiunto" : "Intervento modificato");
return response()->noContent();
}
/**
@ -265,5 +269,7 @@ class ServiceController extends Controller
User::whereIn('id', $usersToDecrement)->decrement('services');
$service->delete();
Logger::log("Intervento eliminato");
return response()->noContent();
}
}

View File

@ -10,12 +10,20 @@ use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\URL;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use App\Http\Resources\UsersListResource;
use App\Http\Resources\UserResource;
class UserController extends Controller
{
/**
* Return the list of users, used in main list.
* Return users list.
*
* Used in main list
*/
public function index(Request $request)
public function index(Request $request): AnonymousResourceCollection
{
$requestedCols = ['id', 'chief', 'last_access', 'name', 'surname', 'available', 'driver', 'services', 'availability_minutes'];
if($request->user()->isAbleTo("users-read")) $requestedCols[] = "phone_number";
@ -42,13 +50,13 @@ class UserController extends Controller
unset($user->last_access);
}
return response()->json($list);
return UsersListResource::collection($list);
}
/**
* Return a single user with all the details.
*/
public function show(Request $request, User $user)
public function show(Request $request, User $user): UserResource
{
if($request->user()->id != $user->id && !$request->user()->hasPermission("users-read")) abort(401);
@ -74,12 +82,11 @@ class UserController extends Controller
if($tc_tmp->count() > 0) {
$user->training_courses = $tc_tmp;
foreach($user->training_courses as $tc) {
if(!is_null($tc->doc_uuid)) {
$tc->doc_url = URL::temporarySignedRoute(
$tc->doc_url = !is_null($tc->doc_uuid) ?
URL::temporarySignedRoute(
'training_course_serve', now()->addHours(1), ['uuid' => $tc->doc_uuid]
);
unset($tc->doc_uuid);
}
) : null;
unset($tc->doc_uuid);
}
} else {
$user->training_courses = [];
@ -94,12 +101,11 @@ class UserController extends Controller
if($me_tmp->count() > 0) {
$user->medical_examinations = $me_tmp;
foreach($user->medical_examinations as $me) {
if(!is_null($me->cert_uuid)) {
$me->cert_url = URL::temporarySignedRoute(
$me->cert_url = !is_null($me->cert_uuid) ?
URL::temporarySignedRoute(
'medical_examination_serve', now()->addHours(1), ['uuid' => $me->cert_uuid]
);
unset($me->cert_uuid);
}
) : null;
unset($me->cert_uuid);
}
} else {
$user->medical_examinations = [];
@ -111,7 +117,7 @@ class UserController extends Controller
);
}
return response()->json($user);
return UserResource::make($user);
}
/**
@ -224,7 +230,7 @@ class UserController extends Controller
Logger::log("Modifica profilo utente", $user);
return response()->json($user);
return response()->noContent();
}
/**
@ -244,6 +250,6 @@ class UserController extends Controller
Logger::log("Modifica password utente", $user);
return response()->json($user);
return response()->noContent();
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class DrivingLicenseResource extends JsonResource
{
/**
* Transform the resource into an array.
*/
public function toArray(Request $request): array
{
return [
'doc_type' => $this->doc_type,
'doc_number' => $this->doc_number,
'expiration_date' => $this->expiration_date,
'scan_uuid' => $this->scan_uuid,
'scan_url' => $this->scan_url,
];
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class MedicalExaminationsListResource extends JsonResource
{
/**
* Transform the resource into an array.
*/
public function toArray(Request $request): array
{
return [
"certifier" => $this->certifier,
"date" => $this->date,
"expiration_date" => $this->expiration_date,
/** @var string|null */
"cert_url" => $this->cert_url,
];
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class MunicipalityResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* Not used for responses, but for the API documentation. (see PlaceResource.php)
*/
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'code' => $this->code,
'name' => $this->name,
'postal_code' => $this->postal_code,
'province' => [
'id' => $this->province->id,
'code' => $this->province->code,
'name' => $this->province->name,
'short_name' => $this->province->short_name,
'region' => $this->province->region,
],
];
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class PlaceCompleteResource extends JsonResource
{
/**
* Transform the resource into an array.
*/
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name !== "" ? $this->name : $this["display_name"],
'village' => $this->village,
/** @var string */
'created_at' => $this->created_at,
/** @var string */
'updated_at' => $this->updated_at,
/** @var MunicipalityResource|null */
'municipality' => $this->municipality,
// OSM data, if selected using map selector
/** @var number|null */
'lat' => $this->lat,
/** @var number|null */
'lon' => $this->lon,
/** @var number */
'place_id' => $this->place_id,
/** @var number|null */
'osm_id' => $this->osm_id,
/** @var string|null */
'osm_type' => $this->osm_type,
/** @var string|null */
'licence' => $this->licence,
/** @var string|null */
'addresstype' => $this->addresstype,
/** @var string|null */
'country' => $this->country,
/** @var string|null */
'country_code' => $this->country_code,
/** @var string|null */
'road' => $this->road,
/** @var string|null */
'house_number' => $this->house_number,
/** @var string|null */
'postcode' => $this->postcode,
/** @var string|null */
'state' => $this->state,
/** @var string|null */
'suburb' => $this->suburb,
/** @var string|null */
'city' => $this->city,
];
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class PlaceResource extends JsonResource
{
/**
* Transform the resource into an array.
*/
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'village' => $this->village,
/** @var string */
'created_at' => $this->created_at,
/** @var string */
'updated_at' => $this->updated_at,
/** @var MunicipalityResource|null */
'municipality' => $this->municipality,
];
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class ServiceOrTrainingUsersListResource extends JsonResource
{
/**
* Transform the resource into an array.
*/
public function toArray(Request $request): array
{
return [
"name" => $this->name,
"surname" => $this->surname,
"pivot" => [
"user_id" => $this->pivot->user_id
],
];
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class ServiceResource extends JsonResource
{
/**
* Transform the resource into an array.
*/
public function toArray(Request $request): array
{
return [
"id" => $this->id,
"code" => $this->code,
"notes" => $this->notes,
"start" => $this->start,
"end" => $this->end,
"added_by_id" => $this->added_by_id,
"updated_by_id" => $this->updated_by_id,
"created_at" => $this->created_at,
"updated_at" => $this->updated_at,
"chief" => $this->chief,
"type" => $this->type,
"drivers" => ServiceOrTrainingUsersListResource::collection($this->drivers),
"crew" => ServiceOrTrainingUsersListResource::collection($this->crew),
"place" => PlaceCompleteResource::make($this->place),
];
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class ServicesListResource extends JsonResource
{
/**
* Transform the resource into an array.
*/
public function toArray(Request $request): array
{
return [
"id" => $this->id,
"code" => $this->code,
"notes" => $this->notes,
"start" => $this->start,
"end" => $this->end,
"added_by_id" => $this->added_by_id,
"updated_by_id" => $this->updated_by_id,
"created_at" => $this->created_at,
"updated_at" => $this->updated_at,
"chief" => $this->chief,
"type" => $this->type,
"drivers" => ServiceOrTrainingUsersListResource::collection($this->drivers),
"crew" => ServiceOrTrainingUsersListResource::collection($this->crew),
"place" => PlaceResource::make($this->place),
];
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class TrainingCoursesListResource extends JsonResource
{
/**
* Transform the resource into an array.
*/
public function toArray(Request $request): array
{
return [
"doc_number" => $this->doc_number,
"date" => $this->date,
"type" => $this->type,
/** @var string|null */
"doc_url" => $this->doc_url,
];
}
}

View File

@ -0,0 +1,55 @@
<?php
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 [
"id" => $this->id,
"name" => $this->name,
"surname" => $this->surname,
"birthplace" => $this->birthplace,
"birthplace_province" => $this->birthplace_province,
"ssn" => $this->ssn,
"address" => $this->address,
"address_zip_code" => $this->address_zip_code,
"suit_size" => $this->suit_size,
"boot_size" => $this->boot_size,
"email" => $this->email,
"email_verified_at" => $this->email_verified_at,
/** @var string|null */
"created_at" => $this->created_at,
/** @var string|null */
"updated_at" => $this->updated_at,
"username" => $this->username,
"phone_number" => $this->phone_number,
"available" => $this->available,
"availability_manual_mode" => $this->availability_manual_mode,
"last_availability_change" => $this->last_availability_change,
"birthday" => $this->birthday,
"course_date" => $this->course_date,
"availability_minutes" => $this->availability_minutes,
"chief" => $this->chief,
"driver" => $this->driver,
"services" => $this->services,
"trainings" => $this->trainings,
"last_access" => $this->last_access,
"banned" => $this->banned,
"hidden" => $this->hidden,
/** @var DrivingLicenseResource|null */
"driving_license" => is_null($this->driving_license) ? null : DrivingLicenseResource::make($this->driving_license),
"training_courses" => TrainingCoursesListResource::collection($this->training_courses),
"medical_examinations" => MedicalExaminationsListResource::collection($this->medical_examinations),
];
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class UsersListResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return [
"id" => $this->id,
"chief" => $this->chief,
"name" => $this->name,
"surname" => $this->surname,
"available" => $this->available,
"driver" => $this->driver,
"services" => $this->services,
"availability_minutes" => $this->availability_minutes,
"phone_number" => $this->phone_number,
"online" => false
];
}
}

View File

@ -10,7 +10,7 @@ class Helpers {
if($option) {
// Cast to correct type
if($option->type == "boolean") {
return $option->value == "true";
return ($option->value == "1" || $option->value == "true");
} else if($option->type == "number") {
return floatval($option->value);
} else {

View File

@ -8,7 +8,7 @@
"php": "^8.1",
"dedoc/scramble": "^0.8.5",
"defstudio/telegraph": "^1.38",
"doctrine/dbal": "3.8.2",
"doctrine/dbal": "*",
"guzzlehttp/guzzle": "^7.8",
"lab404/laravel-impersonate": "^1.7",
"laravel/framework": "^10.0",

20
backend/composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "3622c3b9cec25c888177bb27450e0076",
"content-hash": "e3c0fed07ce4fe383d479d1c9dcc412f",
"packages": [
{
"name": "brick/math",
@ -471,16 +471,16 @@
},
{
"name": "doctrine/dbal",
"version": "3.8.2",
"version": "3.8.3",
"source": {
"type": "git",
"url": "https://github.com/doctrine/dbal.git",
"reference": "a19a1d05ca211f41089dffcc387733a6875196cb"
"reference": "db922ba9436b7b18a23d1653a0b41ff2369ca41c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/a19a1d05ca211f41089dffcc387733a6875196cb",
"reference": "a19a1d05ca211f41089dffcc387733a6875196cb",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/db922ba9436b7b18a23d1653a0b41ff2369ca41c",
"reference": "db922ba9436b7b18a23d1653a0b41ff2369ca41c",
"shasum": ""
},
"require": {
@ -496,12 +496,12 @@
"doctrine/coding-standard": "12.0.0",
"fig/log-test": "^1",
"jetbrains/phpstorm-stubs": "2023.1",
"phpstan/phpstan": "1.10.57",
"phpstan/phpstan": "1.10.58",
"phpstan/phpstan-strict-rules": "^1.5",
"phpunit/phpunit": "9.6.16",
"psalm/plugin-phpunit": "0.18.4",
"slevomat/coding-standard": "8.13.1",
"squizlabs/php_codesniffer": "3.8.1",
"squizlabs/php_codesniffer": "3.9.0",
"symfony/cache": "^5.4|^6.0|^7.0",
"symfony/console": "^4.4|^5.4|^6.0|^7.0",
"vimeo/psalm": "4.30.0"
@ -564,7 +564,7 @@
],
"support": {
"issues": "https://github.com/doctrine/dbal/issues",
"source": "https://github.com/doctrine/dbal/tree/3.8.2"
"source": "https://github.com/doctrine/dbal/tree/3.8.3"
},
"funding": [
{
@ -580,7 +580,7 @@
"type": "tidelift"
}
],
"time": "2024-02-12T18:36:36+00:00"
"time": "2024-03-03T15:55:06+00:00"
},
{
"name": "doctrine/deprecations",
@ -9504,5 +9504,5 @@
"php": "^8.1"
},
"platform-dev": [],
"plugin-api-version": "2.6.0"
"plugin-api-version": "2.3.0"
}

View File

@ -13,7 +13,7 @@ return new class extends Migration
{
Schema::table('places', function (Blueprint $table) {
$table->dropColumn('municipality');
$table->foreignId('municipality_id')->constrained('place_municipalities')->nullable();
$table->foreignId('municipality_id')->constrained('place_municipalities')->nullable()->default(null);
$table->float('lat', 10, 6)->nullable()->change();
$table->float('lon', 10, 6)->nullable()->change();
});

View File

@ -25,7 +25,6 @@ export class TableComponent implements OnInit, OnDestroy {
"type_id",
"pivot",
"place_id",
"display_name",
"licence",
"lat",
"lon",
@ -50,7 +49,7 @@ export class TableComponent implements OnInit, OnDestroy {
_maxPaginationSize: number = 10;
_rowsPerPage: number = 20;
@Input('maxPaginationSize')
get maxPaginationSize(): any {
return this._maxPaginationSize;
@ -58,7 +57,7 @@ export class TableComponent implements OnInit, OnDestroy {
set maxPaginationSize(value: any) {
if(!isNaN(value)) this._maxPaginationSize = value;
}
@Input('rowsPerPage')
get rowsPerPage(): any {
return this._rowsPerPage;
@ -240,7 +239,7 @@ export class TableComponent implements OnInit, OnDestroy {
}
editService(id: number) {
this.router.navigate(['/services', id]);
this.router.navigate(['/services', id]);
}
deleteService(id: number) {
@ -272,7 +271,7 @@ export class TableComponent implements OnInit, OnDestroy {
}
editTraining(id: number) {
this.router.navigate(['/trainings', id]);
this.router.navigate(['/trainings', id]);
}
deleteTraining(id: number) {

View File

@ -60,6 +60,14 @@ export class ApiClientService {
constructor(private http: HttpClient) { }
private returnResponseData(body: any): any {
if(body === null || body === undefined) return null;
if(body.data !== undefined) {
return body.data;
}
return body;
}
public apiEndpoint(endpoint: string): string {
if(endpoint.startsWith('http') || endpoint.startsWith('//')) {
return endpoint;
@ -80,7 +88,9 @@ export class ApiClientService {
next: (v: any) => {
this.lastEtag = v.headers.get("etag");
this.isLastSame = etag === this.lastEtag && etag !== "";
resolve(v.body);
resolve(
this.returnResponseData(v.body)
);
},
error: (e) => reject(e)
});