5

My main issue is perhaps not totally understanding the unique idioms of Yii, but I need to output data from my custom data tables in a template. So obviously I have a MyPluginVariables class and a MyPluginService to handle to actual business logic.

Using my MyPlugin_FooRecord I can use ActiveRecord to fetch some data. But this obviously returns the actual record objects, when I really just need a model passed to the template. From what I understand, I should use models not the records themselves. Is this correct?

Also, how exactly should I build a chainable syntax, similar to fetching entries in a template?

For example, I am trying to get this to work:

{% set rows = craft.myPlugin.someMethod().limit(10).order('some_col').sort('asc') %}

So relating this syntax back to my original question, how would I build this and return an array of models? The ElementCriteriaModel will automatically execute the find() method when it's used an array, should I just overriding the __toArray() method?

I have been searching and looking through the docs, so perhaps I missed something. I have also been looking through the code in Craft, but due to the abstractions and not being familiar with Yii core classes, I am getting a little lost in figuring out how this works.

4

I was able to figure almost all of this stuff out by diving through the code. The only exception is triggering the find() method automatically.

The key here was to follow the code in the ElementCriteriaModel. First thing I did was create a method in the MyPluginService class to fetch the data.

public function foo($criteria = false)
{
    return new MyPlugin_MyCriteriaModel($criteria ?: array());
}

I added a wrapper to my MyPluginVariable class.

public function parcels($criteria = array())
{
    return craft()->myPlugin->foo($criteria);
}

This will allow for the following syntax:

{% set data = craft.myPlugin.foo().limit(5).offset(1).find() %}

You must define a find() method in your MyPlugin_MyCriteriaModel class that will perform the SQL query to actually fetch your data. You also have to loop through the results set and instantiate MyPlugin_FooModel for each db row if you want to return a Model to the template instead of a stdObject or array.

For example, my find method looks like this:

public function find($attributes = null)
{   
    $this->setAttributes($attributes);

    if (!isset($this->_matchedObjects))
    {
        $objects = craft()->myplugin_foo->find($this);

        $this->setMatchedObjects($objects);
    }

    return $this->_matchedObjects;
}

Then in MyPlugin_FooService I have this:

public function query(MyPlugin_MyCriteriaModel $criteria)
{
    $record = new MyPlugin_FooRecord();

    $query = craft()->db->createCommand()
        ->select('*')
        ->from($record->getTableName())
        ->limit($criteria->limit)
        ->offset($criteria->offset)
        ->order($criteria->order);

    if($criteria->id)
    {
        $query->where('id = :id', array(':id' => $criteria->id));
    }

    if($criteria->uid)
    {
        $query->where('uid = :uid', array(':uid' => $criteria->uid));
    }

    // More conditionals may go here

    return $query;
}

public function find(MyPlugin_MyCriteriaModel $criteria)
{
    $query = $this->query($criteria);

    return $this->_populateModelsFromArray($query->queryAll());
}

protected function _populateModelsFromArray(Array $array)
{
    $models = array();

    foreach($array as $row)
    {
        $models[] = MyPlugin_FooModel::populateModel($row);
    }

    return $models;
}
|improve this answer|||||
  • what does your MyPluginVariable class look like with the wrapper function? – aran Jan 9 '15 at 6:24
  • also are you extending ElementCriteriaModel in MyPlugin_MyCriteriaModel? – aran Jan 9 '15 at 17:19
  • 1
    MyPlugin_MyCriteriaModel is a class that extends BaseModel. It's just a regular model with some arbitrary attributes. I did base the logic off ElementCriteriaModel, but it does not extend it. The MyPluginVariable wrapping method looks like this: public function parcels($criteria = array()) { return craft()->postmaster->parcels($criteria); } – Justin Kimbrell Jan 9 '15 at 19:38
  • 1
    That's right. The logic is kinda confusing. You use the variables to call the criteria model, which returns a "chainable" object. Then the find method in the criteria model sends the model to the ParcelService instance, which actually runs the query. – Justin Kimbrell Jan 9 '15 at 19:47
  • 1
    Feel free to download Postmaster to see the complete code I am referencing. github.com/objectivehtml/Postmaster-for-Craft-CMS – Justin Kimbrell Jan 9 '15 at 19:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.