diff --git a/backend/app/Enums/Arrayable.php b/backend/app/Enums/Arrayable.php new file mode 100644 index 0000000..9480289 --- /dev/null +++ b/backend/app/Enums/Arrayable.php @@ -0,0 +1,14 @@ +attempt($request->validated())) + abort(401); + + $token = auth()->user()->createToken('web')->plainTextToken; + + return (new UserResource(auth()->user())) + ->additional(['token' => $token]); + } +} diff --git a/backend/app/Http/Controllers/Auth/LogoutController.php b/backend/app/Http/Controllers/Auth/LogoutController.php new file mode 100644 index 0000000..5ae6f18 --- /dev/null +++ b/backend/app/Http/Controllers/Auth/LogoutController.php @@ -0,0 +1,17 @@ +user()->currentAccessToken()->delete(); + } +} diff --git a/backend/app/Http/Controllers/Auth/RegistrationController.php b/backend/app/Http/Controllers/Auth/RegistrationController.php new file mode 100644 index 0000000..c27ab32 --- /dev/null +++ b/backend/app/Http/Controllers/Auth/RegistrationController.php @@ -0,0 +1,30 @@ +validated(); + $credentials['password'] = Hash::make($credentials['password']); + + auth()->login(User::create($credentials)); + + $token = auth()->user()->createToken('web')->plainTextToken; + + return (new UserResource(auth()->user())) + ->additional(['token' => $token]); + } +} diff --git a/backend/app/Http/Controllers/OfferController.php b/backend/app/Http/Controllers/OfferController.php new file mode 100644 index 0000000..7ab8e53 --- /dev/null +++ b/backend/app/Http/Controllers/OfferController.php @@ -0,0 +1,70 @@ +validated())); + } + + /** + * Update the specified resource in storage. + * + * @param \App\Http\Requests\UpdateOfferRequest $request + * @param int $id + * @return void + */ + public function update(UpdateOfferRequest $request, int $id): void + { + $offer = Offer::findOrFail($id); + $offer->update($request->validated()); + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * @return void + */ + public function destroy(int $id): void + { + $offer = Offer::findOrFail($id); + $offer->delete(); + } +} diff --git a/backend/app/Http/Controllers/OrderController.php b/backend/app/Http/Controllers/OrderController.php new file mode 100644 index 0000000..ffa1f0f --- /dev/null +++ b/backend/app/Http/Controllers/OrderController.php @@ -0,0 +1,70 @@ +validated())); + } + + /** + * Update the specified resource in storage. + * + * @param \App\Http\Requests\UpdateOfferRequest $request + * @param int $id + * @return void + */ + public function update(UpdateOfferRequest $request, int $id): void + { + $order = Order::findOrFail($id); + $order->update($request->validated()); + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * @return void + */ + public function destroy(int $id): void + { + $order = Order::findOrFail($id); + $order->delete(); + } +} diff --git a/backend/app/Http/Controllers/UserController.php b/backend/app/Http/Controllers/UserController.php new file mode 100644 index 0000000..1b4a06f --- /dev/null +++ b/backend/app/Http/Controllers/UserController.php @@ -0,0 +1,30 @@ +user()); + } + + /** + * Update the specified resource in storage. + * + * @param \App\Http\Requests\UpdateUserRequest $request + * @return void + */ + public function update(UpdateUserRequest $request): void + { + auth()->user()->update($request->validated()); + } +} diff --git a/backend/app/Http/Kernel.php b/backend/app/Http/Kernel.php index c3be254..6ead053 100644 --- a/backend/app/Http/Kernel.php +++ b/backend/app/Http/Kernel.php @@ -39,7 +39,7 @@ class Kernel extends HttpKernel ], 'api' => [ - // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, + \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, 'throttle:api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], diff --git a/backend/app/Http/Requests/Auth/LoginRequest.php b/backend/app/Http/Requests/Auth/LoginRequest.php new file mode 100644 index 0000000..9af72c6 --- /dev/null +++ b/backend/app/Http/Requests/Auth/LoginRequest.php @@ -0,0 +1,21 @@ + + */ + public function rules(): array + { + return [ + 'email' => ['required', 'string', 'email', 'exists:users'], + 'password' => ['required', 'string'], + ]; + } +} diff --git a/backend/app/Http/Requests/Auth/RegistrationRequest.php b/backend/app/Http/Requests/Auth/RegistrationRequest.php new file mode 100644 index 0000000..ca18e41 --- /dev/null +++ b/backend/app/Http/Requests/Auth/RegistrationRequest.php @@ -0,0 +1,21 @@ + + */ + public function rules(): array + { + return [ + 'email' => ['required', 'string', 'email', 'unique:users'], + 'password' => ['required', 'string'], + ]; + } +} diff --git a/backend/app/Http/Requests/BaseRequest.php b/backend/app/Http/Requests/BaseRequest.php new file mode 100644 index 0000000..8f64f3d --- /dev/null +++ b/backend/app/Http/Requests/BaseRequest.php @@ -0,0 +1,18 @@ + + */ + public function rules(): array + { + return [ + 'user_id' => ['required', 'numeric', 'exists:users,id'], + 'name' => ['required', 'string'], + 'type' => ['required', new Enum(OrderTypesEnum::class)], + 'price' => ['required', 'numeric', 'min:0.1'], + 'rooms' => ['required', 'numeric', 'min:1'], + 'space' => ['required', 'numeric', 'min:1'], + 'yandex_mark' => ['string'], + 'location' => ['required', 'string'], + 'description' => ['required', 'string'], + 'is_group' => ['required', 'boolean'], + ]; + } +} diff --git a/backend/app/Http/Requests/StoreOrderRequest.php b/backend/app/Http/Requests/StoreOrderRequest.php new file mode 100644 index 0000000..ee44d82 --- /dev/null +++ b/backend/app/Http/Requests/StoreOrderRequest.php @@ -0,0 +1,21 @@ + + */ + public function rules(): array + { + return [ + 'offer_id' => ['required', 'numeric', 'exists:offers,id'], + 'user_id' => ['required', 'numeric', 'exists:users,id'], + 'price' => ['required', 'numeric', 'min:0.1'], + 'discount' => ['required', 'numeric', 'min:0'], + ]; + } +} diff --git a/backend/app/Http/Requests/UpdateOfferRequest.php b/backend/app/Http/Requests/UpdateOfferRequest.php new file mode 100644 index 0000000..4e0903e --- /dev/null +++ b/backend/app/Http/Requests/UpdateOfferRequest.php @@ -0,0 +1,30 @@ + + */ + public function rules(): array + { + return [ + 'user_id' => ['numeric', 'exists:users,id'], + 'name' => ['string'], + 'type' => [new Enum(OrderTypesEnum::class)], + 'price' => ['numeric', 'min:0.1'], + 'rooms' => ['numeric', 'min:1'], + 'space' => ['numeric', 'min:1'], + 'yandex_mark' => ['string'], + 'location' => ['string'], + 'description' => ['string'], + 'is_group' => ['boolean'], + ]; + } +} diff --git a/backend/app/Http/Requests/UpdateOrderRequest.php b/backend/app/Http/Requests/UpdateOrderRequest.php new file mode 100644 index 0000000..abf195d --- /dev/null +++ b/backend/app/Http/Requests/UpdateOrderRequest.php @@ -0,0 +1,21 @@ + + */ + public function rules(): array + { + return [ + 'offer_id' => ['numeric', 'exists:offers,id'], + 'user_id' => ['numeric', 'exists:users,id'], + 'price' => ['numeric', 'min:0.1'], + 'discount' => ['numeric', 'min:0.1'], + ]; + } +} diff --git a/backend/app/Http/Requests/UpdateUserRequest.php b/backend/app/Http/Requests/UpdateUserRequest.php new file mode 100644 index 0000000..8315f86 --- /dev/null +++ b/backend/app/Http/Requests/UpdateUserRequest.php @@ -0,0 +1,22 @@ + + */ + public function rules() + { + return [ + 'first_name' => ['string'], + 'last_name' => ['string'], + 'middle_name' => ['string'], + 'email' => ['string', 'email', 'unique:users'], + 'phone' => ['string'], + ]; + } +} diff --git a/backend/app/Http/Resources/OfferResource.php b/backend/app/Http/Resources/OfferResource.php new file mode 100644 index 0000000..a310268 --- /dev/null +++ b/backend/app/Http/Resources/OfferResource.php @@ -0,0 +1,19 @@ + + */ + protected $fillable = [ + 'user_id', + 'comment', + 'rating', + ]; +} diff --git a/backend/app/Models/Offer.php b/backend/app/Models/Offer.php new file mode 100644 index 0000000..edb7327 --- /dev/null +++ b/backend/app/Models/Offer.php @@ -0,0 +1,42 @@ + + */ + protected $fillable = [ + 'user_id', + 'name', + 'type', + 'price', + 'rooms', + 'space', + 'yandex_mark', + 'location', + 'description', + 'is_group', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'type' => OrderTypesEnum::class, + 'space' => 'double', + 'price' => 'double', + 'is_group' => 'boolean', + ]; +} diff --git a/backend/app/Models/OfferPhoto.php b/backend/app/Models/OfferPhoto.php new file mode 100644 index 0000000..8891912 --- /dev/null +++ b/backend/app/Models/OfferPhoto.php @@ -0,0 +1,21 @@ + + */ + protected $fillable = [ + 'offer_id', + 'file', + ]; +} diff --git a/backend/app/Models/Order.php b/backend/app/Models/Order.php new file mode 100644 index 0000000..d8c0c20 --- /dev/null +++ b/backend/app/Models/Order.php @@ -0,0 +1,33 @@ + + */ + protected $fillable = [ + 'offer_id', + 'user_id', + 'price', + 'discount', + ]; + + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'price' => 'double', + 'discount' => 'double', + ]; +} diff --git a/backend/app/Models/User.php b/backend/app/Models/User.php index 8996368..0dd1542 100644 --- a/backend/app/Models/User.php +++ b/backend/app/Models/User.php @@ -2,7 +2,6 @@ namespace App\Models; -use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; @@ -18,8 +17,12 @@ class User extends Authenticatable * @var array */ protected $fillable = [ - 'name', + 'first_name', + 'last_name', + 'middle_name', 'email', + 'phone', + 'photo', 'password', ]; @@ -30,15 +33,5 @@ class User extends Authenticatable */ protected $hidden = [ 'password', - 'remember_token', - ]; - - /** - * The attributes that should be cast. - * - * @var array - */ - protected $casts = [ - 'email_verified_at' => 'datetime', ]; } diff --git a/backend/app/Providers/AuthServiceProvider.php b/backend/app/Providers/AuthServiceProvider.php index 51b351b..71235d4 100644 --- a/backend/app/Providers/AuthServiceProvider.php +++ b/backend/app/Providers/AuthServiceProvider.php @@ -24,7 +24,5 @@ class AuthServiceProvider extends ServiceProvider public function boot() { $this->registerPolicies(); - - // } } diff --git a/backend/composer.json b/backend/composer.json index 164c94b..18a7cbb 100644 --- a/backend/composer.json +++ b/backend/composer.json @@ -13,7 +13,7 @@ }, "require-dev": { "fakerphp/faker": "^1.9.1", - "laravel/sail": "^1.0.1", + "laravel/sail": "^1.14", "mockery/mockery": "^1.4.4", "nunomaduro/collision": "^6.1", "phpunit/phpunit": "^9.5.10", diff --git a/backend/composer.lock b/backend/composer.lock index d337df3..7b5506a 100644 --- a/backend/composer.lock +++ b/backend/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": "1abc7822bd9f28e9a62986817bf04f76", + "content-hash": "3c9da13e09c625e7ca22f1d572bd7f2d", "packages": [ { "name": "brick/math", diff --git a/backend/config/auth.php b/backend/config/auth.php index d8c6cee..2cbf567 100644 --- a/backend/config/auth.php +++ b/backend/config/auth.php @@ -64,11 +64,6 @@ return [ 'driver' => 'eloquent', 'model' => App\Models\User::class, ], - - // 'users' => [ - // 'driver' => 'database', - // 'table' => 'users', - // ], ], /* diff --git a/backend/database/factories/UserFactory.php b/backend/database/factories/UserFactory.php index 23b61d2..d1b5f38 100644 --- a/backend/database/factories/UserFactory.php +++ b/backend/database/factories/UserFactory.php @@ -18,25 +18,11 @@ class UserFactory extends Factory public function definition() { return [ - 'name' => $this->faker->name(), + 'first_name' => $this->faker->firstName(), + 'last_name' => $this->faker->lastName(), 'email' => $this->faker->unique()->safeEmail(), - 'email_verified_at' => now(), + 'phone' => $this->faker->unique()->e164PhoneNumber(), 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password - 'remember_token' => Str::random(10), ]; } - - /** - * Indicate that the model's email address should be unverified. - * - * @return static - */ - public function unverified() - { - return $this->state(function (array $attributes) { - return [ - 'email_verified_at' => null, - ]; - }); - } } diff --git a/backend/database/migrations/2014_10_12_000000_create_users_table.php b/backend/database/migrations/2014_10_12_000000_create_users_table.php index cf6b776..02eec63 100644 --- a/backend/database/migrations/2014_10_12_000000_create_users_table.php +++ b/backend/database/migrations/2014_10_12_000000_create_users_table.php @@ -15,11 +15,13 @@ return new class extends Migration { Schema::create('users', function (Blueprint $table) { $table->id(); - $table->string('name'); + $table->string('first_name')->nullable(); + $table->string('last_name')->nullable(); + $table->string('middle_name')->nullable(); $table->string('email')->unique(); - $table->timestamp('email_verified_at')->nullable(); + $table->string('phone')->unique()->nullable(); + $table->string('photo')->nullable(); $table->string('password'); - $table->rememberToken(); $table->timestamps(); }); } diff --git a/backend/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php b/backend/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php index fd235f8..3ce0002 100644 --- a/backend/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php +++ b/backend/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php @@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration +class CreatePersonalAccessTokensTable extends Migration { /** * Run the migrations. @@ -14,7 +14,7 @@ return new class extends Migration public function up() { Schema::create('personal_access_tokens', function (Blueprint $table) { - $table->id(); + $table->bigIncrements('id'); $table->morphs('tokenable'); $table->string('name'); $table->string('token', 64)->unique(); @@ -33,4 +33,4 @@ return new class extends Migration { Schema::dropIfExists('personal_access_tokens'); } -}; +} diff --git a/backend/database/migrations/2022_05_26_030738_create_offers_table.php b/backend/database/migrations/2022_05_26_030738_create_offers_table.php new file mode 100644 index 0000000..186ccf1 --- /dev/null +++ b/backend/database/migrations/2022_05_26_030738_create_offers_table.php @@ -0,0 +1,47 @@ +id(); + $table->string('name'); + $table->enum('type', OrderTypesEnum::values()); + $table->decimal('price'); + $table->unsignedSmallInteger('rooms'); + $table->decimal('space'); + $table->string('yandex_mark')->nullable(); + $table->string('location'); + $table->text('description'); + $table->boolean('is_group'); + $table->timestamps(); + + $table + ->foreignId('user_id') + ->constrained() + ->onDelete('set null') + ->onUpdate('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('offers'); + } +}; diff --git a/backend/database/migrations/2022_05_26_035738_create_offer_photos_table.php b/backend/database/migrations/2022_05_26_035738_create_offer_photos_table.php new file mode 100644 index 0000000..4022a15 --- /dev/null +++ b/backend/database/migrations/2022_05_26_035738_create_offer_photos_table.php @@ -0,0 +1,37 @@ +id(); + $table->string('file'); + $table->timestamps(); + + $table + ->foreignId('offer_id') + ->constrained() + ->onUpdate('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('offer_photos'); + } +}; diff --git a/backend/database/migrations/2022_05_26_035905_create_feedback_table.php b/backend/database/migrations/2022_05_26_035905_create_feedback_table.php new file mode 100644 index 0000000..2c452da --- /dev/null +++ b/backend/database/migrations/2022_05_26_035905_create_feedback_table.php @@ -0,0 +1,39 @@ +id(); + $table->text('comment'); + $table->unsignedFloat('rating'); + $table->timestamps(); + + $table + ->foreignId('user_id') + ->constrained() + ->onDelete('cascade') + ->onUpdate('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('feedback'); + } +}; diff --git a/backend/database/migrations/2022_05_26_035920_create_orders_table.php b/backend/database/migrations/2022_05_26_035920_create_orders_table.php new file mode 100644 index 0000000..aa256a6 --- /dev/null +++ b/backend/database/migrations/2022_05_26_035920_create_orders_table.php @@ -0,0 +1,45 @@ +id(); + $table->decimal('price'); + $table->decimal('discount'); + $table->timestamps(); + + $table + ->foreignId('offer_id') + ->constrained() + ->onDelete('set null') + ->onUpdate('cascade'); + + $table + ->foreignId('user_id') + ->constrained() + ->onDelete('set null') + ->onUpdate('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('orders'); + } +}; diff --git a/backend/database/seeders/DatabaseSeeder.php b/backend/database/seeders/DatabaseSeeder.php index c1c48a0..95fd531 100644 --- a/backend/database/seeders/DatabaseSeeder.php +++ b/backend/database/seeders/DatabaseSeeder.php @@ -2,7 +2,7 @@ namespace Database\Seeders; -use Illuminate\Database\Console\Seeds\WithoutModelEvents; +use App\Models\User; use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder @@ -14,7 +14,7 @@ class DatabaseSeeder extends Seeder */ public function run() { - // \App\Models\User::factory(10)->create(); + User::factory(10)->create(); // \App\Models\User::factory()->create([ // 'name' => 'Test User', diff --git a/backend/routes/api.php b/backend/routes/api.php index eb6fa48..42ae39e 100644 --- a/backend/routes/api.php +++ b/backend/routes/api.php @@ -1,6 +1,11 @@ get('/user', function (Request $request) { - return $request->user(); +Route::prefix('auth')->group(function() { + + Route::post('registration', RegistrationController::class); + Route::post('login', LoginController::class); + + Route::middleware('auth:sanctum')->group(function() { + Route::post('logout', LogoutController::class); + }); + }); + +Route::middleware('auth:sanctum')->group(function() { + + Route::prefix('user')->group(function() { + Route::get('', [UserController::class, 'index']); + Route::put('', [UserController::class, 'update']); + }); + + Route::apiResource('orders', OrderController::class); + +}); + +Route::apiResource('offers', OfferController::class); + +// TODO: На главной странице 6 самых дорогих квартир +// TODO: Последние добавленные квартиры + фильтры diff --git a/backend/routes/web.php b/backend/routes/web.php index b130397..edd263d 100644 --- a/backend/routes/web.php +++ b/backend/routes/web.php @@ -13,6 +13,6 @@ use Illuminate\Support\Facades\Route; | */ -Route::get('/', function () { - return view('welcome'); -}); +//Route::get('/', function () { +// return view('welcome'); +//});