scrumble-nl / popo
普通的PHP对象
Requires
- php: >=8.1
- laravel/framework: ^9.0|^10.0|^11.0
- laravel/helpers: ^1.6
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.8
- larastan/larastan: ^2.1
- orchestra/testbench: ^7.0|^8.0|^9.0
- phpunit/phpunit: ^9.0|^10.0
This package is auto-updated.
Last update: 2024-09-15 14:32:41 UTC
README
A Popo(Plain old PHP object)是我们对Pojo(Plain old Java object)的实现。这些对象的目的是用严格类型化的PHP对象替换所有原本应该是键值数组的变量。
此包仅包含一个类(BasePopo),该类旨在由我们创建的每个Popo扩展。BasePopo类确保您可以从前端返回Popo到后端,并且它们将自动转换为JSON,而无需编写代码。除此之外,BasePopo还为所有Popo提供了toArray()
函数。
安装
composer require scrumble-nl/popo
使用
创建新的Popo对象时,只需扩展BasePopo。在将对象转换为数组或将Popo返回到前端时,只会包含公共属性,因此需要将可见属性标记为公共。
基本使用
class ProductPopo extends BasePopo { /** * @var string */ public string $name; /** * @var float */ public float $price; /** * @param string $name * @param float $price */ public function __construct(string $name, float $price) { $this->name = $name; $this->price = $price; } }
初始化
要实例化Popo,您可以使用构造函数,就像您已经习惯的那样,或者在前端本身中开发一个可重用的辅助函数。此函数应考虑输入数据来自何处以及数据的格式。
例如,典型的API响应将解码为JSON对象或数组。实例化Popo的函数如下所示
public static function fromApiResponse(array $apiResponse): ProductPopo { return new self( $apiResponse['name'], (float) $apiResponse['price'], ); }
您可以从项目的任何位置调用此函数来实例化Popo: ProductPopo::fromApiResponse($array);
。
您可以自由实现其他方法,如fromObject
或fromArray
,以适应您的最佳情况。
严格类型和子Popo
您可能还有其他情况,其中Popo有自己的子Popo。例如,一个ProductPopo
可能有一个属性public Collection $categories
,这将是一个CategoryPopo
的集合。
为了确保在使用子Popo的数组或集合时最佳使用严格类型,请确保在PHPDoc中类型提示这些内容。
/** * @var Collection<int, CategoryPopo> */ public Collection $categories;
测试
在编写应用程序测试时,您可能会发现创建Popo对象的实例很有用。一种方法是使用工厂。
工厂是一个负责创建另一个类实例的类。在Popo对象的上下文中,工厂可以用来创建具有随机或特定值的Popo对象实例,这对于测试非常有用。
测试使用
首先,为您的Popo对象创建一个工厂类。工厂类应有一个方法,该方法返回具有随机或特定值的Popo对象实例。以下是为ProductPopo对象创建的示例工厂类。
use Tests\Popo\ProductPopo; use Scrumble\Popo\PopoFactory; class ProductPopoFactory extends PopoFactory { /** * @var null|string */ public ?string $popoClass = ProductPopo::class; /** * {@inheritDoc} */ public function definition(): array { return [ 'name' => $this->faker->name, 'price' => $this->faker->randomNumber(1, 10), ]; } }
然后,确保将工厂包含在Popo的定义中。
use Scrumble\Popo\HasPopoFactory; class ProductPopo extends BasePopo { use HasPopoFactory; /** * @var string */ public string $name; /** * @var float */ public float $price; /** * @param string $name * @param float $price */ public function __construct(string $name, float $price) { $this->name = $name; $this->price = $price; } /** * @return string */ public static function popoFactory(): string { return ProductPopoFactory::class; } }
工厂创建
假设您有一个服务函数create
,它接收一个ProductPopo对象并根据其属性创建一个新的产品。您可以使用Popo工厂轻松地为此函数创建测试数据。
/** @test */ public function can_create_product(): void { $productPopo = ProductPopo::factory()->create(); $product = $this->productService->create($productPopo); $this->assertDatabaseHas('products', [ 'name' => $productPopo->name, 'price' => $productPopo->price, ]); }
创建多个
有时,您可能希望在单元测试中创建具有略微不同属性的Popo对象的多个实例。您可以使用此包提供的count()
和sequence()
方法来实现这一点。
count(int $count)
方法接收一个整数作为参数,告诉工厂要创建多少个实例。sequence(array|closure $sequence)
方法接收一个包含每个要创建的实例的属性值的数组。您还可以向 sequence()
方法提供一个闭包,该闭包返回一个属性值数组。
以下是一个如何使用 count()
和 sequence()
创建具有稍微不同属性的多个 Popo 实例的示例
ProductPopo::factory() ->count(3) ->sequence( ['name' => 'Apples'], ['name' => 'Cookies'], ['name' => 'Bread'], ) ->create();
请注意,向
create()
方法传递属性将覆盖sequence()
中传递的值。
原始数据
raw(array $attributes = [])
函数返回一个数组,包含用于创建具有工厂值的数组的属性。
$productData = ProductPopo::factory()->raw();
覆盖数据
state()
函数是 Laravel 工厂的一个强大功能,允许您定义一组属性,这些属性将覆盖工厂的默认值。当您需要创建一个 Popo,它具有不能通过默认工厂生成的特定属性集时,这非常有用。
以下是一个如何使用 state()
函数设置 Popo 状态的示例
class ProductPopoFactory extends Factory { ... public function published(): array { return $this->state(function (array $attributes) { return [ 'published' => true, ]; }); } }
在这个例子中,我们为 ProductPopo
类定义了一个工厂,其中包含一个默认属性集,包括将 published
属性设置为 false。我们还定义了一个 published()
函数,该函数使用 state()
函数将 published
属性设置为 true。
现在,假设我们想要创建一个已发布的 Popo。我们可以通过在工厂上调用 published() 函数来实现这一点
$popo = ProductPopo::factory()->published()->create();
您可以通过向 create()
函数传递一个包含属性值的数组来覆盖特定数据。该数组应包含属性名称作为键,所需值作为对应值。数组中的值将覆盖任何默认值以及使用 state()
函数设置的任何值。
$popo = ProductPopo::factory()->create([ 'name' => 'New product', ]);
在上面的示例中,我们正在创建一个新的 ProductPopo 实例,并使用我们自己的值覆盖默认名称值。
同样,您也可以使用 raw()
函数覆盖特定数据。通过向 raw()
函数传递一个包含属性值的数组,这些值将覆盖任何默认值以及使用 state()
函数设置的任何值。
$productData = ProductPopo::factory()->raw([ 'name' => 'New product', ]);
在上面的示例中,我们正在使用 raw()
函数创建一个新的 ProductPopo 实例属性数组,并使用我们自己的值覆盖默认名称值。
贡献
如果您想看到对这个包的添加/更改,您始终欢迎添加一些代码或改进它。
Scrumble
该产品最初由 Scrumble 为内部使用开发。因为我们使用了大量的开源包,我们想要回馈社区。我们希望这能帮助您像其他人帮助我们的那样取得进步!