Should I create an object or work with an array?

2019-07-19 18:07发布

问题:

I am fairly new to OOP PHP and as I am building my first project, I am coming across some difficult dilemmas.

I'm trying to build a forum. Yes, I know there are many free ones out there, I just want to have one that I can build according to my own needs :). Plus, it's fun to code.

I have build a base model and controller and template, for the forum, thread and post. The base model has standard database functions, like find all, find by id etc. The individual models extend the model class and have only specific functions to the class. I have set it up like this:

class Post_Model extends Model {

    static protected $_table_name = 'post';
    static protected $_db_fields = array('id', 'thread_id', 'parent_id', 'username', 'user_id',
        'title', 'date_line', 'pagetext', 'show_signature', 'ip_address', 'icon_id', 'visible');

    /**
     * @var db fields
     */
    public $id;
    public $thread_id;
    public $parent_id;
    public $username;
    public $user_id;
    public $title;
    public $date_line;
    public $pagetext;
    public $show_signature;
    public $ip_address;
    public $icon_id;
    public $visible;
}
/*
 * joined fields with user object. needed to display posts in all details
 */

static public function get_posts($id, $limit, $offset){
    $result_set = static::find_by_sql("
        SELECT * 
        FROM post 
        LEFT JOIN user ON (post.user_id = user.id) where post.thread_id=".(int)$id." 
        ORDER BY date_line DESC 
        LIMIT {$offset} , {$limit}");

    return !empty($result_set) ? $result_set : false;  
}

And besides the decisions that were tough, that I already had to make about certain functions and variables being static or not. I have this dilemma:

The basic CRUD works from the base model. That is fine, I can create, update, delete and read from my database through calling static methods from the (base) model, which instantiates objects for these forum, thread and post models. So far so good.

Part of the Model Class:

    <?php
        /**
     * Find rows from database based on sql statement
     * @param string $sql
     * @retun array $result_set
     */
    static public function find_by_sql($sql = '') {
        $db = Database::getInstance();
        $mysqli = $db->getConnection();
        $result = $mysqli->query($sql);
        $object_array = array();
        while ($record = $result->fetch_array()) {
            $object_array[] = static::instantiate($record);
        }
        return $object_array;
    }

    private static function instantiate($record) {
        $object = new static;
        foreach ($record as $attribute => $value) {
            if ($object->has_attritube($attribute)) {
                $object->$attribute = $value;
            }
        }
        return $object;
    }
}
?>

I transfer these objects for displaying in my templates. When reading from one table, that works just fine.

    <?php
$postcount = $pagination->offset + 1;
if (!empty($posts)) {
    foreach ($posts as $post) {
        ?>

        <div class="post">
            <div class="posttime"><?php echo strftime("%A %e %B %G  %H:%M", $post->date_line); ?></div>
            <div class="postnumber">#<?php echo $postcount; ?></div>
            <div class="postuser">
                <div class="username"><?php echo $post->username; ?></div>
                <div class="avatar">avatar</div>
            </div>
            <div class="postcontents">
                <div class="posttext"><?php echo $post->pagetext; ?></div>  
            </div>
        </div>

        <?php
        $postcount++;
    }
    ?>    
<div class="pagination"><?php echo $page_split; ?></div> 

However, for the display of all posts in the threads, I need info from the user table, e.g. there number of their posts, rank, avatar etc.

Okay, so here's the thing:

SELECT * FROM post
LEFT JOIN user ON (post.user_id = user.id)
where thread_id={$id}
ORDER BY date_line DESC
LIMIT {$offset} , {limit}

I now have more fields then variables that I have setup in my Post model. So they are not transferred for displaying.

Should I use an array for displaying these extra fields instead of object? I would rather still use the object approach for displaying, as I am now set up for that.

Or should I maybe add these user fields as variables to my Post model? I am thinking that is not the most tidy way to do it, however it would work.

Or should I go through a foreach loop to get a separate instance of a (yet to create) user class? I am thinking it would drain much more memory then the Left Join that simply gets me the data that I need.

I would like some hints on how you would solve this.

回答1:

You won't be able to solve this with your schema, because you are mixing properties from different entities in your Post entity [i.e. username].

The idea is that you just have to make your code aware of the relationship between entities, like:

class Post extends Entity
{

  protected $id;
  protected $body;
  protected $date;

  // this will be an instance of a Thread entity, let's say
  protected $thread;

  // this will be an instance of the User entity
  protected $user;

  ...

}

This way you will be able to do:

$somePost -> getUser() -> getAvatar();

But how do you inject the correct user and thread to each post? That's what Object Relational Mappers are for: in you case, all the SQL stuff would be handled by the ORM, and you'll be just dealing with you object graph, with no worries about building queries on your own; you just have to define such relationships.

A popular PHP ORM is Doctrine: here is an example about how it handles relationships declarations.

Of course this approach has downsides, but it's up to you investigating and finding out the best tradeoff between flexibility and dependencies.



回答2:

One thing that might break out the dilemma; in PHP an object can also act as an array (or even a function!) but a normal plain array cannot have properties or any other OOP feature.