Usando clases de entidad

Higgs apoya las clases de Entidad como ciudadano de primera clase, manteniendo su uso es completamente opcional. Se utilizan comúnmente como parte del patrón Repositorio, pero pueden ser usado directamente con el Modelo si eso se adapta mejor a sus necesidades.

Uso de entidad

En esencia, una clase Entidad es simplemente una clase que representa una única fila de la base de datos. Tiene propiedades de clase. para representar las columnas de la base de datos y proporciona métodos adicionales para implementar la lógica empresarial para esa fila.

Nota

For ease of understanding, the explanation here is based on the case of utilizando una base de datos. Sin embargo, Entidad también se puede utilizar para datos que no vienen de una base de datos.

La característica principal, sin embargo, es que no sabe nada sobre cómo persistir. Eso es responsabilidad del modelo o de la clase de repositorio. De esa manera, si algo cambia sobre cómo necesita guardar el objeto, no es necesario cambiar la forma en que se utiliza ese objeto en toda la aplicación.

Esto hace posible use archivos JSON o XML para almacenar los objetos durante una etapa de creación rápida de prototipos y luego cambie fácilmente a un base de datos cuando hayas demostrado que el concepto funciona.

Veamos una entidad de usuario muy simple y cómo trabajaríamos con ella para ayudar a aclarar las cosas.

Supongamos que tiene una tabla de base de datos llamada usuarios que tiene el siguiente esquema:

identificación - número entero
nombre de usuario - cadena
correo electrónico - cadena
contraseña - cadena
creado_en - fecha y hora

Importante

attributes is a reserved word for internal use. If you use it as a column name, the Entity does not work correctly.

Crear la clase de entidad

Ahora crea una nueva clase de entidad. Dado que no existe una ubicación predeterminada para almacenar estas clases y no encaja Con la estructura de directorios existente, cree un nuevo directorio en app/Entities. Crea el Entidad misma en app/Entities/User.php.

<?php

namespace App\Entities;

use Higgs\Entity\Entity;

class User extends Entity
{
    // ...
}

En su forma más simple, esto es todo lo que necesita hacer, aunque lo haremos más útil en un minuto.

Crear el modelo

Primero crea el modelo en app/Models/UserModel.php para que podamos interactuar con él:

<?php

namespace App\Models;

use Higgs\Model;

class UserModel extends Model
{
    protected $table         = 'users';
    protected $allowedFields = [
        'username', 'email', 'password',
    ];
    protected $returnType    = \App\Entities\User::class;
    protected $useTimestamps = true;
}

El modelo utiliza la tabla usuarios en la base de datos para todas sus actividades. Hemos configurado la propiedad $allowedFields para incluir todos los campos que queremos que cambien fuera de las clases. Los campos id, created_at y updated_at son manejados automáticamente por la clase o la base de datos, por lo que no queremos cambiarlos. Finalmente, hemos configurado nuestra Entidad clase como $returnType. Esto garantiza que todos los métodos del modelo que devuelven filas de la base de datos devolverán instancias de nuestra clase de entidad de usuario en lugar de un objeto o matriz como de costumbre.

Trabajar con la clase de entidad

Ahora que todas las piezas están en su lugar, trabajará con la clase Entidad como lo haría con cualquier otra clase:

<?php

$user = $userModel->find($id);

// Display
echo $user->username;
echo $user->email;

// Updating
unset($user->username);

if (! isset($user->username)) {
    $user->username = 'something new';
}

$userModel->save($user);

// Create
$user           = new \App\Entities\User();
$user->username = 'foo';
$user->email    = 'foo@example.com';
$userModel->save($user);

Quizás hayas notado que la clase Usuario no ha establecido ninguna propiedad para las columnas, pero aún puedes acceder a ellos como si fueran bienes públicos. La clase base, Higgs\Entity\Entity, se encarga de esto por usted, como además de brindar la capacidad de verificar las propiedades con isset() o unset() la propiedad y realizar un seguimiento de qué columnas han cambiado desde que se creó el objeto o se extrajo de la base de datos.

Nota

The Entity class stores the data in the class property $attributes internally.

Cuando el usuario pasa al método save() del modelo, automáticamente se encarga de leer las propiedades. y guardar cualquier cambio en las columnas enumeradas en la propiedad $allowedFields del modelo. También sabe si crear una nueva fila o actualizar una existente.

Nota

When we are making a call to the insert() all the values from Entity are passed to the method, but when we llame a update(), luego solo se pasan los valores que han cambiado.

Propiedades de relleno rápidamente

La clase Entity también proporciona un método, fill() que le permite insertar una matriz de pares clave/valor en la clase. y completar las propiedades de la clase. Cualquier propiedad de la matriz se establecerá en la Entidad. Sin embargo, al guardar mediante el modelo, sólo los campos en $allowedFields realmente se guardarán en la base de datos, para que pueda almacenar datos adicionales en sus entidades sin preocuparse mucho de que los campos perdidos se guarden incorrectamente.

<?php

$data = $this->request->getPost();

$user = new \App\Entities\User();
$user->fill($data);
$userModel->save($user);

También puede pasar los datos en el constructor y los datos se pasarán a través del método fill() durante la creación de instancias.

<?php

$data = $this->request->getPost();

$user = new \App\Entities\User($data);
$userModel->save($user);

Propiedades de acceso masivo

La clase Entity tiene dos métodos para extraer todas las propiedades disponibles en una matriz: toArray() y toRawArray(). El uso de la versión sin formato evitará los métodos y lanzamientos mágicos de «captadores». Ambos métodos pueden tomar un primer parámetro booleano. para especificar si los valores devueltos deben filtrarse por aquellos que han cambiado, y un parámetro final booleano para haga que el método sea recursivo, en el caso de entidades anidadas.

Manejo de la lógica empresarial

Si bien los ejemplos anteriores son convenientes, no ayudan a aplicar ninguna lógica empresarial. La clase de entidad base implementa algunos métodos inteligentes __get() y __set() que buscarán métodos especiales y los usarán en lugar de usar los atributos directamente, lo que le permite aplicar cualquier lógica empresarial o conversión de datos que necesite.

Aquí hay una entidad de usuario actualizada para proporcionar algunos ejemplos de cómo podría usarse:

<?php

namespace App\Entities;

use Higgs\Entity\Entity;
use Higgs\I18n\Time;

class User extends Entity
{
    public function setPassword(string $pass)
    {
        $this->attributes['password'] = password_hash($pass, PASSWORD_BCRYPT);

        return $this;
    }

    public function setCreatedAt(string $dateString)
    {
        $this->attributes['created_at'] = new Time($dateString, 'UTC');

        return $this;
    }

    public function getCreatedAt(string $format = 'Y-m-d H:i:s')
    {
        // Convert to Higgs\I18n\Time object
        $this->attributes['created_at'] = $this->mutateDate($this->attributes['created_at']);

        $timezone = $this->timezone ?? app_timezone();

        $this->attributes['created_at']->setTimezone($timezone);

        return $this->attributes['created_at']->format($format);
    }
}

Lo primero que hay que notar es el nombre de los métodos que hemos agregado. Para cada uno, la clase espera el caso_serpiente. nombre de la columna que se convertirá a PascalCase y con el prefijo set o get. Estos métodos luego se llamará automáticamente cada vez que configure o recupere la propiedad de clase usando la sintaxis directa (es decir, $usuario->correo electrónico). No es necesario que los métodos sean públicos a menos que desee acceder a ellos desde otras clases. Por ejemplo, created_at Se accederá a la propiedad de clase a través de los métodos setCreatedAt() y getCreatedAt().

Nota

This only works when trying to access the properties from outside of the class. Any methods internal to the La clase debe llamar a los métodos setX() y getX() directamente.

En el método setPassword() nos aseguramos de que la contraseña siempre esté codificada.

En setCreatedAt() convertimos la cadena que recibimos del modelo en un objeto DateTime, asegurando que nuestra zona horaria es UTC para que podamos convertir fácilmente la zona horaria actual del espectador. En getCreatedAt(), convierte la hora a una cadena formateada en la zona horaria actual de la aplicación.

Si bien son bastante simples, estos ejemplos muestran que el uso de clases de entidad puede proporcionar una forma muy flexible de hacer cumplir lógica de negocios y crear objetos que sean agradables de usar.

<?php

// Auto-hash the password - both do the same thing
$user->password = 'my great password';
$user->setPassword('my great password');

Getter/Setter especial

Nuevo en la versión 4.4.0.

Por ejemplo, si la clase principal de su entidad ya tiene un método getParent() definido, y su Entidad también tiene una columna llamada padre, cuando intenta agregar lógica de negocios al método getParent() en su clase Entidad, el método es Ya definido.

En tal caso, puede utilizar el captador/definidor especial. En lugar de getX()/setX(), establezca _getX()/_setX().

En el ejemplo anterior, si su entidad tiene el método _getParent(), el método se usará cuando obtenga $entity->parent y el método _setParent() se utilizará cuando establezca $entidad->padre.

Mapeo de datos

En muchos puntos de tu carrera, te encontrarás con situaciones en las que el uso de una aplicación ha cambiado y la Los nombres de columnas originales en la base de datos ya no tienen sentido. O descubre que su estilo de codificación prefiere camelCase propiedades de clase, pero el esquema de su base de datos requería nombres de casos de serpientes. Estas situaciones se pueden manejar fácilmente con las funciones de mapeo de datos de la clase Entidad.

Como ejemplo, imagine que tiene la entidad de usuario simplificada que se utiliza en toda su aplicación:

<?php

namespace App\Entities;

use Higgs\Entity\Entity;

class User extends Entity
{
    protected $attributes = [
        'id'         => null,
        'name'       => null, // Represents a username
        'email'      => null,
        'password'   => null,
        'created_at' => null,
        'updated_at' => null,
    ];
}

Tu jefe se acerca a ti y te dice que ya nadie usa nombres de usuario, por lo que estás cambiando para usar solo correos electrónicos para iniciar sesión. Pero sí quieren personalizar un poco la aplicación, por lo que quieren que cambies el campo nombre para representar el nombre de un usuario. nombre completo ahora, no su nombre de usuario como lo hace actualmente. Para mantener las cosas ordenadas y garantizar que las cosas sigan teniendo sentido. en la base de datos, organiza una migración para cambiar el nombre del campo nombre a nombre_completo para mayor claridad.

Haciendo caso omiso de lo artificial que es este ejemplo, ahora tenemos dos opciones sobre cómo arreglar la clase Usuario. Podríamos modificar la clase. propiedad de $name a $full_name, pero eso requeriría cambios en toda la aplicación. En cambio, podemos simplemente asigne la columna full_name en la base de datos a la propiedad $name y termine con los cambios de entidad:

<?php

namespace App\Entities;

use Higgs\Entity\Entity;

class User extends Entity
{
    protected $attributes = [
        'id'         => null,
        'full_name'  => null, // In the $attributes, the key is the db column name
        'email'      => null,
        'password'   => null,
        'created_at' => null,
        'updated_at' => null,
    ];

    protected $datamap = [
        // property_name => db_column_name
        'name' => 'full_name',
    ];
}

Al agregar el nuevo nombre de nuestra base de datos a la matriz $datamap, podemos decirle a la clase qué propiedad de clase tiene la columna de la base de datos. debe ser accesible a través de. La clave de la matriz es la propiedad de clase a la que asignarla, donde el valor de la matriz es el nombre de la columna en la base de datos.

En este ejemplo, cuando el modelo establece el campo full_name en la clase Usuario, en realidad asigna ese valor al class” $name, por lo que se puede configurar y recuperar a través de $user->name. El valor seguirá siendo accesible. a través del $user->full_name original, también, ya que esto es necesario para que el modelo recupere los datos y los guarde. a la base de datos. Sin embargo, unset() y isset() solo funcionan en la propiedad asignada, $user->name, no en el nombre de la columna de la base de datos. $usuario->nombre_completo.

Nota

When you use Data Mapping, you must define set*() and get*() method para el nombre de la columna de la base de datos. En este ejemplo, debe definir setFullName() y getFullName().

Mutadores

Mutadores de fecha

De forma predeterminada, la clase Entidad convertirá los campos denominados created_at, updated_at o deleted_at en Tiempo instancias cada vez que se configuran o recuperan. La clase Time proporciona una gran cantidad de métodos útiles de forma inmutable y localizada.

Puede definir qué propiedades se convierten automáticamente agregando el nombre a la propiedad $dates:

<?php

namespace App\Entities;

use Higgs\Entity\Entity;

class User extends Entity
{
    protected $dates = ['created_at', 'updated_at', 'deleted_at'];
}

Ahora, cuando se establezca cualquiera de esas propiedades, se convertirán en una instancia de tiempo, utilizando el comando de la aplicación. zona horaria actual, como se establece en app/Config/App.php:

<?php

$user = new \App\Entities\User();

// Converted to Time instance
$user->created_at = 'April 15, 2017 10:30:00';

// Can now use any Time methods:
echo $user->created_at->humanize();
echo $user->created_at->setTimezone('Europe/London')->toDateString();

Fundición de propiedad

Puede especificar que las propiedades de su entidad se deben convertir a tipos de datos comunes con la propiedad $casts. Esta opción debe ser una matriz donde la clave es el nombre de la propiedad de clase y el valor es el tipo de datos. debe ser lanzado.

La conversión de propiedades afecta tanto a la lectura (obtener) como a la escritura (establecer), pero algunos tipos afectan solo leer (obtener).

Fundición tipo escalar

Las propiedades se pueden convertir a cualquiera de los siguientes tipos de datos:

entero, flotante, doble, cadena, booleano, objeto, matriz, fechahora, ** marca de tiempo**, uri y int-bool. Agregue un signo de interrogación al principio del tipo para marcar la propiedad como anulable, es decir, ?string, ?integer.

Nota

int-bool can be used since v7.3.0.

Por ejemplo, si tuviera una entidad de Usuario con una propiedad is_banned, puede convertirla en booleana:

<?php

namespace App\Entities;

use Higgs\Entity\Entity;

class User extends Entity
{
    protected $casts = [
        'is_banned'          => 'boolean',
        'is_banned_nullable' => '?boolean',
    ];
}

Transmisión de matriz/Json

La conversión de matrices/Json es especialmente útil con campos que almacenan matrices serializadas o json en ellos. Cuando se emite como:

  • una matriz, se deserializarán automáticamente,

  • un json, se establecerán automáticamente como un valor de json_decode($value, false),

  • un json-array, se establecerán automáticamente como un valor de json_decode($value, true),

cuando establece el valor de la propiedad. A diferencia del resto de los tipos de datos a los que puede convertir propiedades,:

  • array el tipo de conversión se serializará,

  • json y json-array cast usarán la función json_encode en

el valor cada vez que se establece la propiedad:

<?php

namespace App\Entities;

use Higgs\Entity\Entity;

class User extends Entity
{
    protected $casts = [
        'options'        => 'array',
        'options_object' => 'json',
        'options_array'  => 'json-array',
    ];
}
<?php

$user    = $userModel->find(15);
$options = $user->options;

$options['foo'] = 'bar';

$user->options = $options;
$userModel->save($user);

Transmisión CSV

Si sabe que tiene una matriz plana de valores simples, codifíquelos como una cadena serializada o JSON. puede ser más complejo que la estructura original. La conversión como valores separados por comas (CSV) es una alternativa más simple dará como resultado una cadena que utiliza menos espacio y se lee más fácilmente por humanos:

<?php

namespace App\Entities;

use Higgs\Entity\Entity;

class Widget extends Entity
{
    protected $casts = [
        'colors' => 'csv',
    ];
}

Almacenado en la base de datos como «rojo, amarillo, verde»:

<?php

$widget->colors = ['red', 'yellow', 'green'];

Nota

Casting as CSV uses PHP’s internal implode and explode methods and assumes all values are string-safe and free of commas. For more complex data casts try array or json.

Fundición personalizada

Puede definir sus propios tipos de conversión para obtener y configurar datos.

Al principio necesitas crear una clase de controlador para tu tipo. Digamos que la clase estará ubicada en el directorio app/Entities/Cast:

<?php

namespace App\Entities\Cast;

use Higgs\Entity\Cast\BaseCast;

// The class must inherit the Higgs\Entity\Cast\BaseCast class
class CastBase64 extends BaseCast
{
    public static function get($value, array $params = [])
    {
        return base64_decode($value, true);
    }

    public static function set($value, array $params = [])
    {
        return base64_encode($value);
    }
}

Ahora necesitas registrarlo:

<?php

namespace App\Entities;

use Higgs\Entity\Entity;

class MyEntity extends Entity
{
    // Specify the type for the field
    protected $casts = [
        'key' => 'base64',
    ];

    // Bind the type to the handler
    protected $castHandlers = [
        'base64' => Cast\CastBase64::class,
    ];
}

// ...

$entity->key = 'test'; // dGVzdA==
echo $entity->key;     // test

Si no necesita cambiar valores al obtener o establecer un valor. Entonces simplemente no implementes el método apropiado:

<?php

namespace App\Entities\Cast;

use Higgs\Entity\Cast\BaseCast;

class CastBase64 extends BaseCast
{
    public static function get($value, array $params = [])
    {
        return base64_decode($value, true);
    }
}

Parámetros

En algunos casos, un tipo no es suficiente. En esta situación, puede utilizar parámetros adicionales. Los parámetros adicionales se indican entre corchetes y se enumeran con una coma. como tipo[param1, param2].

<?php

namespace App\Entities;

use Higgs\Entity\Entity;

class MyEntity extends Entity
{
    // Define a type with parameters
    protected $casts = [
        'some_attribute' => 'class[App\SomeClass, param2, param3]',
    ];

    // Bind the type to the handler
    protected $castHandlers = [
        'class' => 'SomeHandler',
    ];
}
<?php

namespace App\Entities\Cast;

use Higgs\Entity\Cast\BaseCast;

class SomeHandler extends BaseCast
{
    public static function get($value, array $params = [])
    {
        var_dump($params);
        /*
         * Output:
         * array(3) {
         *   [0]=>
         *   string(13) "App\SomeClass"
         *   [1]=>
         *   string(6) "param2"
         *   [2]=>
         *   string(6) "param3"
         * }
         */
    }
}

Nota

If the casting type is marked as nullable like ?bool and the passed value is not null, then the parameter with el valor nullable se pasará al controlador de tipo de conversión. Si el tipo de conversión tiene parámetros predefinidos, se agregará «nullable» al final de la lista.

Comprobación de atributos modificados

Puede verificar si un atributo de Entidad ha cambiado desde que fue creado. El único parámetro es el nombre del atributo a comprobar:

<?php

$user = new \App\Entities\User();
$user->hasChanged('name'); // false

$user->name = 'Fred';
$user->hasChanged('name'); // true

O para verificar toda la entidad en busca de valores modificados, omita el parámetro:

<?php

$user->hasChanged(); // true