duandai7601 2016-05-04 17:03
浏览 29
已采纳

PHP。 创建对象的最佳实践

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. I have the class "Stone", and the class "Talking_Stone", which extends the basic stone class. 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..

  • 写回答

1条回答 默认 最新

  • download12749 2016-05-04 17:25
    关注

    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:

    $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:

    $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. As it's a little bit more complicated, I'm not going to write it down here.

    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.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 2024-五一综合模拟赛
  • ¥15 如何将下列的“无限压缩存储器”设计出来
  • ¥15 下图接收小电路,谁知道原理
  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度
  • ¥30 关于#r语言#的问题:如何对R语言中mfgarch包中构建的garch-midas模型进行样本内长期波动率预测和样本外长期波动率预测
  • ¥15 ETLCloud 处理json多层级问题
  • ¥15 matlab中使用gurobi时报错
  • ¥15 这个主板怎么能扩出一两个sata口