Solved thread

This post is marked as solved. If you think the information contained on this thread must be part of the official documentation, please contribute submitting a pull request to its repository.

Use is_deleted field when accessing childs ORM

Hi,

I've got a question regarding the Phalcon ORM. All my datatables have a field to make sure that they are all soft deleted. (is_deleted)

Now when I try to access a child of the found object I still get the childs that are deleted aswell. For instance:

    $users = Users::find(); //Now only finds users with is_deleted = 0
    $users->getTickets(); //Returns all tickets, also the ones who have is_deleted = 1

My search function that overrides all find actions (find, findFirst and count):

    /**
     * Function to search for all undeleted models
     * @param type $parameters
     * @return type
     */
    public static function getSoftDeleteParams($parameters = null) {

        //Set the deleted field name
        $deletedField = "is_deleted";
        if ($parameters === null) {
            //if there are no params given, make the deleted check
            $parameters = $deletedField . ' = 0';
        } else if (is_numeric($parameters)) {
            //the user is probably getting a single instance of a model by primary key (id)
            $parameters = 'id = ' . $parameters . ' AND ' . $deletedField . ' = 0';
        } else if (is_array($parameters) === false && strpos($parameters, $deletedField) === false) {
            //If the parameter is a string (giving conditions to the query) concatenate is_deleted check to the params string
            $parameters .= ' AND ' . $deletedField . ' = 0';
        } else if (is_array($parameters) === true) {
            //The parameters variable is an array of parameters, add is_deleted check to the first parameter
            if (isset($parameters[0]) === true && strpos($parameters[0], $deletedField) === false) {
                $parameters[0] .= ' AND ' . $deletedField . ' = 0';
            } elseif (isset($parameters['conditions']) === true && strpos($parameters['conditions'], $deletedField) === false) {
                //Else concatenate it to the conditions string given in the array.
                $parameters['conditions'] .= ' AND ' . $deletedField . ' = 0';
            }
        }
        return $parameters;
    }

Is there a way that i can make sure that i never get deleted childs back when accessing them using the magic getter? I hope that there is a way to filter out all deleted values while still using the ORM.

Thanks in advance!

Hi there in getTickets() you are using a getRelated()??? you must overwrite this too



467
edited Feb '18

Thanks for the suggestion, but I want to make it as generic as possible, the find, findFirst and count are all overridden in a base class. I don't want to override the getTickets() function because then ill have to do that for hundreds of functions.



467

I tried overriding the getRelated() function this way:

        public function getRelated($alias, $parameters = null) {
          $parameters = self::getSoftDeleteParams($parameters);
          return parent::getRelated($alias, $parameters);
      }

But sadly this still returns deleted values.

try this $this-.>getRelated('aliasRelationship', ['conditions' => 'is_deleted = 0']) if works ok the problem is other.

Do you have enableLiterals = true?



467

try this $this-.>getRelated('aliasRelationship', ['conditions' => 'is_deleted = 0']) if works ok the problem is other.

Do you have enableLiterals = true?

Thanks for the answer, i've tried using getRelated, but to no avail. I have not changed the default config values for the model, so enableLiterals should still be true.

How does Phalcon do the fetching of the relation models in the background? There must be something to overwrite that im missing here...

Regarding to this post, the functionality was not there yet on November 2015. Shouldn't be that much difference to include right?

Anyway, thanks for the answers

Phalcon fetch related models using model getRelated() then internally call model manager getRelationRecords()



467

Currently i'm overriding the getRelated function, but it seems like it is not even being called.

For instance, i'm currently using this override:

    public function getRelated($alias, $parameters = null) {
        return parent::getRelated($alias, array('conditions' => 'is_deleted = 0')); //only adding the is_deleted here for testing purposes
    }

And I call try to get the relations this way:

    $user = User::findFirst($id);
    $user->getTickets();    //This still returns soft deleted values

It looks like the overridden getRelated function doesn't even get called at all. Weird, because the find, findFirst and count did change.



20.2k
Accepted
answer

I think you should overwrite the static method or maybe use __callStatic



467

I think you should overwrite the static method or maybe use __callStatic

Overriding __callStatic did exactly what I was trying to achieve. The function accepts arguments and is overridable in the base class.

For further reference, this is what I did:

    public static function __callStatic($method, $arguments) {
        return parent::__callStatic($method, self::getSoftDeleteParams($arguments)); //Adds in the ['condition' => 'is_deleted = 0'] see above
    }

Thanks!

Careful though - as that method will overwrite ALL magic static function calls.



467

Yeah I just found out that it does that. I've probably got to find a way around that.

Is there a way to decouple those functions?



467

So after some research of the source code I found that the __callstatic() method is calling the __invokeFinder method (seen here).

        /**
        * Execute the query
         */
         return {modelName}::{type}([
            "conditions": "[" . field . "] = ?0",
            "bind"      : [value]
    ]);

I cannot add another condition to this statement, because the function is marked as final. so its not overridable. That's reasonable, since I shouldn't want to override such a key function.

It seems like there currently is no other way than writing custom fetch methods or compile the edited source code (which has too many downsides)

Might be an idea to add functionality for this in the future.