Skip to content

Commit

Permalink
Add namespaced events
Browse files Browse the repository at this point in the history
  • Loading branch information
dwightwatson committed Jun 27, 2014
1 parent e379c8f commit 5e60860
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 9 deletions.
24 changes: 23 additions & 1 deletion README.md
Expand Up @@ -32,12 +32,14 @@ class Post extends Eloquent
'content' => 'required'
];

protected $messages = [
protected $validationMessages = [
'slug.unique' => "Another post is using that slug already."
];
}
```

If you'd prefer a slightly easier route, just have your models extend from `Watson\Validating\ValidatingModel` instead of Eloquent.

Now, you have access to some plesant functionality.

```php
Expand Down Expand Up @@ -202,6 +204,26 @@ $post->setMessages(['title.required' => "Please, please set a title."])

These are handy if you need to adjust the rules or messages in a specific scenario differently.

### Events

Various events are fired by the trait during the validation process which you can hook into to impact the validation process.

When validation is about to occur, the `validating.$event` event will be fired, where `$event` will be `saving`, `creating`, `updating`, `deleting` or `restoring`. If you listen for any of these events and return a value you can prevent validation from occurring completely.

```php
Event::listen('validating.*', function($model)
{
// Psuedo-Russian roulette validation.
if (rand(1, 6) === 1)
{
return false;
}
}
});
```

After validation occurs, either `validating.passed` or `validating.failed` will be fired depending on the state of the validation.

## Controller usage

There are a few ways to go about using the validating model in your controllers, but here's the simple way I like to do it. Really clean, clear as to what is going on and easy to test. Of course you can mix it up as you need, it's just one approach.
Expand Down
39 changes: 31 additions & 8 deletions src/ValidatingObserver.php
@@ -1,6 +1,7 @@
<?php namespace Watson\Validating;

use \Illuminate\Database\Eloquent\Model;
use \Illuminate\Support\Facades\Event;
use \Watson\Validating\ValidationException;

class ValidatingObserver {
Expand Down Expand Up @@ -74,20 +75,42 @@ public function restoring(Model $model)
protected function performValidation(Model $model, $event)
{
// If the model has validating enabled, perform it.
if ($model->getValidating() && $model->isValid($event) === false)
if ($model->getValidating())
{
if ($model->getThrowValidationExceptions())
// Fire the namespaced validating event and prevent validation
// if it returns a value.
if ($this->fireValidatingEvent($event, $model) !== null) return;

if ($model->isValid($event) === false)
{
$exception = new ValidationException(get_class($model) . ' model could not be persisted as it failed validation.');
// Fire the validating.failed event.
$this->fireValidatedEvent('failed', $model);

$exception->setModel($model);
$exception->setErrors($model->getErrors());
if ($model->getThrowValidationExceptions())
{
$exception = new ValidationException(get_class($model) . ' model could not be persisted as it failed validation.');

throw $exception;
}
$exception->setModel($model);
$exception->setErrors($model->getErrors());

throw $exception;
}

return false;
return false;
}
// Fire the validating.passed event.
$this->fireValidatedEvent('passed', $model);
}
}

protected function fireValidatingEvent($event, Model $model)
{
return Event::until('validating.'.$event, [$model]);
}

protected function fireValidatedEvent($event, Model $model)
{
return Event::fire('validating.'.$event, [$model]);

This comment has been minimized.

Copy link
@dalabarge

dalabarge Jun 27, 2014

Contributor

To be more semantically friendly with Laravel, this should be validated.{event}. That event should always fire before leaving the if statement in the performValidation() method just like validating.{event} fires just as you leave the if statement. I think you should also fire validation.passed or validation.failed based on the response. You might also fire validation.skipped as the else part of the if statement in performValidation(). That would allow for the most flexibility in wildcard listeners and plenty of event hooks:

  • validation.skipped (always when disabled: only event fired)

or

  • validating.{event} (before validation, always when enabled)
    • validation.passed (returns true) or validation.failed (returns false)
  • validated.{event} (after validation, always when enabled)

But you know better what you're trying to accomplish here.

This comment has been minimized.

Copy link
@dwightwatson

dwightwatson Jun 27, 2014

Author Owner

I like the idea of a skipped event, I'll add that. I reason I prefixed all events with validating. was an attempt to "namespace" the events and the expense of readability. What are you referring to exactly when you say "semantically friendly with Laravel", do you just mean that the event names are more grammatically correct?

This comment has been minimized.

Copy link
@dalabarge

dalabarge Jun 27, 2014

Contributor

Welcome to the belly of the beast... there be dragons here:
https://github.com/laravel/framework/blob/master/src/Illuminate/Database/Eloquent/Model.php#L1129-L1223

All before events are conjugated as progressive tense create => creating and all after events are conjugated as past test create => created. So it would follow that validate => validating and validated.

You'll also notice that it taps into the namespace of eloquent and the model FQN: https://github.com/laravel/framework/blob/master/src/Illuminate/Database/Eloquent/Model.php#L1256 So to make it super compatible with Eloquent (which is to say make it look like Taylor wrote it) then you'd want to actually name everything as eloquent.{event}.{method}: {class} such as eloquent.validating.create: \Watson\Validating\ValidatingModel.

And to do that it would be easier to call registerModelEvent() but you'll have to add your own custom validating, and validated keys to $observables property and provide those listeners on both the model and on the ValidatingObserver. This all gets a lot more difficult to implement and test well ... so like I said, put your dragon armor on! :)

Your approach is simple and straight forward but I'm failing to see too much use besides that a copy of the model can be passed to the event handler for post-validation changes (like hashing, encrypting, and purging). But that's the point of the observables and I'm not seeing much use for hooking into a global event like validating.* from outside of the model save context: but I'm just one developer so maybe you or someone has use for it.

This comment has been minimized.

Copy link
@dwightwatson

dwightwatson Jun 27, 2014

Author Owner

Oh wow, it does tend to get pretty busy quickly doesn't it! This is something I'd like to get right, so I'll aim for having this done by version 1.0.x - the simple implementation we have now will get people by if they need it.

Thanks for taking a deeper look into this, it's a good reference for when I get onto it. Honestly haven't done much with events up until this point (I suppose it shows... haha)!

}

}

0 comments on commit 5e60860

Please sign in to comment.