Dzisiejszy wpis poświęcony jest kolejnej części naszego systemu. Tym razem zajmiemy się budową API dostępowego, tak by w niedalekiej przyszłości połączyć się z nim za pomocą Angulara. Nasze API dotyczyć będzie przede wszystkim funkcjonalności związanych z administracją systemem. W opisywanej wersji będzie to kilkanaście metod dostępowych, wraz z rozwojem aplikacji, grupę tą poszerzymy oczywiście o kolejne, nowe żądania.
Do testowania API polecam świetny dodatek do Chrome o nazwie Postman, świetnie nadaje się do wysyłania zarówno prostych jak i bardziej rozbudowanych żądań.
Krótka analiza systemu pozwala na wybranie niezbędnych metod:
- Rent
– getAll() – pobieranie wszystkich wypożyczeń
– getAllByVehicle() – pobieranie wszystkich rezerwacji dla konkretnego pojazdu
– accept() – akceptacja wypożyczenia przez administratora, czyli zmiana statusu na “2” – potwierdzoną rezerwację
– delete() – usunięcie rezerwacji
- Vehicle
– store() – dodanie nowego pojazdu do bazy, w tym zapisanie zdjęcia pojazdu, a także właściciela
– getAll() – pobranie wszystkich pojazdów
– getAllByUser() – pobranie wszystkich pojazdów konkretnego użytkownika
– delete() – usunięcie pojazdu z bazy
– update() – aktualizacja pojazdu
- User
– store() – dodanie nowego użytkownika
– getAll() – pobranie wszystkich użytkowników
– find() – pobranie danych użytkownika o konkretnym id
Przełożenie tego na plik routingu routes/api.php wygląda następująco:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
Route::middleware('auth:api')->get('/rent', 'Api\RentController@getAll'); Route::middleware('auth:api')->get('/rent/{id?}', 'Api\RentController@getAllByVehicle'); Route::middleware('auth:api')->get('/rent/accept/{id?}', 'Api\RentController@accept'); Route::middleware('auth:api')->delete('/rent/{id?}', 'Api\RentController@delete'); Route::middleware('auth:api')->post('/vehicle/{id?}', 'Api\VehicleController@update'); Route::middleware('auth:api')->get('/vehicle', 'Api\VehicleController@getAll'); Route::middleware('auth:api')->get('/vehicle/{id?}', 'Api\VehicleController@getAllByUser'); Route::middleware('auth:api')->post('/vehicle/store', 'Api\VehicleController@store'); Route::middleware('auth:api')->delete('/vehicle/{id?}', 'Api\VehicleController@delete'); Route::middleware('auth:api')->post('/user/store', 'Api\UserController@store'); Route::middleware('auth:api')->get('/user', 'Api\UserController@getAll'); Route::middleware('auth:api')->get('/user/{id?}', 'Api\UserController@find'); |
Ponadto, dla każdego z modeli (Vehicle, User, Rent) potrzebujemy:
- samego modelu (już mamy)
- Repositories
- obiektów Factory
- obiektów Request do walidacji dodawania
- Kontrolerów do API – będziemy przechowywać je w podkatalogu Api
- Testów automatycznych
Rezerwacje
Zaczynamy od DAO:
Repositories\RentRepository.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
<?php namespace App\Repositories; use App\Models\Rent; use Carbon\Carbon; /** * Class BanRepo. * * @package App\Repository */ class RentRepository extends BaseRepository { public function __construct(Rent $model) { $this->model = $model; } public function findDatesByVehicle($idVehicle) { $rents = $this->model->with('vehicle')->where('vehicle_id','=',$idVehicle)->get(); $seasonStart = Carbon::createFromDate(2017, 4, 1, 'Europe/Warsaw'); $seasonEnd = Carbon::createFromDate(2017, 11, 1, 'Europe/Warsaw'); /** * Iterujemy po dniach */ for($date = $seasonStart; $date->lte($seasonEnd); $date->addDay()) { $dates[$date->format('Y-m-d')] = $date->format('Y-m-d'); foreach($rents as $rent){ if($rent->status == 2) { $rentStartDate = Carbon::createFromFormat("Y-m-d H:i:s",$rent->start); $rentEndDate = Carbon::createFromFormat("Y-m-d H:i:s",$rent->end); if($rentStartDate->lte($date) && $rentEndDate->gte($date)) { $dates[$date->format('Y-m-d')] = array($date->format('Y-m-d'),$rent->toArray()); } } } } return $dates; } public function getAllByVehicle($idVehicle) { return $this->model->with('vehicle')->where('vehicle_id','=',$idVehicle)->get(); } public function accept($idRent) { $rent = $this->model->find($idRent); $rent->status = 2; $rent->save(); return $rent; } } |
Dwie nowe metody to getAllByVehicle() i accept(). Pierwsza z nich to zwykłe zapytanie do bazy, natomiast accept, czyli zatwierdzenie rezerwacji najpierw odnajduje potrzebną rezerwację, po czym zmienia jej status, a na koniec dokonaną zmianę zapisuje w bazie.
Obiekt CreateRentRequest został stworzony w poprzednim odcinku, także jego listing pomijam.
Kolejny krok to kontroler:
Controllers\Api\RentController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
<?php namespace App\Http\Controllers\Api; use App\Http\Controllers\Api\DefaultController; use Illuminate\Http\Request; use Carbon\Carbon; use App\Repositories\RentRepository; use App\Requests\CreateRentRequest; class RentController extends DefaultController { /** * Create a new controller instance. * * @return void */ public function __construct() { } public function getAll(RentRepository $rent) { $rents = $rent->getAll(); return response() ->json($rents); } public function getAllByVehicle(RentRepository $rent, $id) { $rents = $rent->getAllByVehicle($id); return response() ->json($rents); } public function accept(RentRepository $rent, $id) { $rent = $rent->accept($id); return response() ->json($rent); } public function delete(RentRepository $rent, $id) { $status = $rent->delete($id); if($status) { return response() ->json(['status'=>'success']); } else{ return response() ->json(['status'=>'fail']); } } } |
Kontroler RentController zawiera 4 metody:
getAll() – pobiera wszystkie rekordy i zwraca jako JSON
getAllByVehicle() – na podstawie przekazanego za pomocą Dependency Injection $id następuje pobranie wszystkich wypożyczeń dla danego pojazdu
accept() – zatwierdzenie rezerwacji dla podanego id
delete() – usunięcie rezerwacji
Krótka próba wysłania żądania:
http://127.0.0.1:8000/api/rent/1/?api_token=mykey
Czyli działa! 🙂 Kolejny etap to testy automatyczne, nie zapominamy o obiekcie Fabryki:
1 2 3 4 5 6 7 8 |
<?php use App\Models\Rent; use Faker\Generator; $factory->define(Rent::class, function (Generator $faker) { return []; }); |
i sam test, plik RentRepositoryTest.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
<?php namespace Tests\Unit\Repositories; use Tests\TestCase; use App\Models\Rent; use App\Repositories\RentRepository; use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Foundation\Testing\DatabaseTransactions; class RentRepositoryTest extends TestCase { use DatabaseTransactions; public function testGetAllByVehicle(){ $data = $this->getCreateData(); $rentsAdded = array(); $rentsAdded[] = factory(Rent::class)->create($data[0]); $rentsAdded[] = factory(Rent::class)->create($data[1]); $rentsAdded[] = factory(Rent::class)->create($data[2]); $all = $this->getAllByVehicleFilteredResponse("2", true); foreach($all as $single){ $this->assertEquals($single['vehicle_id'], "2"); } } private function getAllByVehicleFilteredResponse($vehicle, $id = false){ $vehicleRepository = \App::make(RentRepository::class); $all = app()->make(RentRepository::class)->getAllByVehicle($vehicle)->toArray(); foreach($all as &$single){ unset($single['created_at']); unset($single['updated_at']); if(!$id) { unset($single['id']); } } return $all; } public function testAccept(){ $data = $this->getCreateData(); $rentAdded = factory(Rent::class)->create($data[1]); app()->make(RentRepository::class)->accept($rentAdded->id); $all = $this->getFilteredResponse(true); foreach($all as $single){ if($single['id'] == $rentAdded->id) { $this->assertEquals($single['status'], "2"); } } } /** * Get All Test. * * @return void */ public function testGetAll() { $data = $this->getCreateData(); $rentsAdded = array(); $rentsAdded[] = factory(Rent::class)->create($data[0]); $rentsAdded[] = factory(Rent::class)->create($data[1]); $rentsAdded[] = factory(Rent::class)->create($data[2]); $all = $this->getFilteredResponse(); $this->assertContains($data[0], $all); $this->assertContains($data[1], $all); $this->assertContains($data[2], $all); } /** * Create Test. * * @return void */ public function testCreate() { $data = $this->getCreateData(); $rentsAdded = array(); $rentsAdded[] = factory(Rent::class)->create($data[0]); $all = $this->getFilteredResponse(); $this->assertContains($data[0], $all); } private function getFilteredResponse($id = false){ $vehicleRepository = \App::make(RentRepository::class); $all = app()->make(RentRepository::class)->getAll()->toArray(); foreach($all as &$single){ unset($single['created_at']); unset($single['updated_at']); if(!$id) { unset($single['id']); } } return $all; } private function getCreateData(){ return [ [ "start" => "2017-07-16 00:00:00", "end" => "2017-07-18 23:59:59", "renter" => "Renter 1", "vehicle_id" => "1", "price" => "2000", "payment_status" => "1", "status" => "2" ], [ "start" => "2017-07-15 19:39:33", "end" => "2017-07-17 19:39:33", "renter" => "Renter 2", "vehicle_id" => "2", "price" => "15555", "payment_status" => "0", "status" => "1" ], [ "start" => "2017-06-12 19:39:33", "end" => "2017-06-13 19:39:33", "renter" => "Renter 3", "vehicle_id" => "1", "price" => "1234", "payment_status" => "1", "status" => "0" ] ]; } public function testFindDatesByVehicle() { $data = $this->getCreateData(); $rentsAdded = array(); $rentsAdded[] = factory(Rent::class)->create($data[0]); $all = $this->getFindDatesByVehicleFilteredResponse(); $data = $this->getShowData(); $this->assertEquals($data[0], $all["2017-07-16"]); $this->assertEquals($data[1], $all["2017-07-17"]); $this->assertEquals($data[2], $all["2017-07-18"]); } private function getFindDatesByVehicleFilteredResponse(){ $vehicleRepository = \App::make(RentRepository::class); $all = app()->make(RentRepository::class)->findDatesByVehicle(1); foreach($all as &$single){ if(isset($single[1]) && is_array($single[1])) { unset($single[1]['created_at']); unset($single[1]['updated_at']); unset($single[1]['id']); unset($single[1]['vehicle']); } } return $all; } private function getShowData(){ return [ [ "2017-07-16", array( "start" => "2017-07-16 00:00:00", "end" => "2017-07-18 23:59:59", "renter" => "Renter 1", "vehicle_id" => "1", "price" => "2000", "payment_status" => "1", "status" => "2" ) ], [ "2017-07-17", array( "start" => "2017-07-16 00:00:00", "end" => "2017-07-18 23:59:59", "renter" => "Renter 1", "vehicle_id" => "1", "price" => "2000", "payment_status" => "1", "status" => "2" ) ], [ "2017-07-18", array( "start" => "2017-07-16 00:00:00", "end" => "2017-07-18 23:59:59", "renter" => "Renter 1", "vehicle_id" => "1", "price" => "2000", "payment_status" => "1", "status" => "2" ) ] ]; } } |
Nasze testy sprawdzają dwa nowe elementy (reszta została otestowana wcześniej, np. dodawanie nowej rezerwacji testowaliśmy przy okazji interfejsu klienta), dla zwięzłości opisuję więc tylko nowe testy:
- testGetAllByVehicle() – tworzymy 3 nowe wpisy o dwóch różnych vehicle_id (1 i 2). Za pomocą prywatnej metody getAllByVehicleFilteredResponse wysyłamy żądanie do serwisu i pobieramy wszelkie wypożyczenia dla id = 2. Na końcu porównujemy, czy wszystkie otrzymane wyniki faktycznie mają id =2.
- testAccept() – dodajemy jeden nowy rekord, następnie wywołujemy metodę accept z repozytorium, pobieramy wpisy z bazy i sprawdzamy czy nasz dodany wcześniej rekord faktycznie posiada zmodyfikowany status na “2”.
Wszystko działa, testy system przechodzi bez problemów, możemy przejść dalej – do pojazdów 🙂
Pojazdy
Baza pojazdów, a przede wszystkim API dostępowe także nic odkrywczego, zajrzyjmy do naszego Repository (model już mamy):
Repositories\VehicleRepository.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
<?php namespace App\Repositories; use App\Models\Vehicle; /** * Class BanRepo. * * @package App\Repository */ class VehicleRepository extends BaseRepository { public function __construct(Vehicle $model) { $this->model = $model; } public function getAll($columns = array('*')) { return $this->model->with('user')->get($columns); } public function getAllByUser($idUser) { return $this->model->with('user')->where('user_id','=',$idUser)->get(); } public function create(array $data) { $image_array = $data['image']; unset($data['image']); $model = $this->model->create($data); $img = str_replace('data:image/png;base64,', '', $image_array); $img = str_replace(' ', '+', $image_array); $data = base64_decode($image_array); file_put_contents(public_path().'/img/vehicles/' . $model->id.".jpg", $data); $model->image = $model->id.".jpg"; $model->save(); return $model; } public function update(array $data,$id) { $model = $this->model->find($id); if(isset($data['image'])) { $image_array = $data['image']; unset($data['image']); } $model->update($data); if(isset($image_array)) { $img = str_replace('data:image/png;base64,', '', $image_array); $img = str_replace(' ', '+', $image_array); $data = base64_decode($image_array); file_put_contents(public_path().'/img/vehicles/' . $model->id.".jpg", $data); $model->image = $model->id.".jpg"; $model->save(); } return $model; } } |
Tutaj praktycznie cały kod jest nowy, dokonajmy więc jego analizy:
- getAll() – pobranie wszystkich pojazdów
- getAllByUser() – pobranie wszystkich pojazdów konkretnego użytkownika
- create() – stworzenie nowego pojazdu, zapisanie danych w modelu i bazie, zapisanie obrazka zakodowanego w base64 do pliku na serwerze
- update() – edycja pojazdu, sprawdzenie czy nowe dane zawierają obrazek, jeśli tak to nadpisanie pliku obrazkowego
W następnej kolejności potrzebujemy obiektu Requests/CreateVehicleRequest:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?php namespace App\Requests; use Illuminate\Foundation\Http\FormRequest; use Carbon\Carbon; class CreateVehicleRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } public function rules() { return [ 'title' => 'required', 'description' => 'required', 'user_id' => 'required' ]; } public function filtered() { $data = $this->all(); } } |
Nie jest on w żaden sposób odkrywczy – dodajemy informację, że pola title, description i user_id są polami wymaganymi. Przejdźmy do kodu kontrolera:
Controllers/Api/VehicleController.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
<?php namespace App\Http\Controllers\Api; use App\Http\Controllers\Api\DefaultController; use Illuminate\Http\Request; use Carbon\Carbon; use App\Repositories\VehicleRepository; use App\Requests\CreateVehicleRequest; class VehicleController extends DefaultController { /** * Create a new controller instance. * * @return void */ public function __construct() { //$this->middleware('auth'); } public function store(VehicleRepository $vehicle, CreateVehicleRequest $request) { $success = $vehicle->create($request->all()); return response() ->json($success); } public function getAll(VehicleRepository $vehicle) { $vehicles = $vehicle->getAll(); return response() ->json($vehicles); } public function getAllByUser(VehicleRepository $vehicle, $id) { $vehicles = $vehicle->getAllByUser($id); return response() ->json($vehicles); } public function delete(VehicleRepository $vehicle, $id) { $status = $vehicle->delete($id); if($status) { return response() ->json(['status'=>'success']); } else{ return response() ->json(['status'=>'fail']); } } public function update(VehicleRepository $vehicle, CreateVehicleRequest $request, $id) { $success = $vehicle->update($request->all(),$id); return response() ->json($success); } } |
Konstruktor ewoluował i zawiera teraz kilka niezbędnych metod:
- store() – zapisanie nowego pojazdu w bazie
- getAll() – pobranie wszystkich pojazdów
- getAllByUser() – pobranie wszystkich pojazdów konkretnego użytkownika
- delete() – usunięcie pojazdu
- update() – aktualizacja pojazdu
Przechodzimy do testów i tradycyjnie rozpoczynamy od fabryki, database/factories/VehicleFactory.php:
1 2 3 4 5 6 7 8 |
<?php use App\Models\Vehicle; use Faker\Generator; $factory->define(Vehicle::class, function (Generator $faker) { return []; }); |
i wreszcie same testy, Unit/Repositories/VehicleRepositoryTest.php::
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
<?php namespace Tests\Unit\Repositories; use Tests\TestCase; use App\Models\Vehicle; use App\Repositories\VehicleRepository; use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Foundation\Testing\DatabaseTransactions; class VehicleRepositoryTest extends TestCase { use DatabaseTransactions; public function tesGetAllByUser() { $data = $this->getAllData(); $rentsAdded = array(); $rentsAdded[] = factory(Rent::class)->create($data[0]); $rentsAdded[] = factory(Rent::class)->create($data[1]); $rentsAdded[] = factory(Rent::class)->create($data[2]); $all = $this->getAllByUserFilteredResponse("3", true); foreach($all as $single){ $this->assertEquals($single['user_id'], "3"); } } private function getAllByUserFilteredResponse($idUser){ $vehicleRepository = \App::make(VehicleRepository::class); $all = app()->make(VehicleRepository::class)->getAllByUser($idUser)->toArray(); foreach($all as &$single){ unset($single['user']); unset($single['created_at']); unset($single['updated_at']); unset($single['id']); } return $all; } public function testCreate() { $data = $this->getAllData(); $added = array(); $added[] = factory(Vehicle::class)->create($data[0]); $all = $this->getFilteredResponse(); $this->assertContains($data[0], $all); } public function testUpdate() { $data = $this->getAllData(); $added = factory(Vehicle::class)->create($data[0]); $data[0]['title'] = "Elcihev 1"; $data[0]['image'] = $added->id.".jpg"; app()->make(VehicleRepository::class)->update($data[0],$added->id); $all = $this->getFilteredResponse(); $this->assertContains($data[0], $all); } ///// /** * A basic test example. * * @return void */ public function testGetAll() { $data = $this->getAllData(); $ordersAdded = array(); $ordersAdded[] = factory(Vehicle::class)->create($data[0]); $ordersAdded[] = factory(Vehicle::class)->create($data[1]); $ordersAdded[] = factory(Vehicle::class)->create($data[2]); $all = $this->getFilteredResponse(); $this->assertContains($data[0], $all); $this->assertContains($data[1], $all); $this->assertContains($data[2], $all); } private function getFilteredResponse(){ $vehicleRepository = \App::make(VehicleRepository::class); $all = app()->make(VehicleRepository::class)->getAll()->toArray(); foreach($all as &$single){ unset($single['user']); unset($single['created_at']); unset($single['updated_at']); unset($single['id']); } return $all; } private function getAllData(){ return [ [ "title" => "Vehicle 1", "description" => "Vehicle 1 description", "image" => "Vehicle 1 image", "user_id" => "1" ], [ "title" => "Vehicle 2", "description" => "Vehicle 2 description", "image" => "Vehicle 2 image", "user_id" => "2" ], [ "title" => "Vehicle 3", "description" => "Vehicle 3 description", "image" => "Vehicle 3 image", "user_id" => "3" ] ]; } } |
Testy kolejno:
- tesGetAllByUser() – w tym teście tworzymy 3 nowe pojazdy, każdy z innym właścicielem. Następnie pobieramy z systemu wszystkie pojazdy dla usera o id = 3 i sprawdzamy czy faktycznie wszystkie z zwróconych pojazdów mają przypisanego właściciela o numerze 3.
- testCreate() – dodajemy nowy pojazd, następnie odwołując się do systemu pobieramy listę pojazdów z bazy i sprawdzamy czy na liście istnieje wcześniej dodany wpis.
- testUpdate() – standardowo – tworzymy nowy pojazd, następnie modyfikujemy jego model i sprawdzamy czy system zwraca poprawne, zaktualizowane dane.
- testGetAll() – dodajemy trzy niezależne pojazdy, po czym sprawdzamy czy istnieją na liście otrzymanej z serwera.
Moduł pojazdów oprogramowany i przetestowany. Ostatni etap prac to:
Użytkownicy
Nasze repozytorium nie ulegnie zmianie, przejdźmy od razu do kontrolera:
Controllers/Api/UserController.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
<?php namespace App\Http\Controllers\Api; use App\Http\Controllers\Api\DefaultController; use Illuminate\Http\Request; use Carbon\Carbon; use App\Repositories\UserRepository; use App\Requests\CreateUserRequest; class UserController extends DefaultController { public function __construct() { } public function store(UserRepository $user, CreateUserRequest $request) { $success = $user->create($request->all()); return response() ->json($success); } public function getAll(UserRepository $user) { $users = $user->getAll(); return response() ->json($users); } public function find(UserRepository $user, $id) { $users = $user->find($id); return response() ->json($users); } } |
Jak widzimy, kontroler realizuje podstawowe metody bazowego repozytorium: pobieranie wszystkich użytkowników, odnalezienie użytkownika, czy stworzenie nowego.
Obiekt walidacji, Requests/CreateUserRequest.php, podpowie nam, które z pól obiektu są wymagane:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<?php namespace App\Requests; use Illuminate\Foundation\Http\FormRequest; use Carbon\Carbon; class CreateUserRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'name' => 'required', 'email' => 'required|unique:users', 'password' => 'required' ]; } } |
Obiekt fabryki to także standardowy wpis:
database/factories/UserFactory.php
1 2 3 4 5 6 7 8 |
<?php use App\Models\User; use Faker\Generator; $factory->define(User::class, function (Generator $faker) { return []; }); |
Same testy również nie wyróżniają się niczym szczególnym, Unit/Repositories/UserRepositoryTest.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
<?php namespace Tests\Unit\Repositories; use Tests\TestCase; use App\Models\User; use App\Repositories\UserRepository; use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Foundation\Testing\DatabaseTransactions; class UserRepositoryTest extends TestCase { use DatabaseTransactions; public function testGetAll() { $data = $this->getAllData(); $ordersAdded = array(); $ordersAdded[] = factory(User::class)->create($data[0]); $ordersAdded[] = factory(User::class)->create($data[1]); $ordersAdded[] = factory(User::class)->create($data[2]); unset($data[0]['password']); unset($data[1]['password']); unset($data[2]['password']); $all = $this->getGetAllFilteredResponse(); $this->assertContains($data[0], $all); $this->assertContains($data[1], $all); $this->assertContains($data[2], $all); } private function getGetAllFilteredResponse(){ $userRepository = \App::make(UserRepository::class); $all = app()->make(UserRepository::class)->getAll()->toArray(); foreach($all as &$single){ unset($single['created_at']); unset($single['updated_at']); unset($single['id']); unset($single['api_token']); } return $all; } public function testFind() { $data = $this->getAllData(); $added = factory(User::class)->create($data[0]); unset($data[0]['password']); $added = $this->getFindFilteredResponse($added->id); $this->assertEquals($data[0], $added); } private function getFindFilteredResponse($userId){ $userRepository = \App::make(UserRepository::class); $single = app()->make(UserRepository::class)->find($userId)->toArray(); unset($single['created_at']); unset($single['updated_at']); unset($single['id']); unset($single['api_token']); return $single; } private function getAllData(){ return [ [ "name" => "User 1", "email" => "user1@user.com", "password" => "password1" ], [ "name" => "User 2", "email" => "user2@user.com", "password" => "password2" ], [ "name" => "User 3", "email" => "user3@user.com", "password" => "password3" ] ]; } } |
Powyższe testy nie wprowadzają nic nowego, są to te same, klasyczne wpisy znane z Wypożyczeń i Pojazdów.
Tak oto kończymy cykl dotyczący backendu naszej aplikacji. W następnym odcinku zajmiemy się połączeniem naszego API z Angularem 5.
Podsumowanie API
Żądanie | Opis |
Wypożyczenia | |
GET /rent | zwraca listę wypożyczeń |
GET /rent/{id} | zwraca informacje o pojedynczym wypożyczeniu |
GET /rent/accept/{id} | akceptuje wypozyczenie |
DELETE /rent/{id} | usuwa wypożyczenie |
Pojazdy | |
POST /vehicle/{id} | Aktualizuje sprzęt/pojazd |
GET /vehicle | Pobiera listę sprzętu/pojazdów |
GET /vehicle/{id} | Pobiera listę sprzętu należącego do użytkownika |
POST /vehicle/store | Dodaje pojazd do bazy |
DELETE /vehicle/{id} | Usuwa pojazd |
Użytkownicy | |
POST /user/store | Dodaje użytkownika do bazy |
GET /user | Zwraca listę użytkowników |
GET /user/{id} | Pobiera informacje o konkretnym użytkowniku |