首页  >  文章  >  后端开发  >  探索数据传输对象 (DTO) 的优势以及 PHP 只读类如何提升您的 Laravel 代码

探索数据传输对象 (DTO) 的优势以及 PHP 只读类如何提升您的 Laravel 代码

PHPz
PHPz原创
2024-07-30 08:59:091216浏览

Explore the Advantages of Data Transfer Objects (DTOs) and How PHP  Readonly Classes Can Elevate Your Laravel Code

在现代 Web 应用程序开发中,高效、安全地管理和传输数据至关重要。对此过程有显着帮助的一种设计模式是数据传输对象 (DTO)。这篇文章将深入探讨使用 DTO 的优势,特别是在 Laravel 应用程序中,并展示 PHP 8.2 只读类如何进一步增强其优势。

什么是数据传输对象 (DTO)?

数据传输对象(DTO)是一个简单的对象,旨在在进程或系统之间传输数据。与典型的模型或实体不同,DTO 不受业务逻辑的影响。它们封装数据,提供一种清晰且结构化的方式在应用程序的不同层或不同系统之间传输信息。

DTO模式

DTO 模式用于在软件应用程序内的不同子系统之间传输数据。使用 DTO 的主要目标是最大限度地减少方法调用的数量、聚合必要的数据并提供结构化方法来管理数据转换和验证。

使用 DTO 的好处

  • 关注点分离: DTO 将业务逻辑与数据表示隔离,从而生成更清晰、更易于维护、更易于理解的代码。

  • 数据验证:DTO 允许在其他应用程序层处理数据之前对数据进行验证,确保仅使用有效数据。

  • 一致性:通过提供一致的数据传输结构,DTO 简化了来自各种来源的数据的管理和处理。

  • 安全性:DTO 可以通过控制哪些数据可访问和可修改来保护您的应用程序免受未经授权的数据操纵。

  • 测试:由于 DTO 是没有嵌入业务逻辑的简单对象,因此它们更容易模拟和测试。

  • 转换:DTO 有助于将数据转换为不同应用层所需的格式。

  • 不变性: DTO 通常会促进不变性,这意味着一旦创建,它们的状态就无法更改。此功能带来了几个优点:

    • 可预测性: 不可变对象是可预测的并且更容易推理,因为它们的状态在创建后保持不变。
    • 线程安全:不变性本质上支持线程安全,简化并发处理。
    • 调试:使用不可变对象进行调试更容易,因为它们的状态保证在整个生命周期中保持不变。

PHP 8.2 和只读类

在 PHP 8.2 中,只读类的引入增强了 DTO 的使用。只读类无需将属性显式定义为只读,从而简化了 DTO 实现。以下是 PHP 8.2 的只读类如何改进 DTO:

  • 简化代码:只读类自动使属性不可变,减少样板代码并提高清晰度。
  • 增强的安全性:通过确保属性一旦设置就无法修改,只读类增强了数据完整性和安全性。
  • 提高可维护性:使用只读类可以生成更干净、更易于维护的代码,因为数据的不变性是由语言本身强制执行的。

示例:在物业管理系统中使用 DTO

让我们考虑一个属性管理系统,其中属性可以来自各种来源,例如 API 和 CSV 导入。我们可以使用 DTO 创建属性模型、订阅、资产等,确保数据在整个应用程序中保持一致并经过验证。

定义 PropertyDTO

首先,我们定义一个PropertyDTO类:

app/DTO/PropertyDTO.php

namespace App\DTO;

/**
 * Class PropertyDTO
 *
 * Represents a Data Transfer Object for property data.
 */
readonly class PropertyDTO extends AbstractDTO
{
    /**
     * The name of the property.
     *
     * @var string
     */
    public string $name;

    /**
     * The address of the property.
     *
     * @var string
     */
    public string $address;

    /**
     * The price of the property.
     *
     * @var float
     */
    public float $price;

    /**
     * The subscription status of the property, if applicable.
     *
     * @var string|null
     */
    public ?string $subscription;

    /**
     * The list of assets associated with the property.
     *
     * @var array|null
     */
    public ?array $assets;

    /**
     * Set the properties from a model instance.
     *
     * @param $model The model instance.
     * @return $this
     */
    public function setFromModel($model): self
    {
        $this->name = $model->name;
        $this->address = $model->address;
        $this->price = $model->price;
        $this->subscription = $model->subscription;
        $this->assets = $model->assets;

        return $this;
    }

    /**
     * Set the properties from API data.
     *
     * @param array $data The API data.
     * @return $this
     */
    public function setFromAPI(array $data): self
    {
        $this->name = $data['property_name'];
        $this->address = $data['property_address'];
        $this->price = $data['property_price'];
        $this->subscription = $data['subscription'] ?? null;
        $this->assets = $data['assets'] ?? null;

        return $this;
    }

    /**
     * Set the properties from CSV data.
     *
     * @param array $data The CSV data.
     * @return $this
     */
    public function setFromCSV(array $data): self
    {
        $this->name = $data[0];
        $this->address = $data[1];
        $this->price = (float) $data[2];
        $this->subscription = $data[3] ?? null;
        $this->assets = explode(',', $data[4] ?? '');

        return $this;
    }
}
使用 PropertyDTO

以下是如何使用 PropertyDTO 处理来自不同来源的属性:

// From a Model
$model = Property::find(1);
$propertyDTO = (new PropertyDTO([]))->setFromModel($model);

// From an API
$apiData = [
    'property_name' => 'Beautiful House',
    'property_address' => '1234 Elm Street',
    'property_price' => 450000,
    'subscription' => 'Premium',
    'assets' => ['pool', 'garden']
];
$propertyDTO = (new PropertyDTO([]))->setFromAPI($apiData);

// From a CSV
$csvData = ['Beautiful House', '1234 Elm Street', 450000, 'Premium', 'pool,garden'];
$propertyDTO = (new PropertyDTO([]))->setFromCSV($csvData);

// Convert to Array
$arrayData = $propertyDTO->toArray();

// Convert to JSON
$jsonData = $propertyDTO->toJson();

概括

数据传输对象 (DTO) 通过确保数据一致性、验证和关注点分离,在 Laravel 应用程序中提供了众多优势。通过实施 DTO,您可以使应用程序更易于维护、更安全且更易于测试。在物业管理系统中,DTO 有助于高效处理来自各种来源(例如 API 和 CSV 导入)的数据,确保您的业务逻辑保持干净并专注于处理经过验证的数据。

Moreover, embracing immutability within DTOs enhances predictability, thread-safety, and simplifies debugging.

Extending DTOs with Abstract Classes for Consistency

To streamline the creation of DTOs and promote code reuse, we can use an abstract class or base class. This approach allows us to define common methods and properties in the abstract class and extend it for specific data sources.

Defining the AbstractDTO

app/DTO/AbstractDTO.php

namespace App\DTO;

/**
 * AbstractDTO
 *
 * An abstract base class for Data Transfer Objects (DTOs).
 * Provides common methods and properties for DTO implementations.
 */
abstract class AbstractDTO
{
    /**
     * AbstractDTO constructor.
     *
     * Initialises the DTO with data from an associative array.
     *
     * @param array $data The data array to initialize the DTO.
     */
    public function __construct(array $data)
    {
        $this->setFromArray($data);
    }

    /**
     * Set the properties of the DTO from a model instance.
     *
     * @param $model The model instance from which to populate the DTO.
     * @return $this
     */
    abstract public function setFromModel($model): self;

    /**
     * Set the properties of the DTO from API data.
     *
     * @param array $data The data array from the API.
     * @return $this
     */
    abstract public function setFromAPI(array $data): self;

    /**
     * Set the properties of the DTO from CSV data.
     *
     * @param array $data The data array from the CSV.
     * @return $this
     */
    abstract public function setFromCSV(array $data): self;

    /**
     * Convert the DTO to an associative array.
     *
     * @return array The DTO data as an associative array.
     */
    public function toArray(): array
    {
        $properties = get_object_vars($this);
        return array_filter($properties, function ($property) {
            return $property !== null;
        });
    }

    /**
     * Convert the DTO to a JSON string.
     *
     * @return string The DTO data as a JSON string.
     */
    public function toJson(): string
    {
        return json_encode($this->toArray());
    }

    /**
     * Set the properties of the DTO from an associative array.
     *
     * @param array $data The data array to populate the DTO.
     */
    protected function setFromArray(array $data): void
    {
        foreach ($data as $key => $value) {
            if (property_exists($this, $key)) {
                $this->$key = $value;
            }
        }
    }
}

Final Thoughts

Using an abstract or base class for DTOs not only ensures consistency across different DTO implementations but also promotes code reuse and maintainability. By defining common methods and properties in an abstract class, you can create a structured and efficient way to manage data transfer within your application. This approach aligns well with the principles of clean code and helps in building scalable and robust applications.

Here’s a revised phrase that includes a call to action:

"By leveraging DTOs and abstract classes together, you can refine your Laravel application's design, improving how data is managed and ensuring a more organised and efficient data flow. If you want to further encapsulate and enhance your DTOs with traits and interfaces, explore our guide on Enhancing Object-Oriented Design with Traits, Interfaces, and Abstract Classes."

以上是探索数据传输对象 (DTO) 的优势以及 PHP 只读类如何提升您的 Laravel 代码的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn