diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index 87a034926769f706a54507604e114bbe72737b09..1c4a33756c2386c64ba6b1dfa528af6e846f0cd5 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -2,8 +2,11 @@ namespace App\Http\Controllers; +use App\Models\User; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Validator; +use Laravel\Sanctum\PersonalAccessToken; class AuthController extends Controller { @@ -24,9 +27,12 @@ class AuthController extends Controller ]); } - public function logout() + public function logout(Request $request) { - auth()->user()->tokens()->delete(); - return response()->json(['Token revoked']); + $user = UserController::getUserByToken($request); + if(1 != $user->tokens()->delete()){ + return "false"; + } + return "true"; } } \ No newline at end of file diff --git a/app/Models/Bike.php b/app/Models/Bike.php index 05c406f1ad5065fdd508355e34404aa34f5746ac..75a3e695eb5876248c169e564045b74e386d032e 100644 --- a/app/Models/Bike.php +++ b/app/Models/Bike.php @@ -6,17 +6,25 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; +use Nanigans\SingleTableInheritance\SingleTableInheritanceTrait; /** * @method static create(array $array) + * @method static find(int $int) + * @property mixed $id + * @property mixed $bikeable_type + * @property mixed|null $stop_id */ class Bike extends Model { use HasFactory; + protected $table = "bikes"; + protected $fillable = [ 'stop_id', 'unlocked', + 'type' ]; public function stop(): BelongsTo{ @@ -26,4 +34,8 @@ class Bike extends Model public function routes(): HasMany{ return $this->hasMany(Route::class); } + + public function bikeable(){ + return $this->morphTo(); + } } diff --git a/app/Models/ElectricBike.php b/app/Models/ElectricBike.php index 8991516c9ecc90fa3e9b6aee3eec9e8241a4c42e..de5833eb74cae00762939dcfd6d5ce0b2b33995f 100644 --- a/app/Models/ElectricBike.php +++ b/app/Models/ElectricBike.php @@ -14,4 +14,8 @@ class ElectricBike extends Bike 'id', 'battery', ]; + + public function bike(){ + return $this->morphOne(Bike::class, 'bikeable'); + } } diff --git a/app/Models/Reward.php b/app/Models/Reward.php index 3b40b1923ef096321fe69d667b3787dc7775a995..e108eb03c19a837366e856ff345948d05d082114 100644 --- a/app/Models/Reward.php +++ b/app/Models/Reward.php @@ -8,6 +8,9 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany; /** * @method static create(array $array) + * @property mixed $redeemed + * @property mixed $total_available + * @property mixed $points */ class Reward extends Model { @@ -18,6 +21,10 @@ class Reward extends Model 'description', 'user_id', 'reward_id', + 'points', + 'total_available', + 'image', + 'redeemed', ]; public function users(): BelongsToMany{ diff --git a/app/Models/Route.php b/app/Models/Route.php index 987300d4f49784fd77de0ebeaee262d4367febb0..d33714cfc6884d83495a653dc99873ae9a7dd568 100644 --- a/app/Models/Route.php +++ b/app/Models/Route.php @@ -6,6 +6,12 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +/** + * @property mixed $user_id + * @property \Illuminate\Database\Eloquent\HigherOrderBuilderProxy|mixed $initial_stop_id + * @property mixed $bike_id + * @method static find(mixed $id) + */ class Route extends Model { use HasFactory; @@ -17,6 +23,7 @@ class Route extends Model "bike_id", "distance", "duration", + "estimated_duration", "points", ]; diff --git a/app/Models/Stop.php b/app/Models/Stop.php index 2a256761eadb23783b69fb544495495482c5101b..0b74cb94dd50c52539232c92e15544ce4857c2e6 100644 --- a/app/Models/Stop.php +++ b/app/Models/Stop.php @@ -5,9 +5,16 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; +use phpDocumentor\Reflection\Types\Boolean; +use PhpParser\Node\Expr\Cast\Int_; /** * @method static create(array $array) + * @method static find($initial_stop_id) + * @property mixed $lng + * @property mixed $lat + * @property mixed $total_spaces + * @property mixed $id */ class Stop extends Model { @@ -34,4 +41,27 @@ class Stop extends Model public function routesEnd(): HasMany{ return $this->hasMany(Route::class, "final_stop_id"); } + + public function reservations(): HasMany{ + return $this->hasMany(Reservation::class); + } + + public function reserved_pedal_bikes(): int{ + return $this->reservations()->where('bike_type', '=', 'pedal')->count(); + } + public function reserved_electric_bikes(): int{ + return $this->reservations()->where('bike_type', '=', 'electric')->count(); + } + + public function check_availability_bikes(String $type): bool{ + $available = false; + if ($type=="pedal" || $type=="App\Models\Bike"){ + $bikeCount = $this->bikes()->where('bikeable_type', '=', 'App\Models\Bike')->count(); + $available = $bikeCount > $this->reserved_pedal_bikes(); + }else if ($type=="electric" || $type=="App\Models\ElectricBike"){ + $bikeCount = $this->bikes()->where('bikeable_type', '=', 'App\Models\ElectricBike')->count(); + $available = $bikeCount > $this->reserved_electric_bikes(); + } + return $available; + } } diff --git a/app/Models/User.php b/app/Models/User.php index b9cb8bad4182d986e72afc66e46588fb667c32c9..4bb40e927eb9fc2a669c3869e7dd01f70411f5b8 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -13,6 +13,9 @@ use Laravel\Sanctum\HasApiTokens; /** * @method static create(string[] $array) * @method static find(int $id) + * @method static where(string $string, mixed $email) + * @property mixed $points + * @property mixed $id */ class User extends Authenticatable { @@ -55,4 +58,8 @@ class User extends Authenticatable public function rewards(): BelongsToMany{ return $this->belongsToMany(Reward::class, 'user_reward')->withTimestamps(); } + + public function reservations(): HasMany{ + return $this->hasMany(Reservation::class); + } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 88da5b60052b9951ea3f8090e56d088e2f91b4ad..c29e18286d9a57da7287f04c72c800e2915db46e 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -22,8 +22,11 @@ class AppServiceProvider extends ServiceProvider * * @return void */ - public function boot() + public function boot(\Illuminate\Http\Request $request) { + if (!empty( env('NGROK_URL') ) && $request->server->has('HTTP_X_ORIGINAL_HOST')) { + $this->app['url']->forceRootUrl(env('NGROK_URL')); + } JsonResource::withoutWrapping(); } } diff --git a/composer.json b/composer.json index 0ab4cf735fc13671e559a298985978b4c2dcf0e4..e112bb19fe10252a0ccde03203a2e9dcf77183fb 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,8 @@ "guzzlehttp/guzzle": "^7.2", "laravel/framework": "^9.11", "laravel/sanctum": "^2.15", - "laravel/tinker": "^2.7" + "laravel/tinker": "^2.7", + "nanigans/single-table-inheritance": "~1.0" }, "require-dev": { "fakerphp/faker": "^1.9.1", diff --git a/composer.lock b/composer.lock index 292c550f7374c6998d1a77fcf13efcc3b5fd0045..9ba8178b78f7ec534af1025cac4653c90b15c571 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "58fa573d0eb02b0bca9c8679dea764c0", + "content-hash": "a6074d6cbd7b2a35b7d18370d948483f", "packages": [ { "name": "brick/math", @@ -1702,6 +1702,67 @@ ], "time": "2022-05-10T09:36:00+00:00" }, + { + "name": "nanigans/single-table-inheritance", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/jonspalmer/single-table-inheritance.git", + "reference": "5fd1dd86ddc5ab66bf9d1260b8de1e58ac5826ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jonspalmer/single-table-inheritance/zipball/5fd1dd86ddc5ab66bf9d1260b8de1e58ac5826ff", + "reference": "5fd1dd86ddc5ab66bf9d1260b8de1e58ac5826ff", + "shasum": "" + }, + "require": { + "illuminate/database": ">= 5.2", + "illuminate/support": ">= 5.2", + "php": ">= 7.2" + }, + "require-dev": { + "mockery/mockery": "~1.0", + "orchestra/database": "3.8.*|4.*|5.*|6.*", + "orchestra/testbench": "3.8.*|4.*|5.*|6.*", + "phpunit/phpunit": "~8.0|~9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Nanigans\\SingleTableInheritance\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jon Palmer", + "email": "328224+jonspalmer@users.noreply.github.com" + } + ], + "description": "Single Table Inheritance Trait", + "keywords": [ + "eloquent", + "inheritance", + "laravel", + "model", + "single", + "table" + ], + "support": { + "issues": "https://github.com/jonspalmer/single-table-inheritance/issues", + "source": "https://github.com/jonspalmer/single-table-inheritance/tree/v1.0.0" + }, + "time": "2021-04-17T13:10:00+00:00" + }, { "name": "nesbot/carbon", "version": "2.58.0", diff --git a/database/migrations/2022_06_13_151027_create_bikes_table.php b/database/migrations/2022_06_13_151027_create_bikes_table.php index 705aadf3aab4c15459a871baf8f83029d9027fd8..6e167202085fddbf2395a98a2fc6916f4ea3dea3 100644 --- a/database/migrations/2022_06_13_151027_create_bikes_table.php +++ b/database/migrations/2022_06_13_151027_create_bikes_table.php @@ -16,10 +16,13 @@ return new class extends Migration Schema::create('bikes', function (Blueprint $table) { $table->id(); $table->foreignId('stop_id') + ->nullable() ->index() ->constrained() ->cascadeOnUpdate(); $table->boolean('unlocked'); + $table->unsignedBigInteger('bikeable_id'); + $table->string('bikeable_type'); $table->timestamps(); }); } diff --git a/database/migrations/2022_06_13_153838_create_electric_bikes_table.php b/database/migrations/2022_06_13_153838_create_electric_bikes_table.php index db6b5b73b5caf8e379b40e3ae2d58610f49de036..0946573b306638e14a2e7c05f1818f5538c05ff1 100644 --- a/database/migrations/2022_06_13_153838_create_electric_bikes_table.php +++ b/database/migrations/2022_06_13_153838_create_electric_bikes_table.php @@ -17,8 +17,7 @@ return new class extends Migration $table->id(); $table->integer('battery'); $table->timestamps(); - - $table->foreign('id')->references('id')->on('bikes'); + //$table->foreign('id')->references('id')->on('bikes'); }); } diff --git a/database/migrations/2022_06_14_151011_create_routes_table.php b/database/migrations/2022_06_14_151011_create_routes_table.php index 51d6e35c0c2649c5fa6426286baeef6404e100b9..f73d946e489367cdd01532b1e944b6d90f3dbbba 100644 --- a/database/migrations/2022_06_14_151011_create_routes_table.php +++ b/database/migrations/2022_06_14_151011_create_routes_table.php @@ -48,6 +48,8 @@ return new class extends Migration ->nullable(); $table->integer('duration') ->nullable(); + $table->integer('estimated_duration') + ->nullable(); $table->integer('points') ->nullable(); diff --git a/database/migrations/2022_06_15_144408_create_rewards_table.php b/database/migrations/2022_06_15_144408_create_rewards_table.php index deebdc13d82e5969780f3d6271f280245a308ef3..485b82c633f4ad2315ba667a8f910e2f49592e1b 100644 --- a/database/migrations/2022_06_15_144408_create_rewards_table.php +++ b/database/migrations/2022_06_15_144408_create_rewards_table.php @@ -19,6 +19,7 @@ return new class extends Migration $table->string('description'); $table->integer('points'); $table->integer('total_available'); + $table->integer('redeemed')->default(0); $table->string('image')->nullable(); $table->timestamps(); }); diff --git a/database/seeders/BikeSeeder.php b/database/seeders/BikeSeeder.php index 22543e813c4b525d15a0c86a0ebe286d7772b4ad..b638f24c1af6e24f60e82ec01462073a20014f77 100644 --- a/database/seeders/BikeSeeder.php +++ b/database/seeders/BikeSeeder.php @@ -18,29 +18,36 @@ class BikeSeeder extends Seeder { $bike = Bike::create([ 'stop_id' => 1, - 'unlocked' => false + 'unlocked' => false, + 'bikeable_id' => 1, + 'bikeable_type' => 'App\Models\ElectricBike', ]); ElectricBike::create([ - 'id' => $bike->id, 'battery' => 60 ]); Bike::create([ 'stop_id' => 1, - 'unlocked' => false + 'unlocked' => false, + 'bikeable_id' => 2, + 'bikeable_type' => 'App\Models\Bike', ]); $bike = Bike::create([ 'stop_id' => 2, - 'unlocked' => false + 'unlocked' => false, + 'bikeable_id' => 2, + 'bikeable_type' => 'App\Models\ElectricBike', ]); ElectricBike::create([ - 'id' => $bike->id, 'battery' => 70 ]); Bike::create([ 'stop_id' => 3, - 'unlocked' => false + 'unlocked' => false, + 'bikeable_id' => 4, + 'bikeable_type' => 'App\Models\Bike', ]); + } } diff --git a/database/seeders/RewardSeeder.php b/database/seeders/RewardSeeder.php index 54727890906059adbb61c5bb1d395b618cfb5ee2..e98dbed95f25bc9444ec8589dbb365fe14a76665 100644 --- a/database/seeders/RewardSeeder.php +++ b/database/seeders/RewardSeeder.php @@ -19,33 +19,38 @@ class RewardSeeder extends Seeder 'name' => 'Reward 1', 'description' => 'A short description', 'points' => 100, - 'total_available' => 5 + 'total_available' => 5, + "image"=> "https://images.unsplash.com/photo-1591544940847-e8c860a9464e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1974&q=80" ]); $reward->users()->sync([1,2]); $reward = Reward::create([ 'name' => 'Reward 2', 'description' => 'A short description 2', 'points' => 200, - 'total_available' => 2 + 'total_available' => 2, + "image"=> "https://images.unsplash.com/photo-1621841957884-1210fe19d66d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2340&q=80" ]); $reward->users()->sync([1]); Reward::create([ 'name' => 'Reward 3', 'description' => 'A short description 3', 'points' => 1000, - 'total_available' => 1 + 'total_available' => 1, + "image" => "https://images.unsplash.com/photo-1565599837634-134bc3aadce8?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1335&q=80" ]); Reward::create([ 'name' => 'Reward 4', 'description' => 'A short description 4', 'points' => 4, - 'total_available' => 10 + 'total_available' => 10, + 'image' => 'https://images.unsplash.com/photo-1617257118084-339d30c49b02?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2340&q=80' ]); Reward::create([ 'name' => 'Reward 5', 'description' => 'A short description 5', 'points' => 500, - 'total_available' => 5 + 'total_available' => 5, + "image"=> "https://images.unsplash.com/photo-1655219282218-6a4a8d91a699?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2340&q=80" ]); } } diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php index 53bc2192f850407e3505cc0871960cb70735c872..81d01bf5cbe65c87ad5464532a5b5ff92cf91205 100644 --- a/database/seeders/UserSeeder.php +++ b/database/seeders/UserSeeder.php @@ -18,7 +18,8 @@ class UserSeeder extends Seeder User::create([ 'name' => 'Jorge', 'email' => 'jorge@uva.es', - 'password' => '12345678' + 'password' => bcrypt('123456'), + 'points' => 1000 ]); User::create([ 'name' => 'Mario', diff --git a/routes/api.php b/routes/api.php index fd621dbcf9e902b16e206e12d41295cb02eb7882..0276c009f2c0703dbd916195c7847262a4aad3d1 100644 --- a/routes/api.php +++ b/routes/api.php @@ -2,8 +2,12 @@ use App\Http\Controllers\AuthController; use App\Http\Controllers\BikeController; +use App\Http\Controllers\RewardController; use App\Http\Controllers\RouteController; use App\Http\Controllers\StopController; +use App\Http\Resources\BikeResource; +use App\Http\Resources\RewardColletion; +use App\Models\Bike; use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; @@ -23,10 +27,30 @@ Route::middleware('auth:sanctum')->get('/user', function (Request $request) { }); Route::post('/login', [AuthController::class, 'login']); -Route::get('/retobici/stops', [StopController::class, 'getAllStops']); +Route::get('/stops', [StopController::class, 'getAllStops']); +Route::get('/rewards', [RewardController::class, 'getAllRewards']); Route::middleware('auth:sanctum')->group(function (){ - Route::post('/retobici/bikes/unlock/{bike}', [BikeController::class, 'unlockBike']); - Route::put('/retobici/routes', [RouteController::class, 'createRoute']); + + Route::post('/stops/{bike}/unlock', [StopController::class, 'unlockBike']); + Route::post('/stops/{stop}/reserve/{type}', [StopController::class, 'reserveBike']); + Route::post('/stops/{stop}/lock', [StopController::class, 'lockBike']); + + Route::put('/routes/{bike}', [RouteController::class, 'createRoute']); + Route::post('/routes/finish', [RouteController::class, 'finishRoute']); + + Route::post('/rewards/{reward}', [RewardController::class, 'obtainReward']); + Route::get('/rewards/redeemed', [RewardController::class, 'getUserRewards']); + Route::get('/rewards/not/redeemed', [RewardController::class, 'getNotRedeemedRewards']); + + Route::post('/logout', [AuthController::class, 'logout']); +}); + +Route::get('/test', function (){ + $stops = \App\Models\Stop::all(); + $remove = $stops->first(); + return $stops->filter(function ($value) use ($remove){ + return $value!=$remove; + }); });