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.

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

报告相同问题?

悬赏问题

  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
  • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line
  • ¥500 火焰左右视图、视差(基于双目相机)
  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号
  • ¥15 基于单片机的靶位控制系统
  • ¥15 真我手机蓝牙传输进度消息被关闭了,怎么打开?(关键词-消息通知)