Input WYSIWYG: Updated API to show/accept html descriptions

Also aligned books, shelves and chapters to return description content
and some relations (where not breaking API) in create/update responses
also so that information can be seen direct from that input in a
request.

API docs and tests not yet updated to match.
This commit is contained in:
Dan Brown 2023-12-21 13:23:52 +00:00
parent ed5d67e609
commit 00ae04e0bd
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
9 changed files with 103 additions and 52 deletions

View File

@ -45,7 +45,7 @@ class BookApiController extends ApiController
$book = $this->bookRepo->create($requestData); $book = $this->bookRepo->create($requestData);
return response()->json($book); return response()->json($this->forJsonDisplay($book));
} }
/** /**
@ -56,9 +56,9 @@ class BookApiController extends ApiController
*/ */
public function read(string $id) public function read(string $id)
{ {
$book = Book::visible() $book = Book::visible()->findOrFail($id);
->with(['tags', 'cover', 'createdBy', 'updatedBy', 'ownedBy']) $book = $this->forJsonDisplay($book);
->findOrFail($id); $book->load(['createdBy', 'updatedBy', 'ownedBy']);
$contents = (new BookContents($book))->getTree(true, false)->all(); $contents = (new BookContents($book))->getTree(true, false)->all();
$contentsApiData = (new ApiEntityListFormatter($contents)) $contentsApiData = (new ApiEntityListFormatter($contents))
@ -89,7 +89,7 @@ class BookApiController extends ApiController
$requestData = $this->validate($request, $this->rules()['update']); $requestData = $this->validate($request, $this->rules()['update']);
$book = $this->bookRepo->update($book, $requestData); $book = $this->bookRepo->update($book, $requestData);
return response()->json($book); return response()->json($this->forJsonDisplay($book));
} }
/** /**
@ -108,21 +108,35 @@ class BookApiController extends ApiController
return response('', 204); return response('', 204);
} }
protected function forJsonDisplay(Book $book): Book
{
$book = clone $book;
$book->unsetRelations()->refresh();
$book->load(['tags', 'cover']);
$book->makeVisible('description_html')
->setAttribute('description_html', $book->descriptionHtml());
return $book;
}
protected function rules(): array protected function rules(): array
{ {
return [ return [
'create' => [ 'create' => [
'name' => ['required', 'string', 'max:255'], 'name' => ['required', 'string', 'max:255'],
'description' => ['string', 'max:1000'], 'description' => ['string', 'max:1900'],
'tags' => ['array'], 'description_html' => ['string', 'max:2000'],
'image' => array_merge(['nullable'], $this->getImageValidationRules()), 'tags' => ['array'],
'image' => array_merge(['nullable'], $this->getImageValidationRules()),
'default_template_id' => ['nullable', 'integer'], 'default_template_id' => ['nullable', 'integer'],
], ],
'update' => [ 'update' => [
'name' => ['string', 'min:1', 'max:255'], 'name' => ['string', 'min:1', 'max:255'],
'description' => ['string', 'max:1000'], 'description' => ['string', 'max:1900'],
'tags' => ['array'], 'description_html' => ['string', 'max:2000'],
'image' => array_merge(['nullable'], $this->getImageValidationRules()), 'tags' => ['array'],
'image' => array_merge(['nullable'], $this->getImageValidationRules()),
'default_template_id' => ['nullable', 'integer'], 'default_template_id' => ['nullable', 'integer'],
], ],
]; ];

View File

@ -12,11 +12,9 @@ use Illuminate\Validation\ValidationException;
class BookshelfApiController extends ApiController class BookshelfApiController extends ApiController
{ {
protected BookshelfRepo $bookshelfRepo; public function __construct(
protected BookshelfRepo $bookshelfRepo
public function __construct(BookshelfRepo $bookshelfRepo) ) {
{
$this->bookshelfRepo = $bookshelfRepo;
} }
/** /**
@ -48,7 +46,7 @@ class BookshelfApiController extends ApiController
$bookIds = $request->get('books', []); $bookIds = $request->get('books', []);
$shelf = $this->bookshelfRepo->create($requestData, $bookIds); $shelf = $this->bookshelfRepo->create($requestData, $bookIds);
return response()->json($shelf); return response()->json($this->forJsonDisplay($shelf));
} }
/** /**
@ -56,12 +54,14 @@ class BookshelfApiController extends ApiController
*/ */
public function read(string $id) public function read(string $id)
{ {
$shelf = Bookshelf::visible()->with([ $shelf = Bookshelf::visible()->findOrFail($id);
'tags', 'cover', 'createdBy', 'updatedBy', 'ownedBy', $shelf = $this->forJsonDisplay($shelf);
$shelf->load([
'createdBy', 'updatedBy', 'ownedBy',
'books' => function (BelongsToMany $query) { 'books' => function (BelongsToMany $query) {
$query->scopes('visible')->get(['id', 'name', 'slug']); $query->scopes('visible')->get(['id', 'name', 'slug']);
}, },
])->findOrFail($id); ]);
return response()->json($shelf); return response()->json($shelf);
} }
@ -86,7 +86,7 @@ class BookshelfApiController extends ApiController
$shelf = $this->bookshelfRepo->update($shelf, $requestData, $bookIds); $shelf = $this->bookshelfRepo->update($shelf, $requestData, $bookIds);
return response()->json($shelf); return response()->json($this->forJsonDisplay($shelf));
} }
/** /**
@ -105,22 +105,36 @@ class BookshelfApiController extends ApiController
return response('', 204); return response('', 204);
} }
protected function forJsonDisplay(Bookshelf $shelf): Bookshelf
{
$shelf = clone $shelf;
$shelf->unsetRelations()->refresh();
$shelf->load(['tags', 'cover']);
$shelf->makeVisible('description_html')
->setAttribute('description_html', $shelf->descriptionHtml());
return $shelf;
}
protected function rules(): array protected function rules(): array
{ {
return [ return [
'create' => [ 'create' => [
'name' => ['required', 'string', 'max:255'], 'name' => ['required', 'string', 'max:255'],
'description' => ['string', 'max:1000'], 'description' => ['string', 'max:1900'],
'books' => ['array'], 'description_html' => ['string', 'max:2000'],
'tags' => ['array'], 'books' => ['array'],
'image' => array_merge(['nullable'], $this->getImageValidationRules()), 'tags' => ['array'],
'image' => array_merge(['nullable'], $this->getImageValidationRules()),
], ],
'update' => [ 'update' => [
'name' => ['string', 'min:1', 'max:255'], 'name' => ['string', 'min:1', 'max:255'],
'description' => ['string', 'max:1000'], 'description' => ['string', 'max:1900'],
'books' => ['array'], 'description_html' => ['string', 'max:2000'],
'tags' => ['array'], 'books' => ['array'],
'image' => array_merge(['nullable'], $this->getImageValidationRules()), 'tags' => ['array'],
'image' => array_merge(['nullable'], $this->getImageValidationRules()),
], ],
]; ];
} }

View File

@ -15,18 +15,20 @@ class ChapterApiController extends ApiController
{ {
protected $rules = [ protected $rules = [
'create' => [ 'create' => [
'book_id' => ['required', 'integer'], 'book_id' => ['required', 'integer'],
'name' => ['required', 'string', 'max:255'], 'name' => ['required', 'string', 'max:255'],
'description' => ['string', 'max:1000'], 'description' => ['string', 'max:1900'],
'tags' => ['array'], 'description_html' => ['string', 'max:2000'],
'priority' => ['integer'], 'tags' => ['array'],
'priority' => ['integer'],
], ],
'update' => [ 'update' => [
'book_id' => ['integer'], 'book_id' => ['integer'],
'name' => ['string', 'min:1', 'max:255'], 'name' => ['string', 'min:1', 'max:255'],
'description' => ['string', 'max:1000'], 'description' => ['string', 'max:1900'],
'tags' => ['array'], 'description_html' => ['string', 'max:2000'],
'priority' => ['integer'], 'tags' => ['array'],
'priority' => ['integer'],
], ],
]; ];
@ -61,7 +63,7 @@ class ChapterApiController extends ApiController
$chapter = $this->chapterRepo->create($requestData, $book); $chapter = $this->chapterRepo->create($requestData, $book);
return response()->json($chapter->load(['tags'])); return response()->json($this->forJsonDisplay($chapter));
} }
/** /**
@ -69,9 +71,15 @@ class ChapterApiController extends ApiController
*/ */
public function read(string $id) public function read(string $id)
{ {
$chapter = Chapter::visible()->with(['tags', 'createdBy', 'updatedBy', 'ownedBy', 'pages' => function (HasMany $query) { $chapter = Chapter::visible()->findOrFail($id);
$query->scopes('visible')->get(['id', 'name', 'slug']); $chapter = $this->forJsonDisplay($chapter);
}])->findOrFail($id);
$chapter->load([
'createdBy', 'updatedBy', 'ownedBy',
'pages' => function (HasMany $query) {
$query->scopes('visible')->get(['id', 'name', 'slug']);
}
]);
return response()->json($chapter); return response()->json($chapter);
} }
@ -93,7 +101,7 @@ class ChapterApiController extends ApiController
try { try {
$this->chapterRepo->move($chapter, "book:{$requestData['book_id']}"); $this->chapterRepo->move($chapter, "book:{$requestData['book_id']}");
} catch (Exception $exception) { } catch (Exception $exception) {
if ($exception instanceof PermissionsException) { if ($exception instanceof PermissionsException) {
$this->showPermissionError(); $this->showPermissionError();
} }
@ -103,7 +111,7 @@ class ChapterApiController extends ApiController
$updatedChapter = $this->chapterRepo->update($chapter, $requestData); $updatedChapter = $this->chapterRepo->update($chapter, $requestData);
return response()->json($updatedChapter->load(['tags'])); return response()->json($this->forJsonDisplay($updatedChapter));
} }
/** /**
@ -119,4 +127,16 @@ class ChapterApiController extends ApiController
return response('', 204); return response('', 204);
} }
protected function forJsonDisplay(Chapter $chapter): Chapter
{
$chapter = clone $chapter;
$chapter->unsetRelations()->refresh();
$chapter->load(['tags']);
$chapter->makeVisible('description_html')
->setAttribute('description_html', $chapter->descriptionHtml());
return $chapter;
}
} }

View File

@ -31,7 +31,7 @@ class Book extends Entity implements HasCoverImage
public float $searchFactor = 1.2; public float $searchFactor = 1.2;
protected $fillable = ['name']; protected $fillable = ['name'];
protected $hidden = ['pivot', 'image_id', 'deleted_at']; protected $hidden = ['pivot', 'image_id', 'deleted_at', 'description_html'];
/** /**
* Get the url for this book. * Get the url for this book.

View File

@ -19,7 +19,7 @@ class Bookshelf extends Entity implements HasCoverImage
protected $fillable = ['name', 'description', 'image_id']; protected $fillable = ['name', 'description', 'image_id'];
protected $hidden = ['image_id', 'deleted_at']; protected $hidden = ['image_id', 'deleted_at', 'description_html'];
/** /**
* Get the books in this shelf. * Get the books in this shelf.

View File

@ -20,7 +20,7 @@ class Chapter extends BookChild
public float $searchFactor = 1.2; public float $searchFactor = 1.2;
protected $fillable = ['name', 'description', 'priority']; protected $fillable = ['name', 'description', 'priority'];
protected $hidden = ['pivot', 'deleted_at']; protected $hidden = ['pivot', 'deleted_at', 'description_html'];
/** /**
* Get the pages that this chapter contains. * Get the pages that this chapter contains.

View File

@ -3,6 +3,7 @@
"name": "My own book", "name": "My own book",
"slug": "my-own-book", "slug": "my-own-book",
"description": "This is my own little book", "description": "This is my own little book",
"description_html": "<p>This is my own <em>little</em> book</p>",
"created_at": "2020-01-12T14:09:59.000000Z", "created_at": "2020-01-12T14:09:59.000000Z",
"updated_at": "2020-01-12T14:11:51.000000Z", "updated_at": "2020-01-12T14:11:51.000000Z",
"created_by": { "created_by": {

View File

@ -4,6 +4,7 @@
"slug": "content-creation", "slug": "content-creation",
"name": "Content Creation", "name": "Content Creation",
"description": "How to create documentation on whatever subject you need to write about.", "description": "How to create documentation on whatever subject you need to write about.",
"description_html": "<p>How to create <strong>documentation</strong> on whatever subject you need to write about.</p>",
"priority": 3, "priority": 3,
"created_at": "2019-05-05T21:49:56.000000Z", "created_at": "2019-05-05T21:49:56.000000Z",
"updated_at": "2019-09-28T11:24:23.000000Z", "updated_at": "2019-09-28T11:24:23.000000Z",

View File

@ -3,6 +3,7 @@
"name": "My shelf", "name": "My shelf",
"slug": "my-shelf", "slug": "my-shelf",
"description": "This is my shelf with some books", "description": "This is my shelf with some books",
"description_html": "<p>This is my shelf with some books</p>",
"created_by": { "created_by": {
"id": 1, "id": 1,
"name": "Admin", "name": "Admin",