<p>I’m seeing a few issues with this:</p>

<ul>
<li><p>Service classes aren’t created with any arguments*, so the <code>$traceRecord</code> argument in your <code>__construct()</code> method is pointless.</p></li>
<li><p>Setting <code>$this-&gt;traceRecord</code> to <code>Trackr_TraceRecord::model()</code> is really weird. When you call <code>::model()</code> you’re getting a “meta” version of your record class, which should only be used to call things like <code>find()</code>, and those functions will return the actual record instances. (It’s basically just an awkward workaround for the fact that Yii 1 didn’t require PHP 5.3+, so they couldn’t rely on Late Static Bindings support, which would have made it possible to do things like <code>MyServiceClass::find()</code> directly - as you can in Yii 2.) Point is, you shouldn’t be storing a reference to the meta record returned by <code>::model()</code>.</p></li>
<li><p><code>$trace-&gt;getAttribute('id')</code> implies that your Trackr_TraceModel class has an <code>id</code> attribute, but it doesn’t, unless you didn’t post all of your model attributes code.</p></li>
<li><p>Calling <code>if ($this-&gt;traceRecord-&gt;findByPk($id) === NULL)</code> works as a conditional statement, but it looks like your logic is assuming that if a record was actually found, that <code>$this-&gt;traceRecord</code> would be set to that new record. That won’t actually be the case unless you write <code>$this-&gt;traceRecord = ...</code>.</p></li>
<li><p>Records don’t have a <code>create()</code> method. I’m not sure why that’s not throwing a PHP error for you. Did you add that method yourself? If you want to create a new record instance, you should be typing <code>$this-&gt;traceRecord = new Trackr_TraceRecord();</code></p></li>
</ul>

<p>I’d suggest you remove your service’s <code>__construct()</code> method, and also its protected <code>$traceRecord</code> property. Then update its <code>saveTrace()</code> method to the following:</p>

<p>

<pre><code>public function saveTrace(Trackr_TraceModel &amp;$trace)
{

    if ($id = $trace-&gt;getAttribute('id'))
    {
        $traceRecord = Trackr_TraceRecord::model()-&gt;findById($id);

        if (!$traceRecord)
        {
            throw new Exception(Craft::t('Can\'t find trace with ID "{id}"', array('id' =&gt; $id)));
        }
    }
    else
    {
        $traceRecord = new Trackr_TraceRecord();
    }

    if ($trace-&gt;validate())
    {
        $dt = DateTimeHelper::currentTimeForDb();
        $attributes = array(
            'entry_id' =&gt; $trace-&gt;entry_id,
            'sess_id' =&gt; $trace-&gt;sess_id,
            'dateCreated' =&gt; $dt,
            'dateUpdated' =&gt;$dt,
            'uid' =&gt; StringHelper::UUID(),
            'id' =&gt; 0
        );

        foreach($attributes as $k =&gt; $v)
        {
            $traceRecord-&gt;setAttribute($k,$v);
        }

        if ($traceRecord-&gt;save())
        {
            // update id on model (for new records)
            $trace-&gt;setAttribute('id', $traceRecord-&gt;getAttribute('id'));
            return true;
        }
        else
        {
            $model-&gt;addErrors($traceRecord-&gt;getErrors());
            return false;
        }
    }
}
</code></pre>

<hr>

<p>* Unless you’re creating an instance of your class in some other way besides calling <code>craft()-&gt;trackr_trace</code>, but you shouldn’t be.</p>

CC BY-SA 3.0
2015-08-18T12:48:11.413Z
Brandon, you are a Gentleman and a Scholar! This worked perfectly. Reading through your explanation makes perfect sense. I'm still a noob to Yii coming from Laravel I'm still learning the nuances. Thank you again!
Dan Lake
CC BY-SA 3.0
2015-08-18T12:15:21.380Z
2015-08-18T12:15:21.380Z
Brandon Kelly
<p>I am tearing my hair out with this!</p>

<p>So, I have a variable file setup as follows</p>

<pre><code> &lt;?php
namespace Craft;

class TrackrVariable
{
    public function startTrace()
    {
        if(!isset($_COOKIE['trackr_sess'])):
            $sess = StringHelper::randomString(40, TRUE);
            $expire = time()+60*60*24*120; // 120 days
            setcookie('trackr_sess', $sess, $expire);
            $_COOKIE['trackr_sess'] = $sess;
        else:
            $sess = $_COOKIE['trackr_sess'];
        endif;

        $trace = new Trackr_TraceModel();
        $trace-&gt;entry_id = 1;
        $trace-&gt;sess_id = $sess;
        craft()-&gt;trackr_trace-&gt;saveTrace($trace);
    }
}
</code></pre>

<p>...and I am calling the function in my template using:</p>

<pre><code>{{ craft.trackr.startTrace() }}
</code></pre>

<p>I create the model and set the attributes and hand off to a service. The model looks like this:</p>

<pre><code>&lt;?php
namespace Craft;

class Trackr_TraceModel extends BaseModel
{
   public function rules()
    {
        // Are there any existing rules?
        $rules = parent::rules();

        // Make all attributes safe
        $rules[] = array(implode(',', array_keys($this-&gt;getAttributes())), 'safe');

        // Send it on outta here
        return $rules;
    }

    protected function defineAttributes()
    {
        return [
            'entry_id' =&gt; [
                 'type' =&gt; AttributeType::Number,
                 'required' =&gt; TRUE
             ],
             'sess_id' =&gt; [
                 'type' =&gt; AttributeType::String,
                 'required' =&gt; TRUE
             ]
        ];
    }
}
</code></pre>

<p>and the service looks like:</p>

<pre><code>&lt;?php
namespace Craft;

class Trackr_TraceService extends BaseApplicationComponent
{

    protected $traceRecord;

    public function __construct($traceRecord = null)
    {
        $this-&gt;traceRecord = $traceRecord;
        if(is_null($this-&gt;traceRecord))
        {
            $this-&gt;traceRecord = Trackr_TraceRecord::model();
        }   
    }

    public function saveTrace(Trackr_TraceModel &amp;$trace)
    {

        if ($id = $trace-&gt;getAttribute('id')) :
            if ($this-&gt;traceRecord-&gt;findByPk($id) === NULL):
                throw new Exception(Craft::t('Can\'t find trace with ID "{id}"', array('id' =&gt; $id)));
            endif;
        else:
            $this-&gt;traceRecord-&gt;create();
        endif;

        if ($trace-&gt;validate()):

            $dt = DateTimeHelper::currentTimeForDb();
            $attributes = array(
                'entry_id' =&gt; $trace-&gt;entry_id,
                'sess_id' =&gt; $trace-&gt;sess_id,
                'dateCreated' =&gt; $dt,
                'dateUpdated' =&gt;$dt,
                'uid' =&gt; StringHelper::UUID(),
                'id' =&gt; 0
            );    
            foreach($attributes as $k =&gt; $v) $this-&gt;traceRecord-&gt;setAttribute($k,$v);

            if($this-&gt;traceRecord-&gt;save()):
                // update id on model (for new records)
                $trace-&gt;setAttribute('id', $this-&gt;traceRecord-&gt;getAttribute('id'));
                return TRUE;
            else:
                $model-&gt;addErrors($this-&gt;traceRecord-&gt;getErrors());
                return FALSE;
            endif;

        endif;
    }
}
</code></pre>

<p>In my mind, the service should be accepting the model, assigning it's attributes to the record and saving the record to the database. Currently, $record->save() is returning TRUE and therefore passing the conditional but no record is created in the dataset, no errors, no trace of any issues whatsoever!</p>

<p>At first I though it might be a 'safe validation' issue as outlined in <a href="https://craftcms.stackexchange.com/a/8895/3968">this answer</a> but, as far as I am aware, I am following all of the advice there.</p>

<p>All I want to be able to do is create a random string, store it in a cookie, as use that cookie to track resources that users view by storing the entry IDs in a table.</p>

<p>If I print out the record and the model separately, the data is being assigned to the attributes just fine, and the cookie bit works.</p>

<p>Please help!
Dan</p>

<p><strong>EDIT</strong></p>

<p>The Record file looks like this:</p>

<pre><code>&lt;?php
namespace Craft;

class Trackr_TraceRecord extends BaseRecord
{
    public function getTableName()
    {
        return 'trackr_trace';
    }

    protected function defineAttributes()
    {
        return array(
            'entry_id' =&gt; AttributeType::Number,
            'sess_id' =&gt; AttributeType::String
        );
    }
    public function defineIndexes()
    {
        return array(
            array('columns' =&gt; array('sess_id'), 'unique' =&gt; TRUE),
        );
    }

    /**
     * Create a new instance of the current class.
     *
     * @return BaseRecord
     */
    public function create()
    {
        $class = get_class($this);
        $record = new $class();
        return $record;
    }
}
</code></pre>

CC BY-SA 3.0
2015-08-14T17:16:02.597Z
I think the issue is `$record = $this->traceRecord;`.  Try doing all of your record related operations on `$this->traceRecord` directly instead of copying it to `$record`.
Brad Bell
CC BY-SA 3.0
2015-08-17T09:16:25.863Z
Thanks Brad but unfortunately this didn't make any difference, I have updated the example code in this post to reflect the changes.
Dan Lake
CC BY-SA 3.0
2015-08-18T10:29:44.023Z
Are you able to share your record code at all?
Alec Ritson
CC BY-SA 3.0
2015-08-18T12:38:16.197Z
Added the record into the original question
Dan Lake
<plugin-development><database><records>
Craft CMS Plugin Save Record to DB from Variable
# Craft CMS Plugin Save Record to DB from Variable

(Asked by: username_0 on 2015-08-14)

I am tearing my hair out with this!
So, I have a variable file setup as follows
 entry_id = 1;
        $trace->sess_id = $sess;
        craft()->trackr_trace->saveTrace($trace);
    }
}

...and I am calling the function in my template using:
{{ craft.trackr.startTrace() }}

I create the model and set the attributes and hand off to a service. The model looks like this:
getAttributes())), 'safe');

        // Send it on outta here
        return $rules;
    }

    protected function defineAttributes()
    {
        return [
            'entry_id' => [
                 'type' => AttributeType::Number,
                 'required' => TRUE
             ],
             'sess_id' => [
                 'type' => AttributeType::String,
                 'required' => TRUE
             ]
        ];
    }
}

and the service looks like:
traceRecord = $traceRecord;
        if(is_null($this->traceRecord))
        {
            $this->traceRecord = Trackr_TraceRecord::model();
        }   
    }

    public function saveTrace(Trackr_TraceModel &$trace)
    {

        if ($id = $trace->getAttribute('id')) :
            if ($this->traceRecord->findByPk($id) === NULL):
                throw new Exception(Craft::t('Can\'t find trace with ID "{id}"', array('id' => $id)));
            endif;
        else:
            $this->traceRecord->create();
        endif;

        if ($trace->validate()):

            $dt = DateTimeHelper::currentTimeForDb();
            $attributes = array(
                'entry_id' => $trace->entry_id,
                'sess_id' => $trace->sess_id,
                'dateCreated' => $dt,
                'dateUpdated' =>$dt,
                'uid' => StringHelper::UUID(),
                'id' => 0
            );    
            foreach($attributes as $k => $v) $this->traceRecord->setAttribute($k,$v);

            if($this->traceRecord->save()):
                // update id on model (for new records)
                $trace->setAttribute('id', $this->traceRecord->getAttribute('id'));
                return TRUE;
            else:
                $model->addErrors($this->traceRecord->getErrors());
                return FALSE;
            endif;

        endif;
    }
}

In my mind, the service should be accepting the model, assigning it's attributes to the record and saving the record to the database. Currently, $record->save() is returning TRUE and therefore passing the conditional but no record is created in the dataset, no errors, no trace of any issues whatsoever!
At first I though it might be a 'safe validation' issue as outlined in this answer but, as far as I am aware, I am following all of the advice there.
All I want to be able to do is create a random string, store it in a cookie, as use that cookie to track resources that users view by storing the entry IDs in a table.
If I print out the record and the model separately, the data is being assigned to the attributes just fine, and the cookie bit works.
Please help!
Dan
EDIT
The Record file looks like this:
 AttributeType::Number,
            'sess_id' => AttributeType::String
        );
    }
    public function defineIndexes()
    {
        return array(
            array('columns' => array('sess_id'), 'unique' => TRUE),
        );
    }

    /**
     * Create a new instance of the current class.
     *
     * @return BaseRecord
     */
    public function create()
    {
        $class = get_class($this);
        $record = new $class();
        return $record;
    }
}

**Tags:** plugin-development, database, records

> **Comments:**
> **username_1**: I think the issue is `$record = $this->traceRecord;`.  Try doing all of your record related operations on `$this->traceRecord` directly instead of copying it to `$record`.  
> **username_0**: Thanks Brad but unfortunately this didn't make any difference, I have updated the example code in this post to reflect the changes.  
> **username_2**: Are you able to share your record code at all?  
> **username_0**: Added the record into the original question  

---

## 1 answers

### Answer by username_3 on 2015-08-18. Score: 2 | Accepted answer

I’m seeing a few issues with this:

Service classes aren’t created with any arguments*, so the $traceRecord argument in your __construct() method is pointless.
Setting $this->traceRecord to Trackr_TraceRecord::model() is really weird. When you call ::model() you’re getting a “meta” version of your record class, which should only be used to call things like find(), and those functions will return the actual record instances. (It’s basically just an awkward workaround for the fact that Yii 1 didn’t require PHP 5.3+, so they couldn’t rely on Late Static Bindings support, which would have made it possible to do things like MyServiceClass::find() directly - as you can in Yii 2.) Point is, you shouldn’t be storing a reference to the meta record returned by ::model().
$trace->getAttribute('id') implies that your Trackr_TraceModel class has an id attribute, but it doesn’t, unless you didn’t post all of your model attributes code.
Calling if ($this->traceRecord->findByPk($id) === NULL) works as a conditional statement, but it looks like your logic is assuming that if a record was actually found, that $this->traceRecord would be set to that new record. That won’t actually be the case unless you write $this->traceRecord = ....
Records don’t have a create() method. I’m not sure why that’s not throwing a PHP error for you. Did you add that method yourself? If you want to create a new record instance, you should be typing $this->traceRecord = new Trackr_TraceRecord();

I’d suggest you remove your service’s __construct() method, and also its protected $traceRecord property. Then update its saveTrace() method to the following:

public function saveTrace(Trackr_TraceModel &$trace)
{

    if ($id = $trace->getAttribute('id'))
    {
        $traceRecord = Trackr_TraceRecord::model()->findById($id);

        if (!$traceRecord)
        {
            throw new Exception(Craft::t('Can\'t find trace with ID "{id}"', array('id' => $id)));
        }
    }
    else
    {
        $traceRecord = new Trackr_TraceRecord();
    }

    if ($trace->validate())
    {
        $dt = DateTimeHelper::currentTimeForDb();
        $attributes = array(
            'entry_id' => $trace->entry_id,
            'sess_id' => $trace->sess_id,
            'dateCreated' => $dt,
            'dateUpdated' =>$dt,
            'uid' => StringHelper::UUID(),
            'id' => 0
        );

        foreach($attributes as $k => $v)
        {
            $traceRecord->setAttribute($k,$v);
        }

        if ($traceRecord->save())
        {
            // update id on model (for new records)
            $trace->setAttribute('id', $traceRecord->getAttribute('id'));
            return true;
        }
        else
        {
            $model->addErrors($traceRecord->getErrors());
            return false;
        }
    }
}


* Unless you’re creating an instance of your class in some other way besides calling craft()->trackr_trace, but you shouldn’t be.

> **Comments:**
> **username_0**: Brandon, you are a Gentleman and a Scholar! This worked perfectly. Reading through your explanation makes perfect sense. I'm still a noob to Yii coming from Laravel I'm still learning the nuances. Thank you again!  

---

