setTable(self::TABLE_NAME) ->setCustomRepositoryClass(PageDraftRepository::class) ->addLifecycleEvent('cleanUrlsInContent', Events::preUpdate) ->addLifecycleEvent('cleanUrlsInContent', Events::prePersist); $builder->addId(); $builder->addNullableField('html', Types::TEXT); $builder->addNullableField('template', Types::STRING); $builder->createField('publicPreview', Types::BOOLEAN) ->columnName('public_preview') ->nullable(false) ->option('default', 1) ->build(); $builder->createOneToOne('page', Page::class) ->inversedBy('draft') ->addJoinColumn('page_id', 'id', false) ->build(); } /** * Lifecycle callback to clean URLs in the content. */ public function cleanUrlsInContent(): void { $this->html = $this->decodeAmpersands((string) $this->html); } public function getId(): ?int { return $this->id; } public function setId(?int $id): void { $this->id = $id; } public function getPage(): Page { return $this->page; } public function getHtml(): ?string { return $this->html; } public function setPage(Page $page): void { $this->page = $page; } public function setHtml(?string $html): void { $this->html = $html; } public function getTemplate(): ?string { return $this->template; } public function setTemplate(?string $template): void { $this->template = $template; } public function isPublicPreview(): bool { return (bool) $this->publicPreview; } public function setPublicPreview(bool $publicPreview): void { $this->publicPreview = $publicPreview; } /** * Check all links in content and decode & * This even works with double encoded ampersands. */ private function decodeAmpersands(string $content): string { if (!preg_match_all(self::REGEX_DECODE_AMPERSAND, $content, $matches)) { return $content; } foreach ($matches[0] as $url) { $newUrl = $url; while (str_contains($newUrl, '&')) { $newUrl = str_replace('&', '&', $newUrl); } $content = str_replace($url, $newUrl, $content); } return $content; } }