vincent15000's avatar

What's better between prepareForValidation() vs model Observer to add a slug ?

Hello,

I have seen an example in the documentation where the prepareForValidation() function is used to add a slug to a model.

https://laravel.com/docs/11.x/validation#preparing-input-for-validation

I usually do that inside the creating() function in the model observer.

Is there a best pratice between both ones ?

According to me, it is not the role of a form request to add a slug.

What about your opinion ?

Thanks ;).

V

0 likes
3 replies
LaryAI's avatar
Level 58

When deciding between using prepareForValidation() in a Form Request and using a Model Observer to add a slug, it's important to consider the responsibilities and separation of concerns in your application.

prepareForValidation()

  • Purpose: This method is part of the Form Request lifecycle and is intended for modifying or preparing the input data before validation occurs.
  • Use Case: It's suitable for scenarios where you need to transform or sanitize input data before validation. For example, trimming whitespace or converting data formats.
  • Pros:
    • Keeps input transformation logic close to validation logic.
    • Useful if the slug is derived from user input and needs to be validated as part of the request.
  • Cons:
    • Mixing data transformation with validation logic can blur the separation of concerns.
    • Not ideal if the slug generation is purely a model concern.

Model Observer

  • Purpose: Observers are designed to listen to model events, such as creating, updating, etc., and perform actions in response.
  • Use Case: Ideal for handling model-specific logic, such as generating a slug based on model attributes.
  • Pros:
    • Keeps model-related logic encapsulated within the model layer.
    • Ensures that the slug is generated consistently, regardless of how the model is created or updated.
  • Cons:
    • Adds an additional layer of abstraction, which might be unnecessary for simple applications.

Best Practice Recommendation

In general, generating a slug is more of a model concern than a request concern. Therefore, using a Model Observer is often the better choice. It keeps the logic related to the model's data encapsulated within the model layer, ensuring that the slug is generated consistently across different parts of your application.

Here's a simple example of how you might implement this in a Model Observer:

namespace App\Observers;

use App\Models\Post;
use Illuminate\Support\Str;

class PostObserver
{
    public function creating(Post $post)
    {
        if (empty($post->slug)) {
            $post->slug = Str::slug($post->title);
        }
    }
}

And don't forget to register the observer in your AppServiceProvider or a dedicated service provider:

use App\Models\Post;
use App\Observers\PostObserver;

public function boot()
{
    Post::observe(PostObserver::class);
}

This approach ensures that the slug is generated whenever a Post model is created, regardless of where or how the creation is triggered.

martinbean's avatar
Level 80

@vincent15000 I use a trait. Can be as simple as:

trait Sluggable
{
    public static function bootSluggable(): void
    {
        static::saving(function (Model $model) {
            $model->setAttribute('slug', $model->getAttribute('name'));
        });
    }
}

I’m also not a fan of “adding” data to requests using methods like prepareForValidation. It just feels a bit of an anti-pattern to magically add data to the request that wasn’t sent by the user in a form submission.

1 like

Please or to participate in this conversation.