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

Looking for a proper way to put a default filter on Model finds

So here is the deal.

I have a system where visibilty to certains things is prohibited by business logic. so something that one user can see might not neccesarily be visible to another user and vice verca.

I have a Method built onto the model that defines this called isVisible. This simply returns a true false.

So my finds can be something like this.

Things::find()->filter(function($thing){
    return ($thing->isVisible()) ? $thing : false});

This method works great. BUT it would be easier if could directly affect the find method to do this automatically. so that i wouldn't have to add taht filter anywhere that i am getting "Things" etc.

I have been looking through documents and haven't found what i am looking for so far.

AfterFetch doesn't appear to offer the option to remove an item from a resultset.

How can I decorate the default find method?

Any ideas?

edited Jun '16

Just override find method and add there proper conditions(use acl here maybe ?).

Actually you shouldn't remove an item from a resultset, this should be done on query level(sql), just select items which are visible to user. Using filter adds not needed overhead. On small resultset i guess it's not gonna be a big problem.

To be honest i would reccomend to implement Repository pattern and get rid of calling Things::find in controllers etc, only in repositories.

so i had never really looked into the repo methoad a lot until last night after you had mentioned it. I had heard it mentioned but never researched it. Quick question on how you would implement this. becuase what it looks ike to me a is a broker or a collection of objects that exists between my models, controllers, services etc.

So it is basically and abastraction built in between the model and the controller?

I found a couple of different implemenations of it with factories and gateways to build and get and set data. Do i ahve the right logic here? I am not crazy about adding another lay of abstraction to my system right now. as its a pretty small project at the moment but could grow. I don't know that i am crazy about the extra layers of complexity.

edited Jun '16

Just to make sure i have your idea right here is what i was looking at.


use Namespace\Models\Things;

interface RepoInterface 
{
    public function get();
    public function getAll();
}

class ThingRepo implements RepoInterface

{
    public function get($params)
    {
        // this doesn't really solve the problem of getting the data i don't need an filtering it after
        // it would be better for optimization in the long run to edit teh sql query. 
        // so i suppose i could add it into the query logic with teh querry builder instead of the magic functions 
        // I just don't htink it is worth the time to reimplemnt the ORM when it is already built at this time. 
        return Things::find($params)->filter( logic )
    }
    public function getAll()
    {

    };
}
edited Jun '16

Don't USE FILTER. Just use proper conditions in find method.

$thing->isVisible() how this method looks ?

edited Jun '16

It grabs teh Acl, and user auth.

When a user logs in their application visibility is set in a session object. getVisibiltiy returns this object as an array of id's taht is used to filter it. It is an admittedly messy logic.

The ACL is used to override this filtering based on wheather or not you have "admin" privelages.

public function isVisible()
    {
        $acl = new Acl;
        $auth = new Auth;

        // if can see all return all
        if ( $acl->isAllowed( $auth->getIdentity()['profile'] , "GlobalSettings", "canSeeAll") ) return true;

        // return filtered list
        return (in_array($this->id, $auth->getVisibility())) ? true : false;
    }

// This logic could be abstracted into the querry wih a little more work. 

public function getThings($params)
    {
        $acl = new Acl;
        $auth = new Auth;
        // this doesn't really solve the problem of getting the data i don't need an filtering it after
        // it would be better for optimization in the long run to edit teh sql query. 
        // so i suppose i could add it into the query logic with teh querry builder instead of the magic functions 
        // I just don't htink it is worth the time to reimplemnt the ORM when it is already built at this time. 

        // this is probalby the clearn solution. I might have to use the querry builder instead of the direct $params. i haven't looked at the phalcon ORM enough to know which solution would be easier to implement. 
        if ( !$acl->isAllowed( $auth->getIdentity()['profile'] , "GlobalSettings", "canSeeAll") ) {
            $params = $params // + logic on visiblity. 
        }

        return Things::find($params);
    }

I think this is what i might end up doing long term. but for now i am gonna stick wtih filter until the qurery times actualyl create a problem. I am still sub 10 MS on large pulls.



145.0k
Accepted
answer
edited Jun '16

You shouldn't totally use any filter method. That make everything in sql conditions and that's it. It costs performance to filter every record.

Also why you creating Acl and Auth ? Shouldn't you get it from DI ? Look on ORM and query builder then. You should use filter only if there is no other solution.

edited Jun '16

HA good call. my brain was hurting at the end of a 16 hour day yesterday.

Here is the updated method.


public function getThings($params)
    {
        $auth = this->getDi()->getAuth();
        $acl = this->getDi()->getAcl();

        if ( !$acl->isAllowed( $auth->getIdentity()['profile'] , "GlobalSettings", "canSeeAll") ) {
            $params = $params // + logic on visiblity. 
        }
        return Things::find($params);
    }
edited Jun '16

Agreed. thanks for the insight. it workd for the moment and it was fast enough but i knew as soon as i wrote it that it wasnt going to scale...

You shouldn't totally use any filter method. That make everything in sql conditions and that's it. It costs performance to filter every record.

Also why you creating Acl and Auth ? Shouldn't you get it from DI ? Look on ORM and query builder then. You should use filter only if there is no other solution.