如何在使用活动记录的框架中实现模型工厂?

How to implement models factory in frameworks which using active records?

所以,在我的框架X中,让它成为Phalcon,我经常创建模型对象。

Let's assume that all fields already validated. Questions related only about creation logic.

创建 Users 对象并将其保存到数据库的简单示例:

<?php

$user = new Users();
$user->setName($name);
$user->setLastName($lastname);
$user->setAge($age);
$user->create();

为简单起见,我在这里只显示 3 个要设置的字段,在现实世界中它们总是更多。

我有 3 个问题:

1) 在 Factory class 中封装此逻辑的最佳方法是什么?如果我创建 Factory class 将创建像 Users 对象这样的对象,每次我都需要传递大量参数。

示例:

<?php

$factory = new UsersFactory();
$factory->make($name, $lastname, $address, $phone, $status, $active);

2) 即使我以上面显示的方式实现 Factory - Factory 是否应该在数据库中插入数据?在我的示例调用方法 create()?或者只执行所有 setter 操作?

3) 更重要的是,如果我需要创建具有关系的 Users 对象,以及其他相关对象怎么办?

感谢您的任何建议。

您的问题开始时很简单,然后变得复杂起来。阅读您的 post 听起来您担心必须传递给构建对象的方法的参数数量。这是一个合理的担心,因为你应该尽量避免使用超过 2 或 3 个参数的函数,并且因为有时你需要传递第一个、第三个和第五个参数,而不是第二个和第四个,它们只会变得不舒服 table .

相反,我鼓励您查看构建器模式。

最后,它与直接使用 User 对象没有太大区别,但它会帮助您防止 User 对象处于无效状态(未设置必填字段)

1) What the best way to encapsulate this logic in Factory class? If I create Factory class that will create objects like Users object, every time I will need pass long amount of parameters.

这就是我推荐建造者模式的原因。避免将大量参数传递给单个函数。它还允许您在构建方法中验证状态并处理或抛出异常。

class UserBuilder {
  protected $data = [];
  public static function named($fname, $lname) {
    $b = new static;
    return $b
      ->withFirstName($fname)
      ->withLastName($lname);
  }
  public function withFirstName($fname) {
    $this->data['first_name'] = $fname;
    return $this;
  }
  public function withFirstName($lname) {
    $this->data['last_name'] = $lname;
    return $this;
  }
  public function withAge($age) {
    $this->data['age'] = $age;
    return $this;
  }
  public function build() {
    $this->validate();
    $d = $this->data;
    $u = new User;
    $u->setFirstName($d['first_name']);
    $u->setLastName($d['last_name']);
    $u->setAge($d['age']);
    return $u;
  }
  protected function validate() {
    $d = $this->data;
    if (empty($d['age'])) {
      throw new Exception('age is required');
    }
  }
}

那你就做..

$user = UserBuilder::named('John','Doe')->withAge(32);

现在函数参数的数量不会随着每个参数的增加而增加,而是方法的数量会增加。

2) Even if I implement Factory in a way showed above - should Factory insert data in DB? In my example call method create()? Or just perform all setters operations?

不应该插入。它应该只是帮助您构建对象,而不是假设您将如何处理它。您可以发布它,一旦您构建它,您将希望在插入之前用它做一些其他事情。

3) And even more, what if i will need to create Users objects with relations, with other related objects?

在 Phalcon 中,这些关系是实体的一部分。你可以在 their docs 中看到这个例子:

// Create an artist
$artist = new Artists();

$artist->name    = 'Shinichi Osawa';
$artist->country = 'Japan';

// Create an album
$album = new Albums();

$album->name   = 'The One';
$album->artist = $artist; // Assign the artist
$album->year   = 2008;

// Save both records
$album->save();

因此,为了将此与您的用户示例相关联,假设您想要存储有关用户的地址信息,但地址存储在不同的 table 中。构建器可以公开定义地址的方法,构建方法将同时创建两个实体和 return 构建的 User 对象,由于 Phalcon 模型的工作方式,该对象在其中引用了 Address 对象。

我认为完全没有必要使用构建器或 "pattern" 来动态填充您的模型属性。尽管它对您所追求的是主观的。

您可以像这样通过构造函数填充模型

$user = new Users([
    'name' => $name,
    'lastName' => $lastname,
    'age' => $age,
]);
$user->create();

这样您就可以通过构建数组而不是多次调用方法来动态填充您的模型。


还值得注意的是,如果您想使用 "setters" 和 "getter" 方法,您应该将属性定义为 protected。这样做的原因是当你给受保护的 属性 赋值时,Phalcon 会自动调用 set/get 方法(如果它们存在)。

例如:

class User extends \Phalcon\Mvc\Model
{
    protected $name;

    public function setName(string $name): void
    {
        $this->name = $name;
    }

    public function getName(): string
    {
        return $this->name;
    }
}

$user= new MyModel();
$user->name = 'Cameron'; // This will invoke User::setName
echo $user->name; // This will invoke User::getName

还值得注意的是,如果缺少相应的方法,这些属性的行为将与您期望的受保护 属性 的行为与传统的受保护 属性 的行为相同。例如,您不能在没有 setter 方法的情况下为受保护模型 属性 赋值。