Refactor Quill handling as a separate database column

This commit is contained in:
Christopher C. Wells 2021-03-08 06:04:54 -08:00 committed by Christopher Charbonneau Wells
parent 99300d1b2d
commit 053c8305a1
7 changed files with 71 additions and 32 deletions

View File

@ -154,6 +154,7 @@ class RecipeController extends Controller
$input = $request->validate([ $input = $request->validate([
'name' => ['required', 'string'], 'name' => ['required', 'string'],
'description' => ['nullable', 'string'], 'description' => ['nullable', 'string'],
'description_delta' => ['nullable', 'string'],
'servings' => ['required', 'numeric'], 'servings' => ['required', 'numeric'],
'time_prep' => ['nullable', 'numeric'], 'time_prep' => ['nullable', 'numeric'],
'time_active' => ['nullable', 'numeric'], 'time_active' => ['nullable', 'numeric'],
@ -186,6 +187,7 @@ class RecipeController extends Controller
$recipe->fill([ $recipe->fill([
'name' => Str::lower($input['name']), 'name' => Str::lower($input['name']),
'description' => $input['description'], 'description' => $input['description'],
'description_delta' => $input['description_delta'],
'servings' => (int) $input['servings'], 'servings' => (int) $input['servings'],
'weight' => $input['weight'], 'weight' => $input['weight'],
'time_prep' => (int) $input['time_prep'], 'time_prep' => (int) $input['time_prep'],

View File

@ -28,7 +28,7 @@ class RecipeSchema extends SchemaProvider
return [ return [
'slug' => $resource->slug, 'slug' => $resource->slug,
'name' => $resource->name, 'name' => $resource->name,
'description' => $resource->description_html, 'description' => $resource->description,
'time_prep' => $resource->time_prep, 'time_prep' => $resource->time_prep,
'time_active' => $resource->time_active, 'time_active' => $resource->time_active,
'time_total' => $resource->time_total, 'time_total' => $resource->time_total,

View File

@ -62,7 +62,8 @@ use Spatie\Tags\HasTags;
* @method static \Illuminate\Database\Eloquent\Builder|Recipe whereTimeActive($value) * @method static \Illuminate\Database\Eloquent\Builder|Recipe whereTimeActive($value)
* @method static \Illuminate\Database\Eloquent\Builder|Recipe whereTimePrep($value) * @method static \Illuminate\Database\Eloquent\Builder|Recipe whereTimePrep($value)
* @method static \Illuminate\Database\Eloquent\Builder|Recipe withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug) * @method static \Illuminate\Database\Eloquent\Builder|Recipe withUniqueSlugConstraints(\Illuminate\Database\Eloquent\Model $model, string $attribute, array $config, string $slug)
* @property-read string $description_html * @property string|null $description_delta
* @method static \Illuminate\Database\Eloquent\Builder|Recipe whereDescriptionDelta($value)
*/ */
final class Recipe extends Model final class Recipe extends Model
{ {
@ -81,6 +82,7 @@ final class Recipe extends Model
protected $fillable = [ protected $fillable = [
'name', 'name',
'description', 'description',
'description_delta',
'time_prep', 'time_prep',
'time_active', 'time_active',
'source', 'source',
@ -114,7 +116,6 @@ final class Recipe extends Model
* @inheritdoc * @inheritdoc
*/ */
protected $appends = [ protected $appends = [
'description_html',
'serving_weight', 'serving_weight',
'time_total', 'time_total',
]; ];
@ -127,30 +128,13 @@ final class Recipe extends Model
return [ return [
'name' => $this->name, 'name' => $this->name,
'tags' => $this->tags->pluck('name')->toArray(), 'tags' => $this->tags->pluck('name')->toArray(),
'description' => $this->description_html, 'description' => $this->description,
'source' => $this->source, 'source' => $this->source,
'created_at' => $this->created_at, 'created_at' => $this->created_at,
'updated_at' => $this->updated_at, 'updated_at' => $this->updated_at,
]; ];
} }
/**
* Get description as an HTML string.
*/
public function getDescriptionHtmlAttribute(): ?string {
$description = $this->description;
if (!empty($description)) {
try {
$quill = new Render($this->description);
$description = $quill->render();
} catch (\Exception $e) {
// TODO: Log this or something.
$description = null;
}
}
return $description;
}
/** /**
* Get total recipe time. * Get total recipe time.
*/ */

View File

@ -18,6 +18,7 @@ class CreateRecipesTable extends Migration
$table->string('name'); $table->string('name');
$table->string('slug')->unique(); $table->string('slug')->unique();
$table->longText('description')->nullable(); $table->longText('description')->nullable();
$table->longText('description_delta')->nullable();
$table->integer('time_prep')->nullable(); $table->integer('time_prep')->nullable();
$table->integer('time_active')->nullable(); $table->integer('time_active')->nullable();
$table->string('source')->nullable(); $table->string('source')->nullable();

View File

@ -0,0 +1,45 @@
<?php
use App\Models\Recipe;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddDescriptionDeltaToRecipes extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('recipes', function (Blueprint $table) {
$table->longText('description_delta')->nullable()->after('description');
});
foreach (Recipe::all() as $recipe) {
if (empty($recipe->description)) {
continue;
}
// Format as a basic Quill Delta.
// See: https://quilljs.com/docs/delta/
$delta = ['ops' => [['insert' => "{$recipe->description}\n"]]];
$recipe->description_delta = json_encode($delta);
$recipe->save();
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('recipes', function (Blueprint $table) {
$table->dropColumn('description_delta');
});
}
}

View File

@ -76,6 +76,10 @@
:value="old('description', $recipe->description)" /> :value="old('description', $recipe->description)" />
<div class="quill-editor"></div> <div class="quill-editor"></div>
<x-inputs.input name="description_delta"
type="hidden"
:value="old('description_delta', $recipe->description_delta)" />
</div> </div>
<!-- Source --> <!-- Source -->
@ -152,7 +156,7 @@
theme: 'snow' theme: 'snow'
}); });
try { try {
description.setContents(JSON.parse(document.querySelector('input[name="description"]').value)); description.setContents(JSON.parse(document.querySelector('input[name="description_delta"]').value));
} catch (e) {} } catch (e) {}
new Draggable.Sortable(document.querySelector('.ingredients'), { new Draggable.Sortable(document.querySelector('.ingredients'), {
@ -195,8 +199,11 @@
// Remove any hidden templates before form submit. // Remove any hidden templates before form submit.
document.querySelectorAll(':scope .entry-template').forEach(e => e.remove()); document.querySelectorAll(':scope .entry-template').forEach(e => e.remove());
// Add description value to hidden field. // Add description values to hidden fields.
document.querySelector('input[name="description"]').value = JSON.stringify(description.getContents()); document.querySelector('input[name="description_delta"]').value = JSON.stringify(description.getContents());
document.querySelector('input[name="description"]').value = description.root.innerHTML
// Remove extraneous spaces from rendered result.
.replaceAll('<p><br></p>', '');
} }
</script> </script>
@endpush @endpush

View File

@ -20,15 +20,9 @@
</x-slot> </x-slot>
<div class="flex flex-col-reverse justify-between pb-4 sm:flex-row"> <div class="flex flex-col-reverse justify-between pb-4 sm:flex-row">
<div x-data="{showNutrientsSummary: false}"> <div x-data="{showNutrientsSummary: false}">
@if($recipe->description_html) @if($recipe->description)
<section class="mb-2 prose prose-lg md:prose-xl"> <section class="mb-2 prose prose-lg md:prose-xl">
{!! $recipe->description_html !!} {!! $recipe->description !!}
</section>
@endif
@if(!$recipe->tags->isEmpty())
<section class="mb-2 text-gray-700 text-sm">
<h1 class="font-extrabold inline">Tags:</h1>
{{ implode(', ', $recipe->tags->pluck('name')->all()) }}
</section> </section>
@endif @endif
@if($recipe->time_total > 0) @if($recipe->time_total > 0)
@ -138,4 +132,10 @@
@endif @endif
</footer> </footer>
@endif @endif
@if(!$recipe->tags->isEmpty())
<section class="mb-2 text-gray-500 text-sm">
<h1 class="font-extrabold inline">Tags:</h1>
{{ implode(', ', $recipe->tags->pluck('name')->all()) }}
</section>
@endif
</x-app-layout> </x-app-layout>