php

¿Quieres patrocinar?

¿Quieres aparecer aquí? Si quieres patrocinar este blog, ponte en contacto conmigo a través de este formulario

Named constructors en PHP

Como sabrás, cuando desarrollas sobre PHP y creas una clase, esta permite tener, entre otras cosas, constructores. Un constructor te permite por ejemplo inicializar propiedades al instanciar el objeto antes de ser utilizado.

Esto es muy útil, pero a veces las necesidades de negocio nos hacen desarrollar cosas inapropiadas en el __construct(), como añadir lógica que no debería ir allí.

Aunque los named constructors pueden ser utilizados simplemente para que el código sea más legible.

¿En qué consisten? Básicamente en instanciar un objeto a través de uno o varios métodos estáticos. Estos métodos estáticos tendrán la lógica necesaria y acabarán instanciando el objeto. De este modo, evitamos esa lógica en el constructor, y el código quedará más legible y semántico. Para evitar instanciar un objeto con new, declararemos el __construct() privado.

Como todo esto se ve mejor con un ejemplo, imaginemos que nos piden una clase que tiene que devolvernos la latitud y la longitud según unos datos de entrada y pintarlos en un mapa. Estos datos de entrada pueden ser variopintos: podrían ser coordenadas directamente, podrían ser una dirección, un registro en base de datos… con estos 3 ejemplos es suficiente.

¿Cuál es el problema? Que la clase recibe una información de tres maneras diferentes:

  • Coordenadas: Dos valores numéricos
  • Dirección: Una cadena de texto con el nombre de la calle, población, provincia, etc…
  • Base de datos: Un registro de una ubicación que tengamos almacenada en nuestra base de datos

Vamos a obviar en el ejemplo toda la lógica necesaria para hacer los cálculos y las consultas pertinentes para obtener las coordenadas, simplemente vamos a ver cómo podríamos tener 3 named constructors en una clase con estas necesidades de negocio.

<?php declare( strict_types = 1 );

namespace App;

/**
 * Class PrintMarker
 */
class PrintMarker {
    public $latitude;
    public $longitude;

    private function __construct( float $latitude, float $longitude ) {
        $this->latitude  = $latitude;
        $this->longitude = $longitude;
    }

    public static function fromCoordinates( float $latitude, float $longitude ): self {
        return new self( $latitude, $longitude );
    }

    public static function fromAddress( string $address ): self {
        // Aquí realizaríamos por ejemplo una petición a la API de Google Maps, y obtendríamos las coordenadas
        $latitude  = 40.416906;
        $longitude = -3.7056721;

        return new self( $latitude, $longitude );
    }

    public static function fromDatabase( string $client ): self {
        // Aquí realizaríamos una consulta a la base de datos de clientes y obtendríamos las coordenadas
        $latitude  = 40.416906;
        $longitude = -3.7056721;

        return new self( $latitude, $longitude );
    }
}

// Named Constructor para pintar un marcador en un mapa desde unas coordenadas
PrintMarker::fromCoordinates( 40.416906, -3.7056721 );

// Named Constructor para pintar un marcador en un mapa desde una dirección
PrintMarker::fromAddress( "Puerta del Sol, Madrid" );

// Named Constructor para pintar un marcador en un mapa desde un registro en base de datos
PrintMarker::fromDatabase( "km0" );

Como podemos observar, hemos creado 3 named constructors. Los 3 devolverán una instancia de sí mismos (self) y el constructor privado seteará las propiedades latitud y longitud de este objeto.

  • PrintMarker::fromCoordinates – Recibirá directamente unas coordenadas.
  • PrintMarker::fromAddress – Recibirá una dirección y, por ejemplo, podríamos hacer una petición a la API de Google Maps y obtener la latitud y longitud de una dirección dada.
  • PrintMarker::fromDatabase – Realizaremos una consulta a nuestra base de datos y buscaremos, por ejemplo, un cliente sobre el que tendremos almacenados sus coordenadas o su dirección (sobre la cual podremos hacer una petición a una API como en el ejemplo anterior)

De este modo evitamos lógica en el constructor de la clase y cada vez que necesitemos instanciar un objeto para pintar unas coordenadas en un mapa, el código será más semántico ya que en su uso nos da información sobre de dónde vienen los datos.

¿Te ha resultado útil esta información?

Si este post te ha resuelto un problema, invítame a un café o a una cerveza. Con este pequeño gesto me animas a seguir escribiendo.