简体   繁体   English

PHP。创建对象的最佳实践

[英]PHP. The best practices of creating objects

Let's say I have an entity (a stone) and I have a few more specific entities, that extend the basic enity - a talking stone for example, which have a few different properties and methods. 假设我有一个实体(一块石头),我有一些更具体的实体,它们扩展了基本的实体 - 例如,一个有说服力的石头,它有一些不同的属性和方法。 All the properties of a stone are stored in the database in the same table. 石头的所有属性都存储在同一个表中的数据库中。 All the specific properties of a specific object are stored serialized in the field "specific_options" of the table. 特定对象的所有特定属性都序列化存储在表的“specific_options”字段中。 I have the class "Stone", and the class "Talking_Stone", which extends the basic stone class. 我有“石头”课程和“Talking_Stone”课程,它扩展了基本的石头课程。 I need to create a stone object based on the data I get from the database. 我需要根据从数据库中获取的数据创建一个石头对象。

What is the best practice to emplement such thing? 实现这样的事情的最佳做法是什么?

What I've got so far: 到目前为止我得到了什么:

class Stone_Data {
    ...
    public static function get_item( $id ) {
            $sql = 'SELECT * FROM `' . self::$table . '` WHERE `id`=' . ( int ) $id;
            $item = self::$db->get_row( $sql );
            return $item;
    }
    ...
}

class Stone_Factory {
    ...
    public static function create( $id = null ) {
            // if the stone's type is set
            if ( !is_null( $id ) && !is_null( $data = Stone_Data::get_item( $id ) ) && !empty( $data['type'] ) ) {
                    // create a stone of this type
                    $class_name = ucfirst( $data['type'] ) . '_Stone';
                    return new $class_name( $data );
            }
            // otherwise create a basic stone
            return new Stone;
    }
    ...
}

class Stone {
    private $data = array(
        ...
            'specific_options'  => '',
        ...
    );
    ...
    public function __construct( $data = array() ) {
            $this->set( $data );
    }
    ...
    public function __set( $name, $value ) {
            if ( array_key_exists( $name, $this->data ) ) {
                    // serialize if the value is an array or an object
                    if ( is_array( $value ) || is_object( $value ) ) {
                        $value = serialize( $value );
                    }
                    $this->data[$name] = $value;
            }
    }
    public function set( $data = array() ) {
            foreach ( $data as $key => $value ) {
                    // serialize if the value is an array or an object
                    if ( is_array( $value ) || is_object( $value ) ) {
                        $data[$key] = serialize( $value );
                    }
            }
            $this->data = array_merge( $this->data, $data );
            return $this;
    }
    ...
}
class Talking_Stone extends Stone {
    ...
    public function __construct( $data = array() ) {
        parent::__construct( $data );
        // $this->type = 'talking'
        $this->specific_options->language = 'French';
        ...
    }
    ...
}

But somehow, it doesn't feel quite right.. 但不知何故,它感觉不太对..

You want to use a repository class for this: 您想为此使用存储库类:

class StoneRepository
{
    private $stoneFactory;

    public function __construct(StoneFactory $factory) {
        $this->stoneFactory = $factory;
    }

    /**
     * @param integer $id
     * @return Stone
     */
    public function findStoneById($id) {
        // Fetch your data from the database
        $row = ...;

        // Use the factory to create a new stone from the data
        // that has already been fetched
        $stone = $this->stoneFactory->create($row);

        // Return the new stone
        return $stone;
    }
}

The greatest advantage here is that you split your database logic from your model. 这里最大的优点是您可以从模型中分离数据库逻辑。 This is great as you get a clean model with no awareness of the database. 这很好,因为你得到一个没有数据库意识的干净模型。 And you're free to do queries however you like, also keeping them clean and separated. 而且你可以随心所欲地进行查询,也可以保持清洁和分离。

Then you can use this to fetch the stone with the given id in, for example, your controller: 然后你可以使用它来获取具有给定id的石头,例如你的控制器:

$factory = new StoneFactory();
$repository = new StoneRepository($factory);

$stone = $repository->findStoneById($id);

Fetching collections 获取集合

Fetching collections is just as easy: 获取集合同样简单:

class StoneRepository
{
    ...

    public function findAllStones() {
        // Again, fetch the data
        $rows = ...;

       // Transform each row into a Stone object
        $collection = array_map(function ($row) {
            return $this->stoneFactory->create($row);
        }, $rows);

        return $collection;
    }
}

Returning objects with relations 用关系返回对象

Sooner or later you might want to fetch some related objects. 迟早你可能想要获取一些相关的对象。 There are a few approaches you can take here. 您可以在这里采取一些方法。 The simplest one is to actually just fetch each type of object separately and build your model up in the controller. 最简单的方法是实际上单独获取每种类型的对象并在控制器中构建您的模型。 For example, if you wanted a Bag object with related Stone objects in it: 例如,如果您想要一个包含相关Stone对象的Bag对象:

$bag = $bagRepository->findBagById($bagId);
$stones = $stoneRepository->findStonesByBagId($bagId);

$bag->addStones($stones);

Another approach would be to use a join and actually build the Bag and the Stones within BagRepository . 另一种方法是使用join并在BagRepository实际构建BagStones As it's a little bit more complicated, I'm not going to write it down here. 因为它有点复杂,我不打算在这里写下来。

CRUD CRUD

You should just as well use the repository to insert/update/deleta the data in the database based on your model. 您还应该使用存储库根据您的模型插入/更新/删除数据库中的数据。 Again, it's just as simple as adding an insert , update and delete methods which accept a Stone object and persist it's data in the database. 同样,它就像添加一个接受Stone对象并将其数据保存在数据库中的insertupdatedelete方法一样简单。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM