The best way to implement filtering rules on Find, FindFirst and PHQL

I've written an API in Phalcon and used the ORM and PHQL to fetch data. I've been stuck on adding a feature, namely Object level or Record level security througout the entire API. In particular, I'm looking for a way to add fitering rules that enforce business logic even when using low level Phalcon classes like

Robots::find();

....
// how do i enforce filters here for all developers?
$robot = Robots::findFirst();

// how do i enforce filters here for all developers?
$robotsParts = $robot->robotsParts;

Is there a way I can transparently apply business logic in the form of filtering rules to functions like find() and findFirst()?

The only way I can see to do that is to create a wraper function like...

// apply my own filtering?
$robot = Robots::myFind();
$robot = Robots::myFindFirst();

// custom function to apply extra filtering?
$robotsParts = $robot->myGetRelated('robotsParts');

Where I can insert filtering rules on a Model? I've seen some talk in that directly years back over here: https://forum.phalconphp.com/discussion/499/beforefetch-

I have some developer friends that use Django and they are teasing that they can do something like this with Django's ORM and I'm a sad Phalcon users :(



78.1k

evry model extends "phalcon's base model" you can do whatever you want there..


Class Robot extends \Phalcon\Mvc\Model {
  ///...bla bla bla

  public static function find($params = null) {
    if (is_array($params)) {
      if (false === isset($params['deleted'])) {
        $params['deleted'] = 0;
      }
    }

    return parent::find($params);
  }
}

obviosuly what i just wrote doesnt make much of a sence but you get the idea



3.1k

I think that bad ideas are:

  • extending/expanding models (even pure it's overloaded)
  • overwriting methods, using call super etc.
  • fetching data using global methods (like Model::findFirst())

Good idea is to create a special component class, for instance:

use Phalcon\Mvc\Model\Query\BuilderInterface;
use Phalcon\Mvc\User\Component;

class MyData extends Component {

    public function getSomething($libraryCategoryId = null){
        $qb = $this->modelsManager->createBuilder();
        $qb->addFrom('App\Model\Library', 'library');
        $qb->columns(['library.*', 'libraryCategory.*', 'file.*', 'organization.*']);
        $qb->join('App\Model\LibraryCategory', 'library.libraryCategoryId = libraryCategory.id', 'libraryCategory');
        $qb->join('App\Model\File', 'library.fileId = file.id', 'file');
        $qb->join('App\Model\Organization', 'library.organizationId = organization.id', 'organization');
        if ($libraryCategoryId) {
            $qb->andWhere('libraryCategory.id = :libraryCategoryId:', ['libraryCategoryId' => $libraryCategoryId]);
        }
        return $qb;
    }

}

Now I have:

  • one access point to data and one standard (when I need to add isActive fiiter or ACL I can do it in one place in few minutes)
  • one class for one task (single responsibility principle)
  • full control how many queries are created
  • full control what is returned (I can use pagination or get one or many records the same way)


21.9k

Michael, if I understand the jist of what you are saying, it is: Approach the problem by writing helper type utilities that pull data while also following buisiness/security rules. Then have our developers work through these tools rather than writing their own custom PHQL, SQL or using find/findeFIrst whe working elsewhere within the API.

Is that correct?

I think that bad ideas are......



3.1k

Approach the problem by writing helper type utilities that pull data while also following buisiness/security rules

Correct, maybe not "helper" but rather something like datasource, data access layer, data service etc. Naming doesn't make any sense when everything is called "helper" :) If You use query builder object You can also add additional modification in simply way (by providing query builder as method parameter)

Then have our developers work through these tools rather than writing their own custom PHQL, SQL or using find/findeFIrst whe working elsewhere within the API

They can write their own PHQL, but in more elegant way, in one place, under one abstract layer where security methods are implemented etc. Problem is, that global attitude like Model::findFirst() tempts, what might cause security bugs.

Alternative is using events: https://docs.phalconphp.com/ar/3.2/db-models-events

But "magic" is hard to debug and control, there is a problem with queries when model isn't involwed or providing parameters.

I'm a sad Phalcon user

Don't be sad, Doctrine provides something like query filters, but also a lot of problems with them - it's not java + oracle. If You can avoid backstage logic You should do it.



78.1k

i like all my queries being inside the model, i tough that was the idea .. every single possible "get" for my robots is in my model. But that is just me i guess