aternos/model

简单和复杂数据库模型的PHP库

v3.2.5 2024-07-15 13:07 UTC

README

简单和复杂数据库模型的PHP库。

关于

此库是为了提供一个灵活但易于使用的模型系统而创建的,它可以用于新项目,也可以集成到现有项目中。库的每个部分都可以单独覆盖和替换,以实现自定义逻辑。

与其他模型库相比,此库不绑定到任何特定的数据库后端,而是每个模型都可以包含用于访问不同数据库的定制逻辑,例如用于不同的缓存或搜索后端。因此,此库不提供任何配置功能。

注意:此库仍在开发中,特别是关于更多驱动程序功能的一些功能将在未来添加。

安装

composer require aternos/model

基本用法

驱动

该库包含一些驱动程序,但您也可以创建自己的驱动程序并在项目中使用它们,或将它们提交以添加到库中。

当前包含的驱动程序包括

所有这些驱动程序都需要额外的扩展或包,请参阅 composer.json 中的 "suggest"。

大多数驱动程序可以在没有密码的本地数据库设置中直接工作,但对于大多数用例,您必须使用不同的凭据。要使用包含的驱动程序执行此操作,您可以通过构造函数创建一个新实例并设置凭据

$driver = new \Aternos\Model\Driver\Mysqli\Mysqli("localhost", 3306, "username", "password");

或使用流畅设置器

$driver = (new \Aternos\Model\Driver\Mysqli\Mysqli())->setUsername("username")->setPassword("password");

或创建一个新的驱动程序类,扩展库驱动程序并覆盖受保护的凭据属性(在类本身或在构造函数中),例如

<?php

namespace MyModel;

class Mysqli extends \Aternos\Model\Driver\Mysqli\Mysqli 
{
    protected ?string $user = 'username';
    protected ?string $password = 'password';
    
    public function __construct(?string $host = null, ?int $port = null, ?string $username = null, ?string $password = null, ?string $socket = null, ?string $database = null) {
        parent::__construct($host, $port, $username, $password, $socket, $database);
        $this->host = \Config::getHost();
    }
}

之后,您必须在 DriverRegistry 中注册该类

<?php

\Aternos\Model\Driver\DriverRegistry::getInstance()->registerDriver($driver);

// or using your own class
\Aternos\Model\Driver\DriverRegistry::getInstance()->registerDriverClass(\MyModel\Mysqli::ID, \MyModel\Mysqli::class);

所有驱动程序都有一个用于标识和(如果需要)覆盖现有驱动程序的ID。通常建议为每种驱动程序类型使用唯一的ID,但如果您需要同一类型的多个驱动程序(例如,如果您有两个不同的MySQL数据库),则可以创建和注册具有不同ID的多个驱动程序。您可以使用任何驱动程序上的 setId() 流畅设置器或通过覆盖您自己的类中的 getId() 函数来设置ID。

<?php
$driverA = (new \Aternos\Model\Driver\Mysqli\Mysqli())->setId("mysqli-a")->setHost("a.mysql.host");
\Aternos\Model\Driver\DriverRegistry::getInstance()->registerDriver($driverA);

$driverB = (new \Aternos\Model\Driver\Mysqli\Mysqli())->setId("mysqli-b")->setHost("b.mysql.host");
\Aternos\Model\Driver\DriverRegistry::getInstance()->registerDriver($driverB);

模型

现在您可以创建一个模型类。所有模型类都必须遵循 ModelInterface。此库包含两个不同的抽象模型类,以简化模型创建

  • BaseModel - 实现基本模型逻辑,与任何驱动程序无关
  • GenericModel - 可选实现所有驱动程序和注册表

建议从 GenericModel 开始,因为它已经实现了多个驱动程序,并且您可以为每个模型或所有模型(通过使用您自己的父模型为所有模型)启用所需的驱动程序。

这是一个使用Mysqli数据库作为后端和缓存的示例实现,使用GenericModel。驱动配置是一个有序数组$drivers,包含驱动ID,第一个驱动首先用于获取模型。其他操作的驱动依赖于$drivers数组(例如,对于save()delete()是逆序的),但可以单独配置,例如设置$saveDrivers数组。

<?php

class User extends \Aternos\Model\GenericModel 
{
    // use model registry (default: true)
    protected static bool $registry = true; 
    
    // cache the model for 60 seconds (default: null)
    protected static ?int $cache = 60;

    // configure the generic model drivers (this is the default)
    protected static array $drivers = [
        \Aternos\Model\Driver\Redis\Redis::ID,
        \Aternos\Model\Driver\Mysqli\Mysqli::ID
    ];
    
    // the name of your model (and table)
    public static function getName() : string
    {
        return "users";
    }
    
    // all public properties are database fields
    public $id;
    public $username;
    public $email;
}

使用您的模型

现在您可以在代码中使用您的模型

<?php

// create new user
$user = new User();
$user->username = "username";
$user->email = "mail@example.org";
$user->save();

// get a user by id
$user = User::get($id);
echo $user->username;

// you can force to skip the registry and the cache
$user = User::get($id, true);

// update a user
$user->email = "othermail@example.org";
$user->save();

// delete a user
$user->delete();

查询

您可以使用Query对象查询模型,例如SelectQuery。它允许不同的语法可能性,例如将简单的数组/字符串/整型值直接传递给构造函数,或者基于Query/...类构建所有参数。所有查询都返回一个QueryResult对象,该对象是可迭代的并可计数的。

选择

<?php

// the following lines are all the same
User::select(["email" => "mail@example.org"]); // ::select() is only a helper function of GenericModel
User::query(new \Aternos\Model\Query\SelectQuery(["email" => "mail@example.org"]));
User::query((new \Aternos\Model\Query\SelectQuery())->where(["email" => "mail@example.org"]));
User::query(new \Aternos\Model\Query\SelectQuery(
    new \Aternos\Model\Query\WhereCondition("email", "mail@example.org")
));
User::query(new \Aternos\Model\Query\SelectQuery(
    new \Aternos\Model\Query\WhereGroup([
        new \Aternos\Model\Query\WhereCondition("email", "mail@example.org")
    ])
));

// use the result
$userQueryResult = User::select(["email" => "mail@example.org"]);

if (!$userQueryResult->wasSuccessful()) {
    echo "Query failed";
}

echo "Found " . count($userQueryResult) . " users";

foreach($userQueryResult as $user) {
    /** @var User $user */
    echo $user->username;
}

// another query example
User::select(
    ["field" => "value", "hello" => "world", "foo" => "bar"],
    ["field" => "ASC", "hello" => "DESC", "foo" => "ASC"],
    ["field", "hello", "foo"],
    [100, 10]
);
// can also be written as
User::query((new \Aternos\Model\Query\SelectQuery)
    ->where(["field" => "value", "hello" => "world", "foo" => "bar"])
    ->orderBy(["field" => "ASC", "hello" => "DESC", "foo" => "ASC"])
    ->fields(["field", "hello", "foo"])
    ->limit([100, 10])
); 

// a more complex query with nested where groups using the query parameter classes
User::query(new \Aternos\Model\Query\SelectQuery(
    new \Aternos\Model\Query\WhereGroup([
        new \Aternos\Model\Query\WhereCondition("field", "value", "<>"),
        new \Aternos\Model\Query\WhereGroup([
            new \Aternos\Model\Query\WhereCondition("hello", "world"),
            new \Aternos\Model\Query\WhereCondition("foo", "bar")
        ], \Aternos\Model\Query\WhereGroup:: OR)
    ]),
    [
        new \Aternos\Model\Query\OrderField("field", \Aternos\Model\Query\OrderField::DESCENDING),
        new \Aternos\Model\Query\OrderField("hello", \Aternos\Model\Query\OrderField::ASCENDING),
        new \Aternos\Model\Query\OrderField("foo", \Aternos\Model\Query\OrderField::DESCENDING)
    ],
    [
        new \Aternos\Model\Query\SelectField("field"), 
        new \Aternos\Model\Query\SelectField("hello"), 
        new \Aternos\Model\Query\SelectField("foo")
    ],
    new \Aternos\Model\Query\Limit(10, 100)
));

更新

<?php

// update mail to "mail@example.org" where username is "username"
User::query(new \Aternos\Model\Query\UpdateQuery(["email" => "mail@example.org"], ["username" => "username"]));
User::query((new \Aternos\Model\Query\UpdateQuery())
    ->fields(["email" => "mail@example.org"])
    ->where(["username" => "username"]));
User::query(new \Aternos\Model\Query\UpdateQuery(
    new \Aternos\Model\Query\UpdateField("email", "mail@example.org"),
    new \Aternos\Model\Query\WhereCondition("username", "username")
));
User::query(new \Aternos\Model\Query\UpdateQuery(
    [new \Aternos\Model\Query\UpdateField("email", "mail@example.org")],
    new \Aternos\Model\Query\WhereGroup([
        new \Aternos\Model\Query\WhereCondition("username", "username")
    ])
));

删除

<?php

// delete where email is mail@example.org
User::query(new \Aternos\Model\Query\DeleteQuery(["email" => "mail@example.org"]));
User::query((new \Aternos\Model\Query\DeleteQuery())->where(["email" => "mail@example.org"]));
User::query(new \Aternos\Model\Query\DeleteQuery(
    new \Aternos\Model\Query\WhereCondition("email", "mail@example.org")
));
User::query(new \Aternos\Model\Query\DeleteQuery(
    new \Aternos\Model\Query\WhereGroup([
        new \Aternos\Model\Query\WhereCondition("email", "mail@example.org")
    ])
));

测试

此库包含一个TestDriver,可用于在不使用数据库的情况下编写测试。它使用一个简单的数组作为存储,并且不是持久的。它支持大多数基本操作和查询,但可能尚不支持所有用例,特别是特定于数据库的查询。

您可以直接向模型添加测试数据,这将同时启用该模型的测试驱动程序。

<?php

// add a single entry
User::addTestEntry([
    "id" => 1,
    "name" => "Test",
    "email" => "test@example.org"
]);

// add multiple entries at once
User::addTestEntries($entries);

// clear all test entries
User::clearTestEntries();

或者,您也可以直接向测试驱动程序添加数据。

/** @var \Aternos\Model\Driver\Test\TestDriver $testDriver */
$testDriver = \Aternos\Model\Driver\DriverRegistry::getInstance()->getDriver(\Aternos\Model\Driver\Test\TestDriver::ID);

// add multiple tables at once
$testDriver->addTables([
    "user" => [
        [
            "id" => 1,
            "name" => "Test",
            "email" => "test@example.org"
        ],
        ...
    ],
    "another_table" => [
        [
            "id" => 1,
            ...
        ]
    ],
    ...
]);

// add a single table
$testDriver->addTable("user", [
    [
        "id" => 1,
        "name" => "Test",
        "email" => "test@example.org"
    ],
    ...
]);

// add an entry to a table
$testDriver->addEntry("user", [
    "id" => 1,
    "name" => "Test",
    "email" => "test@example.org"
]);

// clear all entries from a table
$testDriver->clearEntries("user");

// clear all tables
$testDriver->clearTables();

如果您直接向驱动程序添加数据,您仍然需要为每个想要测试的模型启用测试驱动程序。

<?php

// enable the test driver for the user model
User::enableTestDriver();

// you can enable the test driver for all models at once by enabling it on a shared parent
class MyModel extends Aternos\Model\GenericModel {}
class User extends MyModel { ... }
class AnotherModel extends MyModel { ... }
MyModel::enableTestDriver();

高级用法

有关编写自己的驱动程序、驱动程序工厂或模型的更多信息,将在未来添加,同时请查看源代码。