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


Ordenar tabla relacionada por id descendente

Hola, necesito usar el orderBy('id', 'DESC') en mi controlador pero no se como incorporarlo sin que me de error necesito que la tabla post se ordene por id descendente pero al ser una tabla relacionada no lo consigo

  public function show($id)
    {

        $product = Product::with('Dsa')->with('Post')->find($id);
        
  

        return view('products...

Carga de registros, con datatables

Sera que datatables no aguanta una cantidad de registro mayor a 1 millon. tengo problemas con esa parte las consultas de busquedas se toman mucho tiempo. alguien que mepueda apoyar en esa parte. ...

Controlador laravel modificar ruta path

Actualmente al subir una imagen con mi Controlador la guarda en una capeta llamada image situada en el directorio public y me gustaria que cuando suba una imagen dentro de la carpeta image cree una nueva carpeta dentro de image con el nombre del usuario y esta sea donde guarde las imagenes pero no consigo agregar esa variable al controlador me da error...

  public functi...

Muriendo a causa de SOAP

Soy nuevo en el uso de web service y en rimorsoft, sin embargo no tengo idea de como crear y que necesito para hacer un servidor soap en php alguien podria ayudarme/orientarme con una guia aunque esta sea pequeña sera bien agradecida...

Tengo un archivo XSD para trabajarlo pero repito, no tengo idea ni de como comenzar y trabajarlo, se que tengo que crear un server soap y d...