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


Spatie/Laravel Permission ¿Cómo agregar campos en la tabla "roles"?

Hola, mi consulta es la siguiente:

Relacioné la tabla "roles" con mi tabla** "empresa**", es decir, en la tabla "roles", tengo el campo "id_empresa", en la vista, están los campos necesarios y el select de la empresa pero al querer guardar los datos, me arroja el siguiente error: ** "The given role or permission should use guard web instead of Empresas. "** No ...

SOFTWARE PARA CONSULTORIOS MEDICOS

Hola Amigos. soy nuevo en el grupo.. me gustaria saber si alguien tiene o tuvo algun proyecto basado en laravel, para consultorios medicos? ...

sincronización de componentes hermanos

un gran saludo a la comunidad rimorsorf... espero de su valiosa ayuda para mi problema que es el siguiente:

tengo dos componentes en el mismo nivel, el primero crea una tabla con una lista de procesos por tramitar y el segundo componente lista en una tabla los procesos que se han asignado para tramitar; cuando en la primera tabla se asigna el proceso este debe desaparecer de...

plantilla blade y vuejs2

buenas noches comunidad, por casualidad alguien sabrá si se puede integrar la plantilla de laravel blade en vuejs2 component??, gracias dee antemano...