Implemented a block editor for changing the layout of the page
This commit is contained in:
251
app/Services/PreregistrationPageBlockWriter.php
Normal file
251
app/Services/PreregistrationPageBlockWriter.php
Normal file
@@ -0,0 +1,251 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\PageBlock;
|
||||
use App\Models\PreregistrationPage;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class PreregistrationPageBlockWriter
|
||||
{
|
||||
/**
|
||||
* @param array<int, array<string, mixed>> $blocks
|
||||
*/
|
||||
public function replaceBlocks(PreregistrationPage $page, array $blocks, Request $request): void
|
||||
{
|
||||
DB::transaction(function () use ($page, $blocks, $request): void {
|
||||
$page->load('blocks');
|
||||
$oldPaths = $this->collectBlockImagePaths($page);
|
||||
$page->blocks()->delete();
|
||||
|
||||
$orderedKeys = array_keys($blocks);
|
||||
usort($orderedKeys, function (string|int $a, string|int $b) use ($blocks): int {
|
||||
/** @var array<string, mixed> $rowA */
|
||||
$rowA = $blocks[$a];
|
||||
/** @var array<string, mixed> $rowB */
|
||||
$rowB = $blocks[$b];
|
||||
|
||||
return ((int) ($rowA['sort_order'] ?? 0)) <=> ((int) ($rowB['sort_order'] ?? 0));
|
||||
});
|
||||
|
||||
$allNewPaths = [];
|
||||
foreach ($orderedKeys as $blockKey) {
|
||||
/** @var array<string, mixed> $blockRow */
|
||||
$blockRow = $blocks[$blockKey];
|
||||
$type = (string) $blockRow['type'];
|
||||
/** @var array<string, mixed> $content */
|
||||
$content = is_array($blockRow['content'] ?? null) ? $blockRow['content'] : [];
|
||||
if ($type === 'text' && array_key_exists('body', $content) && is_string($content['body'])) {
|
||||
$content['body'] = ltrim($content['body']);
|
||||
}
|
||||
if ($type === 'image') {
|
||||
$content = $this->mergeImageBlockFiles($page, (string) $blockKey, $content, $request);
|
||||
}
|
||||
$allNewPaths = array_merge($allNewPaths, $this->pathsFromImageTypeContent($type, $content));
|
||||
|
||||
PageBlock::query()->create([
|
||||
'preregistration_page_id' => $page->id,
|
||||
'type' => $type,
|
||||
'content' => $content,
|
||||
'sort_order' => (int) ($blockRow['sort_order'] ?? 0),
|
||||
'is_visible' => (bool) ($blockRow['is_visible'] ?? true),
|
||||
]);
|
||||
}
|
||||
|
||||
$allNewPaths = array_values(array_unique($allNewPaths));
|
||||
foreach (array_diff($oldPaths, $allNewPaths) as $orphan) {
|
||||
if (is_string($orphan) && $orphan !== '') {
|
||||
Storage::disk('public')->delete($orphan);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function seedDefaultBlocks(PreregistrationPage $page): void
|
||||
{
|
||||
$rows = [
|
||||
[
|
||||
'type' => 'hero',
|
||||
'sort_order' => 0,
|
||||
'is_visible' => true,
|
||||
'content' => [
|
||||
'headline' => $page->title,
|
||||
'subheadline' => '',
|
||||
'eyebrow_text' => '',
|
||||
'eyebrow_style' => 'badge',
|
||||
'text_alignment' => 'center',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$next = 1;
|
||||
if ($page->start_date->isFuture()) {
|
||||
$rows[] = [
|
||||
'type' => 'countdown',
|
||||
'sort_order' => $next,
|
||||
'is_visible' => true,
|
||||
'content' => [
|
||||
'target_datetime' => $page->start_date->toIso8601String(),
|
||||
'title' => 'De pre-registratie opent over:',
|
||||
'expired_action' => 'reload',
|
||||
'style' => 'large',
|
||||
'show_labels' => true,
|
||||
'labels' => [
|
||||
'days' => 'dagen',
|
||||
'hours' => 'uren',
|
||||
'minutes' => 'minuten',
|
||||
'seconds' => 'seconden',
|
||||
],
|
||||
],
|
||||
];
|
||||
$next++;
|
||||
}
|
||||
|
||||
$rows[] = [
|
||||
'type' => 'benefits',
|
||||
'sort_order' => $next,
|
||||
'is_visible' => true,
|
||||
'content' => [
|
||||
'title' => 'Waarom voorregistreren?',
|
||||
'items' => [
|
||||
['icon' => 'ticket', 'text' => 'Exclusieve korting op tickets'],
|
||||
['icon' => 'clock', 'text' => 'Eerder toegang tot de ticketshop'],
|
||||
],
|
||||
'layout' => 'list',
|
||||
'max_columns' => 2,
|
||||
],
|
||||
];
|
||||
$next++;
|
||||
|
||||
$rows[] = [
|
||||
'type' => 'social_proof',
|
||||
'sort_order' => $next,
|
||||
'is_visible' => true,
|
||||
'content' => [
|
||||
'template' => 'Al {count} bezoekers aangemeld!',
|
||||
'min_count' => 10,
|
||||
'show_animation' => true,
|
||||
'style' => 'pill',
|
||||
],
|
||||
];
|
||||
$next++;
|
||||
|
||||
$rows[] = [
|
||||
'type' => 'form',
|
||||
'sort_order' => $next,
|
||||
'is_visible' => true,
|
||||
'content' => [
|
||||
'title' => 'Registreer nu',
|
||||
'description' => '',
|
||||
'button_label' => 'Registreer nu!',
|
||||
'button_color' => '#F47B20',
|
||||
'button_text_color' => '#FFFFFF',
|
||||
'fields' => [
|
||||
'first_name' => [
|
||||
'enabled' => true,
|
||||
'required' => true,
|
||||
'label' => 'Voornaam',
|
||||
'placeholder' => 'Je voornaam',
|
||||
],
|
||||
'last_name' => [
|
||||
'enabled' => true,
|
||||
'required' => true,
|
||||
'label' => 'Achternaam',
|
||||
'placeholder' => 'Je achternaam',
|
||||
],
|
||||
'email' => [
|
||||
'enabled' => true,
|
||||
'required' => true,
|
||||
'label' => 'E-mailadres',
|
||||
'placeholder' => 'je@email.nl',
|
||||
],
|
||||
'phone' => [
|
||||
'enabled' => false,
|
||||
'required' => false,
|
||||
'label' => 'Mobiel',
|
||||
'placeholder' => '+31 6 12345678',
|
||||
],
|
||||
],
|
||||
'show_field_icons' => true,
|
||||
'privacy_text' => 'Door je te registreren ga je akkoord met onze privacyverklaring.',
|
||||
'privacy_url' => '',
|
||||
],
|
||||
];
|
||||
|
||||
$page->blocks()->createMany($rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
private function collectBlockImagePaths(PreregistrationPage $page): array
|
||||
{
|
||||
$paths = [];
|
||||
foreach ($page->blocks as $b) {
|
||||
if ($b->type !== 'image') {
|
||||
continue;
|
||||
}
|
||||
$paths = array_merge($paths, $this->pathsFromImageTypeContent('image', is_array($b->content) ? $b->content : []));
|
||||
}
|
||||
|
||||
return array_values(array_unique($paths));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $content
|
||||
* @return list<string>
|
||||
*/
|
||||
private function pathsFromImageTypeContent(string $type, array $content): array
|
||||
{
|
||||
if ($type !== 'image') {
|
||||
return [];
|
||||
}
|
||||
$p = data_get($content, 'image');
|
||||
if (is_string($p) && $p !== '') {
|
||||
return [$p];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $content
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function mergeImageBlockFiles(PreregistrationPage $page, string $blockKey, array $content, Request $request): array
|
||||
{
|
||||
$disk = Storage::disk('public');
|
||||
$dir = "preregister/pages/{$page->id}";
|
||||
|
||||
$link = $content['link_url'] ?? null;
|
||||
$content['link_url'] = is_string($link) && trim($link) !== '' ? trim($link) : null;
|
||||
|
||||
if ($request->boolean("blocks.$blockKey.remove_block_image")) {
|
||||
$prev = $content['image'] ?? null;
|
||||
if (is_string($prev) && $prev !== '') {
|
||||
$disk->delete($prev);
|
||||
}
|
||||
$content['image'] = null;
|
||||
}
|
||||
|
||||
$file = $request->file("blocks.$blockKey.block_image");
|
||||
if ($this->isValidUpload($file)) {
|
||||
if (isset($content['image']) && is_string($content['image']) && $content['image'] !== '') {
|
||||
$disk->delete($content['image']);
|
||||
}
|
||||
$content['image'] = $file->store($dir, 'public');
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
private function isValidUpload(?UploadedFile $file): bool
|
||||
{
|
||||
return $file !== null && $file->isValid();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user