简体   繁体   中英

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. 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..

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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