Skip to content

Commit b935422

Browse files
committed
fix: optimize preview rendering to only process changed blocks
1 parent ba2342e commit b935422

File tree

5 files changed

+95
-91
lines changed

5 files changed

+95
-91
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"andreiio/blade-remix-icon": "^3.6",
2828
"blade-ui-kit/blade-heroicons": "^2.4",
2929
"blade-ui-kit/blade-icons": "^1.7",
30-
"craftile/laravel": "^0.4.1",
30+
"craftile/laravel": "^0.4.2",
3131
"illuminate/contracts": "^11.0||^12.0",
3232
"mallardduck/blade-lucide-icons": "^1.23",
3333
"matthieumastadenis/couleur": "^0.1.2",

src/Http/Controllers/Admin/ThemeEditorController.php

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public function index($themeCode)
4444
'config' => [
4545
'baseUrl' => parse_url(route('visual.admin.editor', ['theme' => $themeCode]), PHP_URL_PATH),
4646
'imagesBaseUrl' => Storage::disk(config('bagisto_visual.images_storage'))->url(''),
47-
'storefrontUrl' => url('/').'?'.http_build_query(['_designMode' => $themeCode]),
47+
'storefrontUrl' => url('/') . '?' . http_build_query(['_designMode' => $themeCode]),
4848
'channels' => $this->getChannels(),
4949
'defaultChannel' => core()->getDefaultChannelCode(),
5050
'blockSchemas' => $this->loadBlocks(),
@@ -87,13 +87,14 @@ public function persistUpdates(Request $request)
8787
'updates.regions' => ['present', 'array'],
8888
]);
8989

90-
$this->persistEditorUpdates->handle($validated);
90+
$result = $this->persistEditorUpdates->handle($validated);
91+
$loadedBlocks = $result['loadedBlocks'] ?? [];
9192

92-
$changedBlockIds = $this->extractChangedBlockIds($validated['updates']);
93+
$allBlockIds = $this->buildRenderSet($validated['updates'], $loadedBlocks);
9394

9495
$url = $request->input('template.url');
9596

96-
return $this->renderPreview->execute($url, $changedBlockIds);
97+
return $this->renderPreview->execute($url, $allBlockIds);
9798
}
9899

99100
public function persistThemeSettings(Request $request)
@@ -136,7 +137,7 @@ public function uploadImages(Request $request)
136137
return $images->map(function ($image) {
137138
$originalName = pathinfo($image->getClientOriginalName(), PATHINFO_FILENAME);
138139
$extension = $image->guessExtension();
139-
$storedName = bin2hex($originalName).'_'.uniqid().'.'.$extension;
140+
$storedName = bin2hex($originalName) . '_' . uniqid() . '.' . $extension;
140141

141142
$path = $image->storeAs(
142143
config('bagisto_visual.images_directory'),
@@ -211,15 +212,15 @@ public function icons(Request $request, Factory $factory, IconsManifest $iconsMa
211212

212213
$icons->push([
213214
'name' => $name,
214-
'id' => $set['prefix'].'-'.$name,
215+
'id' => $set['prefix'] . '-' . $name,
215216
'svg' => File::get($file->getRealPath()),
216217
]);
217218
}
218219
}
219220

220221
return [
221222
'currentSet' => $selectedSet,
222-
'sets' => collect($sets)->map(fn ($set, $key) => ['id' => $key, 'prefix' => $set['prefix'], 'name' => Str::headline($key)])->values(),
223+
'sets' => collect($sets)->map(fn($set, $key) => ['id' => $key, 'prefix' => $set['prefix'], 'name' => Str::headline($key)])->values(),
223224
'icons' => $icons->values(),
224225
];
225226
}
@@ -262,7 +263,7 @@ protected function loadBlocks()
262263
'category' => $blockSchema->category,
263264
'description' => $blockSchema->description,
264265
'previewImageUrl' => asset($blockSchema->previewImageUrl),
265-
'isSection' => collect([SimpleSection::class, BladeSection::class, LivewireSection::class])->some(fn ($class) => is_subclass_of($blockSchema->class, $class)),
266+
'isSection' => collect([SimpleSection::class, BladeSection::class, LivewireSection::class])->some(fn($class) => is_subclass_of($blockSchema->class, $class)),
266267
'enabledOn' => $blockSchema->enabledOn ?? [],
267268
'disabledOn' => $blockSchema->disabledOn ?? [],
268269
],
@@ -294,7 +295,7 @@ protected function loadTheme($themeCode)
294295
protected function loadTemplates()
295296
{
296297
return collect(app(\BagistoPlus\Visual\ThemeEditor::class)->getTemplates())
297-
->map(fn ($template) => [
298+
->map(fn($template) => [
298299
'template' => $template->template,
299300
'label' => $template->label,
300301
'icon' => $template->icon,
@@ -331,7 +332,7 @@ protected function translateSettingsSchema(array $settingsSchema): array
331332

332333
protected function getChannels()
333334
{
334-
return core()->getAllChannels()->map(fn ($channel) => [
335+
return core()->getAllChannels()->map(fn($channel) => [
335336
'code' => $channel->code,
336337
'name' => $channel->name,
337338
'locales' => $channel->locales,
@@ -342,7 +343,7 @@ protected function getChannels()
342343
protected function getChannelCodes(): array
343344
{
344345
return $this->getChannels()
345-
->map(fn ($channel) => $channel['code'])
346+
->map(fn($channel) => $channel['code'])
346347
->toArray();
347348
}
348349

@@ -354,28 +355,66 @@ protected function getLocaleCodes(string $channel): array
354355
return [];
355356
}
356357

357-
return $channel['locales']->map(fn ($locale) => $locale['code'])->toArray();
358+
return $channel['locales']->map(fn($locale) => $locale['code'])->toArray();
358359
}
359360

360361
protected function getVisualThemes(): array
361362
{
362363
return collect(config('themes.shop', []))
363-
->filter(fn ($config) => $config['visual_theme'] ?? false)
364-
->map(fn ($config) => $config['code'])
364+
->filter(fn($config) => $config['visual_theme'] ?? false)
365+
->map(fn($config) => $config['code'])
365366
->toArray();
366367
}
367368

368369
/**
369-
* Extract changed block IDs from updates.
370-
* Tree expansion (parents + children) happens during render via BlockRenderFilter.
370+
* Build complete render set: changed blocks + all their parents + all their children.
371371
*/
372-
protected function extractChangedBlockIds(array $updates): array
372+
protected function buildRenderSet(array $updates, array $loadedBlocks): array
373373
{
374374
$changes = $updates['changes'] ?? [];
375-
376-
return array_merge(
375+
$changedIds = array_merge(
377376
$changes['added'] ?? [],
378377
$changes['updated'] ?? []
379378
);
379+
380+
$renderSet = [];
381+
382+
foreach ($changedIds as $id) {
383+
$renderSet[] = $id;
384+
385+
// Walk up parent chain
386+
$currentId = $id;
387+
while (isset($loadedBlocks[$currentId]['parentId']) && $loadedBlocks[$currentId]['parentId']) {
388+
$parentId = $loadedBlocks[$currentId]['parentId'];
389+
$renderSet[] = $parentId;
390+
391+
// If parent is a repeated block, include its entire children structure
392+
if (isset($loadedBlocks[$parentId]['repeated']) && $loadedBlocks[$parentId]['repeated']) {
393+
$this->addChildren($parentId, $loadedBlocks, $renderSet);
394+
}
395+
396+
$currentId = $parentId;
397+
}
398+
399+
// Walk down children
400+
$this->addChildren($id, $loadedBlocks, $renderSet);
401+
}
402+
403+
return array_unique($renderSet);
404+
}
405+
406+
/**
407+
* Recursively add all children to the render set.
408+
*/
409+
protected function addChildren(string $blockId, array $loadedBlocks, array &$renderSet): void
410+
{
411+
if (! isset($loadedBlocks[$blockId]['children'])) {
412+
return;
413+
}
414+
415+
foreach ($loadedBlocks[$blockId]['children'] as $childId) {
416+
$renderSet[] = $childId;
417+
$this->addChildren($childId, $loadedBlocks, $renderSet);
418+
}
380419
}
381420
}

src/Persistence/RenderPreview.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Craftile\Laravel\Facades\Craftile;
77
use Craftile\Laravel\View\JsonViewParser;
88
use Illuminate\Http\Request;
9+
use Illuminate\Support\Str;
910

1011
class RenderPreview
1112
{
@@ -20,17 +21,20 @@ public function execute(string $url, ?array $blockIds = null): string
2021
$baseUrl = rtrim(config('app.url'));
2122
$basePath = parse_url($baseUrl, PHP_URL_PATH);
2223

24+
// Store block IDs in session with unique key if provided
25+
if ($blockIds !== null && count($blockIds) > 0) {
26+
$key = Str::random(16);
27+
session()->put("visual.render.{$key}", $blockIds);
28+
29+
$separator = str_contains($url, '?') ? '&' : '?';
30+
$url .= $separator.'_vkey='.$key;
31+
}
32+
2333
// Handle subdirectory installs by redirecting
2434
if ($basePath !== null) {
2535
return redirect($url);
2636
}
2737

28-
// Append _blocks query parameter if specific blocks are requested
29-
if ($blockIds !== null && count($blockIds) > 0) {
30-
$separator = str_contains($url, '?') ? '&' : '?';
31-
$url .= $separator.'_blocks='.implode(',', $blockIds);
32-
}
33-
3438
// Reset Craftile's preview mode cache before sub-request
3539
Craftile::detectPreviewUsing(function () {
3640
return \BagistoPlus\Visual\Facades\ThemeEditor::inDesignMode();

src/Providers/CoreServiceProvider.php

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ protected function bootCraftile(): void
106106
Craftile::normalizeTemplateUsing(new \BagistoPlus\Visual\Support\TemplateNormalizer);
107107

108108
Craftile::checkIfBlockCanRenderUsing(function (BlockData $blockData) {
109-
if (! ThemeEditor::inDesignMode() || ! request()->has('_blocks')) {
109+
if (! ThemeEditor::inDesignMode() || ! request()->has('_vkey')) {
110110
return ! $blockData->disabled;
111111
}
112112

@@ -150,7 +150,7 @@ protected function registerBlockCompilers(): void
150150
// Register Livewire blocks as Livewire components when they're discovered
151151
Event::listen(BlockSchemaRegistered::class, function (BlockSchemaRegistered $event) {
152152
if ($event->schema->class && is_subclass_of($event->schema->class, \Livewire\Component::class)) {
153-
$componentName = 'craftile-'.$event->schema->slug;
153+
$componentName = 'craftile-' . $event->schema->slug;
154154
\Livewire\Livewire::component($componentName, $event->schema->class);
155155
}
156156
});
@@ -161,13 +161,13 @@ protected function bootShopRoutes(): void
161161
{
162162
Route::prefix('/visual/template-preview')
163163
->middleware(['web', 'locale', 'theme', 'currency'])
164-
->group(__DIR__.'/../../routes/shop.php');
164+
->group(__DIR__ . '/../../routes/shop.php');
165165
}
166166

167167
protected function bootViewsAndTranslations(): void
168168
{
169-
$this->loadViewsFrom(__DIR__.'/../../resources/views', 'visual');
170-
$this->loadTranslationsFrom(__DIR__.'/../../resources/lang', 'visual');
169+
$this->loadViewsFrom(__DIR__ . '/../../resources/views', 'visual');
170+
$this->loadTranslationsFrom(__DIR__ . '/../../resources/lang', 'visual');
171171
}
172172

173173
protected function bootMiddlewares(): void
@@ -213,11 +213,11 @@ protected function bootCommands(): void
213213
protected function bootPublishAssets(): void
214214
{
215215
$this->publishes([
216-
__DIR__.'/../../public/vendor/bagistoplus' => public_path('vendor/bagistoplus'),
216+
__DIR__ . '/../../public/vendor/bagistoplus' => public_path('vendor/bagistoplus'),
217217
], ['public', 'visual', 'visual-assets']);
218218

219219
$this->publishes([
220-
__DIR__.'/../../config/bagisto-visual.php' => config_path('bagisto_visual.php'),
220+
__DIR__ . '/../../config/bagisto-visual.php' => config_path('bagisto_visual.php'),
221221
], ['config', 'visual', 'visual-config']);
222222
}
223223

@@ -241,8 +241,8 @@ protected function bootTemplates(): void
241241

242242
protected function registerConfigs(): void
243243
{
244-
$this->mergeConfigFrom(__DIR__.'/../../config/bagisto-visual.php', 'bagisto_visual');
245-
$this->mergeConfigFrom(__DIR__.'/../../config/svg-iconmap.php', 'bagisto_visual_iconmap');
244+
$this->mergeConfigFrom(__DIR__ . '/../../config/bagisto-visual.php', 'bagisto_visual');
245+
$this->mergeConfigFrom(__DIR__ . '/../../config/svg-iconmap.php', 'bagisto_visual_iconmap');
246246
}
247247

248248
protected function registerSingletons(): void
@@ -265,7 +265,7 @@ protected function registerCustomUrlGenerator(): void
265265

266266
return new UrlGenerator(
267267
$routes,
268-
$app->rebinding('request', fn ($app, $request) => $app['url']->setRequest($request)),
268+
$app->rebinding('request', fn($app, $request) => $app['url']->setRequest($request)),
269269
$app['config']['app.asset_url']
270270
);
271271
});

src/Support/BlockRenderFilter.php

Lines changed: 16 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,78 +7,39 @@
77

88
class BlockRenderFilter
99
{
10-
protected ?array $renderSet = null;
10+
protected array $blockIds;
1111

12-
protected array $processed = [];
13-
14-
/**
15-
* Check if a block should be rendered based on the _blocks query parameter.
16-
*/
17-
public function shouldRender(BlockData $blockData): bool
18-
{
19-
if ($this->renderSet === null) {
20-
$this->buildRenderSet();
21-
}
22-
23-
return isset($this->renderSet[$blockData->id]);
24-
}
25-
26-
/**
27-
* Build the set of blocks to render from query parameter.
28-
*/
29-
protected function buildRenderSet(): void
12+
public function __construct()
3013
{
31-
$changedBlocks = explode(',', request()->query('_blocks', ''));
32-
$this->renderSet = array_flip($changedBlocks);
14+
$key = request()->query('_vkey');
3315

34-
foreach ($changedBlocks as $blockId) {
35-
$this->expandBlockTree($blockId);
16+
if ($key) {
17+
$this->blockIds = session()->get("visual.render.{$key}", []);
18+
session()->forget("visual.render.{$key}");
19+
} else {
20+
$this->blockIds = [];
3621
}
3722
}
3823

3924
/**
40-
* Recursively expand block tree to include all parents and children.
25+
* Check if a block should be rendered.
4126
*/
42-
protected function expandBlockTree(string $blockId): void
27+
public function shouldRender(BlockData $blockData): bool
4328
{
44-
if (isset($this->processed[$blockId])) {
45-
return;
29+
// No filter = render all blocks
30+
if (empty($this->blockIds)) {
31+
return true;
4632
}
4733

48-
$this->processed[$blockId] = true;
49-
50-
try {
51-
$blockData = BlockDatastore::getBlock($blockId);
52-
53-
if (! $blockData) {
54-
return;
55-
}
56-
57-
$this->renderSet[$blockData->id] = true;
58-
59-
// Walk up: include all parents
60-
if ($blockData->parentId) {
61-
$this->expandBlockTree($blockData->parentId);
62-
}
63-
64-
// Walk down: include all children
65-
if ($blockData->hasChildren()) {
66-
foreach ($blockData->childrenIds() as $childId) {
67-
$this->expandBlockTree($childId);
68-
}
69-
}
70-
} catch (\Exception $e) {
71-
// Block not found or error - skip silently
72-
return;
73-
}
34+
// Check if block is in render set
35+
return in_array($blockData->id, $this->blockIds);
7436
}
7537

7638
/**
7739
* Reset the filter state (useful for testing).
7840
*/
7941
public function reset(): void
8042
{
81-
$this->renderSet = null;
82-
$this->processed = [];
43+
$this->blockIds = [];
8344
}
8445
}

0 commit comments

Comments
 (0)