Nếu bạn có nhu cầu cụ thể để chuyển đổi dữ liệu giữa biểu mẫu và quá trình ghi của thực thể của mình và nghĩ rằng mã chuyển đổi này nên được đặt trong một dịch vụ vì nó có ý nghĩa hơn, bạn có thể sử dụng dataMapper bằng cách tận dụng tính năng tiêm phụ thuộc do Symfony cung cấp.
Nhưng làm thế nào để triển khai nó?
Một lưu ý nhỏ về DataMapper. Nếu chúng ta đồng ý với tài liệu chính thức của Symfony về DataMapper và DataTransformer, một dataMapper có trách nhiệm đọc và viết một đối tượng (một thực thể) để truyền nó cho một biểu mẫu. Nói ngắn gọn, nó làm cầu nối giữa một biểu mẫu và thực thể của nó.
Để tóm lại: Bộ biến đổi dữ liệu thay đổi một yếu tố của thực thể, dataMapper chịu trách nhiệm cho toàn bộ đối tượng.
DataMapperInterface một cách lô-gic đề xuất hai phương thức:
Một lấy dữ liệu từ biểu mẫu để cho chúng ta khả năng chuyển đổi nó thành thực thể mục tiêu (mapFormsToData()). Phương thức kia, ngược lại, lấy dữ liệu từ thực thể để chuyển đổi nó cho biểu mẫu (mapDataToForms()).
Hãy cẩn thận, quá trình chuyển đổi này diễn ra trước khi ghi thực thể, do đó trước khi xác nhận.
Nếu bạn có nhu cầu phức tạp hơn, bạn có thể sử dụng các sự kiện Symfony được định nghĩa trong lớp Symfony\Component\Form\FormEvents. Điều này, theo cách, được khuyến nghị.
FormEvents::PRE_SUBMIT, FormEvents::SUBMIT, FormEvents::POST_SUBMIT, FormEvents::PRE_SET_DATA, FormEvents::POST_SET_DATA. Chúng tôi chắc chắn sẽ nói về điều này lại trong một bài viết tương lai.
Hiện tại, hãy quay trở lại với biểu mẫu của chúng ta. Chúng tôi sẽ chỉ cho bạn cách bạn có thể dễ dàng sử dụng các khai báo dịch vụ và tự động ghép nối của Symfony để sử dụng một dịch vụ trực tiếp trong một dataMapper với Sonata. Ví dụ của chúng tôi không thực sự liên quan, vì một cơ chế tiên tiến hơn đã tồn tại trong Symfony. Chúng tôi sẽ sử dụng trường văn bản "Title" để viết phiên bản "slug" của nó trong trường "slug" tự động. Để định dạng Slug, chúng tôi sẽ sử dụng dịch vụ mà Symfony cung cấp cho chúng tôi: Symfony\Component\String\Slugger\SluggerInterface.
Do đó, chúng tôi sẽ triển khai một biểu mẫu với trường đơn 'title'. Khi biểu mẫu này được gửi, chúng tôi sẽ đi qua DataMapper sẽ gọi dịch vụ SlugService, và dịch vụ này sẽ có phương thức slugify() sẽ tìm dịch vụ Symfony SluggerInterface. Dưới đây là sơ đồ của bảng:
CREATE TABLE `titles` (
`id` int NOT NULL,
`title` varchar(350) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
`slug` varchar(350) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
Dưới đây là định nghĩa của các dịch vụ của chúng tôi:
admin.titles:
class: App\Admin\SlugTitlesAdmin
tags:
- { name: sonata.admin, model_class: App\Entity\SlugTitles, controller: App\Controller\SlugTitlesAdminController, manager_type: orm, group: admin , label: "Administrations de vos titres de pages" }
arguments: [ ~, ~, ~, "@slug.datamapper" ]
app.slug_service:
class: App\Service\SlugService
slug.datamapper:
class: App\Form\DataMapper\SlugDataMapper
arguments : ["@app.slug_service"]
public: true
Theo thứ tự, chúng tôi có biểu mẫu, dịch vụ Slug, và DataMapper của chúng tôi cho phép chúng tôi kết nối giữa hai cái này. Mẹo là xem xét dataMapper như một dịch vụ. Tiêm phụ thuộc sẽ được thực hiện một cách tự nhiên bởi Symfony. DataMapper sẽ được áp dụng cho Biểu mẫu mà nó sẽ tự nhiên có dịch vụ của mình thiết lập vì nó được khai báo trong hàm tạo của nó.
Giờ đây, chúng tôi cần thêm khai báo của biểu mẫu của mình trong sonata_admin.yaml.
dashboard:
groups:
runroom:
slug_titles:
label: "Titles"
icon: <i class="fa fa-users"></i>
on_top: true
items:
- admin.titles
Dưới đây là biểu mẫu đó:
<?php
declare(strict_types=1);
namespace App\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Form\Type\TemplateType;
use Sonata\AdminBundle\Show\ShowMapper;
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
final class SlugTitlesAdmin extends AbstractAdmin
{
private DataMapperInterface $slugDataMapper;
/**
* @param string|null $code
* @param string|null $class
* @param string|null $baseControllerName
* @param DataMapperInterface $slugDataMapper
*/
public function __construct(?string $code, ?string $class,?string $baseControllerName, DataMapperInterface $slugDataMapper)
{
parent::__construct($code, $class, $baseControllerName);
$this->slugDataMapper = $slugDataMapper;
}
/**
* @param DatagridMapper $filter
* @return void
*/
protected function configureDatagridFilters(DatagridMapper $filter): void
{
$filter
->add('id')
->add('title')
->add('slug')
;
}
/**
* @param ListMapper $list
* @return void
*/
protected function configureListFields(ListMapper $list): void
{
$list
->add('title')
->add('slug')
->add(ListMapper::NAME_ACTIONS, null, [
'actions' => [
'show' => [],
'edit' => [],
'delete' => [],
],
]);
;
}
/**
* @param FormMapper $form
* @return void
*/
protected function configureFormFields(FormMapper $form): void
{
$form
->add('title', TextType::class, ['help' => 'Renseignez le titre de votre article'])
->add('slug', HiddenType::class)
->add('informations',TemplateType::class, [
"template"=>'slug_informations.html.twig',
'label' => false,
'parameters'=> ['subject'=> $this->getSubject()]
]
)
;
$builder = $form->getFormBuilder();
$builder->setDataMapper($this->slugDataMapper);
}
/**
* @param ShowMapper $show
* @return void
*/
protected function configureShowFields(ShowMapper $show): void
{
$show
->add('id')
->add('title')
->add('slug')
;
}
}
Như bạn có thể thấy, title là một trường văn bản chuẩn. Chúng tôi thêm trường slug để có thể truy cập nó trong DataMapper của chúng tôi (nhưng chúng tôi không hiển thị nó) và chúng tôi đã thêm trường template chỉ để chúng tôi có hiển thị của trường slug được thông tin của chúng tôi.
Lưu ý dịch vụ DataMapper được truyền trong hàm tạo mà chúng tôi trực tiếp tái sử dụng trong formBuilder qua setDataMapper().
<?php
namespace App\Form\DataMapper;
use App\Entity\SlugTitles;
use App\Service\SlugService;
use Symfony\Component\Form\DataMapperInterface;
use Traversable;
final class SlugDataMapper implements DataMapperInterface
{
protected SlugService $slugService;
/**
* @param SlugService $slugService
*/
public function __construct(SlugService $slugService){
$this->slugService = $slugService;
}
/**
* @param mixed $viewData
* @param Traversable $forms
* @return void
*/
public function mapDataToForms(mixed $viewData, Traversable $forms): void
{
if(is_null($viewData)){
return;
}
$forms = iterator_to_array($forms);
$forms['title']->setData($viewData->getTitle());
$forms['slug']->setData($viewData->getSlug());
}
/**
* @param Traversable $forms
* @param mixed $viewData
* @return void
*/
public function mapFormsToData(Traversable $forms, mixed &$viewData) :void
{
$submitedForm = iterator_to_array($forms);
$formEntity = new SlugTitles();
$formEntity->setTitle($submitedForm['title']->getData());
$formEntity->setSlug($this->slugService->slugify($submitedForm['title']->getData()));
$viewData = $formEntity;
}
}
Chúng tôi điền vào mảng $form trong phương thức đầu tiên \App\Form\DataMapper\SlugDataMapper::mapDataToForms. Trong phương thức thứ hai \App\Form\DataMapper\SlugDataMapper::mapFormsToData, chúng tôi tạo một thực thể với thông tin mới của chúng tôi để thông qua xác nhận và ghi. Hàm tạo tự động cung cấp cho chúng tôi dịch vụ SlugService của chúng tôi sẽ có dịch vụ Symfony Slug trong hàm tạo của nó.
<?php
namespace App\Service;
use Symfony\Component\String\Slugger\SluggerInterface;
class SlugService
{
private SluggerInterface $slugger;
/**
* @param SluggerInterface $slugger
*/
public function __construct(SluggerInterface $slugger){
$this->slugger = $slugger;
}
/**
* @param string $string
* @return String|null
*/
public function slugify(string $string): ?String
{
return $this->slugger->slug($string) ;;
}
}
Để kết thúc ví dụ, mã của trường "template" của chúng tôi cho phép chúng tôi hình dung kết quả:
<h3>Valeurs de notre entité</h3>
Id: {{ subject.id }} <br />
Title : {{ subject.title }} <br />
Slug : {{ subject.slug }} <br />
Đó là xong!