From e0fad7af2ea98e0d0d6cc0ec03ef6434d8b0ea79 Mon Sep 17 00:00:00 2001 From: Niek van den Bos Date: Fri, 16 Jun 2023 15:51:54 +0200 Subject: [PATCH 01/12] Start working on sorting books --- app/Http/Controllers/BookController.php | 21 ++++++++++++ app/Models/Book.php | 2 ++ database/factories/BookFactory.php | 2 ++ ...2226_add_sort_order_and_stock_to_books.php | 33 +++++++++++++++++++ resources/views/books/create.blade.php | 5 +++ resources/views/books/edit.blade.php | 5 +++ resources/views/books/index.blade.php | 8 +++++ routes/web.php | 1 + 8 files changed, 77 insertions(+) create mode 100644 database/migrations/2023_06_16_132226_add_sort_order_and_stock_to_books.php diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php index 65a1194..e2bc4dc 100644 --- a/app/Http/Controllers/BookController.php +++ b/app/Http/Controllers/BookController.php @@ -42,6 +42,7 @@ public function store(Request $request) 'publication_year' => 'required|integer|min:1900|max:' . date('Y'), 'price' => 'required|numeric|min:0', 'genre' => 'required|string|max:255', + 'stock_amount' => 'required|integer|min:0', 'subgenre' => 'required|string|max:255', 'writer_id' => 'required|exists:writers,id', 'publisher_id' => 'required|exists:publishers,id', @@ -53,6 +54,8 @@ public function store(Request $request) 'publication_year' => $request->input('publication_year'), 'price' => $request->input('price'), 'genre' => $request->input('genre'), + 'sort_order' => -1, + 'stock_amount' => $request->input('stock_amount'), 'subgenre' => $request->input('subgenre'), 'writer_id' => $request->input('writer_id'), 'publisher_id' => $request->input('publisher_id'), @@ -91,6 +94,7 @@ public function update(Request $request, Book $book) 'price' => 'required|numeric|min:0', 'genre' => 'required|string|max:255', 'subgenre' => 'required|string|max:255', + 'stock_amount' => 'required|integer|min:0', 'writer_id' => 'required|exists:writers,id', 'publisher_id' => 'required|exists:publishers,id', ]); @@ -102,10 +106,27 @@ public function update(Request $request, Book $book) 'price' => $request->input('price'), 'genre' => $request->input('genre'), 'subgenre' => $request->input('subgenre'), + 'sort_order' => -1, + 'stock_amount' => $request->input('stock_amount'), 'writer_id' => $request->input('writer_id'), 'publisher_id' => $request->input('publisher_id'), ]); return redirect()->route('books.index'); } + + /** + * Reorder the books. + * + * @param \Illuminate\Http\Request $request + * @param \App\Models\Book $book + * + * @return \Illuminate\Http\RedirectResponse + */ + public function reOrder(Request $request, Book $book) + { + throw new \Exception('Not implemented yet'); + + return redirect()->route('books.index'); + } } diff --git a/app/Models/Book.php b/app/Models/Book.php index 2ef59e5..ae57ea8 100644 --- a/app/Models/Book.php +++ b/app/Models/Book.php @@ -13,6 +13,8 @@ * @property float $price * @property string $genre * @property string $subgenre + * @property int $sort_order + * @property int $stock_amount * @property int $writer_id * @property int $publisher_id * @property \Carbon\Carbon $created_at diff --git a/database/factories/BookFactory.php b/database/factories/BookFactory.php index ba933a2..b0fc665 100644 --- a/database/factories/BookFactory.php +++ b/database/factories/BookFactory.php @@ -23,6 +23,8 @@ public function definition() 'price' => $this->faker->randomFloat(2, 10, 100), 'genre' => $this->randomGenre(), 'subgenre' => $this->randomSubGenre(), + 'sort_order' => -1, + 'stock_amount' => $this->faker->numberBetween(0, 10) < 8 ? $this->faker->numberBetween(0, 100) : 0, ]; } diff --git a/database/migrations/2023_06_16_132226_add_sort_order_and_stock_to_books.php b/database/migrations/2023_06_16_132226_add_sort_order_and_stock_to_books.php new file mode 100644 index 0000000..b8f69b6 --- /dev/null +++ b/database/migrations/2023_06_16_132226_add_sort_order_and_stock_to_books.php @@ -0,0 +1,33 @@ +integer('sort_order')->after('subgenre'); + $table->integer('stock_amount')->after('sort_order'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('books', function (Blueprint $table) { + $table->dropColumn(['sort_order', 'stock_amount']); + }); + } +}; diff --git a/resources/views/books/create.blade.php b/resources/views/books/create.blade.php index fb25c38..ffc5f6e 100644 --- a/resources/views/books/create.blade.php +++ b/resources/views/books/create.blade.php @@ -37,6 +37,11 @@ +
+ + +
+
+
+ + +
+
+ + + @endforeach diff --git a/routes/web.php b/routes/web.php index fc6c194..aa6bc1f 100644 --- a/routes/web.php +++ b/routes/web.php @@ -35,3 +35,4 @@ Route::post('/books', [\App\Http\Controllers\BookController::class, 'store'])->name('books.store'); Route::get('/books/{book}/edit', [\App\Http\Controllers\BookController::class, 'edit'])->name('books.edit'); Route::put('/books/{book}', [\App\Http\Controllers\BookController::class, 'update'])->name('books.update'); +Route::post('/books/{book}/reorder', [\App\Http\Controllers\BookController::class, 'reOrder'])->name('books.reOrder'); From 53619cebf186f583ae516d3bf2da559c985ea83f Mon Sep 17 00:00:00 2001 From: Anja Basara Date: Sat, 17 Jun 2023 11:20:23 +0200 Subject: [PATCH 02/12] JIRA-002 Sorted Books by sort_order --- app/Http/Controllers/BookController.php | 4 +++- .../2023_06_16_132226_add_sort_order_and_stock_to_books.php | 4 ++-- resources/views/books/index.blade.php | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php index e2bc4dc..ce887e0 100644 --- a/app/Http/Controllers/BookController.php +++ b/app/Http/Controllers/BookController.php @@ -11,7 +11,9 @@ class BookController extends Controller { public function index() { - $books = Book::all(); + $books = Book::all()->sortBy(function (Book $book) { + return $book->sort_order > 0 ? $book->sort_order : PHP_INT_MAX; + })->values()->all(); return view('books.index', compact('books')); } diff --git a/database/migrations/2023_06_16_132226_add_sort_order_and_stock_to_books.php b/database/migrations/2023_06_16_132226_add_sort_order_and_stock_to_books.php index b8f69b6..53f35ba 100644 --- a/database/migrations/2023_06_16_132226_add_sort_order_and_stock_to_books.php +++ b/database/migrations/2023_06_16_132226_add_sort_order_and_stock_to_books.php @@ -14,8 +14,8 @@ public function up() { Schema::table('books', function (Blueprint $table) { - $table->integer('sort_order')->after('subgenre'); - $table->integer('stock_amount')->after('sort_order'); + $table->integer('sort_order')->default('-1')->after('subgenre'); + $table->integer('stock_amount')->default('0')->after('sort_order'); }); } diff --git a/resources/views/books/index.blade.php b/resources/views/books/index.blade.php index d14b258..88571e1 100644 --- a/resources/views/books/index.blade.php +++ b/resources/views/books/index.blade.php @@ -34,7 +34,7 @@ {{ $book->publisher->name }} Edit -
+ @csrf From db8eb90bf1d5fab0f6a151302749f057c7cb0945 Mon Sep 17 00:00:00 2001 From: Anja Basara Date: Sat, 17 Jun 2023 17:16:11 +0200 Subject: [PATCH 03/12] JIRA-002 Implemented logic for moving and reordering Books --- app/Http/Controllers/BookController.php | 36 +++++++++++++++++++---- app/Services/BookService.php | 38 +++++++++++++++++++++++++ resources/views/books/index.blade.php | 12 +++++++- routes/web.php | 2 +- 4 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 app/Services/BookService.php diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php index ce887e0..f7cf5d9 100644 --- a/app/Http/Controllers/BookController.php +++ b/app/Http/Controllers/BookController.php @@ -5,10 +5,17 @@ use App\Models\Book; use App\Models\Publisher; use App\Models\Writer; +use App\Services\BookService; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; class BookController extends Controller { + + public function __construct(public BookService $bookService) + { + } + public function index() { $books = Book::all()->sortBy(function (Book $book) { @@ -34,7 +41,7 @@ public function create() * Store a newly created book in the database. * * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function store(Request $request) { @@ -85,7 +92,7 @@ public function edit(Book $book) * * @param \Illuminate\Http\Request $request * @param \App\Models\Book $book - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ public function update(Request $request, Book $book) { @@ -123,11 +130,30 @@ public function update(Request $request, Book $book) * @param \Illuminate\Http\Request $request * @param \App\Models\Book $book * - * @return \Illuminate\Http\RedirectResponse + * @return RedirectResponse */ - public function reOrder(Request $request, Book $book) + public function reorder(Request $request, Book $book): RedirectResponse { - throw new \Exception('Not implemented yet'); + if ($book->stock_amount === 0) { + return back(); + } + + if ($request->input('up') && $request->input('down')) { + return back(); + } + + $request->validate([ + 'up' => 'nullable|integer|min:0', + 'down' => 'nullable|integer|min:0', + ]); + + if ($request->input('up')) { + $moveCount = (int) $request->input('up'); + } else { + $moveCount = -1 * ((int) $request->input('down')); + } + + $this->bookService->reorderBooks($book, $moveCount); return redirect()->route('books.index'); } diff --git a/app/Services/BookService.php b/app/Services/BookService.php new file mode 100644 index 0000000..5909d70 --- /dev/null +++ b/app/Services/BookService.php @@ -0,0 +1,38 @@ +sort_order; + $newPlacement = $oldPlacement - $moveCount; + + $arrayBetween = [$newPlacement, $oldPlacement]; + asort($arrayBetween); + + /** @var Book[] $books */ + $books = Book::whereBetween('sort_order', $arrayBetween)->orderBy('sort_order')->get(); + + foreach ($books as $book) { + if ($book->id === $movedBook->id) { + $book->sort_order = $newPlacement; + } else { + $book->sort_order += ($moveCount > 0 ? 1 : -1); + } + $book->save(); + } + } +} diff --git a/resources/views/books/index.blade.php b/resources/views/books/index.blade.php index 88571e1..e660703 100644 --- a/resources/views/books/index.blade.php +++ b/resources/views/books/index.blade.php @@ -34,7 +34,7 @@ {{ $book->publisher->name }} Edit - + @csrf @@ -45,5 +45,15 @@ @endforeach + + @if ($errors->any()) +
+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif
@endsection diff --git a/routes/web.php b/routes/web.php index aa6bc1f..5ceea8c 100644 --- a/routes/web.php +++ b/routes/web.php @@ -35,4 +35,4 @@ Route::post('/books', [\App\Http\Controllers\BookController::class, 'store'])->name('books.store'); Route::get('/books/{book}/edit', [\App\Http\Controllers\BookController::class, 'edit'])->name('books.edit'); Route::put('/books/{book}', [\App\Http\Controllers\BookController::class, 'update'])->name('books.update'); -Route::post('/books/{book}/reorder', [\App\Http\Controllers\BookController::class, 'reOrder'])->name('books.reOrder'); +Route::post('/books/{book}/reorder', [\App\Http\Controllers\BookController::class, 'reOrder'])->name('books.reorder'); From ee535a85ec0a19d17a279e3283b6da29d781d0ab Mon Sep 17 00:00:00 2001 From: Anja Basara Date: Sat, 17 Jun 2023 17:19:28 +0200 Subject: [PATCH 04/12] JIRA-002 Replaced qualifiers with imports --- app/Http/Controllers/BookController.php | 17 +++++----- app/Http/Controllers/PublisherController.php | 18 +++++----- app/Http/Controllers/WriterController.php | 18 +++++----- routes/web.php | 35 +++++++++++--------- 4 files changed, 48 insertions(+), 40 deletions(-) diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php index f7cf5d9..ac064d6 100644 --- a/app/Http/Controllers/BookController.php +++ b/app/Http/Controllers/BookController.php @@ -8,6 +8,7 @@ use App\Services\BookService; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; +use Illuminate\View\View; class BookController extends Controller { @@ -27,7 +28,7 @@ public function index() /** * Show the form for creating a new book. * - * @return \Illuminate\View\View + * @return View */ public function create() { @@ -40,7 +41,7 @@ public function create() /** * Store a newly created book in the database. * - * @param \Illuminate\Http\Request $request + * @param Request $request * @return RedirectResponse */ public function store(Request $request) @@ -76,8 +77,8 @@ public function store(Request $request) /** * Show the form for editing the specified book. * - * @param \App\Models\Book $book - * @return \Illuminate\View\View + * @param Book $book + * @return View */ public function edit(Book $book) { @@ -90,8 +91,8 @@ public function edit(Book $book) /** * Update the specified book in the database. * - * @param \Illuminate\Http\Request $request - * @param \App\Models\Book $book + * @param Request $request + * @param Book $book * @return RedirectResponse */ public function update(Request $request, Book $book) @@ -127,8 +128,8 @@ public function update(Request $request, Book $book) /** * Reorder the books. * - * @param \Illuminate\Http\Request $request - * @param \App\Models\Book $book + * @param Request $request + * @param Book $book * * @return RedirectResponse */ diff --git a/app/Http/Controllers/PublisherController.php b/app/Http/Controllers/PublisherController.php index ee132a6..5768c4d 100644 --- a/app/Http/Controllers/PublisherController.php +++ b/app/Http/Controllers/PublisherController.php @@ -3,7 +3,9 @@ namespace App\Http\Controllers; use App\Models\Publisher; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; +use Illuminate\View\View; class PublisherController extends Controller { @@ -16,7 +18,7 @@ public function index() /** * Show the form for creating a new publisher. * - * @return \Illuminate\View\View + * @return View */ public function create() { @@ -26,8 +28,8 @@ public function create() /** * Store a newly created publisher in the database. * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\RedirectResponse + * @param Request $request + * @return RedirectResponse */ public function store(Request $request) { @@ -47,8 +49,8 @@ public function store(Request $request) /** * Show the form for editing the specified publisher. * - * @param \App\Models\Publisher $publisher - * @return \Illuminate\View\View + * @param Publisher $publisher + * @return View */ public function edit(Publisher $publisher) { @@ -58,9 +60,9 @@ public function edit(Publisher $publisher) /** * Update the specified publisher in the database. * - * @param \Illuminate\Http\Request $request - * @param \App\Models\Publisher $publisher - * @return \Illuminate\Http\RedirectResponse + * @param Request $request + * @param Publisher $publisher + * @return RedirectResponse */ public function update(Request $request, Publisher $publisher) { diff --git a/app/Http/Controllers/WriterController.php b/app/Http/Controllers/WriterController.php index 9ebc6aa..a18bee4 100644 --- a/app/Http/Controllers/WriterController.php +++ b/app/Http/Controllers/WriterController.php @@ -3,7 +3,9 @@ namespace App\Http\Controllers; use App\Models\Writer; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; +use Illuminate\View\View; class WriterController extends Controller { @@ -16,7 +18,7 @@ public function index() /** * Show the form for creating a new writer. * - * @return \Illuminate\View\View + * @return View */ public function create() { @@ -26,8 +28,8 @@ public function create() /** * Store a newly created writer in the database. * - * @param \Illuminate\Http\Request $request - * @return \Illuminate\Http\RedirectResponse + * @param Request $request + * @return RedirectResponse */ public function store(Request $request) { @@ -47,8 +49,8 @@ public function store(Request $request) /** * Show the form for editing the specified writer. * - * @param \App\Models\Writer $writer - * @return \Illuminate\View\View + * @param Writer $writer + * @return View */ public function edit(Writer $writer) { @@ -58,9 +60,9 @@ public function edit(Writer $writer) /** * Update the specified writer in the database. * - * @param \Illuminate\Http\Request $request - * @param \App\Models\Writer $writer - * @return \Illuminate\Http\RedirectResponse + * @param Request $request + * @param Writer $writer + * @return RedirectResponse */ public function update(Request $request, Writer $writer) { diff --git a/routes/web.php b/routes/web.php index 5ceea8c..406bb88 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,5 +1,8 @@ name('publishers.index'); -Route::get('/publishers/create', [\App\Http\Controllers\PublisherController::class, 'create'])->name('publishers.create'); -Route::post('/publishers', [\App\Http\Controllers\PublisherController::class, 'store'])->name('publishers.store'); -Route::get('/publishers/{publisher}/edit', [\App\Http\Controllers\PublisherController::class, 'edit'])->name('publishers.edit'); -Route::put('/publishers/{publisher}', [\App\Http\Controllers\PublisherController::class, 'update'])->name('publishers.update'); +Route::get('/publishers', [PublisherController::class, 'index'])->name('publishers.index'); +Route::get('/publishers/create', [PublisherController::class, 'create'])->name('publishers.create'); +Route::post('/publishers', [PublisherController::class, 'store'])->name('publishers.store'); +Route::get('/publishers/{publisher}/edit', [PublisherController::class, 'edit'])->name('publishers.edit'); +Route::put('/publishers/{publisher}', [PublisherController::class, 'update'])->name('publishers.update'); -Route::get('/writers', [\App\Http\Controllers\WriterController::class, 'index'])->name('writers.index'); -Route::get('/writers/create', [\App\Http\Controllers\WriterController::class, 'create'])->name('writers.create'); -Route::post('/writers', [\App\Http\Controllers\WriterController::class, 'store'])->name('writers.store'); -Route::get('/writers/{writer}/edit', [\App\Http\Controllers\WriterController::class, 'edit'])->name('writers.edit'); -Route::put('/writers/{writer}', [\App\Http\Controllers\WriterController::class, 'update'])->name('writers.update'); +Route::get('/writers', [WriterController::class, 'index'])->name('writers.index'); +Route::get('/writers/create', [WriterController::class, 'create'])->name('writers.create'); +Route::post('/writers', [WriterController::class, 'store'])->name('writers.store'); +Route::get('/writers/{writer}/edit', [WriterController::class, 'edit'])->name('writers.edit'); +Route::put('/writers/{writer}', [WriterController::class, 'update'])->name('writers.update'); -Route::get('/books', [\App\Http\Controllers\BookController::class, 'index'])->name('books.index'); -Route::get('/books/create', [\App\Http\Controllers\BookController::class, 'create'])->name('books.create'); -Route::post('/books', [\App\Http\Controllers\BookController::class, 'store'])->name('books.store'); -Route::get('/books/{book}/edit', [\App\Http\Controllers\BookController::class, 'edit'])->name('books.edit'); -Route::put('/books/{book}', [\App\Http\Controllers\BookController::class, 'update'])->name('books.update'); -Route::post('/books/{book}/reorder', [\App\Http\Controllers\BookController::class, 'reOrder'])->name('books.reorder'); +Route::get('/books', [BookController::class, 'index'])->name('books.index'); +Route::get('/books/create', [BookController::class, 'create'])->name('books.create'); +Route::post('/books', [BookController::class, 'store'])->name('books.store'); +Route::get('/books/{book}/edit', [BookController::class, 'edit'])->name('books.edit'); +Route::put('/books/{book}', [BookController::class, 'update'])->name('books.update'); +Route::post('/books/{book}/reorder', [BookController::class, 'reOrder'])->name('books.reorder'); From aec619de5d962834a7b5cc1455b0b8e26d774d02 Mon Sep 17 00:00:00 2001 From: Anja Basara Date: Sun, 18 Jun 2023 19:02:57 +0200 Subject: [PATCH 05/12] JIRA-002 Added logic for placing book out of bounds --- app/Services/BookService.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Services/BookService.php b/app/Services/BookService.php index 5909d70..0eca846 100644 --- a/app/Services/BookService.php +++ b/app/Services/BookService.php @@ -20,6 +20,10 @@ public function reorderBooks(Book $movedBook, int $moveCount): void $oldPlacement = $movedBook->sort_order; $newPlacement = $oldPlacement - $moveCount; + if ($newPlacement < 1) { + $newPlacement = 1; + } + $arrayBetween = [$newPlacement, $oldPlacement]; asort($arrayBetween); @@ -28,7 +32,7 @@ public function reorderBooks(Book $movedBook, int $moveCount): void foreach ($books as $book) { if ($book->id === $movedBook->id) { - $book->sort_order = $newPlacement; + $book->sort_order = (count($books) > $newPlacement) ? $newPlacement : ($oldPlacement + count($books) - 1); } else { $book->sort_order += ($moveCount > 0 ? 1 : -1); } From 6d25c54e150e51ba4cecf0b546707b26b60aa650 Mon Sep 17 00:00:00 2001 From: Anja Basara Date: Sun, 18 Jun 2023 21:10:02 +0200 Subject: [PATCH 06/12] JIRA-002 Implemented editing a Book stock_amount logic and refactored code --- app/Http/Controllers/BookController.php | 76 ++++++------------------- app/Http/Requests/StoreBookRequest.php | 50 ++++++++++++++++ app/Http/Requests/UpdateBookRequest.php | 50 ++++++++++++++++ app/Models/Book.php | 10 ++-- app/Repositories/BookRepository.php | 55 ++++++++++++++++++ app/Services/BookService.php | 53 +++++++++++++++++ resources/views/books/edit.blade.php | 2 +- 7 files changed, 232 insertions(+), 64 deletions(-) create mode 100644 app/Http/Requests/StoreBookRequest.php create mode 100644 app/Http/Requests/UpdateBookRequest.php create mode 100644 app/Repositories/BookRepository.php diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php index ac064d6..c556600 100644 --- a/app/Http/Controllers/BookController.php +++ b/app/Http/Controllers/BookController.php @@ -2,6 +2,8 @@ namespace App\Http\Controllers; +use App\Http\Requests\StoreBookRequest; +use App\Http\Requests\UpdateBookRequest; use App\Models\Book; use App\Models\Publisher; use App\Models\Writer; @@ -13,15 +15,17 @@ class BookController extends Controller { - public function __construct(public BookService $bookService) + public function __construct(private BookService $bookService) { } - public function index() + /** + * @return View + */ + public function index(): View { - $books = Book::all()->sortBy(function (Book $book) { - return $book->sort_order > 0 ? $book->sort_order : PHP_INT_MAX; - })->values()->all(); + $books = $this->bookService->getBooksSorted(); + return view('books.index', compact('books')); } @@ -30,7 +34,7 @@ public function index() * * @return View */ - public function create() + public function create(): View { $writers = Writer::all(); $publishers = Publisher::all(); @@ -41,35 +45,12 @@ public function create() /** * Store a newly created book in the database. * - * @param Request $request + * @param StoreBookRequest $request * @return RedirectResponse */ - public function store(Request $request) + public function store(StoreBookRequest $request): RedirectResponse { - $request->validate([ - 'title' => 'required|string|max:255', - 'ISBN' => 'required|string|max:255', - 'publication_year' => 'required|integer|min:1900|max:' . date('Y'), - 'price' => 'required|numeric|min:0', - 'genre' => 'required|string|max:255', - 'stock_amount' => 'required|integer|min:0', - 'subgenre' => 'required|string|max:255', - 'writer_id' => 'required|exists:writers,id', - 'publisher_id' => 'required|exists:publishers,id', - ]); - - Book::create([ - 'title' => $request->input('title'), - 'ISBN' => $request->input('ISBN'), - 'publication_year' => $request->input('publication_year'), - 'price' => $request->input('price'), - 'genre' => $request->input('genre'), - 'sort_order' => -1, - 'stock_amount' => $request->input('stock_amount'), - 'subgenre' => $request->input('subgenre'), - 'writer_id' => $request->input('writer_id'), - 'publisher_id' => $request->input('publisher_id'), - ]); + $this->bookService->createBook($request); return redirect()->route('books.index'); } @@ -80,7 +61,7 @@ public function store(Request $request) * @param Book $book * @return View */ - public function edit(Book $book) + public function edit(Book $book): View { $writers = Writer::all(); $publishers = Publisher::all(); @@ -91,36 +72,13 @@ public function edit(Book $book) /** * Update the specified book in the database. * - * @param Request $request + * @param UpdateBookRequest $request * @param Book $book * @return RedirectResponse */ - public function update(Request $request, Book $book) + public function update(UpdateBookRequest $request, Book $book): RedirectResponse { - $request->validate([ - 'title' => 'required|string|max:255', - 'ISBN' => 'required|string|max:255', - 'publication_year' => 'required|integer|min:1900|max:' . date('Y'), - 'price' => 'required|numeric|min:0', - 'genre' => 'required|string|max:255', - 'subgenre' => 'required|string|max:255', - 'stock_amount' => 'required|integer|min:0', - 'writer_id' => 'required|exists:writers,id', - 'publisher_id' => 'required|exists:publishers,id', - ]); - - $book->update([ - 'title' => $request->input('title'), - 'ISBN' => $request->input('ISBN'), - 'publication_year' => $request->input('publication_year'), - 'price' => $request->input('price'), - 'genre' => $request->input('genre'), - 'subgenre' => $request->input('subgenre'), - 'sort_order' => -1, - 'stock_amount' => $request->input('stock_amount'), - 'writer_id' => $request->input('writer_id'), - 'publisher_id' => $request->input('publisher_id'), - ]); + $this->bookService->updateBook($request, $book); return redirect()->route('books.index'); } diff --git a/app/Http/Requests/StoreBookRequest.php b/app/Http/Requests/StoreBookRequest.php new file mode 100644 index 0000000..cef5434 --- /dev/null +++ b/app/Http/Requests/StoreBookRequest.php @@ -0,0 +1,50 @@ + + */ + public function rules(): array + { + return [ + 'title' => 'required|string|max:255', + 'ISBN' => 'required|string|max:255', + 'publication_year' => 'required|integer|min:1900|max:' . date('Y'), + 'price' => 'required|numeric|min:0', + 'genre' => 'required|string|max:255', + 'stock_amount' => 'required|integer|min:0', + 'subgenre' => 'required|string|max:255', + 'writer_id' => 'required|exists:writers,id', + 'publisher_id' => 'required|exists:publishers,id', + ]; + } +} diff --git a/app/Http/Requests/UpdateBookRequest.php b/app/Http/Requests/UpdateBookRequest.php new file mode 100644 index 0000000..b54731e --- /dev/null +++ b/app/Http/Requests/UpdateBookRequest.php @@ -0,0 +1,50 @@ + + */ + public function rules(): array + { + return [ + 'title' => 'required|string|max:255', + 'ISBN' => 'required|string|max:255', + 'publication_year' => 'required|integer|min:1900|max:' . date('Y'), + 'price' => 'required|numeric|min:0', + 'genre' => 'required|string|max:255', + 'subgenre' => 'required|string|max:255', + 'stock_amount' => 'required|integer|min:0', + 'writer_id' => 'required|exists:writers,id', + 'publisher_id' => 'required|exists:publishers,id', + ]; + } +} diff --git a/app/Models/Book.php b/app/Models/Book.php index ae57ea8..c266f60 100644 --- a/app/Models/Book.php +++ b/app/Models/Book.php @@ -2,8 +2,10 @@ namespace App\Models; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; /** * @property int $id @@ -17,8 +19,8 @@ * @property int $stock_amount * @property int $writer_id * @property int $publisher_id - * @property \Carbon\Carbon $created_at - * @property \Carbon\Carbon $updated_at + * @property Carbon $created_at + * @property Carbon $updated_at */ class Book extends Model { @@ -29,7 +31,7 @@ class Book extends Model /** * Get the writer associated with the book. */ - public function writer() + public function writer(): BelongsTo { return $this->belongsTo(Writer::class); } @@ -37,7 +39,7 @@ public function writer() /** * Get the publisher associated with the book. */ - public function publisher() + public function publisher(): BelongsTo { return $this->belongsTo(Publisher::class); } diff --git a/app/Repositories/BookRepository.php b/app/Repositories/BookRepository.php new file mode 100644 index 0000000..9c01cf2 --- /dev/null +++ b/app/Repositories/BookRepository.php @@ -0,0 +1,55 @@ +title = $request->title; + $book->ISBN = $request->ISBN; + $book->publication_year = $request->publication_year; + $book->price = $request->price; + $book->genre = $request->genre; + $book->subgenre = $request->subgenre; + $book->stock_amount = $request->stock_amount; + $book->writer_id = $request->writer_id; + $book->publisher_id = $request->publisher_id; + $book->save(); + } + + /** + * Get Books sorted by sort_order, where Books with sort_order -1 are placed at the bottom. + * + * @return Book[] + */ + public function getBooksSorted(): array + { + return Book::all()->sortBy(function (Book $book) { + return $book->sort_order > 0 ? $book->sort_order : PHP_INT_MAX; + })->values()->all(); + } + + /** + * Get next sort_order. + * + * @return int + */ + public function getNextSortPlacement(): int + { + /** @var Book $lastBook */ + $lastBook = Book::where('sort_order', '>', 0)->orderBy('sort_order')->last(); + + return $lastBook->sort_order + 1; + } +} diff --git a/app/Services/BookService.php b/app/Services/BookService.php index 0eca846..44d4c8a 100644 --- a/app/Services/BookService.php +++ b/app/Services/BookService.php @@ -2,10 +2,25 @@ namespace App\Services; +use App\Http\Requests\StoreBookRequest; +use App\Http\Requests\UpdateBookRequest; use App\Models\Book; +use App\Repositories\BookRepository; class BookService { + public function __construct(private BookRepository $bookRepository) + { + } + + /** + * @return Book[] + */ + public function getBooksSorted(): array + { + return $this->bookRepository->getBooksSorted(); + } + /** * @param Book $movedBook * @param int $moveCount how many places the Book needs to be moved @@ -39,4 +54,42 @@ public function reorderBooks(Book $movedBook, int $moveCount): void $book->save(); } } + + /** + * Update Book logic. + * If the updated Book has stock amount 0, it should not be sorted. + * Otherwise, it should be ordered last. + * + * @param UpdateBookRequest $request + * @param Book $book + * @return void + */ + public function updateBook(UpdateBookRequest $request, Book $book): void + { + if ($request->stock_amount !== $book->stock_amount) { + if ($request->stock_amount == 0) { + $book->sort_order = -1; + } else if ($book->stock_amount != 0) { + $book->sort_order = $this->bookRepository->getNextSortPlacement(); + } + } + + $this->bookRepository->createOrUpdateBook($request, $book); + } + + /** + * Create Book logic. + * If the new Book has stock amount 0, it should not be sorted. + * Otherwise, it should be ordered last. + * + * @param StoreBookRequest $request + * @return void + */ + public function createBook(StoreBookRequest $request): void + { + $book = new Book(); + $book->sort_order = $request->stock_amount == 0 ? -1 : $this->bookRepository->getNextSortPlacement(); + + $this->bookRepository->createOrUpdateBook($request, $book); + } } diff --git a/resources/views/books/edit.blade.php b/resources/views/books/edit.blade.php index 8f8597d..1378806 100644 --- a/resources/views/books/edit.blade.php +++ b/resources/views/books/edit.blade.php @@ -40,7 +40,7 @@
- +
From 3744ec5278c1aff07534ac4ec0258f6d0ef68869 Mon Sep 17 00:00:00 2001 From: Anja Basara Date: Sun, 18 Jun 2023 22:30:03 +0200 Subject: [PATCH 07/12] JIRA-002 Refactored Book moving and reordering logic --- app/Http/Controllers/BookController.php | 23 +++------------- app/Http/Requests/MoveBookRequest.php | 35 +++++++++++++++++++++++++ app/Services/BookService.php | 15 +++++++++-- resources/views/books/index.blade.php | 2 +- routes/web.php | 2 +- 5 files changed, 54 insertions(+), 23 deletions(-) create mode 100644 app/Http/Requests/MoveBookRequest.php diff --git a/app/Http/Controllers/BookController.php b/app/Http/Controllers/BookController.php index c556600..4030d53 100644 --- a/app/Http/Controllers/BookController.php +++ b/app/Http/Controllers/BookController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use App\Http\Requests\MoveBookRequest; use App\Http\Requests\StoreBookRequest; use App\Http\Requests\UpdateBookRequest; use App\Models\Book; @@ -9,7 +10,6 @@ use App\Models\Writer; use App\Services\BookService; use Illuminate\Http\RedirectResponse; -use Illuminate\Http\Request; use Illuminate\View\View; class BookController extends Controller @@ -86,33 +86,18 @@ public function update(UpdateBookRequest $request, Book $book): RedirectResponse /** * Reorder the books. * - * @param Request $request + * @param MoveBookRequest $request * @param Book $book * * @return RedirectResponse */ - public function reorder(Request $request, Book $book): RedirectResponse + public function move(MoveBookRequest $request, Book $book): RedirectResponse { if ($book->stock_amount === 0) { return back(); } - if ($request->input('up') && $request->input('down')) { - return back(); - } - - $request->validate([ - 'up' => 'nullable|integer|min:0', - 'down' => 'nullable|integer|min:0', - ]); - - if ($request->input('up')) { - $moveCount = (int) $request->input('up'); - } else { - $moveCount = -1 * ((int) $request->input('down')); - } - - $this->bookService->reorderBooks($book, $moveCount); + $this->bookService->moveAndReorderBooks($request, $book); return redirect()->route('books.index'); } diff --git a/app/Http/Requests/MoveBookRequest.php b/app/Http/Requests/MoveBookRequest.php new file mode 100644 index 0000000..2830d74 --- /dev/null +++ b/app/Http/Requests/MoveBookRequest.php @@ -0,0 +1,35 @@ + + */ + public function rules(): array + { + return [ + 'up' => 'nullable|integer|min:0|prohibits:down', + 'down' => 'nullable|integer|min:0|prohibits:up', + ]; + } +} diff --git a/app/Services/BookService.php b/app/Services/BookService.php index 44d4c8a..25ad6a5 100644 --- a/app/Services/BookService.php +++ b/app/Services/BookService.php @@ -2,6 +2,7 @@ namespace App\Services; +use App\Http\Requests\MoveBookRequest; use App\Http\Requests\StoreBookRequest; use App\Http\Requests\UpdateBookRequest; use App\Models\Book; @@ -22,12 +23,22 @@ public function getBooksSorted(): array } /** + * Move Book logic. + * Book should be moved as many places as indicated, + * while other books need to be reordered. + * + * @param MoveBookRequest $request * @param Book $movedBook - * @param int $moveCount how many places the Book needs to be moved * @return void */ - public function reorderBooks(Book $movedBook, int $moveCount): void + public function moveAndReorderBooks(MoveBookRequest $request, Book $movedBook): void { + if ($request->up) { + $moveCount = (int) $request->up; + } else { + $moveCount = -1 * ((int) $request->down); + } + if ($moveCount === 0) { return; } diff --git a/resources/views/books/index.blade.php b/resources/views/books/index.blade.php index e660703..99face1 100644 --- a/resources/views/books/index.blade.php +++ b/resources/views/books/index.blade.php @@ -34,7 +34,7 @@ {{ $book->publisher->name }} Edit - + @csrf diff --git a/routes/web.php b/routes/web.php index 406bb88..9736e18 100644 --- a/routes/web.php +++ b/routes/web.php @@ -38,4 +38,4 @@ Route::post('/books', [BookController::class, 'store'])->name('books.store'); Route::get('/books/{book}/edit', [BookController::class, 'edit'])->name('books.edit'); Route::put('/books/{book}', [BookController::class, 'update'])->name('books.update'); -Route::post('/books/{book}/reorder', [BookController::class, 'reOrder'])->name('books.reorder'); +Route::post('/books/{book}/move', [BookController::class, 'move'])->name('books.move'); From ec2f202cc1fdf75f54d201c23473557e988bf120 Mon Sep 17 00:00:00 2001 From: Anja Basara Date: Sun, 18 Jun 2023 22:50:12 +0200 Subject: [PATCH 08/12] JIRA-002 Updated BookFactory to reflect the ordering logic --- database/factories/BookFactory.php | 13 +++++++------ database/seeders/DatabaseSeeder.php | 17 ++++++++++++++--- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/database/factories/BookFactory.php b/database/factories/BookFactory.php index b0fc665..b590cbd 100644 --- a/database/factories/BookFactory.php +++ b/database/factories/BookFactory.php @@ -2,10 +2,11 @@ namespace Database\Factories; +use App\Models\Book; use Illuminate\Database\Eloquent\Factories\Factory; /** - * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Book> + * @extends Factory */ class BookFactory extends Factory { @@ -14,7 +15,7 @@ class BookFactory extends Factory * * @return array */ - public function definition() + public function definition(): array { return [ 'title' => $this->randomTitle(), @@ -24,11 +25,11 @@ public function definition() 'genre' => $this->randomGenre(), 'subgenre' => $this->randomSubGenre(), 'sort_order' => -1, - 'stock_amount' => $this->faker->numberBetween(0, 10) < 8 ? $this->faker->numberBetween(0, 100) : 0, + 'stock_amount' => $this->faker->numberBetween(1, 100), ]; } - protected function randomTitle() + private function randomTitle() { return $this->faker->randomElement([ 'To Kill a Mockingbird', @@ -65,7 +66,7 @@ protected function randomTitle() ]); } - protected function randomGenre() + private function randomGenre() { return $this->faker->randomElement([ 'Fiction', @@ -81,7 +82,7 @@ protected function randomGenre() ]); } - protected function randomSubGenre() + private function randomSubGenre() { return $this->faker->randomElement([ 'Contemporary', diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index dc5f5b0..f4d005f 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -16,17 +16,28 @@ class DatabaseSeeder extends Seeder * * @return void */ - public function run() + public function run(): void { $publishers = Publisher::factory()->count(5)->create(); $writers = Writer::factory()->count(5)->create(); - for ($i = 0; $i < 20; $i++) { + for ($i = 1; $i <= 12; $i++) { Book::factory() ->for($publishers->random()) ->for($writers->random()) - ->create(); + ->create([ + 'sort_order' => $i, + ]); + } + + for ($i = 1; $i <= 8; $i++) { + Book::factory() + ->for($publishers->random()) + ->for($writers->random()) + ->create([ + 'stock_amount' => 0, + ]); } } } From 5f976259eef35e6247ee4ee565f16b9d36088da5 Mon Sep 17 00:00:00 2001 From: Anja Basara Date: Mon, 19 Jun 2023 09:40:41 +0200 Subject: [PATCH 09/12] JIRA-002 If the old and new placement are the same, do not go through the logic --- app/Services/BookService.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/Services/BookService.php b/app/Services/BookService.php index 25ad6a5..1cd5e91 100644 --- a/app/Services/BookService.php +++ b/app/Services/BookService.php @@ -50,6 +50,10 @@ public function moveAndReorderBooks(MoveBookRequest $request, Book $movedBook): $newPlacement = 1; } + if ($oldPlacement === $newPlacement) { + return; + } + $arrayBetween = [$newPlacement, $oldPlacement]; asort($arrayBetween); From 657442273de3c6bc2090a58c0cebd41402b69f66 Mon Sep 17 00:00:00 2001 From: Anja Basara Date: Mon, 19 Jun 2023 09:48:45 +0200 Subject: [PATCH 10/12] JIRA-002 Fixed sort_order for books that get in stock --- app/Repositories/BookRepository.php | 5 ++++- app/Services/BookService.php | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/Repositories/BookRepository.php b/app/Repositories/BookRepository.php index 9c01cf2..d5ee5b6 100644 --- a/app/Repositories/BookRepository.php +++ b/app/Repositories/BookRepository.php @@ -48,7 +48,10 @@ public function getBooksSorted(): array public function getNextSortPlacement(): int { /** @var Book $lastBook */ - $lastBook = Book::where('sort_order', '>', 0)->orderBy('sort_order')->last(); + $lastBook = Book::where('sort_order', '>', 0) + ->orderBy('sort_order') + ->get() + ->last(); return $lastBook->sort_order + 1; } diff --git a/app/Services/BookService.php b/app/Services/BookService.php index 1cd5e91..d83a370 100644 --- a/app/Services/BookService.php +++ b/app/Services/BookService.php @@ -84,7 +84,7 @@ public function updateBook(UpdateBookRequest $request, Book $book): void if ($request->stock_amount !== $book->stock_amount) { if ($request->stock_amount == 0) { $book->sort_order = -1; - } else if ($book->stock_amount != 0) { + } else if ($book->stock_amount == 0) { $book->sort_order = $this->bookRepository->getNextSortPlacement(); } } From 7222a48dbe9a806373bcc06b4059d93e00fbd570 Mon Sep 17 00:00:00 2001 From: Anja Basara Date: Mon, 19 Jun 2023 11:15:04 +0200 Subject: [PATCH 11/12] JIRA-002 Reordered books after updating stock to 0 and refactored code --- app/Repositories/BookRepository.php | 4 +-- app/Services/BookService.php | 48 +++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/app/Repositories/BookRepository.php b/app/Repositories/BookRepository.php index d5ee5b6..8fa4f12 100644 --- a/app/Repositories/BookRepository.php +++ b/app/Repositories/BookRepository.php @@ -41,11 +41,11 @@ public function getBooksSorted(): array } /** - * Get next sort_order. + * Get the sort_order of the last sorted book incremented by one. * * @return int */ - public function getNextSortPlacement(): int + public function getNextSortOrder(): int { /** @var Book $lastBook */ $lastBook = Book::where('sort_order', '>', 0) diff --git a/app/Services/BookService.php b/app/Services/BookService.php index d83a370..a1e4a73 100644 --- a/app/Services/BookService.php +++ b/app/Services/BookService.php @@ -43,28 +43,52 @@ public function moveAndReorderBooks(MoveBookRequest $request, Book $movedBook): return; } - $oldPlacement = $movedBook->sort_order; - $newPlacement = $oldPlacement - $moveCount; + $oldSortOrder = $movedBook->sort_order; + $newSortOrder = $oldSortOrder - $moveCount; - if ($newPlacement < 1) { - $newPlacement = 1; + if ($newSortOrder < 1) { + $newSortOrder = 1; } - if ($oldPlacement === $newPlacement) { + if ($oldSortOrder === $newSortOrder) { return; } - $arrayBetween = [$newPlacement, $oldPlacement]; - asort($arrayBetween); + $this->reorder($movedBook, $oldSortOrder, $newSortOrder); + } + + /** + * Helper function for reordering logic. + * Iterate through all the books between old and new sort order and move them one place up/down. + * + * @param Book $movedBook + * @param int $oldSortOrder + * @param int $newSortOrder + * @return void + */ + private function reorder(Book $movedBook, int $oldSortOrder, int $newSortOrder): void + { + if ($newSortOrder === -1) { + $arrayBetween = [$oldSortOrder, $this->bookRepository->getNextSortOrder()]; + $sign = -1; + } else { + $arrayBetween = [$oldSortOrder, $newSortOrder]; + asort($arrayBetween); + $sign = ($oldSortOrder - $newSortOrder) > 0 ? 1 : -1; + } /** @var Book[] $books */ $books = Book::whereBetween('sort_order', $arrayBetween)->orderBy('sort_order')->get(); + if ($newSortOrder !== -1) { + $newSortOrder = (count($books) > $newSortOrder) ? $newSortOrder : ($oldSortOrder + count($books) - 1); + } + foreach ($books as $book) { if ($book->id === $movedBook->id) { - $book->sort_order = (count($books) > $newPlacement) ? $newPlacement : ($oldPlacement + count($books) - 1); + $book->sort_order = $newSortOrder; } else { - $book->sort_order += ($moveCount > 0 ? 1 : -1); + $book->sort_order += $sign; } $book->save(); } @@ -83,9 +107,9 @@ public function updateBook(UpdateBookRequest $request, Book $book): void { if ($request->stock_amount !== $book->stock_amount) { if ($request->stock_amount == 0) { - $book->sort_order = -1; + $this->reorder($book, $book->sort_order, -1); } else if ($book->stock_amount == 0) { - $book->sort_order = $this->bookRepository->getNextSortPlacement(); + $book->sort_order = $this->bookRepository->getNextSortOrder(); } } @@ -103,7 +127,7 @@ public function updateBook(UpdateBookRequest $request, Book $book): void public function createBook(StoreBookRequest $request): void { $book = new Book(); - $book->sort_order = $request->stock_amount == 0 ? -1 : $this->bookRepository->getNextSortPlacement(); + $book->sort_order = $request->stock_amount == 0 ? -1 : $this->bookRepository->getNextSortOrder(); $this->bookRepository->createOrUpdateBook($request, $book); } From fe4dbe038696de136cfffb43d294eb235a7c19d0 Mon Sep 17 00:00:00 2001 From: Anja Basara Date: Mon, 19 Jun 2023 11:37:54 +0200 Subject: [PATCH 12/12] JIRA-002 Fixed sort_order for books that move down out of bounds --- app/Services/BookService.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/Services/BookService.php b/app/Services/BookService.php index a1e4a73..37b40ed 100644 --- a/app/Services/BookService.php +++ b/app/Services/BookService.php @@ -73,15 +73,16 @@ private function reorder(Book $movedBook, int $oldSortOrder, int $newSortOrder): $sign = -1; } else { $arrayBetween = [$oldSortOrder, $newSortOrder]; - asort($arrayBetween); + sort($arrayBetween); $sign = ($oldSortOrder - $newSortOrder) > 0 ? 1 : -1; } /** @var Book[] $books */ $books = Book::whereBetween('sort_order', $arrayBetween)->orderBy('sort_order')->get(); - if ($newSortOrder !== -1) { - $newSortOrder = (count($books) > $newSortOrder) ? $newSortOrder : ($oldSortOrder + count($books) - 1); + if ($newSortOrder > $oldSortOrder) { + $nextLastSortOrder = $this->bookRepository->getNextSortOrder(); + $newSortOrder = ($newSortOrder >= $nextLastSortOrder) ? $nextLastSortOrder - 1 : $newSortOrder; } foreach ($books as $book) {