0

This is something that's been bugging me with PHP for a while now and I still can't figure out how I would do it. Basically at the moment for my SQL class I'm dumping the results in an array and converting them to an object to give something like this:

$first_post = $Post->find(1);

(I've also been trying to get the syntax to look like: $Post::find(1); but that's a different and purely aesthetic issue altogether)

and then you can use first_post like so:

$first_post->title;

This is done by just converting the contents of $post->find(1) to an object like so:

$first_post = (object) array("title" => "blah");

and I know that instantiates a new STDClass but I can't figure out how to bind methods to that new instance of STDClass. It's mainly so I can do stuff like:

$posts = $Post->all();
$last_post = $posts->last();
$specific  = $posts->find("name" => "hello");

Any ideas how I would get PHP to do something like this?

andy
  • 2,369
  • 2
  • 31
  • 50

5 Answers5

1

You can't bind methods to StdClass as you could dynamically add methods with JavaScript.

You need to create a class with the methods you want. (You can use extends or trait if you will have common methods for different classes.)

For example:

class Post
{
    static function find()
    {
        $post = new Post();
        $array = array('title' => 'First Post');
        foreach ($array as $key => $value)
        {
            $post->$key = $value;
        }
        return $post;
    }

    public function do_something()
    {
        echo $this->title;
        return $this;
    }
}
J. Bruni
  • 20,322
  • 12
  • 75
  • 92
1

(I've also been trying to get the syntax to look like: $Post::find(1); but that's a different and purely aesthetic issue altogether)

When using the scope resolution operator you are accessing static methods and/or constants. These are methods that work on the class rather than an instance of the class. For example:

// the find method retuns a new Post instance and 
// does not try to access $this
$post = Post::find(1);  

As for creating an object for the post from an array, it's probably better to either create your own Post class and populate it accordingly or use one of the database functions that return the table data as an object rather than array such as mysqli_result::fetch_object or mysql_fetch_object.

If creating your own Post class, you can store the database information in an array and use the __call(), __get() and __get() Magic Methods to access that data. For example:

class Post() {
    protected $data;

    public function __construct($data) {
        $this->data = $data;
    }

    // access data with a getFoobar() style call
    public function __call($name, $args) {
        if (substr($name, 0, 3) == 'get') {
            $key = strtolower(substr($name, 3));
            if (array_key_exists($key, $this->data)) {
                return $this->data[$key];
            }
        }

        $class = get_class($this);
        throw new Exception("Call to undefined method $class::$name");
    }

    // access data like a class property
    public function __get($key) {
        if (array_key_exists($key, $this->data)) {
            return $this->data[$key];
        }

        throw new Exception("Attempt to access non-existant property $class::$name");
    }

    // set the data like a class property
    public function __set($name, $value) {
        $this->data[$name] = $value;
    }
}
Micah Carrick
  • 9,967
  • 3
  • 31
  • 43
0

You can but would need to do it every time at each instantiation:

$obj = new stdClass();
$obj->myMethod = function($name) {echo 'Hello '.$name;};

Ok above does'nt work ... I have used this approach from docs successfuly tho which was along the same lines: http://uk3.php.net/manual/en/reserved.classes.php#105404

Brian
  • 8,418
  • 2
  • 25
  • 32
  • As far as I can tell, that doesn't work. I wouldn't mind doing it with each instantiation as this 'model' object is created dynamically so it'd only need to have a bit of code added. – andy Jun 18 '12 at 13:58
0

I guess you could create a Post class

When instantiated it could get all posts and store as an array.

It could have a find method that would search through all posts for the specified query.

It could also have a last and a first method that would get the last Item or first item respectively.

I believe you can dynamically create class properties with the __get method. This accepts a property name, It could search through your array of posts and return the value if it exists.

Can you create instance properties dynamically in PHP?

Community
  • 1
  • 1
dm03514
  • 54,664
  • 18
  • 108
  • 145
  • Hmm that's currently the way I'm doing it. The only reason I want to edit it is so you'll be able to 'daisychain' results in the framework. So you could just do: $post->like($username)->find("password" => $password)->first(); – andy Jun 18 '12 at 14:03
  • 1
    @andy can't you just return the class instance `$this`? So each functions result could be added to a `results` property. Then the user would have to call `execute()` or somethign to return the actual results? – dm03514 Jun 18 '12 at 14:09
  • Possibly. I'll have to look into it. – andy Jun 18 '12 at 14:11
0

If you're using PDO to retrieve data from database, look at this method PDOStatement::setFetchMode. You can tell PDO to instantiate a new class for each row in the result with PDO::FETCH_CLASS. Exact method definition is like this:

bool PDOStatement::setFetchMode ( int $PDO::FETCH_CLASS , string $classname , array $ctorargs )

and to put it into your context:

namespace My\Web;

class Post {
    public function __construct($arg) {
        var_dump($arg);
    }

    public function getTitle() {
        return $this->title;
    }

    public function getFormatedContent() {
        return whatever($this->content);
    }
}

fetching objects:

$stmt = $conn->query($sql);
$posts = $stmt->setFetchMode(PDO::FETCH_CLASS, 'My\Web\Post', array(rand()));
print_r($posts);
// print array of My\Web\Post objects, each will print some random number in it's constructor.

So what I'm trying to show you is that you don't have to convert array of properties into stdClass and then bind some methods.
You can create class as you need and let PDO wrap query results with that class.

By the way it works with the latest Doctrine DBAL as well (because it's built on the top of PDO).

martin
  • 93,354
  • 25
  • 191
  • 226