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

How to do validation at model?

Hi,

after lot's of code not working at the new version I decided to validate my data inside my model instead of my form. I hope this will fix some problems I have.

The problem is that the validation function inside my model isn't executed.

Controller

    public function createAction() {
        $user_run = new UsersRuns();

        $success = $user_run->save($this->request->getPost());

        if ($success) {
            echo "Thanks for registering!";
        } else {
            echo "Sorry, the following problems were generated: ";
            foreach ($user_run->getMessages() as $message) {
                echo $message->getMessage(), "<br/>";
            }
        }

        $this->view->disable();
    }

Model

    namespace Run\Backend\Models;

    use Phalcon\Validation;
    use Phalcon\Mvc\Model;
    use Phalcon\Validation\Validator\PresenceOf;

    class UsersRuns extends Model {

    public function initialize() {
        $this->hasOne('users_id', 'Users', 'id');
    }

    public function validation() {
        $validator = new Validation();

        $validator->add(
                'date', new PresenceOf([
            'message' => 'Please enter a date'
                ])
        );

        if ($this->validationHasFailed() == true) {
            return false;
        }

    }

I always get the default messages from $message->getMessage(). What I am doing wrong?

edited Jul '16

As i already wrote in github issue - it will won't fix ANY of your problems. Your getter must just return data as THEIR IS( what getters should always do anyway ), not changed or modified. This is why your validation in form is not working. Any value modification should be done in afterSave/afterFetch method.

Back back to your question:

public function validation() {
    $validator = new Validation();

    $validator->add(
            'date', new PresenceOf([
        'message' => 'Please enter a date'
            ])
    );

   return $this->validate($validator);
}

Since 2.1.x/3.0.0 validation in form and model works exactly the same, same classes, same mechanism, same code etc.



14.4k
edited Jul '16

Thanks for hanging in again.

I removed all setters and getters like you said and everything was fine. But my problem is the format of the form entities (when edited). I get a sql date from my databse of course: '2000-01-01'. But I need to format it to '01.01.2000'. I made this with getters before but now I don't know how to do it.

edited Jul '16

With afterSave/afterFetch/beforeValidation etc events.



14.4k
edited Jul '16

That would work, yes, but what if I really need the "pure" database result at another point? Sometimes I need seconds to do calculations and for the final output I need the HH:MM:SS format.

edited Jul '16

Then getters wouldn't do it anyway. What you mean pure database result ?



14.4k

I save durations as seconds but the user sees HH:MM:SS (hours, minutes, seconds). So, in my database one result might be 3600 (seconds). But the user sees 01:00:00. And of course inside the form I also need HH:MM:SS but save it as seconds.

Then you jsut need to display it, for doing it is afterFetch and that's it. Getters shouldn't doo such a things.



14.4k

Okay, I won't use getters for it.

But there is the problem with afterfetch that I can't calculate with these values. When I manupulate the date it can't be used for mathematical aggregation. Also the mentioned duration isn't useable.

If I see that correctly, there is no solution for that for now.

edited Aug '16

But for what exactly ? What you want achieve ? You can solve everything with afterFetch/afterSave.



14.4k

I made a flow chart:

edited Aug '16

Just make filter in view and siplay it as a time and everywhere use int ? You can do everything what you wrote here with afterFetch/beforeValidationOnSave/afterSave.



14.4k

That's the problem with forms. I use

$form->render('entity')

in the view. I can't filter at this point.

Where does afterSave comes in place?

In model, after saving in database. What you mean you can't filter ?

User pass time - in validation beforeValidation you are changing to int, and afterSave/afterFetch you change it back to time for displaying - what's a problem with it ?



14.4k

But with the manipulation at afterFetch I can't do calculations because the duration is allready converted to time.

I need both int and time. time at forms, overview etc. int for internal calculations. Sorry, I miss some point.

edited Aug '16

What you mean by caluclations ? Can you give me any example of those calculations, where they are done and WHEN ? Then just convert value back to int where you are doing calculations, what's a problem ?

You can't just have both values without defining them, in any framework or language. On some point you have to decide what value have at what point, and when to make them back to int or whatever. There is basically right now no phalcon problem. You should just redifine your application structure or something.



14.4k
edited Aug '16

I am using this library for calculations:

https://github.com/powder96/numbers.php

Okay, I understand. It's a conceptual problem, not a Phalcon one.

To sum it up: I use afterFetch for manipulate the data from the database. And I have to remanipulate it for doing calculations within components like numbers.php (the library above). Think I got it now :)

But where you are doing those calculations ? Where and for what ? Can't it be done in model in afterfetch ? Like before changing it to time calculate whatever you want on ints ?



14.4k

I am instantiating the math object inside the controllers and I retrieve the model data from a base controller on every backend page.

I have pages where I may need the average and pages where I may need the max value of an array. My whole app consists of multiple statistical calculations, so I guess it can't be done in afterFetch.

What a hell, those calculations should be done in database then if possible..... not in php, and just use query builder for displaying it men :( You shouldn't do such a calculations like simple average in php.



14.4k

I know, these were just examples ;) I am also doing more complex calculations.



14.4k
edited Aug '16

Isn't is possible to have both, a method that returns the manipulated value plus a afterFetch method like here:

    class MyModel extends Model {

        // Returns hh:mm:ss
        private function afterFetch() {
            $this->seconds = gmdate("H:i:s", $this->seconds);
        }

        // Returns seconds
        public function returnSeconds {
            return $this->seconds;
        }
    }

And later when I do the calculation:

    $myModel = new MyModel();
    $seconds = $myModel->returnSeconds();

Would this work and is this a sensible way of doing this?

Until you DON'T FETCH THEN YES.



14.4k
edited Aug '16

I now solved it with this:

class MyModel extends Model {

    // Returns hh:mm:ss
    private function afterFetch() {
        $this->duration = gmdate("H:i:s", $this->seconds);
    }

    // Returns seconds
    public function returnSeconds {
        return strtotime($this->duration) - strtotime('TODAY');
    }
}

Not perfect, because it gets first convert to HH:MM:SS and then, if I do $model->returnSeconds() again back to seconds. But obviously this is the only solution.

Thanks for clarifying the php stuff and for your persistent help.