++Mutator & Accessor++
Mutator Un Mutator transforma un atributo antes de ser almacenado. Accessor Un Accessor transforma un atributo antes de ser presentado.
Probar que los datos se transforman correctamente antes de almacenarse es una práctica fundamental en cualquier aplicación robusta con Laravel. Cuando un usuario ingresa información con mayúsculas o espacios, el sistema debe garantizar que esa información se normalice de forma consistente. Aquí se explica cómo escribir pruebas unitarias para validar mutadores y accesores (campos virtuales) en un modelo de Laravel, siguiendo el enfoque de escribir primero el test y luego la implementación.
¿Cómo crear un test unitario para un modelo que aún no existe?
El primer paso consiste en generar el archivo de prueba antes de crear el modelo. Esto sigue la filosofía de test-driven development (TDD): primero defines el comportamiento esperado y después escribes el código que lo cumple.
Se ejecuta el comando para crear el test unitario [01:00]:
php artisan make:test Models/PostTest --unit genera la clase de prueba en la carpeta correspondiente.--unit indica que se trata de un test unitario, no de un test de integración.Post todavía no existe, pero ya se referencia desde el test.Dentro del test se instancia un nuevo objeto Post y se le asigna un valor al campo name con mayúsculas y minúsculas mezcladas, por ejemplo: "Proyecto en PHP". La aserción verifica que el valor almacenado se haya convertido completamente a minúsculas [02:18].
Al ejecutar las pruebas con php artisan test, el resultado falla en la línea donde se referencia el modelo inexistente. Ese error es precisamente lo esperado: el test guía la implementación.
¿Qué es un mutador y cómo se implementa en el modelo?
Un mutador es un método dentro del modelo que altera el valor de un atributo antes de que se almacene. En Laravel, se define con la convención set + nombre del atributo + Attribute [03:30].
Para crear el modelo se ejecuta php artisan make:model Post. Dentro de la clase Post se agrega el método:
php public function setNameAttribute($value) { $this->attributes['name'] = strtolower($value); }
strtolower() convierte toda la cadena a minúsculas.name pasará por esta transformación automáticamente.Este patrón permite garantizar la consistencia de los datos sin depender de que el usuario ingrese la información en un formato específico.
¿Cómo funciona un accesor o campo virtual en Laravel?
Un accesor genera un campo que no existe físicamente en la base de datos, pero que se calcula a partir de otros atributos. Se le conoce también como campo virtual. En este ejercicio se crea un accesor para el campo slug [05:00].
El test verifica que al asignar "Proyecto en PHP" como nombre, el campo slug devuelva "proyecto-en-php": todo en minúsculas y con los espacios reemplazados por guiones.
La implementación en el modelo utiliza la convención get + nombre del campo + Attribute:
php public function getSlugAttribute() { return str_replace(' ', '-', strtolower($this->name)); }
strtolower() convierte a minúsculas.str_replace() sustituye los espacios por guiones.name ya pasa por el mutador, así que llega en minúsculas; la función refuerza esa transformación para el slug.Para ejecutar un test específico se puede usar un filtro con el nombre del método [06:08]:
bash php artisan test --filter=test_get_slug
El resultado confirma que el campo virtual se genera correctamente.
¿Por qué conviene ejecutar todas las pruebas después de cada cambio?
Al finalizar la implementación, se ejecutan todas las pruebas del proyecto para confirmar que ningún cambio previo se haya roto [06:50]:
set_name, get_slug) pasan sin error.home, acerca de) siguen funcionando.Escribir pruebas para mutadores y accesores no solo valida la lógica de transformación, sino que documenta el comportamiento esperado del modelo. Si en el futuro alguien modifica la lógica del slug o del campo name, las pruebas alertarán de inmediato sobre cualquier regresión. ¿Ya estás aplicando TDD en tus proyectos con Laravel? Comparte tu experiencia.
Probar que los datos se transforman correctamente antes de almacenarse es una práctica fundamental en cualquier aplicación robusta con Laravel. Cuando un usuario ingresa información con mayúsculas o espacios, el sistema debe garantizar que esa información se normalice de forma consistente. Aquí se explica cómo escribir pruebas unitarias para validar mutadores y accesores (campos virtuales) en un modelo de Laravel, siguiendo el enfoque de escribir primero el test y luego la implementación.
El primer paso consiste en generar el archivo de prueba antes de crear el modelo. Esto sigue la filosofía de test-driven development (TDD): primero defines el comportamiento esperado y después escribes el código que lo cumple.
Se ejecuta el comando para crear el test unitario [01:00]:
php artisan make:test Models/PostTest --unit genera la clase de prueba en la carpeta correspondiente.--unit indica que se trata de un test unitario, no de un test de integración.Post todavía no existe, pero ya se referencia desde el test.Dentro del test se instancia un nuevo objeto Post y se le asigna un valor al campo name con mayúsculas y minúsculas mezcladas, por ejemplo: "Proyecto en PHP". La aserción verifica que el valor almacenado se haya convertido completamente a minúsculas [02:18].
Al ejecutar las pruebas con php artisan test, el resultado falla en la línea donde se referencia el modelo inexistente. Ese error es precisamente lo esperado: el test guía la implementación.
Un mutador es un método dentro del modelo que altera el valor de un atributo antes de que se almacene. En Laravel, se define con la convención set + nombre del atributo + Attribute [03:30].
Para crear el modelo se ejecuta php artisan make:model Post. Dentro de la clase Post se agrega el método:
php public function setNameAttribute($value) { $this->attributes['name'] = strtolower($value); }
strtolower() convierte toda la cadena a minúsculas.name pasará por esta transformación automáticamente.Este patrón permite garantizar la consistencia de los datos sin depender de que el usuario ingrese la información en un formato específico.
Un accesor genera un campo que no existe físicamente en la base de datos, pero que se calcula a partir de otros atributos. Se le conoce también como campo virtual. En este ejercicio se crea un accesor para el campo slug [05:00].
El test verifica que al asignar "Proyecto en PHP" como nombre, el campo slug devuelva "proyecto-en-php": todo en minúsculas y con los espacios reemplazados por guiones.
La implementación en el modelo utiliza la convención get + nombre del campo + Attribute:
php public function getSlugAttribute() { return str_replace(' ', '-', strtolower($this->name)); }
strtolower() convierte a minúsculas.str_replace() sustituye los espacios por guiones.name ya pasa por el mutador, así que llega en minúsculas; la función refuerza esa transformación para el slug.Para ejecutar un test específico se puede usar un filtro con el nombre del método [06:08]:
bash php artisan test --filter=test_get_slug
El resultado confirma que el campo virtual se genera correctamente.
Al finalizar la implementación, se ejecutan todas las pruebas del proyecto para confirmar que ningún cambio previo se haya roto [06:50]:
set_name, get_slug) pasan sin error.home, acerca de) siguen funcionando.Escribir pruebas para mutadores y accesores no solo valida la lógica de transformación, sino que documenta el comportamiento esperado del modelo. Si en el futuro alguien modifica la lógica del slug o del campo name, las pruebas alertarán de inmediato sobre cualquier regresión. ¿Ya estás aplicando TDD en tus proyectos con Laravel? Comparte tu experiencia.
Mutator Un Mutator transforma un atributo antes de ser almacenado. Accessor Un Accessor transforma un atributo antes de ser presentado.
Esto no es directamente vinculado a Testing, pero en laravel existen algunas funciones helpers por ej la siguiente sirve para crear slugs.
use Illuminate\Support\Str;
//...
Str::slug($this->name)
Desde la version 8.77 de laravel, se puede configurar el setter y getter de un atributo de la siguiente forma:

Hola les comparto como hacer los ejercicios usando la nueva clase de laravel para interactuar con strings de una manera más amigable
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class Post extends Model
{
use HasFactory;
public function setNameAttribute($value)
{
$this->attributes['name'] = Str::lower($value);
}
public function getSlugAttribute()
{
return Str::slug($this->attributes['name']);
}
}
<?php
namespace Tests\Unit\Models;
use App\Models\Post;
use Illuminate\Support\Str;
use PHPUnit\Framework\TestCase;
class PostTest extends TestCase
{
public function test_set_name_in_lowercase()
{
$post = new Post();
$post->name = "Proyecto en PHP";
self::assertEquals(Str::lower($post->name), $post->name);
}
public function test_get_slug()
{
$post = new Post();
$post->name = "Proyecto en PHP";
self::assertEquals(Str::slug($post->name), $post->slug);
}
}
Ref
En laravel 10 los Accessors y Mutators
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class Post extends Model
{
use HasFactory;
/**
* @return Attribute
*/
protected function name(): Attribute {
return Attribute::make(
set: fn ( string $value ) => strtolower($value),
);
}
protected function slug(): Attribute {
return Attribute::make(
get: fn () => Str::slug($this->name),
);
}
}
para laravel 9
use Illuminate\Database\Eloquent\Casts\Attribute;
// Accessor and mutator
public function name(): Attribute
{
return new Attribute(
get: fn ($value) => strtoupper($value),
set: fn ($value) => $value
);
}
Excelente clase...todo fue muy interesante
para los que quieran revisar en el navegador (aunque no es el sentido de los test-automatizados), deben crear en web.php la Route
Podemos empezar a jugar con los test y ver que no basta con uno solo para testear una funcionalidad. Por ejemplo, podríamos tener:
public function test_get_slug()
{
$post = new Post;
$post->name = 'Proyecto en PHP';
$this->assertEquals('proyecto-en-php', $post->slug);
$post = new Post;
$post->name = '¡Platzi es increíble!';
$this->assertEquals('platzi-es-increible', $post->slug);
}
Y el segundo assert daría error ya que nuestro getSlugAttribute() está devolviendo ¡platzi-es-increíble! y no es un slug válido (o al menos no el que esperábamos).
Acá es cuando considero que el Test Driven Develpment se hace más fuerte (seguro tiene muchas cosas más, recién empecé a aprenderlo hace poco) ya que podemos forzar muchas situaciones posibles y cada vez que corramos php artisan test se van a ejecutar esas situaciones para comprobar si algo se rompió sin tener que probar a mano.
La manera de obtener un slug a partir de un string aparece en otro curso del profe Ítalo (ya no recuerdo cuál de tantos en los que lo vi) y es la siguiente:
public function getSlugAttribute(){
return Str::slug($this->name);
}
Usamos la clase Str con su método slug que ya viene preparado para esto. Pero no podemos usar Str así nada más ya que esta es una clase que viene en Illuminate así que hay que colocar en el archivo app/Models/Post.php, en la parte de arriba, lo siguiente:
use Illuminate\Support\Str;
Y ahora el test debería funcionar sin problemas :).
mutators and accessors, encelente contenido para esta clase lo recomiendo.