Traits
Los traits permiten reutilizar codigo en multiples clases sin usar herencia. Son como "copiar y pegar" inteligente de metodos y propiedades entre clases no relacionadas.
¿Que es un trait?
Un trait es un conjunto de metodos que puedes incluir en cualquier clase. Soluciona el problema de que PHP solo permite herencia simple:
<?php
declare(strict_types=1);
trait Saludable
{
public function saludar(): string
{
return "Hola, soy {$this->nombre}";
}
}
class Persona
{
use Saludable;
private string $nombre;
public function __construct(string $nombre)
{
$this->nombre = $nombre;
}
}
class Robot
{
use Saludable;
private string $nombre;
public function __construct(string $nombre)
{
$this->nombre = $nombre;
}
}
$persona = new Persona('Ana');
$robot = new Robot('R2D2');
echo $persona->saludar(); // Hola, soy Ana
echo $robot->saludar(); // Hola, soy R2D2
Usar multiples traits
Una clase puede usar varios traits, combinando funcionalidades:
<?php
declare(strict_types=1);
trait Timestamps
{
private string $creadoEn;
private string $actualizadoEn;
public function setTimestamps(): void
{
$ahora = date('Y-m-d H:i:s');
$this->creadoEn = $this->creadoEn ?? $ahora;
$this->actualizadoEn = $ahora;
}
public function getCreadoEn(): string
{
return $this->creadoEn;
}
}
trait SoftDelete
{
private ?string $eliminadoEn = null;
public function eliminar(): void
{
$this->eliminadoEn = date('Y-m-d H:i:s');
}
public function estaEliminado(): bool
{
return $this->eliminadoEn !== null;
}
}
class Articulo
{
use Timestamps, SoftDelete;
private string $titulo;
public function __construct(string $titulo)
{
$this->titulo = $titulo;
$this->setTimestamps();
}
}
$articulo = new Articulo('Mi primer articulo');
echo $articulo->getCreadoEn(); // 2024-01-15 10:30:00
$articulo->eliminar();
var_dump($articulo->estaEliminado()); // true
Los traits comparten funcionalidad "horizontal" entre clases no relacionadas. La herencia modela relaciones "es un/una" verticales. Usa traits para capacidades compartidas, no para jerarquias.
Traits con propiedades
<?php
declare(strict_types=1);
trait Identificable
{
private string $id;
public function generarId(): void
{
$this->id = uniqid('', true);
}
public function getId(): string
{
return $this->id;
}
}
class Usuario
{
use Identificable;
private string $nombre;
public function __construct(string $nombre)
{
$this->nombre = $nombre;
$this->generarId();
}
}
class Producto
{
use Identificable;
private string $nombre;
public function __construct(string $nombre)
{
$this->nombre = $nombre;
$this->generarId();
}
}
$usuario = new Usuario('Ana');
$producto = new Producto('Laptop');
echo $usuario->getId(); // 65a1b2c3d4e5f6.12345678
echo $producto->getId(); // 65a1b2c3d4e5f7.87654321
Resolver conflictos entre traits
Si dos traits tienen metodos con el mismo nombre, debes resolver el conflicto explicitamente:
<?php
declare(strict_types=1);
trait A
{
public function saludar(): string
{
return 'Hola desde A';
}
}
trait B
{
public function saludar(): string
{
return 'Hola desde B';
}
}
class MiClase
{
use A, B {
A::saludar insteadof B; // Usar el de A
B::saludar as saludarB; // El de B con otro nombre
}
}
$obj = new MiClase();
echo $obj->saludar(); // Hola desde A
echo $obj->saludarB(); // Hola desde B
Ejemplo practico: Sistema de logging
<?php
declare(strict_types=1);
trait Loggeable
{
private array $logs = [];
protected function log(string $mensaje): void
{
$this->logs[] = [
'fecha' => date('Y-m-d H:i:s'),
'mensaje' => $mensaje,
];
}
public function getLogs(): array
{
return $this->logs;
}
}
class Pedido
{
use Loggeable;
private string $id;
private string $estado = 'pendiente';
public function __construct(string $id)
{
$this->id = $id;
$this->log("Pedido $id creado");
}
public function procesar(): void
{
$this->estado = 'procesando';
$this->log("Pedido {$this->id} en proceso");
}
public function completar(): void
{
$this->estado = 'completado';
$this->log("Pedido {$this->id} completado");
}
}
$pedido = new Pedido('P001');
$pedido->procesar();
$pedido->completar();
print_r($pedido->getLogs());
/*
[
['fecha' => '2024-01-15 10:30:00', 'mensaje' => 'Pedido P001 creado'],
['fecha' => '2024-01-15 10:30:01', 'mensaje' => 'Pedido P001 en proceso'],
['fecha' => '2024-01-15 10:30:02', 'mensaje' => 'Pedido P001 completado'],
]
*/
Ejercicios
Ejercicio 1: Trait Serializable
Crea un trait
Serializable con metodos
toJson(): string y
toArray(): array. Usalo en
clases Usuario y
Producto para serializar
sus propiedades.
Ver solucion
<?php
declare(strict_types=1);
trait Serializable
{
public function toArray(): array
{
return get_object_vars($this);
}
public function toJson(): string
{
return json_encode($this->toArray(), JSON_PRETTY_PRINT);
}
}
class Usuario
{
use Serializable;
public string $nombre;
public string $email;
public function __construct(string $nombre, string $email)
{
$this->nombre = $nombre;
$this->email = $email;
}
}
class Producto
{
use Serializable;
public string $nombre;
public float $precio;
public function __construct(string $nombre, float $precio)
{
$this->nombre = $nombre;
$this->precio = $precio;
}
}
$usuario = new Usuario('Ana', 'ana@email.com');
$producto = new Producto('Laptop', 999.99);
echo $usuario->toJson();
// {"nombre":"Ana","email":"ana@email.com"}
print_r($producto->toArray());
// ['nombre' => 'Laptop', 'precio' => 999.99]
Ejercicio 2: Trait Validable
Crea un trait con metodos
validarEmail(string $email):
bool
y
validarTelefono(string $tel):
bool. Usalo en una clase
Contacto para validar sus
datos.
Ver solucion
<?php
declare(strict_types=1);
trait Validable
{
public function validarEmail(string $email): bool
{
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
public function validarTelefono(string $tel): bool
{
// Solo digitos, minimo 9
$soloDigitos = preg_replace('/[^0-9]/', '', $tel);
return strlen($soloDigitos) >= 9;
}
}
class Contacto
{
use Validable;
private string $nombre;
private string $email;
private string $telefono;
public function __construct(string $nombre, string $email, string $telefono)
{
$this->nombre = $nombre;
if (!$this->validarEmail($email)) {
throw new InvalidArgumentException('Email invalido');
}
$this->email = $email;
if (!$this->validarTelefono($telefono)) {
throw new InvalidArgumentException('Telefono invalido');
}
$this->telefono = $telefono;
}
public function getInfo(): string
{
return "{$this->nombre} - {$this->email} - {$this->telefono}";
}
}
$contacto = new Contacto('Ana Garcia', 'ana@email.com', '+34 666 777 888');
echo $contacto->getInfo(); // Ana Garcia - ana@email.com - +34 666 777 888
Ejercicio 3: Traits combinados
Crea traits Cacheable (con
metodos get/set cache) y
Configurable (con metodos
get/set config). Combinalos en una clase
Aplicacion.
Ver solucion
<?php
declare(strict_types=1);
trait Cacheable
{
private array $cache = [];
public function setCache(string $key, mixed $value): void
{
$this->cache[$key] = $value;
}
public function getCache(string $key): mixed
{
return $this->cache[$key] ?? null;
}
public function hasCache(string $key): bool
{
return isset($this->cache[$key]);
}
}
trait Configurable
{
private array $config = [];
public function setConfig(string $key, mixed $value): void
{
$this->config[$key] = $value;
}
public function getConfig(string $key): mixed
{
return $this->config[$key] ?? null;
}
}
class Aplicacion
{
use Cacheable, Configurable;
private string $nombre;
public function __construct(string $nombre)
{
$this->nombre = $nombre;
}
public function iniciar(): void
{
echo "Iniciando {$this->nombre}...\n";
}
}
$app = new Aplicacion('MiApp');
$app->setConfig('debug', true);
$app->setConfig('version', '1.0.0');
$app->setCache('usuarios', ['Ana', 'Luis']);
echo $app->getConfig('version'); // 1.0.0
print_r($app->getCache('usuarios')); // ['Ana', 'Luis']
¿Has encontrado un error o tienes una sugerencia para mejorar esta leccion?
Escribenos¿Te está gustando el curso?
Tenemos cursos premium con proyectos reales y soporte personalizado.
Descubrir cursos premium