10 Commits b11db23779 ... 008e036489

Auteur SHA1 Message Date
  axkuhta 008e036489 Implement comments il y a 1 an
  axkuhta dd3576b6d8 Shrink book info table a little il y a 1 an
  axkuhta ff55220692 Add some boring books il y a 1 an
  axkuhta 8b082ec83d Do not show book annotations on the author page either il y a 1 an
  axkuhta 90649d0e03 Improve table css il y a 1 an
  axkuhta cf6095e34e Do not show annotation in the book index, there is nowhere near enough space il y a 1 an
  axkuhta f194fae61c Improved look and feel il y a 1 an
  axkuhta 6ed1b48487 Link authors from book index table, eager load authors il y a 1 an
  axkuhta 23ca7f0fbc Link books on author page il y a 1 an
  axkuhta 9a49fb38f5 Link author from book page il y a 1 an

+ 91 - 0
app/Console/Commands/LoadSampleData.php

@@ -0,0 +1,91 @@
+<?php
+
+namespace App\Console\Commands;
+
+use Illuminate\Console\Command;
+use App\Models\Author;
+use App\Models\Book;
+
+class LoadSampleData extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'app:load-sample-data';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Command description';
+
+    /**
+     * Execute the console command.
+     */
+    public function handle()
+    {
+        $author = new Author;
+        $author->name = "Howard Johnson";
+        $author->save();
+
+        $book = new Book;
+        $book->name = "High-Speed Digital Design: A Handbook of Black Magic";
+        $book->isbn = "9780133957242";
+        $book->year = 1993;
+        $book->pagecount = 446;
+        $book->author_id = $author->id;
+        $book->annotation = "Focuses on a combination of digital and analog circuit theory. Helps engineers who work with digital systems, shorten their product development cycles and fix their latest high-speed design problems. DLC: Digital electronics.";
+        $book->save();
+
+        $author = new Author;
+        $author->name = "Харкевич А.А.";
+        $author->save();
+
+        $book = new Book;
+        $book->name = "Основы радиотехники";
+        $book->isbn = "978-5-9221-0790-7";
+        $book->year = 2007;
+        $book->pagecount = 512;
+        $book->author_id = $author->id;
+        $book->annotation = "В курс теоретических основ радиотехники вошли: общие вопросы передачи и приема сигналов, исследование прохождения электрических сигналов через внутренние цепи аппаратуры и распространения сигналов по линиям и волноводам, исследование основных радиотехнических процессов. Математический аппарат курса включает решение линейных дифференциальных уравнений с постоянными и переменными коэффициентами и решение нелинейных дифференциальных уравнений. Настоящее издание полностью воспроизводит текст издания 1962 года, которое было допущено Министерством высшего и среднего специального образования СССР в качестве учебного пособия для высших учебных заведений СССР.";
+        $book->save();
+
+        $author = new Author;
+        $author->name = "Allen Downey";
+        $author->save();
+
+        $book = new Book;
+        $book->name = "Think DSP";
+        $book->isbn = "978-1491938454";
+        $book->year = 2016;
+        $book->pagecount = 153;
+        $book->author_id = $author->id;
+        $book->annotation = "If you understand basic mathematics and know how to program with Python, you're ready to dive into signal processing. While most resources start with theory to teach this complex subject, this practical book introduces techniques by showing you how they're applied in the real world. In the first chapter alone, you'll be able to decompose a sound into its harmonics, modify the harmonics, and generate new sounds.";
+        $book->save();
+
+        $book = new Book;
+        $book->name = "Think Stats";
+        $book->isbn = "978-1491907337";
+        $book->year = 2014;
+        $book->pagecount = 264;
+        $book->author_id = $author->id;
+        $book->annotation = "If you know how to program, you have the skills to turn data into knowledge, using tools of probability and statistics. This concise introduction shows you how to perform statistical analysis computationally, rather than mathematically, with programs written in Python.";
+        $book->save();
+
+        $author = new Author;
+        $author->name = "Robert Lucky";
+        $author->save();
+
+        $book = new Book;
+        $book->name = "Silicon Dreams: Information, Man, and Machine";
+        $book->isbn = "9780312029609";
+        $book->year = 1989;
+        $book->pagecount = 440;
+        $book->author_id = $author->id;
+        $book->annotation = "Silicon Dreams is a highly informed discussion of the new information age, from the Executive Director of Research at Bell Labs. Robert Lucky addresses such questions as what information is, how it is generated, captured, stored, and communicated, and goes on to explain information theory, cryptology, speech synthesis and recognition, and much more. Charts, diagrams, photographs.";
+        $book->save();
+    }
+}

+ 23 - 1
app/Http/Controllers/AuthorController.php

@@ -17,7 +17,7 @@ class AuthorController extends Controller {
 	}
 
 	function view(Author $author) {
-		return view("author", ["author" => $author]);
+		return view("author", ["author" => $author->load(["books", "comments"])]);
 	}
 
 	function edit(Author $author) {
@@ -43,6 +43,28 @@ class AuthorController extends Controller {
 		return view("success");
 	}
 
+	function comment(Author $author, Request $request) {
+		$request->validate([
+			"name" => "required",
+			"email" => "required|email",
+			"content" => "required"
+		], [
+			"name" => "Укажите ваше имя.",
+			"email" => "Укажите ваш email.",
+			"content" => "Введите комментарий."
+		]);
+
+		$arr = $request;
+
+		$author->comments()->create([
+			"name" => $arr->name,
+			"email" => $arr->email,
+			"content" => $arr->content
+		]);
+
+		return view("success");
+	}
+
 	function drop(Author $author) {
 		$author->delete();
 		return view("success");

+ 24 - 2
app/Http/Controllers/BookController.php

@@ -13,7 +13,7 @@ use App\Models\Author;
 
 class BookController extends Controller {
 	function index() {
-		$books = Book::all(); // Достать все книги
+		$books = Book::all()->load("author"); // Достать все книги
 
 		return view("books", ["rows" => $books]);
 	}
@@ -23,7 +23,7 @@ class BookController extends Controller {
 	}
 
 	function view(Book $book) {
-		return view("book", ["book" => $book]);
+		return view("book", ["book" => $book->load(["author", "comments"])]);
 	}
 
 	function edit(Book $book) {
@@ -58,6 +58,28 @@ class BookController extends Controller {
 		return view("success");
 	}
 
+	function comment(Book $book, Request $request) {
+		$request->validate([
+			"name" => "required",
+			"email" => "required|email",
+			"content" => "required"
+		], [
+			"name" => "Укажите ваше имя.",
+			"email" => "Укажите ваш email.",
+			"content" => "Введите комментарий."
+		]);
+
+		$arr = $request;
+
+		$book->comments()->create([
+			"name" => $arr->name,
+			"email" => $arr->email,
+			"content" => $arr->content
+		]);
+
+		return view("success");
+	}
+
 	function drop(Book $book) {
 		$book->delete();
 		return view("success");

+ 7 - 2
app/Models/Author.php

@@ -2,10 +2,15 @@
 
 namespace App\Models;
 
-use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
 
 class Author extends Model
 {
-    use HasFactory;
+	function books() {
+		return $this->hasMany(Book::class);
+	}
+
+	function comments() {
+		return $this->morphMany(Comment::class, "commentable");
+	}
 }

+ 7 - 2
app/Models/Book.php

@@ -2,10 +2,15 @@
 
 namespace App\Models;
 
-use Illuminate\Database\Eloquent\Factories\HasFactory;
 use Illuminate\Database\Eloquent\Model;
 
 class Book extends Model
 {
-    use HasFactory;
+	function author() {
+		return $this->belongsTo(Author::class);
+	}
+
+	function comments() {
+		return $this->morphMany(Comment::class, "commentable");
+	}
 }

+ 15 - 0
app/Models/Comment.php

@@ -0,0 +1,15 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+
+class Comment extends Model
+{
+	// Наличие fillable требуется только при использовании mass assignment, т.е. создание записи из ассоциативного массива
+	protected $fillable = [
+		"name",
+		"email",
+		"content"
+	];
+}

+ 31 - 0
database/migrations/2023_11_28_191724_create_comments_table.php

@@ -0,0 +1,31 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('comments', function (Blueprint $table) {
+            $table->id();
+            $table->timestamps();
+            $table->string("name");
+            $table->string("email");
+            $table->text("content");
+            $table->morphs("commentable");
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('comments');
+    }
+};

+ 30 - 3
resources/views/author.blade.php

@@ -3,10 +3,37 @@
 @section("content")
 <h1>{{$author->name}}</h1>
 <p>
-<div>Описание автора:</div>
-<div>{{$author->description ?? "N/A"}}</div>
+	<div>Описание автора:</div>
+	<div>{{$author->description ?? "N/A"}}</div>
 </p>
 <p>
-<a href="/author/{{ $author->id }}/edit">Редактировать</a> | <a href="/author/{{ $author->id }}/delete">Удалить</a>
+	<a href="/author/{{ $author->id }}/edit">Редактировать</a> | <a href="/author/{{ $author->id }}/delete">Удалить</a>
+</p>
+<p>
+	<div>Книги автора:</div>
+	<table>
+	<tr>
+		<th>Название</th>
+		<th>ISBN</th>
+		<th>Год</th>
+		<th>Страниц</th>
+	</tr>
+	@foreach ($author->books as $row)
+		<tr>
+			<td><a href="/book/{{$row->id}}">{{$row->name}}</a></td>
+			<td>{{$row->isbn}}</td>
+			<td>{{$row->year}}</td>
+			<td>{{$row->pagecount}}</td>
+		</tr>
+	@endforeach
+	</table>
+</p>
+<p>
+<div>Добавить комментарий:</div>
+@include("include.comment_form", ["comment_form_target" => "/author/$author->id/comment"])
+</p>
+<p>
+<h3>Комментарии:</h3>
+@include("include.comments", ["comments" => $author->comments])
 </p>
 @endsection

+ 30 - 4
resources/views/book.blade.php

@@ -3,15 +3,41 @@
 @section("content")
 <h1>{{$book->name}}</h1>
 <p>
+<table>
+	<tr>
+		<th colspan=2>Информация о книге</th>
+	</tr>
+	<tr>
+		<td>Автор</td>
+		<td><a href="/author/{{ $book->author->id }}">{{$book->author->name}}</a></td>
+	</tr>
+	<tr>
+		<td>ISBN</td>
+		<td>{{$book->isbn ?? "N/A"}}</td>
+	</tr>
+	<tr>
+		<td>Количество страниц</td>
+		<td>{{$book->pagecount ?? "N/A"}}</td>
+	</tr>
+	<tr>
+		<td>Год</td>
+		<td>{{$book->year ?? "N/A"}}</td>
+	</tr>
+</table>
+</p>
+<p>
 <div>Аннотация:</div>
 <div>{{$book->annotation ?? "N/A"}}</div>
 </p>
 <p>
-<div>ISBN: {{$book->isbn ?? "N/A"}}</div>
-<div>Количество страниц: {{$book->pagecount ?? "N/A"}}</div>
-<div>Год: {{$book->year ?? "N/A"}}</div>
+<a href="/book/{{ $book->id }}/edit">Редактировать</a> | <a href="/book/{{ $book->id }}/delete">Удалить</a>
+</p>
+<p>
+<div>Добавить комментарий:</div>
+@include("include.comment_form", ["comment_form_target" => "/book/$book->id/comment"])
 </p>
 <p>
-<a href="/book/{{ $book->id }}/edit">Редактировать</a> | <a href="/book/{{ $book->id }}/delete">Удалить</a>
+<h3>Комментарии:</h3>
+@include("include.comments", ["comments" => $book->comments])
 </p>
 @endsection

+ 2 - 2
resources/views/books.blade.php

@@ -5,7 +5,7 @@
 <table>
 <tr>
 	<th>Название</th>
-	<th>Аннотация</th>
+	<th>Автор</th>
 	<th>ISBN</th>
 	<th>Год</th>
 	<th>Страниц</th>
@@ -13,7 +13,7 @@
 @foreach ($rows as $row)
 	<tr>
 		<td><a href="/book/{{$row->id}}">{{$row->name}}</a></td>
-		<td>{{$row->annotation}}</td>
+		<td><a href="/author/{{$row->author->id}}">{{$row->author->name}}</a></td>
 		<td>{{$row->isbn}}</td>
 		<td>{{$row->year}}</td>
 		<td>{{$row->pagecount}}</td>

+ 35 - 0
resources/views/include/comment_form.blade.php

@@ -0,0 +1,35 @@
+<form method="POST" action="{{ $comment_form_target }}">
+	@csrf
+
+	<div>
+		<label>
+			<div>Ваше имя:</div>
+			<input type="text" name="name" placeholder="Ваше имя..." value="{{ old("name") }}">
+			@error("name")
+				<span class="alert">{{ $message }}</span>
+			@enderror
+		</label>
+	</div>
+
+	<div>
+		<label>
+			<div>Ваше имя:</div>
+			<input type="email" name="email" placeholder="Ваш email..." value="{{ old("email") }}">
+			@error("email")
+				<span class="alert">{{ $message }}</span>
+			@enderror
+		</label>
+	</div>
+
+	<div>
+		<label>
+			<div>Комментарий:</div>
+			<textarea name="content" placeholder="Комментарий...">{{ old("comment") }}</textarea>
+			@error("comment")
+				<span class="alert">{{ $message }}</span>
+			@enderror
+		</label>
+	</div>
+
+	<input type="submit">
+</form>

+ 6 - 0
resources/views/include/comments.blade.php

@@ -0,0 +1,6 @@
+@foreach ($comments as $row)
+	<p>
+		<div>{{ $row->name }} ({{ $row->created_at }}):</div>
+		<div>{{ $row->content }}</div>
+	</p>
+@endforeach

+ 1 - 0
resources/views/layouts/app.blade.php

@@ -100,6 +100,7 @@
 
 			td, th {
 				padding: .2rem 1rem;
+				text-align: left;
 			}
 
 			th {

+ 2 - 0
routes/web.php

@@ -25,6 +25,7 @@ Route::get('/book/{book}', [Controllers\BookController::class, 'view']);
 Route::get('/book/{book}/delete', [Controllers\BookController::class, 'drop']);
 Route::get('/book/{book}/edit', [Controllers\BookController::class, 'edit']);
 Route::post('/book/{book}/edit', [Controllers\BookController::class, 'store']);
+Route::post('/book/{book}/comment', [Controllers\BookController::class, 'comment']);
 Route::post('/book/add', [Controllers\BookController::class, 'store']);
 
 // Авторы
@@ -34,4 +35,5 @@ Route::get('/author/{author}', [Controllers\AuthorController::class, 'view']);
 Route::get('/author/{author}/delete', [Controllers\AuthorController::class, 'drop']);
 Route::get('/author/{author}/edit', [Controllers\AuthorController::class, 'edit']);
 Route::post('/author/{author}/edit', [Controllers\AuthorController::class, 'store']);
+Route::post('/author/{author}/comment', [Controllers\AuthorController::class, 'comment']);
 Route::post('/author/add', [Controllers\AuthorController::class, 'store']);