We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

FInd not returning model or running afterFetch

I'm having an issue with models.

when running find or query on a model the afterfetch is not called, nor returned in the object. Neither is the toarray from the model.

If i change to findfirst then both AfterFetch and toarray are both present.

class DataExports extends extends \Phalcon\Mvc\Model
{

    public function initialize()
    {
        $this->setConnectionService('dbPostgres');
    }
    public function initialize() {
        parent::initialize();
        $this->setSchema('public');
        $this->setSource('tbl_data_exports');
    }

    public function afterFetch() {
        var_dump('afterFetch');
        $this->file_data = base64_encode(stream_get_contents($this->file_data));
        //$this->AfterFetchSingle($this);
    }
}
public function select() {
        $dataExports = DataExports::find();
        $dataExports2 = DataExports::findFirst(6683);

        print_r(get_class_methods($dataExports));
        print_r(get_class_methods($dataExports2));
        return $dataExports->toArray();
    }

find return

Array
(
    [0] => __construct
    [1] => valid
    [2] => toArray
    [3] => serialize
    [4] => unserialize
    [5] => next
    [6] => key
    [7] => rewind
    [8] => seek
    [9] => count
    [10] => offsetExists
    [11] => offsetGet
    [12] => offsetSet
    [13] => offsetUnset
    [14] => getType
    [15] => getFirst
    [16] => getLast
    [17] => setIsFresh
    [18] => isFresh
    [19] => setHydrateMode
    [20] => getHydrateMode
    [21] => getCache
    [22] => current
    [23] => getMessages
    [24] => delete
    [25] => filter
)

Find First return (processQuery and AfterFetchSingle are 2 methods i created for testing)

Array
(
    [0] => initialize
    [1] => afterFetch
    [2] => processQuery
    [3] => AfterFetchSingle
    [4] => toArray
    [5] => __construct
    [6] => setDI
    [7] => getDI
    [8] => getModelsMetaData
    [9] => getModelsManager
    [10] => setTransaction
    [11] => getSource
    [12] => getSchema
    [13] => setConnectionService
    [14] => setReadConnectionService
    [15] => setWriteConnectionService
    [16] => getReadConnectionService
    [17] => getWriteConnectionService
    [18] => setDirtyState
    [19] => getDirtyState
    [20] => getReadConnection
    [21] => getWriteConnection
    [22] => assign
    [23] => cloneResultMap
    [24] => cloneResultMapHydrate
    [25] => cloneResult
    [26] => find
    [27] => findFirst
    [28] => query
    [29] => count
    [30] => sum
    [31] => maximum
    [32] => minimum
    [33] => average
    [34] => fireEvent
    [35] => fireEventCancel
    [36] => appendMessage
    [37] => validationHasFailed
    [38] => getMessages
    [39] => save
    [40] => create
    [41] => update
    [42] => delete
    [43] => getOperationMade
    [44] => refresh
    [45] => skipOperation
    [46] => readAttribute
    [47] => writeAttribute
    [48] => hasOne
    [49] => belongsTo
    [50] => hasMany
    [51] => hasManyToMany
    [52] => addBehavior
    [53] => setSnapshotData
    [54] => hasSnapshotData
    [55] => getSnapshotData
    [56] => hasChanged
    [57] => getChangedFields
    [58] => getRelated
    [59] => __call
    [60] => __callStatic
    [61] => __set
    [62] => __get
    [63] => __isset
    [64] => serialize
    [65] => unserialize
    [66] => dump
    [67] => setup
) 


2.0k

your second initialize method overwrites the first so the model doesnt know from where to select the data. also two extends keywords?

edited Oct '14

Gah sorry. I was pulling in code from the base model I extend so it was all one model to make the shown example easier to see. I screwed that one up pretty bad lol. 1 extend and 1 init.

Should look like this.

class DataExports extends \Phalcon\Mvc\Model
{

    public function initialize()
    {
        $this->setConnectionService('dbPostgres');
        $this->setSchema('public');
        $this->setSource('tbl_data_exports');
    }

    public function afterFetch() {
        var_dump('afterFetch');
        $this->file_data = base64_encode(stream_get_contents($this->file_data));
    }
}


2.0k

did you read this part about find() and findFirst() differences in the documentation? https://docs.phalcon.io/en/latest/reference/models.html#model-resultsets

im fairly sure that simply printing methods from find() wont show you any of your data.

Yea that was only to show what methods were being passed back. Basically when you do findfirst it returns the model you ran findfirst off of, if you run find then it returns the simple class which has no connection to the model you ran therefor any afterfetch calls and toarray calls i wish to modify, i can not actually do. basically making the model useless for any formatting of data i want to do for all data pulled wether it be a find, query, or findfirst. there is no way to make all of these return data the same way for all the calls i want to make.



2.0k

Doesnt the find() method return basicaly a stack of your models? Just loop through it and all your data should be under the ids handled by your afterfetch and you can do toarray inside the loop.

Yes, i could do that, but then the functionality to convert the data (in this case ByteA to Base64encoded) has to be written in every controller i use to pull this data. so now every time i use this model i need to remember to convert this data. i could make it a function in the model, but once again, the functions i create in the model don't get returned with the find. so i then need to either invoke the class as a variable and run it or make it a static call.

either way, i would like find to just pull and convert the data when accessed just like findfirst. i can do that in afterfetch when i run findfirst and it works fine..

but it's not setup that way.

i think i have found a lengthy and somewhat convoluted workaround but i just wish this was functionality that was already in phalcon.

i'll post up my solution tomorrow.

Can't you do two models class for the same table? One model for normal operation and second extended with afterFetch to use whenever you want to get encoded data?

DataExports for normal operation without afterFetch() method without extensive operations on records and big data and DataExportOnSteroids extends DataExports with afterFetch() method which encode what you want and do what you want?

Second solution is when you write data to table use afterUpdate and/or afterSave() method which encode your data after insert it to data table for eg data_file_encoded field, so you will access to encoded and decoded data at the same time without messing operations.

PS: It could be a method that would be triggered only when findFirst is used, this method could be name afterFetchFirst or something.



5.5k
Accepted
answer
edited Oct '14

So here is my solution,

Base Model

<?php
namespace API\Models;

class BaseModel extends \Phalcon\Mvc\Model
{
    public $parent = null;

    public function initialize() {
        $this->setConnectionService('dbPostgres');
    }

    public static function find($params = null, $class = null) {
        $new = new $class;
        $new->parent = parent::find($params);

        return $new;
    }

    //placeholder call
    public function transformData(&$data) {

    }

    public function toArray() {
        $data = $this->parent->toarray();
        foreach ($data as &$value) {
            $this->transformData($value);
        }

        return $data;
    }
}

Model

namespace API\Models;

class DataExports extends \API\Models\BaseModel
{
    public static function find($params = null) {
        return parent::find($params, __CLASS__);
    }

    public function initialize() {
        parent::initialize();
        $this->setSchema('public');
        $this->setSource('data_exports');
    }

    public function afterFetch() {
        return $this->transformData($this);
    }

    public function transformData(&$data) {
        //This will need a check for if its an array or an object
        if (isset($data['file_data']) && $data['file_data'] !== null) {
            $data['file_data'] = base64_encode(stream_get_contents($data['file_data']));
        }
    }
}

Controller Use

public function select() {
        //$this->findArray Set earlier in the code
        $dataExports = DataExports::find($this->findArray);

        return $dataExports->toArray();
    }