Qué es la clase Fluent
La clase Fluent es una utilidad genérica que permite tratar un array como si fuera un objeto. Está implementada en Illuminate\Support\Fluent y existe desde las primeras versiones de Laravel, aunque la documentación oficial apenas la menciona.
Internamente gestiona un array como una propiedad y, mediante métodos mágicos (__get / __set / __call), permite leer y escribir como si fueran propiedades.
En Laravel 11 se añadió el helper fluent() y se reforzaron las funcionalidades de la clase Fluent, así que ahora es el momento perfecto para aprovechar esta gran clase.
Crear instancias
Constructor
use Illuminate\Support\ Fluent ;
// Inicialización con un array
$user = new Fluent ([ 'name' => 'Laravel' , 'type' => 'Framework' ]);
echo $user -> name ; // 'Laravel'
echo $user -> type ; // 'Framework'
Método factory make()
use Illuminate\Support\ Fluent ;
$config = Fluent :: make ([
'host' => 'localhost' ,
'port' => 3306 ,
'database' => 'laravel'
]);
echo $config -> host ; // 'localhost'
Helper fluent()
En Laravel 11 se añadió el helper fluent(). Es equivalente a Fluent::make().
$request = fluent ([
'method' => 'POST' ,
'path' => '/api/users' ,
'status' => 201
]);
echo $request -> method ; // 'POST'
Acceso a las propiedades
Lectura y escritura de propiedades dinámicas
$fluent = new Fluent ();
// Escritura
$fluent -> name = 'Laravel' ;
$fluent -> version = 13 ;
// Lectura
echo $fluent -> name ; // 'Laravel'
echo $fluent -> version ; // 13
Encadenamiento de métodos
Gracias al método mágico __call, si llamas a un método inexistente se establece una propiedad. Como el método devuelve $this, puedes encadenarlos.
$config = new Fluent ();
$config
-> host ( 'localhost' )
-> port ( 3306 )
-> database ( 'laravel' )
-> username ( 'root' )
-> password ( 'secret' );
echo $config -> host ; // 'localhost'
echo $config -> password ; // 'secret'
De esta manera puedes definir valores mediante una API fluida (Fluent Interface) en lugar de con un array.
Métodos principales
get() — acceso con notación de puntos
$user = new Fluent ([
'profile' => [
'email' => '[email protected] ' ,
'phone' => '090-xxxx-xxxx'
]
]);
// Obtener valores anidados con notación de puntos
$email = $user -> get ( 'profile.email' ); // '[email protected] '
$phone = $user -> get ( 'profile.phone' ); // '090-xxxx-xxxx'
// Se puede indicar un valor por defecto
$fax = $user -> get ( 'profile.fax' , 'N/A' ); // 'N/A'
set() — asignación con notación de puntos
$fluent = new Fluent ();
$fluent -> set ( 'user.name' , 'Laravel' );
$fluent -> set ( 'user.email' , '[email protected] ' );
print_r ( $fluent -> toArray ());
// Array (
// [user] => Array (
// [name] => Laravel
// [email] => [email protected]
// )
// )
fill() — establecer varias propiedades a la vez
$fluent = new Fluent ([ 'initial' => 'value' ]);
$fluent -> fill ([
'name' => 'Laravel' ,
'version' => 13 ,
'license' => 'MIT'
]);
echo $fluent -> name ; // 'Laravel'
echo $fluent -> version ; // 13
all() — obtener todas las propiedades como array
$fluent = fluent ([
'name' => 'Laravel' ,
'version' => 13 ,
'license' => 'MIT'
]);
// Todas las propiedades
$all = $fluent -> all ();
// ['name' => 'Laravel', 'version' => 13, 'license' => 'MIT']
// Solo algunas
$subset = $fluent -> all ([ 'name' , 'license' ]);
// ['name' => 'Laravel', 'license' => 'MIT']
scope() — convertir un valor anidado en un nuevo Fluent
$config = fluent ([
'database' => [
'host' => 'localhost' ,
'port' => 3306 ,
'name' => 'laravel'
]
]);
$dbConfig = $config -> scope ( 'database' );
// $dbConfig es una nueva instancia de Fluent
echo $dbConfig -> host ; // 'localhost'
echo $dbConfig -> port ; // 3306
Así puedes tratar arrays anidados como otros objetos Fluent independientes.
value() — valor por defecto dinámico con callback
$user = fluent ([ 'role' => 'admin' ]);
// Si la clave existe, devuelve el valor
$role = $user -> value ( 'role' ); // 'admin'
// Si la clave no existe, devuelve el valor por defecto
$status = $user -> value ( 'status' , 'active' );
// 'active'
// Puedes indicar el valor por defecto con un callback
$timestamp = $user -> value ( 'updated_at' , function () {
return now () -> toIso8601String ();
});
Operaciones sobre el array
toArray() — convertir a array
$fluent = fluent ([ 'name' => 'Laravel' , 'version' => 13 ]);
$array = $fluent -> toArray ();
// ['name' => 'Laravel', 'version' => 13]
print_r ( $array );
getAttributes() — acceder directamente a los atributos internos
$fluent = fluent ([ 'a' => 1 , 'b' => 2 ]);
$attributes = $fluent -> getAttributes ();
// ['a' => 1, 'b' => 2]
Interfaz ArrayAccess
Como Fluent implementa ArrayAccess, puedes usarlo como si fuera un array.
$config = new Fluent ();
// Asignar como si fuera un array
$config [ 'host' ] = 'localhost' ;
$config [ 'port' ] = 3306 ;
// Leer como si fuera un array
echo $config [ 'host' ]; // 'localhost'
// Comprobar existencia
if ( isset ( $config [ 'port' ])) {
echo $config [ 'port' ];
}
// Eliminar
unset ( $config [ 'port' ]);
Interfaz IteratorAggregate
Fluent se puede recorrer con foreach.
$settings = fluent ([
'debug' => true ,
'cache' => 'redis' ,
'queue' => 'database'
]);
foreach ( $settings as $key => $value ) {
echo " $key : $value \n " ;
// debug: 1
// cache: redis
// queue: database
}
Manejo de JSON
toJson() — convertir a cadena JSON
$response = fluent ([
'success' => true ,
'data' => [ 'id' => 1 , 'name' => 'User' ]
]);
$json = $response -> toJson ();
// {"success":true,"data":{"id":1,"name":"User"}}
// Se puede usar en respuestas de API
return $json ;
$data = fluent ([
'users' => [
[ 'id' => 1 , 'name' => 'Alice' ],
[ 'id' => 2 , 'name' => 'Bob' ]
]
]);
echo $data -> toPrettyJson ();
// {
// "users": [
// {
// "id": 1,
// "name": "Alice"
// },
// {
// "id": 2,
// "name": "Bob"
// }
// ]
// }
Interfaz JsonSerializable
Como Fluent implementa JsonSerializable, se puede convertir directamente con json_encode().
$fluent = fluent ([ 'status' => 'ok' , 'code' => 200 ]);
$json = json_encode ( $fluent );
// {"status":"ok","code":200}
$data = json_decode ( $json , true );
// ['status' => 'ok', 'code' => 200]
Comprobación de estado
isEmpty() / isNotEmpty()
$empty = new Fluent ();
$filled = fluent ([ 'value' => 1 ]);
$empty -> isEmpty (); // true
$empty -> isNotEmpty (); // false
$filled -> isEmpty (); // false
$filled -> isNotEmpty (); // true
Trait Conditionable
Fluent usa el trait Conditionable, lo que le permite ejecutar procesamiento condicional.
$config = fluent ([ 'env' => 'production' ]);
$config
-> when ( $config -> env === 'production' , function ( $fluent ) {
$fluent -> debug = false ;
$fluent -> cache = 'redis' ;
})
-> when ( $config -> env === 'local' , function ( $fluent ) {
$fluent -> debug = true ;
$fluent -> cache = 'array' ;
});
echo $config -> debug ;
echo $config -> cache ;
Con when() / unless() puedes escribir configuraciones condicionales de forma fluida.
Trait Macroable
Fluent también usa el trait Macroable, así que puedes añadirle métodos de forma dinámica.
use Illuminate\Support\ Fluent ;
// Definidos en boot() de un provider
Fluent :: macro ( 'isProduction' , function () {
/** @var Fluent $this */
return $this -> env === 'production' ;
});
Fluent :: macro ( 'isDevelopment' , function () {
/** @var Fluent $this */
return $this -> env === 'development' ;
});
// Uso
$config = fluent ([ 'env' => 'production' ]);
if ( $config -> isProduction ()) {
// Lógica de producción
}
Casos de uso prácticos
Constructor de respuestas de API
namespace App\Support ;
use Illuminate\Support\ Fluent ;
class ApiResponse
{
public static function success ( $data = null , string $message = 'Success' ) : string
{
return fluent ([
'success' => true ,
'message' => $message ,
'data' => $data ,
'timestamp' => now () -> toIso8601String ()
]) -> toJson ();
}
public static function error ( string $message , int $code = 400 ) : string
{
return fluent ([
'success' => false ,
'message' => $message ,
'code' => $code ,
'timestamp' => now () -> toIso8601String ()
]) -> toJson ();
}
}
// Uso en un controlador
public function store ( Request $request )
{
$user = User :: create ( $request -> validated ());
return response () -> json (
json_decode ( ApiResponse :: success ([ 'id' => $user -> id ]))
);
}
Constructor de configuración
$dbConfig = fluent ()
-> host ( env ( 'DB_HOST' , 'localhost' ))
-> port ( env ( 'DB_PORT' , 3306 ))
-> database ( env ( 'DB_DATABASE' , 'laravel' ))
-> username ( env ( 'DB_USERNAME' , 'root' ))
-> password ( env ( 'DB_PASSWORD' , '' ))
-> charset ( 'utf8mb4' )
-> collation ( 'utf8mb4_unicode_ci' )
-> when ( env ( 'APP_ENV' ) === 'production' , function ( $config ) {
$config -> sslmode ( 'require' );
$config -> sslcert ( env ( 'DB_SSL_CERT' ));
});
// Verifica y aplica la configuración
config ([ 'database.connections.mysql' => $dbConfig -> toArray ()]);
namespace App\Services ;
use Illuminate\Support\ Fluent ;
class SearchFilter
{
public function apply ( array $params ) : Fluent
{
$filter = fluent ()
-> page ( $params [ 'page' ] ?? 1 )
-> perPage ( $params [ 'per_page' ] ?? 15 )
-> sort ( $params [ 'sort' ] ?? 'created_at' )
-> order ( $params [ 'order' ] ?? 'desc' )
-> when ( isset ( $params [ 'search' ]), function ( $f ) use ( $params ) {
$f -> search = $params [ 'search' ];
})
-> when ( isset ( $params [ 'status' ]), function ( $f ) use ( $params ) {
$f -> status = $params [ 'status' ];
});
// Cálculo de la paginación
$filter -> offset = ( $filter -> page - 1 ) * $filter -> perPage ;
return $filter ;
}
}
// Uso
$filter = app ( SearchFilter :: class ) -> apply ( request () -> all ());
$users = User :: query ()
-> when ( $filter -> has ( 'search' ), fn ( $q ) => $q -> search ( $filter -> search ))
-> when ( $filter -> has ( 'status' ), fn ( $q ) => $q -> where ( 'status' , $filter -> status ))
-> orderBy ( $filter -> sort , $filter -> order )
-> offset ( $filter -> offset )
-> limit ( $filter -> perPage )
-> get ();
Combinar un modelo con Fluent
namespace App\Models ;
use Illuminate\Database\Eloquent\ Model ;
use Illuminate\Support\ Fluent ;
class Post extends Model
{
protected $casts = [
'metadata' => 'json'
];
public function getMetadataAttribute ( $value ) : Fluent
{
return new Fluent ( $value ?? []);
}
public function setMetadataAttribute ( $value ) : void
{
if ( $value instanceof Fluent ) {
$this -> attributes [ 'metadata' ] = $value -> toJson ();
} else {
$this -> attributes [ 'metadata' ] = json_encode ( $value );
}
}
}
// Uso
$post = new Post ();
$post -> metadata = fluent ()
-> title ( 'SEO Title' )
-> description ( 'Meta Description' )
-> keywords ([ 'laravel' , 'fluent' , 'tutorial' ])
-> author ( 'Laravel Community' );
$post -> save ();
// Lectura
$post = Post :: first ();
echo $post -> metadata -> title ; // 'SEO Title'
echo $post -> metadata -> author ; // 'Laravel Community'
namespace App\Http\Requests ;
use Illuminate\Foundation\Http\ FormRequest ;
use Illuminate\Support\ Fluent ;
class CreateUserRequest extends FormRequest
{
public function rules () : array
{
return [
'name' => 'required|string' ,
'email' => 'required|email|unique:users' ,
'password' => 'required|min:8|confirmed' ,
'role' => 'in:user,admin' ,
'preferences' => 'json' ,
];
}
// Devuelve los datos validados como Fluent
public function toFluent () : Fluent
{
$preferences = is_string ( $this -> preferences )
? json_decode ( $this -> preferences , true )
: $this -> preferences ;
return fluent ([
'name' => $this -> name ,
'email' => $this -> email ,
'password' => bcrypt ( $this -> password ),
'role' => $this -> role ?? 'user' ,
'preferences' => new Fluent ( $preferences ?? [])
]);
}
}
// Uso en el controlador
public function store ( CreateUserRequest $request )
{
$data = $request -> toFluent ();
$user = User :: create ( $data -> all ());
return response () -> json ([ 'success' => true , 'user_id' => $user -> id ]);
}
Comparativa con otras clases
Fluent vs Array
Función Fluent Array Acceso a propiedades $fluent->name$array['name']Method chaining ✓ Soportado ✗ No Conversión a JSON Método toJson() Función json_encode() Notación de puntos ✓ get('user.name') ✗ Manual Comprobación de estado isEmpty()Función empty() Añadir métodos dinámicos Macroable ✗ No
Fluent vs Modelo
Función Fluent Modelo Persistencia en BD ✗ No ✓ Automática Eficiencia de memoria ✓ Ligero ✗ Pesado Relaciones ✗ No ✓ Soportadas Casts ✗ No ✓ Soportados Validación ✗ No ✓ Soportada API fluida ✓ Sí △ Limitada
Detalles de la implementación interna
class Fluent
{
protected $attributes = [];
// Método mágico: acceso a propiedades inexistentes
public function __get ( $key )
{
return $this -> value ( $key );
}
// Método mágico: asignación de propiedades inexistentes
public function __set ( $key , $value )
{
$this -> offsetSet ( $key , $value );
}
// Método mágico: llamada a métodos inexistentes
// El nombre del método se convierte en la clave de la propiedad
public function __call ( $method , $parameters )
{
$this -> attributes [ $method ] = count ( $parameters ) > 0
? $parameters [ 0 ]
: true ;
return $this ;
}
}
Si no hay una macro registrada, __call toma el nombre del método como clave y almacena el valor en los atributos, devolviendo $this. Esto es lo que hace posible el encadenamiento.
Fluent utiliza los siguientes traits, cada uno de los cuales aporta funcionalidades diferentes:
Conditionable — Procesamiento condicional con when() / unless().
InteractsWithData — Métodos para manipular datos como data().
Macroable — Añadir métodos de forma dinámica.
Próximos pasos
Clase Collection Profundiza en la clase Collection para manejar múltiples elementos.
Trait Conditionable Aprende el trait Conditionable para escribir procesamiento condicional de forma fluida.
Trait Macroable Aprende el trait Macroable para añadir métodos dinámicamente a clases existentes.