Trait en PHP

lección sobre trait en php

Ya vimos en Rimorsoft un artículo completo sobre la herencia, de esa manera extender nuestras clases y compartir funcionalidad. El problema básico con extender extends clases, es que solo puedes extender de una, solo una. Créeme, esto limita y mucho.


Esto es Traits en PHP

Ejemplo real

<?php

namespace App\Entities;

use App\Classes\Permission;
use Illuminate\Notifications\Notifiable;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable, Permission;

    // ...
}

Esta es la entidad User que viene por defecto al instalar Laravel:

  • Permission: Es una clase personal, es un Traits.
  • Notifiable: Es un Traits de Laravel
  • Authenticatable: Es un alias asignado a la clase Illuminate\Foundation\Auth\User, y la entidad User la usa para heredar.

¿Este ejemplo que dice realmente? muestra que extendemos de una (1) clase y usamos dos (2) traits.

  • Al extender usamos extends Authenticatable
  • Los traits tienen efecto al colocar dentro de la clase la línea use Notifiable, Permission;

Es una gran ventaja, un Trait es un código que reutilizamos y podemos incluirlo en cualquier clase... Piensa en esto "código reutilizable"

Definición: Con los traits es posible que nuestras clases de PHP hereden métodos y propiedades. Es la solución directa al problema de herencia única.

Estructura

<?php

trait TagTrait
{

    // ...

}

Un trait se parece mucho a una clase pero aquí usamos la palabra clave trait. No es obligatorio pero podemos seguir la convención de colocar el sufijo Trait en el nombre, ejemplo TagTrait.

Notas

  • En un Trait no puedes usar extends ni implements, tampoco puedes crear un objeto a partir de él (no puedes instanciar un trait).
  • Un Trait no reemplaza a una Clase, el único propósito es dar apoyo...
<?php

trait TagTrait
{

    /**
     * @param $tags string or array
     */
    public function tags($tags)
    {
        // Code here
    }

    /**
     * @param $tag string
     */
    private function addTag($tag)
    {
        // Code here
    }

    // ...

}

Con este simple Trait aislamos toda la lógica sobre las etiquetas Tag y es la mejor manera de hacerlo, porque así tendremos un único código y el resto de nuestro sistema tendrá la función de etiquetas. De esta manera los usuarios, productos, artículos, etc tendrán la opción de ser segmentados por etiquetas y cada clase solo deberá incluir el archivo TagTrait.

Es decir, puede la clase User extender de una clase muy relacionada con ella, lo mismo Product, Post y así, pero lo interesante aquí es que pueden todas incluir el Trait TagTrait.

Recuerda: El sufijo Trait no es obligatorio, es una convención y aquí es muy apropiado usarlo en la explicación

Usa traits para extender y mejorar la funcionalidad de tus clases, esto es todo... Extensión de código, aislar código útil.

Mi primer Trait

Veamos ejemplos: Archivo public/index.php

<?php

require '../vendor/autoload.php';

use App\User;

$user = new User;

echo $user->greet();

Archivo app/User.php

<?php

namespace App;

use App\Traits\Test;

/**
* this class represents a user
*/
class User
{
    
    use Test;
    
}

Archivo app/Traits/Test.php

<?php

namespace App\Traits;

/**
* this trait represents a Test
*/
trait Test 
{

    function greet()
    {
        return "Mi primer Trait";
    }

}

La lógica de greet() está en el archivo Trait llamado Test y User lo puede usar porque incluye el Trait.

Prioridad

Seguramente te has preguntado ¿Qué pasaría si extendemos de una clase, usamos un trait y ambos tienen un método con el mismo nombre?

Imagina: Mi clase User extiende, es decir, hereda de la clase Person y además incluye el trait TagTrait, entonces, ¿Qué pasa si Person y TagTrait tienen un método con el mismo nombre?

La respuesta es: El método del trait tiene prioridad sobre el método de la clase padre. En otras palabras, con los traits podemos reemplazar la funcionalidad heredada.

Otra pregunta ¿Qué pasa si mi clase padre, el Trait y mi clase actual tienen un método con el mismo nombre?, ejemplo:

  • Clase User método getName()
  • Clase padre Person método getName()
  • Trait TagTrait método getName()

Gana la clase; Se revisa de esta manera: (1) clase, (2) trait, (3) clase padre.

Ejemplo: Archivo public/index.php

<?php

require '../vendor/autoload.php';

use App\User;

$user = new User;

echo $user->greet();

Aquí creamos un objeto y usamos el método greet()

Archivo app/User.php

<?php

namespace App;

use App\Traits\Test;
/**
* this class represents a user
*/
class User extends Person
{
    
    use Test;

    function greet()
    {
        return "Hola, soy la clase User";
    }
    
}

Nuestra clase User tiene el método greet() y al mismo tiempo extendemos o heredamos de Person y ademas hacemos uso del Trait Test.php

Archivo app/Person.php

<?php

namespace App;

/**
* this class represents a person
*/
class Person 
{

    function greet()
    {
        return "Hola, soy la clase padre";
    }

}

Archivo app/Traits/Test.php

<?php

namespace App\Traits;

/**
* this trait represents a Test
*/
trait Test 
{

    function greet()
    {
        return "Hola, soy un Trait";
    }

}

Se ve claramente que nuestra clase User, nuestra clase padre Person y nuestro trait Test usan el método greet() ¿Quien gana?

¿Quien gana ahí?, vamos a simplificarlo de la siguiente manera:

  • El método de un Trait sobreescribe un método heredado.
  • Un métodos propio (de mi clase actual) sobreescribe los métodos de un trait.

En otras palabras, los métodos propios tienen la última palabra

Conflictos, cuando dos (2) o más Trait tienen métodos con el mismo nombre

Cuando esto sucede te saldrá un error Fatal

Fatal error: Trait method greet has not been applied, because there are collisions with other trait methods on ... in ... on line 10

Archivo public/index.php

<?php

require '../vendor/autoload.php';

use App\User;

$user = new User;

echo $user->greetOne(); 
echo '<br>';
echo $user->greetTwo(); 

Archivo app/User.php

<?php

namespace App;

use App\Traits\TestOne;
use App\Traits\TestTwo;

/**
* this class represents a user
*/
class User
{
    
    use TestOne, TestTwo {
        TestOne::greet insteadof TestTwo; // Usa el TestOne en lugar de TestTwo (esto resuelve el conflicto)
        TestOne::greet as greetOne; // Usando alias
        TestTwo::greet as greetTwo; // Usando alias
    }
    
}

Aquí la clave está en la palabra insteadof, básicamente traduce a "Usa esta clase en lugar de esta otra"

Archivo app/Traits/TestOne.php

<?php

namespace App\Traits;

/**
* this trait represents a Test
*/
trait TestOne 
{

    function greet()
    {
        return "Hola, soy un TraitOne";
    }

}

Archivo app/Traits/TestTwo.php

<?php

namespace App\Traits;

/**
* this trait represents a Test
*/
trait TestTwo 
{

    function greet()
    {
        return "Hola, soy un TraitTwo";
    }

}

¿Qué podemos hacer? conclusión

Los Traits inyectan flexibilidad, esta es la palabra clave

  • Es posible usar varios traits en una clase.
  • Un Trait puede tener dentro otro trait (Esto es frecuente cuando la aplicación crece)

Repositorio: https://github.com/rimorsoft/Serie-de-PHP/tree/master/trait

Adquiere en preventa y aprovecha un gran descuento TDD en Laravel

Comparte en

Creado por: Venezuela Italo Morales

Profesor de #Laravel y #PHP en Rimorsoft Online

Más información


El auth de laravel trabaja con $_SESSION?.

el auth de laravel trabaja basado en tokens o con el objeto session de php ...

Rutas de Laravel no funcionan

Buenas a todos, tengo un problema, ultimamente, cada vez que creo un nuevo proyecto de Laravel las rutas de este no funcionan. Por ejemplo: si creo el proyecto, éste se crea, pero al agregarle el sistema de autenticación: "auth" se generan tanto las rutas en el archivo web.php y las vistas. pero si intento acceder a la sección de registro o inicio de sesión me aparece el e...

form request ajax laravel (Mostrar error de validación de formulario con ajax)

Hola, tengo una consulta por hacer. Quisiera conocer sus opiniones, sobre como mostrar los errores de validación con ajax cuando utlizo la clase Form Request. Debo decir que el mensaje de registrado con éxito si lo muestra, sin embargo los mensajes de error no los he podido mostrar. Gracias

//FORM REQUEST


public function rules()
    {
        return [
     ...

Select Dinámico

Hola buenas a todos estoy trabajando en un sistema en el cuál, en un formulario tengo un select para seleccionar datos de una tabla, y en la vista del formulario me funciona ya que me carga todos los datos que tengo almacenados en dicha tabla; pero cuando selecciono una de las opciones, sea cual séa la que seleccione, siempre se guarda el primer registro de la tabla... Es com...