From fe2e5d316c0317f4d34c4fd6fc7ab6e8e79ed306 Mon Sep 17 00:00:00 2001 From: robonen Date: Sun, 29 May 2022 04:21:56 +0700 Subject: [PATCH 1/3] Offer filters and user feedback --- backend/app/Filters/Filterable.php | 18 +++++ backend/app/Filters/OfferFilter.php | 57 +++++++++++++++ backend/app/Filters/QueryFilter.php | 46 +++++++++++++ .../Http/Controllers/FeedbackController.php | 69 +++++++++++++++++++ .../app/Http/Controllers/OfferController.php | 5 +- .../Http/Requests/StoreFeedbackRequest.php | 19 +++++ .../Http/Requests/UpdateFeedbackRequest.php | 19 +++++ .../app/Http/Resources/FeedbackResource.php | 19 +++++ backend/app/Models/Feedback.php | 9 +++ backend/app/Models/Offer.php | 3 +- backend/app/Models/User.php | 5 ++ backend/config/cors.php | 2 +- backend/routes/api.php | 11 ++- 13 files changed, 272 insertions(+), 10 deletions(-) create mode 100644 backend/app/Filters/Filterable.php create mode 100644 backend/app/Filters/OfferFilter.php create mode 100644 backend/app/Filters/QueryFilter.php create mode 100644 backend/app/Http/Controllers/FeedbackController.php create mode 100644 backend/app/Http/Requests/StoreFeedbackRequest.php create mode 100644 backend/app/Http/Requests/UpdateFeedbackRequest.php create mode 100644 backend/app/Http/Resources/FeedbackResource.php diff --git a/backend/app/Filters/Filterable.php b/backend/app/Filters/Filterable.php new file mode 100644 index 0000000..cf7d5af --- /dev/null +++ b/backend/app/Filters/Filterable.php @@ -0,0 +1,18 @@ +apply($builder); + } +} diff --git a/backend/app/Filters/OfferFilter.php b/backend/app/Filters/OfferFilter.php new file mode 100644 index 0000000..3b03f1c --- /dev/null +++ b/backend/app/Filters/OfferFilter.php @@ -0,0 +1,57 @@ +builder->where('type', $type); + + return $this->builder; + } + + /** + * @param int $rooms + * @return \Illuminate\Database\Eloquent\Builder + */ + protected function rooms(int $rooms): Builder + { + if ($rooms >= 1) + return $this->builder->where('rooms', '>=', $rooms); + + return $this->builder; + } + + /** + * @param int $price + * @return \Illuminate\Database\Eloquent\Builder + */ + protected function startPrice(int $price): Builder + { + if ($price >= 0) + return $this->builder->where('price', '>=', $price); + + return $this->builder; + } + + /** + * @param int $price + * @return \Illuminate\Database\Eloquent\Builder + */ + protected function endPrice(int $price): Builder + { + if ($price >= 0) + return $this->builder->where('price', '<=', $price); + + return $this->builder; + } +} diff --git a/backend/app/Filters/QueryFilter.php b/backend/app/Filters/QueryFilter.php new file mode 100644 index 0000000..f55ad87 --- /dev/null +++ b/backend/app/Filters/QueryFilter.php @@ -0,0 +1,46 @@ +request = $request; + } + + /** + * @param \Illuminate\Database\Eloquent\Builder $builder + * @return \Illuminate\Database\Eloquent\Builder + */ + public function apply(Builder $builder): Builder + { + $this->builder = $builder; + + foreach ($this->filters() as $name => $value) { + if (method_exists($this, $name)) { + call_user_func_array([$this, $name], array_filter([$value])); + } + } + + return $this->builder; + } + + /** + * @return array|string|null + */ + private function filters(): array|string|null + { + return $this->request->query(); + } +} diff --git a/backend/app/Http/Controllers/FeedbackController.php b/backend/app/Http/Controllers/FeedbackController.php new file mode 100644 index 0000000..1cf93d8 --- /dev/null +++ b/backend/app/Http/Controllers/FeedbackController.php @@ -0,0 +1,69 @@ +user()->feedback()->get()); + } + + /** + * Display the specified resource. + * + * @param int $id + * @return \App\Http\Resources\FeedbackResource + */ + public function show(int $id): FeedbackResource + { + $feedback = auth()->user()->feedback()->findOrFail($id); + return new FeedbackResource($feedback); + } + + /** + * Store a newly created resource in storage. + * + * @param \App\Http\Requests\StoreFeedbackRequest $request + * @return \App\Http\Resources\FeedbackResource + */ + public function store(StoreFeedbackRequest $request): FeedbackResource + { + $feedback = auth()->user()->feedback()->create($request->validated()); + return new FeedbackResource($feedback); + } + + /** + * Update the specified resource in storage. + * + * @param \App\Http\Requests\UpdateFeedbackRequest $request + * @param int $id + * @return void + */ + public function update(UpdateFeedbackRequest $request, int $id): void + { + $feedback = auth()->user()->feedback()->findOrFail($id); + $feedback->update($request->validated()); + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * @return void + */ + public function destroy(int $id): void + { + auth()->user()->feedback()->findOrFail($id)->delete(); + } +} diff --git a/backend/app/Http/Controllers/OfferController.php b/backend/app/Http/Controllers/OfferController.php index 7ab8e53..915494f 100644 --- a/backend/app/Http/Controllers/OfferController.php +++ b/backend/app/Http/Controllers/OfferController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use App\Filters\OfferFilter; use App\Http\Requests\StoreOfferRequest; use App\Http\Requests\UpdateOfferRequest; use App\Http\Resources\OfferResource; @@ -15,9 +16,9 @@ class OfferController extends Controller * * @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection */ - public function index(): AnonymousResourceCollection + public function index(OfferFilter $filters): AnonymousResourceCollection { - return OfferResource::collection(Offer::all()); + return OfferResource::collection(Offer::filter($filters)->get()); } /** diff --git a/backend/app/Http/Requests/StoreFeedbackRequest.php b/backend/app/Http/Requests/StoreFeedbackRequest.php new file mode 100644 index 0000000..abbf80e --- /dev/null +++ b/backend/app/Http/Requests/StoreFeedbackRequest.php @@ -0,0 +1,19 @@ + + */ + public function rules() + { + return [ + 'comment' => ['required', 'string', 'max:2000'], + 'rating' => ['required', 'numeric', 'min:1', 'max:5'], + ]; + } +} diff --git a/backend/app/Http/Requests/UpdateFeedbackRequest.php b/backend/app/Http/Requests/UpdateFeedbackRequest.php new file mode 100644 index 0000000..3b1a355 --- /dev/null +++ b/backend/app/Http/Requests/UpdateFeedbackRequest.php @@ -0,0 +1,19 @@ + + */ + public function rules() + { + return [ + 'comment' => ['string', 'max:2000'], + 'rating' => ['numeric', 'min:1', 'max:5'], + ]; + } +} diff --git a/backend/app/Http/Resources/FeedbackResource.php b/backend/app/Http/Resources/FeedbackResource.php new file mode 100644 index 0000000..6fb29be --- /dev/null +++ b/backend/app/Http/Resources/FeedbackResource.php @@ -0,0 +1,19 @@ + + */ + protected $casts = [ + 'rating' => 'integer', + ]; } diff --git a/backend/app/Models/Offer.php b/backend/app/Models/Offer.php index edb7327..d345902 100644 --- a/backend/app/Models/Offer.php +++ b/backend/app/Models/Offer.php @@ -3,12 +3,13 @@ namespace App\Models; use App\Enums\OrderTypesEnum; +use App\Filters\Filterable; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Offer extends Model { - use HasFactory; + use HasFactory, Filterable; /** * The attributes that are mass assignable. diff --git a/backend/app/Models/User.php b/backend/app/Models/User.php index 0dd1542..3088a5e 100644 --- a/backend/app/Models/User.php +++ b/backend/app/Models/User.php @@ -34,4 +34,9 @@ class User extends Authenticatable protected $hidden = [ 'password', ]; + + public function feedback() + { + return $this->hasMany(Feedback::class); + } } diff --git a/backend/config/cors.php b/backend/config/cors.php index 8a39e6d..94f07be 100644 --- a/backend/config/cors.php +++ b/backend/config/cors.php @@ -29,6 +29,6 @@ return [ 'max_age' => 0, - 'supports_credentials' => false, + 'supports_credentials' => true, ]; diff --git a/backend/routes/api.php b/backend/routes/api.php index 42ae39e..c4c0c45 100644 --- a/backend/routes/api.php +++ b/backend/routes/api.php @@ -3,6 +3,7 @@ use App\Http\Controllers\Auth\LoginController; use App\Http\Controllers\Auth\LogoutController; use App\Http\Controllers\Auth\RegistrationController; +use App\Http\Controllers\FeedbackController; use App\Http\Controllers\OfferController; use App\Http\Controllers\OrderController; use App\Http\Controllers\UserController; @@ -32,16 +33,14 @@ Route::prefix('auth')->group(function() { Route::middleware('auth:sanctum')->group(function() { - Route::prefix('user')->group(function() { + Route::apiResource('orders', OrderController::class); + + Route::prefix('users')->group(function() { Route::get('', [UserController::class, 'index']); Route::put('', [UserController::class, 'update']); + Route::apiResource('feedback', FeedbackController::class); }); - Route::apiResource('orders', OrderController::class); - }); Route::apiResource('offers', OfferController::class); - -// TODO: На главной странице 6 самых дорогих квартир -// TODO: Последние добавленные квартиры + фильтры From 675b692c81e9e39bd9000aac5115fdc78b77878f Mon Sep 17 00:00:00 2001 From: robonen Date: Wed, 1 Jun 2022 00:23:05 +0700 Subject: [PATCH 2/3] Image uploading --- backend/app/Enums/RoomTypesEnum.php | 13 ++++ backend/app/Filters/OfferFilter.php | 4 +- .../app/Http/Controllers/OfferController.php | 38 +++++++++-- .../Controllers/UploadImageController.php | 36 +++++++++++ .../app/Http/Requests/StoreOfferRequest.php | 5 +- .../app/Http/Requests/UpdateOfferRequest.php | 5 +- .../app/Http/Requests/UploadImageRequest.php | 18 ++++++ .../Http/Resources/UploadImageResource.php | 19 ++++++ backend/app/Models/Offer.php | 11 ++++ backend/app/Models/User.php | 5 ++ .../2022_05_26_030738_create_offers_table.php | 5 +- ...05_26_035738_create_offer_photos_table.php | 1 + backend/docker-compose.yml | 2 +- backend/docker/7.4/Dockerfile | 62 ++++++++++++++++++ backend/docker/7.4/php.ini | 4 ++ backend/docker/7.4/start-container | 17 +++++ backend/docker/7.4/supervisord.conf | 14 ++++ backend/docker/8.0/Dockerfile | 64 +++++++++++++++++++ backend/docker/8.0/php.ini | 4 ++ backend/docker/8.0/start-container | 17 +++++ backend/docker/8.0/supervisord.conf | 14 ++++ backend/docker/8.1/Dockerfile | 63 ++++++++++++++++++ backend/docker/8.1/php.ini | 4 ++ backend/docker/8.1/start-container | 17 +++++ backend/docker/8.1/supervisord.conf | 14 ++++ backend/routes/api.php | 7 +- backend/routes/web.php | 3 +- 27 files changed, 451 insertions(+), 15 deletions(-) create mode 100644 backend/app/Enums/RoomTypesEnum.php create mode 100644 backend/app/Http/Controllers/UploadImageController.php create mode 100644 backend/app/Http/Requests/UploadImageRequest.php create mode 100644 backend/app/Http/Resources/UploadImageResource.php create mode 100644 backend/docker/7.4/Dockerfile create mode 100644 backend/docker/7.4/php.ini create mode 100644 backend/docker/7.4/start-container create mode 100644 backend/docker/7.4/supervisord.conf create mode 100644 backend/docker/8.0/Dockerfile create mode 100644 backend/docker/8.0/php.ini create mode 100644 backend/docker/8.0/start-container create mode 100644 backend/docker/8.0/supervisord.conf create mode 100644 backend/docker/8.1/Dockerfile create mode 100644 backend/docker/8.1/php.ini create mode 100644 backend/docker/8.1/start-container create mode 100644 backend/docker/8.1/supervisord.conf diff --git a/backend/app/Enums/RoomTypesEnum.php b/backend/app/Enums/RoomTypesEnum.php new file mode 100644 index 0000000..cf41e7a --- /dev/null +++ b/backend/app/Enums/RoomTypesEnum.php @@ -0,0 +1,13 @@ +builder->where('type', $type); return $this->builder; diff --git a/backend/app/Http/Controllers/OfferController.php b/backend/app/Http/Controllers/OfferController.php index 915494f..eeaa127 100644 --- a/backend/app/Http/Controllers/OfferController.php +++ b/backend/app/Http/Controllers/OfferController.php @@ -7,6 +7,7 @@ use App\Http\Requests\StoreOfferRequest; use App\Http\Requests\UpdateOfferRequest; use App\Http\Resources\OfferResource; use App\Models\Offer; +use App\Models\OfferPhoto; use Illuminate\Http\Resources\Json\AnonymousResourceCollection; class OfferController extends Controller @@ -14,11 +15,17 @@ class OfferController extends Controller /** * Display a listing of the resource. * + * @param \App\Filters\OfferFilter $filters * @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection */ public function index(OfferFilter $filters): AnonymousResourceCollection { - return OfferResource::collection(Offer::filter($filters)->get()); + return OfferResource::collection( + Offer::filter($filters) + ->latest() + ->with('images') + ->get() + ); } /** @@ -29,8 +36,7 @@ class OfferController extends Controller */ public function show(int $id): OfferResource { - $offer = Offer::findOrFail($id); - return new OfferResource($offer); + return new OfferResource(Offer::with('images')->findOrFail($id)); } /** @@ -41,7 +47,19 @@ class OfferController extends Controller */ public function store(StoreOfferRequest $request): OfferResource { - return new OfferResource(Offer::create($request->validated())); + $data = $request->validated(); + $images = $data['images']; + + $offer = auth()->user()->offers()->create($data); + + foreach ($images as $image) + { + $offer_photo = OfferPhoto::findOrFail($image['id']); + $offer_photo->offer_id = $offer->id; + $offer_photo->save(); + } + + return new OfferResource($offer); } /** @@ -68,4 +86,16 @@ class OfferController extends Controller $offer = Offer::findOrFail($id); $offer->delete(); } + + public function allByUser(): AnonymousResourceCollection + { + return OfferResource::collection( + auth() + ->user() + ->offers() + ->latest() + ->with('images') + ->get() + ); + } } diff --git a/backend/app/Http/Controllers/UploadImageController.php b/backend/app/Http/Controllers/UploadImageController.php new file mode 100644 index 0000000..cf99d4b --- /dev/null +++ b/backend/app/Http/Controllers/UploadImageController.php @@ -0,0 +1,36 @@ +file('image')->store('public/offers'); + + $offer_photo = OfferPhoto::create([ + 'file' => $image, + ]); + + return new UploadImageResource($offer_photo); + } + + public function deleteCatalog(int $id): void + { + $offer_photo = OfferPhoto::findOrFail($id); + + if (Storage::exists($offer_photo->file)) + Storage::delete($offer_photo->file); + + $offer_photo->delete(); + } +} diff --git a/backend/app/Http/Requests/StoreOfferRequest.php b/backend/app/Http/Requests/StoreOfferRequest.php index 15615dd..558032b 100644 --- a/backend/app/Http/Requests/StoreOfferRequest.php +++ b/backend/app/Http/Requests/StoreOfferRequest.php @@ -3,6 +3,7 @@ namespace App\Http\Requests; use App\Enums\OrderTypesEnum; +use App\Enums\RoomTypesEnum; use Illuminate\Validation\Rules\Enum; class StoreOfferRequest extends BaseRequest @@ -15,16 +16,16 @@ class StoreOfferRequest extends BaseRequest 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'], + 'rooms' => ['required', new Enum(RoomTypesEnum::class)], 'space' => ['required', 'numeric', 'min:1'], 'yandex_mark' => ['string'], 'location' => ['required', 'string'], 'description' => ['required', 'string'], 'is_group' => ['required', 'boolean'], + 'images' => ['required', 'array'], ]; } } diff --git a/backend/app/Http/Requests/UpdateOfferRequest.php b/backend/app/Http/Requests/UpdateOfferRequest.php index 4e0903e..d48895c 100644 --- a/backend/app/Http/Requests/UpdateOfferRequest.php +++ b/backend/app/Http/Requests/UpdateOfferRequest.php @@ -3,6 +3,7 @@ namespace App\Http\Requests; use App\Enums\OrderTypesEnum; +use App\Enums\RoomTypesEnum; use Illuminate\Validation\Rules\Enum; class UpdateOfferRequest extends BaseRequest @@ -15,16 +16,16 @@ class UpdateOfferRequest extends BaseRequest 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'], + 'rooms' => [new Enum(RoomTypesEnum::class)], 'space' => ['numeric', 'min:1'], 'yandex_mark' => ['string'], 'location' => ['string'], 'description' => ['string'], 'is_group' => ['boolean'], + 'images' => ['array'], ]; } } diff --git a/backend/app/Http/Requests/UploadImageRequest.php b/backend/app/Http/Requests/UploadImageRequest.php new file mode 100644 index 0000000..654b390 --- /dev/null +++ b/backend/app/Http/Requests/UploadImageRequest.php @@ -0,0 +1,18 @@ + + */ + public function rules() + { + return [ + 'image' => ['required', 'image', 'mimes:jpg,png,jpeg', 'max:15000'], + ]; + } +} diff --git a/backend/app/Http/Resources/UploadImageResource.php b/backend/app/Http/Resources/UploadImageResource.php new file mode 100644 index 0000000..3bac106 --- /dev/null +++ b/backend/app/Http/Resources/UploadImageResource.php @@ -0,0 +1,19 @@ + OrderTypesEnum::class, + 'rooms' => RoomTypesEnum::class, 'space' => 'double', 'price' => 'double', 'is_group' => 'boolean', ]; + + /** + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function images(): HasMany + { + return $this->hasMany(OfferPhoto::class); + } } diff --git a/backend/app/Models/User.php b/backend/app/Models/User.php index 3088a5e..7b63a17 100644 --- a/backend/app/Models/User.php +++ b/backend/app/Models/User.php @@ -39,4 +39,9 @@ class User extends Authenticatable { return $this->hasMany(Feedback::class); } + + public function offers() + { + return $this->hasMany(Offer::class); + } } 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 index 186ccf1..2385de9 100644 --- a/backend/database/migrations/2022_05_26_030738_create_offers_table.php +++ b/backend/database/migrations/2022_05_26_030738_create_offers_table.php @@ -1,6 +1,7 @@ id(); $table->string('name'); $table->enum('type', OrderTypesEnum::values()); - $table->decimal('price'); - $table->unsignedSmallInteger('rooms'); + $table->decimal('price', 15); + $table->enum('rooms', RoomTypesEnum::values()); $table->decimal('space'); $table->string('yandex_mark')->nullable(); $table->string('location'); 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 index 4022a15..bd31bb4 100644 --- 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 @@ -20,6 +20,7 @@ return new class extends Migration $table ->foreignId('offer_id') + ->nullable() ->constrained() ->onUpdate('cascade'); }); diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml index e3067be..d21d912 100644 --- a/backend/docker-compose.yml +++ b/backend/docker-compose.yml @@ -3,7 +3,7 @@ version: '3' services: laravel.test: build: - context: ./vendor/laravel/sail/runtimes/8.1 + context: ./docker/8.1 dockerfile: Dockerfile args: WWWGROUP: '${WWWGROUP}' diff --git a/backend/docker/7.4/Dockerfile b/backend/docker/7.4/Dockerfile new file mode 100644 index 0000000..cc500ce --- /dev/null +++ b/backend/docker/7.4/Dockerfile @@ -0,0 +1,62 @@ +FROM ubuntu:21.10 + +LABEL maintainer="Taylor Otwell" + +ARG WWWGROUP +ARG NODE_VERSION=16 +ARG POSTGRES_VERSION=14 + +WORKDIR /var/www/html + +ENV DEBIAN_FRONTEND noninteractive +ENV TZ=UTC + +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update \ + && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 \ + && mkdir -p ~/.gnupg \ + && chmod 600 ~/.gnupg \ + && echo "disable-ipv6" >> ~/.gnupg/dirmngr.conf \ + && echo "keyserver hkp://keyserver.ubuntu.com:80" >> ~/.gnupg/dirmngr.conf \ + && gpg --recv-key 0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c \ + && gpg --export 0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c > /usr/share/keyrings/ppa_ondrej_php.gpg \ + && echo "deb [signed-by=/usr/share/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu impish main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ + && apt-get update \ + && apt-get install -y php7.4-cli php7.4-dev \ + php7.4-pgsql php7.4-sqlite3 php7.4-gd \ + php7.4-curl php7.4-memcached \ + php7.4-imap php7.4-mysql php7.4-mbstring \ + php7.4-xml php7.4-zip php7.4-bcmath php7.4-soap \ + php7.4-intl php7.4-readline php7.4-pcov \ + php7.4-msgpack php7.4-igbinary php7.4-ldap \ + php7.4-redis php7.4-xdebug \ + && php -r "readfile('https://getcomposer.org/installer');" | php -- --install-dir=/usr/bin/ --filename=composer \ + && curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \ + && apt-get install -y nodejs \ + && npm install -g npm \ + && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /usr/share/keyrings/yarnkey.gpg >/dev/null \ + && echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ + && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/pgdg.gpg >/dev/null \ + && echo "deb [signed-by=/usr/share/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt impish-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ + && apt-get update \ + && apt-get install -y yarn \ + && apt-get install -y mysql-client \ + && apt-get install -y postgresql-client-$POSTGRES_VERSION \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN setcap "cap_net_bind_service=+ep" /usr/bin/php7.4 + +RUN groupadd --force -g $WWWGROUP sail +RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail + +COPY start-container /usr/local/bin/start-container +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf +COPY php.ini /etc/php/7.4/cli/conf.d/99-sail.ini +RUN chmod +x /usr/local/bin/start-container + +EXPOSE 8000 + +ENTRYPOINT ["start-container"] diff --git a/backend/docker/7.4/php.ini b/backend/docker/7.4/php.ini new file mode 100644 index 0000000..66d04d5 --- /dev/null +++ b/backend/docker/7.4/php.ini @@ -0,0 +1,4 @@ +[PHP] +post_max_size = 100M +upload_max_filesize = 100M +variables_order = EGPCS diff --git a/backend/docker/7.4/start-container b/backend/docker/7.4/start-container new file mode 100644 index 0000000..b99ddd0 --- /dev/null +++ b/backend/docker/7.4/start-container @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +if [ ! -z "$WWWUSER" ]; then + usermod -u $WWWUSER sail +fi + +if [ ! -d /.composer ]; then + mkdir /.composer +fi + +chmod -R ugo+rw /.composer + +if [ $# -gt 0 ]; then + exec gosu $WWWUSER "$@" +else + /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf +fi diff --git a/backend/docker/7.4/supervisord.conf b/backend/docker/7.4/supervisord.conf new file mode 100644 index 0000000..9d28479 --- /dev/null +++ b/backend/docker/7.4/supervisord.conf @@ -0,0 +1,14 @@ +[supervisord] +nodaemon=true +user=root +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid + +[program:php] +command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80 +user=sail +environment=LARAVEL_SAIL="1" +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/backend/docker/8.0/Dockerfile b/backend/docker/8.0/Dockerfile new file mode 100644 index 0000000..096adad --- /dev/null +++ b/backend/docker/8.0/Dockerfile @@ -0,0 +1,64 @@ +FROM ubuntu:21.10 + +LABEL maintainer="Taylor Otwell" + +ARG WWWGROUP +ARG NODE_VERSION=16 +ARG POSTGRES_VERSION=14 + +WORKDIR /var/www/html + +ENV DEBIAN_FRONTEND noninteractive +ENV TZ=UTC + +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update \ + && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 \ + && mkdir -p ~/.gnupg \ + && chmod 600 ~/.gnupg \ + && echo "disable-ipv6" >> ~/.gnupg/dirmngr.conf \ + && echo "keyserver hkp://keyserver.ubuntu.com:80" >> ~/.gnupg/dirmngr.conf \ + && gpg --recv-key 0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c \ + && gpg --export 0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c > /usr/share/keyrings/ppa_ondrej_php.gpg \ + && echo "deb [signed-by=/usr/share/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu impish main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ + && apt-get update \ + && apt-get install -y php8.0-cli php8.0-dev \ + php8.0-pgsql php8.0-sqlite3 php8.0-gd \ + php8.0-curl php8.0-memcached \ + php8.0-imap php8.0-mysql php8.0-mbstring \ + php8.0-xml php8.0-zip php8.0-bcmath php8.0-soap \ + php8.0-intl php8.0-readline php8.0-pcov \ + php8.0-msgpack php8.0-igbinary php8.0-ldap \ + php8.0-redis php8.0-swoole php8.0-xdebug \ + && php -r "readfile('https://getcomposer.org/installer');" | php -- --install-dir=/usr/bin/ --filename=composer \ + && curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \ + && apt-get install -y nodejs \ + && npm install -g npm \ + && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /usr/share/keyrings/yarnkey.gpg >/dev/null \ + && echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ + && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/pgdg.gpg >/dev/null \ + && echo "deb [signed-by=/usr/share/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt impish-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ + && apt-get update \ + && apt-get install -y yarn \ + && apt-get install -y mysql-client \ + && apt-get install -y postgresql-client-$POSTGRES_VERSION \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN update-alternatives --set php /usr/bin/php8.0 + +RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.0 + +RUN groupadd --force -g $WWWGROUP sail +RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail + +COPY start-container /usr/local/bin/start-container +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf +COPY php.ini /etc/php/8.0/cli/conf.d/99-sail.ini +RUN chmod +x /usr/local/bin/start-container + +EXPOSE 8000 + +ENTRYPOINT ["start-container"] diff --git a/backend/docker/8.0/php.ini b/backend/docker/8.0/php.ini new file mode 100644 index 0000000..66d04d5 --- /dev/null +++ b/backend/docker/8.0/php.ini @@ -0,0 +1,4 @@ +[PHP] +post_max_size = 100M +upload_max_filesize = 100M +variables_order = EGPCS diff --git a/backend/docker/8.0/start-container b/backend/docker/8.0/start-container new file mode 100644 index 0000000..b99ddd0 --- /dev/null +++ b/backend/docker/8.0/start-container @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +if [ ! -z "$WWWUSER" ]; then + usermod -u $WWWUSER sail +fi + +if [ ! -d /.composer ]; then + mkdir /.composer +fi + +chmod -R ugo+rw /.composer + +if [ $# -gt 0 ]; then + exec gosu $WWWUSER "$@" +else + /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf +fi diff --git a/backend/docker/8.0/supervisord.conf b/backend/docker/8.0/supervisord.conf new file mode 100644 index 0000000..9d28479 --- /dev/null +++ b/backend/docker/8.0/supervisord.conf @@ -0,0 +1,14 @@ +[supervisord] +nodaemon=true +user=root +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid + +[program:php] +command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80 +user=sail +environment=LARAVEL_SAIL="1" +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/backend/docker/8.1/Dockerfile b/backend/docker/8.1/Dockerfile new file mode 100644 index 0000000..0277709 --- /dev/null +++ b/backend/docker/8.1/Dockerfile @@ -0,0 +1,63 @@ +FROM ubuntu:22.04 + +LABEL maintainer="Taylor Otwell" + +ARG WWWGROUP +ARG NODE_VERSION=16 +ARG POSTGRES_VERSION=14 + +WORKDIR /var/www/html + +ENV DEBIAN_FRONTEND noninteractive +ENV TZ=UTC + +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt-get update \ + && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 \ + && mkdir -p ~/.gnupg \ + && chmod 600 ~/.gnupg \ + && echo "disable-ipv6" >> ~/.gnupg/dirmngr.conf \ + && echo "keyserver hkp://keyserver.ubuntu.com:80" >> ~/.gnupg/dirmngr.conf \ + && gpg --recv-key 0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c \ + && gpg --export 0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c > /usr/share/keyrings/ppa_ondrej_php.gpg \ + && echo "deb [signed-by=/usr/share/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ + && apt-get update \ + && apt-get install -y php8.1-cli php8.1-dev \ + php8.1-pgsql php8.1-sqlite3 php8.1-gd \ + php8.1-curl \ + php8.1-imap php8.1-mysql php8.1-mbstring \ + php8.1-xml php8.1-zip php8.1-bcmath php8.1-soap \ + php8.1-intl php8.1-readline \ + php8.1-ldap \ + php8.1-msgpack php8.1-igbinary php8.1-redis php8.1-swoole \ + php8.1-memcached php8.1-pcov php8.1-xdebug \ + && php -r "readfile('https://getcomposer.org/installer');" | php -- --install-dir=/usr/bin/ --filename=composer \ + && curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \ + && apt-get install -y nodejs \ + && npm install -g npm \ + && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /usr/share/keyrings/yarn.gpg >/dev/null \ + && echo "deb [signed-by=/usr/share/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ + && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/pgdg.gpg >/dev/null \ + && echo "deb [signed-by=/usr/share/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ + && apt-get update \ + && apt-get install -y yarn \ + && apt-get install -y mysql-client \ + && apt-get install -y postgresql-client-$POSTGRES_VERSION \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.1 + +RUN groupadd --force -g $WWWGROUP sail +RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail + +COPY start-container /usr/local/bin/start-container +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf +COPY php.ini /etc/php/8.1/cli/conf.d/99-sail.ini +RUN chmod +x /usr/local/bin/start-container + +EXPOSE 8000 + +ENTRYPOINT ["start-container"] diff --git a/backend/docker/8.1/php.ini b/backend/docker/8.1/php.ini new file mode 100644 index 0000000..bf782b2 --- /dev/null +++ b/backend/docker/8.1/php.ini @@ -0,0 +1,4 @@ +[PHP] +upload_max_filesize = 15M +post_max_size = 16M +variables_order = EGPCS diff --git a/backend/docker/8.1/start-container b/backend/docker/8.1/start-container new file mode 100644 index 0000000..b99ddd0 --- /dev/null +++ b/backend/docker/8.1/start-container @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +if [ ! -z "$WWWUSER" ]; then + usermod -u $WWWUSER sail +fi + +if [ ! -d /.composer ]; then + mkdir /.composer +fi + +chmod -R ugo+rw /.composer + +if [ $# -gt 0 ]; then + exec gosu $WWWUSER "$@" +else + /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf +fi diff --git a/backend/docker/8.1/supervisord.conf b/backend/docker/8.1/supervisord.conf new file mode 100644 index 0000000..9d28479 --- /dev/null +++ b/backend/docker/8.1/supervisord.conf @@ -0,0 +1,14 @@ +[supervisord] +nodaemon=true +user=root +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid + +[program:php] +command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80 +user=sail +environment=LARAVEL_SAIL="1" +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/backend/routes/api.php b/backend/routes/api.php index c4c0c45..51f73ea 100644 --- a/backend/routes/api.php +++ b/backend/routes/api.php @@ -6,6 +6,7 @@ use App\Http\Controllers\Auth\RegistrationController; use App\Http\Controllers\FeedbackController; use App\Http\Controllers\OfferController; use App\Http\Controllers\OrderController; +use App\Http\Controllers\UploadImageController; use App\Http\Controllers\UserController; use Illuminate\Support\Facades\Route; @@ -38,9 +39,13 @@ Route::middleware('auth:sanctum')->group(function() { Route::prefix('users')->group(function() { Route::get('', [UserController::class, 'index']); Route::put('', [UserController::class, 'update']); + Route::get('offers', [OfferController::class, 'allByUser']); Route::apiResource('feedback', FeedbackController::class); }); + Route::apiResource('offers', OfferController::class); + + Route::post('offers/images', [UploadImageController::class, 'uploadCatalog']); + Route::delete('offers/images/{id}', [UploadImageController::class, 'deleteCatalog']); }); -Route::apiResource('offers', OfferController::class); diff --git a/backend/routes/web.php b/backend/routes/web.php index edd263d..652982b 100644 --- a/backend/routes/web.php +++ b/backend/routes/web.php @@ -14,5 +14,6 @@ use Illuminate\Support\Facades\Route; */ //Route::get('/', function () { -// return view('welcome'); +// phpinfo(); +//// return view('welcome'); //}); From 210077df9d5e3500c12aa9630f41b1ed499f34ca Mon Sep 17 00:00:00 2001 From: robonen Date: Wed, 1 Jun 2022 23:32:14 +0700 Subject: [PATCH 3/3] Offer relations controller --- backend/app/Enums/OrderTypesEnum.php | 3 +- backend/app/Filters/OfferFilter.php | 33 ++++++++++++++++--- .../app/Http/Controllers/OfferController.php | 16 +++++++-- backend/app/Models/Feedback.php | 11 +++++++ backend/app/Models/Offer.php | 12 +++++++ backend/app/Models/User.php | 5 +-- 6 files changed, 69 insertions(+), 11 deletions(-) diff --git a/backend/app/Enums/OrderTypesEnum.php b/backend/app/Enums/OrderTypesEnum.php index 6ae3c41..64f7224 100644 --- a/backend/app/Enums/OrderTypesEnum.php +++ b/backend/app/Enums/OrderTypesEnum.php @@ -8,6 +8,5 @@ enum OrderTypesEnum: string case HOUSE = 'House'; case FLAT = 'Flat'; - case GUEST_HOUSE = 'Guest'; - case HOTEL = 'Hotel'; + case Land = 'Land'; } diff --git a/backend/app/Filters/OfferFilter.php b/backend/app/Filters/OfferFilter.php index 1cd0779..5a9ae7b 100644 --- a/backend/app/Filters/OfferFilter.php +++ b/backend/app/Filters/OfferFilter.php @@ -2,6 +2,7 @@ namespace App\Filters; +use App\Enums\OrderTypesEnum; use App\Enums\RoomTypesEnum; use Illuminate\Database\Eloquent\Builder; @@ -13,7 +14,7 @@ class OfferFilter extends QueryFilter */ protected function type(string $type): Builder { - if (RoomTypesEnum::tryFrom($type) !== null) + if (OrderTypesEnum::tryFrom($type) !== null) return $this->builder->where('type', $type); return $this->builder; @@ -23,10 +24,10 @@ class OfferFilter extends QueryFilter * @param int $rooms * @return \Illuminate\Database\Eloquent\Builder */ - protected function rooms(int $rooms): Builder + protected function rooms(string $type): Builder { - if ($rooms >= 1) - return $this->builder->where('rooms', '>=', $rooms); + if (RoomTypesEnum::tryFrom($type) !== null) + return $this->builder->where('rooms', $type); return $this->builder; } @@ -54,4 +55,28 @@ class OfferFilter extends QueryFilter return $this->builder; } + + /** + * @param int $space + * @return \Illuminate\Database\Eloquent\Builder + */ + protected function startSpace(int $space): Builder + { + if ($space >= 0) + return $this->builder->where('space', '>=', $space); + + return $this->builder; + } + + /** + * @param int $space + * @return \Illuminate\Database\Eloquent\Builder + */ + protected function endSpace(int $space): Builder + { + if ($space >= 0) + return $this->builder->where('space', '<=', $space); + + return $this->builder; + } } diff --git a/backend/app/Http/Controllers/OfferController.php b/backend/app/Http/Controllers/OfferController.php index eeaa127..48b66e3 100644 --- a/backend/app/Http/Controllers/OfferController.php +++ b/backend/app/Http/Controllers/OfferController.php @@ -36,7 +36,7 @@ class OfferController extends Controller */ public function show(int $id): OfferResource { - return new OfferResource(Offer::with('images')->findOrFail($id)); + return new OfferResource(Offer::with(['images', 'user'])->findOrFail($id)); } /** @@ -71,8 +71,18 @@ class OfferController extends Controller */ public function update(UpdateOfferRequest $request, int $id): void { - $offer = Offer::findOrFail($id); - $offer->update($request->validated()); + $data = $request->validated(); + $images = $data['images']; + + $offer = auth()->user()->offers()->findOrFail($id); + $offer->update($data); + + foreach ($images as $image) + { + $offer_photo = OfferPhoto::findOrFail($image['id']); + $offer_photo->offer_id = $offer->id; + $offer_photo->save(); + } } /** diff --git a/backend/app/Models/Feedback.php b/backend/app/Models/Feedback.php index 7084b9e..9f47e2c 100644 --- a/backend/app/Models/Feedback.php +++ b/backend/app/Models/Feedback.php @@ -4,6 +4,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; class Feedback extends Model { @@ -28,4 +29,14 @@ class Feedback extends Model protected $casts = [ 'rating' => 'integer', ]; + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ +// public function user(): BelongsTo +// { +// return $this +// ->belongsTo(User::class) +// ->select('first_name'); +// } } diff --git a/backend/app/Models/Offer.php b/backend/app/Models/Offer.php index 1671c78..d5e530b 100644 --- a/backend/app/Models/Offer.php +++ b/backend/app/Models/Offer.php @@ -7,6 +7,7 @@ use App\Enums\RoomTypesEnum; use App\Filters\Filterable; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; class Offer extends Model @@ -51,4 +52,15 @@ class Offer extends Model { return $this->hasMany(OfferPhoto::class); } + + /** + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + */ + public function user(): BelongsTo + { + return $this + ->belongsTo(User::class) + ->select('id','first_name', 'last_name', 'middle_name', 'phone', 'photo') + ->with('feedback'); + } } diff --git a/backend/app/Models/User.php b/backend/app/Models/User.php index 7b63a17..75a3954 100644 --- a/backend/app/Models/User.php +++ b/backend/app/Models/User.php @@ -3,6 +3,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; @@ -35,12 +36,12 @@ class User extends Authenticatable 'password', ]; - public function feedback() + public function feedback(): HasMany { return $this->hasMany(Feedback::class); } - public function offers() + public function offers(): HasMany { return $this->hasMany(Offer::class); }