Điều lạ lùng nhỉ!
Xưa kia, một lập trình viên đã được yêu cầu chuyển một form từ một ứng dụng sang một ứng dụng khác. Ứng dụng nguồn gốc là một ứng dụng Symfony. Ứng dụng đích là WordPress, hệ quản trị nội dung chạy trên web.
Bạn đang xem: Sử dụng Symfony Form trong WordPress
Hãy theo chúng tôi trong cuộc hành trình này, nó sẽ đưa bạn tới chừng mực của những gì có thể và những gì không nên làm, nhưng quan trọng nhất nó sẽ chỉ cho bạn cách sử dụng sức mạnh toàn diện của Symfony Form trong WordPress.
Tìm kiếm trên web cho loại phát triển này, bạn có thể tìm thấy ekino-wordpress-symfony hoặc LIN3S/WPSymfonyForm, nhưng những gì đó không được duy trì nữa. Vậy nên, hãy bắt đầu cuộc phiêu lưu nào!
Contents
WordPress của chúng tôi không phải là một cuộc đi hằng ngày
Có nhiều “hương vị” của WordPress trong thế giới này, và câu chuyện này diễn ra trong Bedrock.
Đây là một nền tảng thay đổi cuộc chơi bởi vì bạn có thể:
- Composer: tự động tải các lớp, quản lý plugin và giao diện, không cần push code thư viện.
- Các biến môi trường.
- Cấu trúc thư mục tốt hơn.
Vì vậy, việc thêm thành phần Symfony vào dự án kiểu này dễ như ăn bánh, Composer tự động quản lý tất cả các phụ thuộc và việc không sử dụng nó là một ý tưởng rất tồi tệ.
Một điều tuyệt vời khác về WordPress này là việc sử dụng Timber. Nó cho phép tách biệt logic PHP và HTML bằng cách sử dụng Twig trong giao diện WordPress của bạn.
Bản cài đặt WordPress thông thường không có tích hợp Composer hoặc Twig.
Chạy một Symfony Form
Thành phần form rất đơn giản, chúng ta cần kết nối các thành phần khác với symfony/form:
- Bộ máy render, với Twig, thông qua Timber.
- Bảo vệ CSRF để bảo mật.
- Bộ kiểm tra để kiểm tra dữ liệu.
- Bộ dịch để nhận thông báo lỗi kiểm tra trong ngôn ngữ tương ứng.
Dưới đây là danh sách đầy đủ các phụ thuộc mới chúng ta phải cài đặt:
$ composer require symfony/form symfony/twig-bridge symfony/validator symfony/security-csrf doctrine/annotations symfony/translation
Việc xử lý HTTP và phiên được thực hiện bởi thành phần HttpFoundation trong Symfony – tất nhiên không tồn tại trong WordPress; vì vậy, điều tuyệt vời là CSRF có thể sử dụng phiên PHP nguyên bản (NativeSessionTokenStorage) và thành phần form có thể xử lý yêu cầu từ superglobals PHP (NativeRequestHandler). Điều đó có nghĩa là không cần thiết phải sử dụng thành phần HttpFoundation.
Xây dựng người phiên dịch
Xem thêm : Top 5 Ngôn Ngữ Lập Trình Web Nổi Bật Trong Năm 2023
Để sử dụng bộ lọc Twig |trans và thông báo lỗi kiểm tra, chúng ta cần một phiên bản SymfonyContractsTranslationTranslatorInterface hợp lệ.
WordPress có các hàm dịch riêng của mình __($text, $domain = ‘default’), vì vậy chúng ta có thể xây dựng một loại cầu nối như sau:
$translator = new class implements TranslatorInterface {
public function getLocale() {
return get_locale();
}
public function trans(string $id, array $parameters = [], string $domain = null, string $locale = null) {
return __($id, $domain);
}
};
Nhưng điều này yêu cầu thêm các thông báo lỗi kiểm tra vào tệp dịch của WordPress, và không thể xử lý các tham số với nó, vì vậy nó sẽ trở nên lộn xộn.
Vì chúng ta không dịch nội dung trong mẫu hoặc bất kỳ mã WordPress nào khác, hãy sử dụng trực tiếp các tệp dịch và người dịch được cung cấp bởi Symfony:
$translator = new SymfonyComponentTranslationTranslator('fr');
$translator->addLoader('xliff', new SymfonyComponentTranslationLoaderXliffFileLoader());
$translator->addResource(
'xliff',
__DIR__ . '/../../../../vendor/symfony/validator/Resources/translations/validators.fr.xlf',
'fr',
'validators'
);
Cấu hình Timber
Tiếp theo, chúng ta thêm phần mở rộng Form Twig cho Timber. Điều này phải được thực hiện thông qua bộ lọc, một cách thông thường trong WordPress để mở rộng và cấu hình các thứ:
use SymfonyBridgeTwigExtensionFormExtension;
use SymfonyBridgeTwigExtensionTranslationExtension;
use SymfonyBridgeTwigFormTwigRendererEngine;
use SymfonyComponentFormFormRenderer;
use TwigRuntimeLoaderFactoryRuntimeLoader;
// Đăng ký một vị trí mới để tìm kiếm các mẫu
add_filter('timber/locations', function ($loc) {
$loc[] = __DIR__ . '/../../../../vendor/symfony/twig-bridge/Resources/views/Form/';
return $loc;
});
add_filter('timber/twig', function(TwigEnvironment $twig) use ($translator) {
// Khởi động bộ máy render Form với theme form của chúng ta
$rendererEngine = new TwigRendererEngine([
'bootstrap_3_horizontal_layout.html.twig',
'your_custom_theme.html.twig'
], $twig);
$twig->addRuntimeLoader(new FactoryRuntimeLoader([
FormRenderer::class => function () use ($rendererEngine) {
return new FormRenderer($rendererEngine);
},
]));
// Thêm các phần mở rộng để thêm các hàm tương ứng (form(), |trans...)
$twig->addExtension(new FormExtension());
$twig->addExtension(new TranslationExtension($translator));
return $twig;
});
Như vậy, phiên bản Twig được sử dụng bởi Timber hiện đã có khả năng hiển thị một Symfony Form bằng cách sử dụng Form Theme và các bản dịch.
Nhưng không có tự động thoát
Một khác biệt giữa Twig trong Symfony và Twig trong Timber là chiến lược thoát dấu. Trong Timber, nó hoàn toàn bị tắt theo mặc định. Twig không tự động thoát dữ liệu.
Hãy thử có các thuộc tính dữ liệu phong phú (với HTML) trên các loại form của bạn và bạn sẽ gặp sự cố hiển thị. Hãy thử gửi “><script>alert(1337)</script>” vào một trường văn bản và bạn sẽ lo lắng cho sự bảo mật của mình.
Hãy thử đặt Timber::$autoescape = true; và bạn sẽ thấy mã nguồn của mọi thứ. Điều đó giống như xem màn hình xanh màu xanh của Matrix nhưng trên nội dung WYSIWYG của bạn.
Điều này chủ yếu là một vấn đề “mã cũ”, vì tất cả các mẫu của ứng dụng đích chúng tôi giả định không có tính thoát, không có bộ lọc |raw trên các biến chứa mã HTML an toàn.
Để tránh việc viết lại tất cả các mẫu cũ nếu thay đổi một hành vi mặc định của Timber, chúng tôi đã thêm một chủ đề form tùy chỉnh để bắt buộc thoát các thuộc tính:
{% block attributes -%}
{%- for attrname, attrvalue in attr -%}
{{- " " -}}
{%- if attrname in ['placeholder', 'title'] -%}
{{- attrname }}="{{ translation_domain is same as(false) or attrvalue is null ? attrvalue|escape('html_attr') : attrvalue|trans(attr_translation_parameters, translation_domain)|escape('html_attr') }}"
{%- elseif attrvalue is same as(true) -%}
{{- attrname }}="{{ attrname }}"
{%- elseif attrvalue is not same as(false) -%}
{{- attrname }}="{{ attrvalue|escape('html_attr') }}"
{%- endif -%}
{%- endfor -%}
{%- endblock attributes -%}
{%- block textarea_widget -%}
{% autoescape 'html' %}
{% set attr = attr|merge({class: (attr.class|default('') ~ ' form-control')|trim}) %}
<textarea {{ block('widget_attributes') }}>{{ value }}</textarea>
{% endautoescape %}
{%- endblock textarea_widget -%}
{%- block form_widget_simple -%}
{% if type is not defined or type not in ['file', 'hidden'] %}
{%- set attr = attr|merge({class: (attr.class|default('') ~ ' form-control')|trim}) -%}
{% endif %}
{%- set type = type|default('text') -%}
{%- if type == 'range' or type == 'color' -%}
{# Không hỗ trợ thuộc tính "required" #}
{%- set required = false -%}
{%- endif -%}
{% autoescape 'html' %}
<input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
{% endautoescape %}
{%- endblock form_widget_simple -%}
Đó là một trở ngại tốt – chắc chắn có một vấn đề ở đây, tôi đề xuất bạn bật chế độ tự động thoát càng sớm càng tốt khi sử dụng Timber.
Lấy FormFactory
Đây là dịch vụ phức tạp nhất để khởi động thủ công trong dự án này.
function init_form_factory(TranslatorInterface $translator): FormFactoryInterface {
// Khởi tạo FormBuilder và Factory
$formFactoryBuilder = new FormFactoryBuilder(true);
// Thêm hỗ trợ kiểm tra
$validator = Validation::createValidatorBuilder()
->enableAnnotationMapping()
->setTranslator($translator)
->setTranslationDomain('validators')
->getValidator();
$formFactoryBuilder->addExtension(new ValidatorExtension($validator));
// Thêm hỗ trợ CSRF
$csrfGenerator = new UriSafeTokenGenerator();
$csrfStorage = new NativeSessionTokenStorage();
$csrfManager = new CsrfTokenManager($csrfGenerator, $csrfStorage);
$formFactoryBuilder->addExtension(new CsrfExtension($csrfManager));
return $formFactoryBuilder->getFormFactory();
}
Xem thêm : Tạo WordPress Pagination không cần plugin
Builder Form hoạt động rất tốt chỉ với new FormFactoryBuilder
, nhưng chúng ta cần thêm các extension của chúng ta:
- Hỗ trợ kiểm tra: tự động truyền dữ liệu của chúng tôi cho thành phần kiểm tra khi nộp.
- Bảo vệ CSRF: thêm trường ẩn token lên form của chúng tôi và đảm bảo rằng nó hợp lệ.
Tổng hợp trong chủ đề
Bây giờ bạn có thể khởi động một Form và truyền nó vào Timber bất cứ nơi nào bạn muốn, ví dụ như Trang Mẫu:
$context = Timber::get_context();
$contact = new Contact();
$formFactory = init_form_factory($translator);
$formBuilder = $formFactory->createBuilder(ContactType::class, $contact);
$form = $formBuilder->getForm();
$form->handleRequest();
if ($form->isSubmitted() && $form->isValid()) {
// Làm điều gì đó, ví dụ như gửi email?
wp_mail($contact->getEmail(), 'Cám ơn từ ứng dụng kiểm tra WordPress', 'nội dung lorem ipsum');
// Chuyển hướng để tránh nộp hai lần
wp_redirect($successUrl);
exit;
}
$context['form'] = $form->createView();
Timber::render('page-contact.twig', $context);
Trong view của bạn, bạn chạy form như thường lệ:
{{ form_start(form) }}
{{ form_errors(form) }}
{{ form_row(form.firstName) }}
{{ form_row(form.lastName) }}
{{ form_row(form.email) }}
<button class="btn" type="submit">Gửi</button>
{{ form_end(form) }}
Tất cả các giá trị của tôi đều có dấu gạch chéo ngược như trong năm 2010 😱
Bởi vì Symfony Form sử dụng NativeRequestHandler, nó lấy dữ liệu form từ biến siêu toàn cục $_POST. Đây là cách tiêu chuẩn để thu thập giá trị form trong PHP, và chúng ta đã sử dụng nó một lần hoặc nhiều lần.
Cũng có một hành vi trong PHP gọi là Magic Quote. Những người mới phát triển có thể không biết về nó vì nó là một mảnh vỡ của quá khứ; nó đã bị gỡ bỏ khỏi PHP vì nó hơi bị hỏng. Magic Quote tự động thêm các dấu gạch chéo ngược vào trong dữ liệu GPCS3 cho các ký tự sau:
- dấu nháy đơn (‘);
- dấu nháy kép (“);
- dấu gạch ngược ();
- NUL (byte NUL).
Tại sao chúng ta quan tâm nếu nó đã bị tắt trong PHP? Bởi vì WordPress là một phần mềm tương thích ngược với mức độ cao đặc biệt và vẫn hỗ trợ PHP 5.6 dưới sự bề ngoài!
Và để giữ tính tương thích đó với nhiều cài đặt hiện có, plugin và giao diện, WordPress áp dụng Magic Quote theo mặc định, thủ công và có hệ thống. Có một vấn đề đã tồn tại suốt mười một năm về vấn đề này và nó chỉ cho thấy việc tiến lên khó khăn tới đâu khi bạn có một cơ sở người dùng QUÁ LỚN.
Chúng tôi đã phải tìm ra một giải pháp vì nó đang phá vỡ dữ liệu của chúng tôi: nếu tôi gửi “Vegan Mac ‘n’ Cheese”, tôi sẽ nhận được “Vegan Mac ‘n’ Cheese”, sau đó là “Vegan Mac ‘n’ Cheese”, v.v.
Vì vậy, chúng tôi đã phải làm sạch các biến siêu toàn cục như sau:
$_POST = stripslashes_deep($_POST);
Để tránh tạo ra rủi ro cho các plugin và giao diện WordPress hiện có bằng cách mong đợi các gạch chéo ngược ở đó, chúng tôi sẽ thoát giá trị chỉ đối với Trình xử lý Yêu cầu. Vì vậy, chúng ta phải mở rộng NativeRequestHandler như sau:
$formBuilder->setRequestHandler(new class extends NativeRequestHandler {
public function handleRequest(FormInterface $form, $request = null)
{
$initialPostData = $_POST;
try {
// Chúng ta cần RAW $_POST mà không có Dấu gạch ngược
$_POST = stripslashes_deep($_POST);
// Xem https://stackoverflow.com/questions/8949768/with-magic-quotes-disabled-why-does-php-wordpress-continue-to-auto-escape-my
parent::handleRequest($form, $request);
} finally {
// Khôi phục dữ liệu
$_POST = $initialPostData;
}
}
});
Kết thúc cuộc hành trình
Chúng tôi đã thành công và an toàn chuyển form từ Symfony sang WordPress và cứu vãn cuộc sống. Tuần phát triển đã được tái chế vì chúng tôi không phải viết lại các form của mình, vì vậy trong khía cạnh đó đây là một chiến thắng.
Tuy nhiên, thật lộn xộn! Kết hợp hai framework không nên là lựa chọn mặc định: WordPress có rất nhiều plugin xây dựng form tuyệt vời, và chúng được tích hợp tốt hơn với hệ quản trị nội dung hơn những gì chúng tôi đã làm. Hãy sử dụng chúng.
Dự án có thể bị giới hạn về thời gian, ngân sách và di sản, đôi khi đưa bạn vào các lãnh thổ lạ lẫm; nhưng tôi đã học được nhiều thứ trong quá trình này!
Nguồn: https://laptrinhc.edu.vn
Danh mục: Web