entre Desarrolladores

Recibe ayuda de expertos

Registrate y pregunta

Es gratis y fácil

Recibe respuestas

Respuestas, votos y comentarios

Vota y selecciona respuestas

Recibe puntos, vota y da la solución

Pregunta

2votos

Saber de qué clase es el objeto que llama al método

Supongamos que tengo una interface:

<?php

interface ActiveRecord
{
    public function save();
    public function update();
    public function delete();
    public function find($id);
    public function all();
}

Y una clase que lo implementa:

<?php

abstract class ActiveRecordImpl implements ActiveRecord
{

    public function save()
    {

    }

    public function update()
    {

    }

    public function delete()
    {

    }

    public function find($id)
    {

    }

    public function all()
    {

    }

}

Además dos clases: Customer y User (con sus get y set) que heredan de Active Record.

Bien, supongamos que el método save() de ActiveRecord necesita guardar un objeto en la BD de la siguiente manera:

<?php

public function save()
{
    $caller = REFERENCIA_AL_OBJETO_LLAMANTE;
    $callerClass = getClass($caller); // 'Customer' o 'User'

    // obtiene el servicio dinámicamente, de acuerdo al objeto llamante
    $service = ServiceFactory::create($callerClass);

    // le envía el objeto llamante al servicio Customer para que
    // lo guarde en la BD
    $service->save($caller);
}

El método save() obtiene una referencia al método que lo llamó (que puede ser un objeto tipo Customer o User), y obtiene la clase del objeto llamante para poder crear su servicio respectivo. Posteriormente, el servicio guarda el objeto por medio del método save()

La clase ServiceFactory solo crea y devuelve un servicio para el tipo de clase indicado:

<?php

public static function create($class)
{
    switch ($class) {
        case 'Customer':
            return new CustomerService();

        case 'User':
            return new UserService();
    }
}

Y los servicios de 'Customer' y 'User' hacen uso de sus DAOs respectivos:

<?php

class CustomerService
{
    public function save(Customer $customer)
    {
        new CustomerDAO()->save($customer);
    }
}

<?php

class UserService
{
    public function create(User $user)
    {
        new UserDAO()->save($user);
    }
}

Ahora, para guardar un objeto 'Customer' y 'User' se haría lo siguiente:

<?php

$customer = new Customer();
$customer->username = $username;
$customer->email = $email;
$customer->password = $password;
$customer->dni = $dni;
$customer->address = $address;

$user = new User();
$user->username = $username;
$user->email = $email;
$user->password = $password;

¿Es ésto posible? Si es es así, ¿cómo podría obtener la referencia del objeto llamante?

1 Respuesta

3votos

Leonardo-Tadei Puntos227320

Y dónde está el "objeto llamante" o "llamador" en este código???

Para el caso:

$user = new User();
$user->username = $username;
$user->email = $email;
$user->password = $password;

asumiendo que luego harás

$user->save();

para guardarlo, el que ejecuta los métodos, ya seam propios o por herencia es $user y no otro objeto que lo llama.

bastaría con hacer:

$caller = get_class($this);

Igual, me pierdo un poco porque en tu código ni save() ni create() aparecen como métodos de ninguna clase.. incluso en create() esás esperando que la clase sea un string con el nombre, cuando podrías pasar directamente un Objeto y preguntarle de qué clase es.

Tampoco me queda claro el momento de instanciación de los servicios, es decir, quiés en el que llama a create(), cuándo lo llama, y si el factory es parte del DAO por composición o si es solo usado por un método de las clases concretas.

Si la llamada fuese a un método de otro objeto que no es parte de una composición o agregación, podrías simplemente hacer la llamada pasando al propio objeto como mensaje, porque tu código:

public function save() {
    $caller = REFERENCIA_AL_OBJETO_LLAMANTE;
    $callerClass = getClass($caller); // 'Customer' o 'User'
    // obtiene el servicio dinámicamente, de acuerdo al objeto llamante
    $service = ServiceFactory::create($callerClass);
    // le envía el objeto llamante al servicio Customer para que
    // lo guarde en la BD
    $service->save($caller);
}

lo que está enviado a save() es un string con un nombre de clase del que no podrás acceder a los atributos del objeto instanciado.

Tal vez sea el caso de que estés queriendo escribir un DAO sin haber usado antes 4 o 5... nunca es buena estrategia de programación hacer abstracciones de alto nivel sin antes haberse ensuciado mucho las manos con código concreto.

Respecto de la persistencia, un profesor de un posgrado de Ingeniería del Software que hice decía que DAO era la sigla de "Desde Ahora Olvídenlos". ;-)

Comparado con un mapeador Objeto/Relacional que haga persistencia no invasiva y por alcance, los DAO son solo un conjunto de malas prácticas y violaciones al paradigma de la POO :-( Puede que parezca que se usen mucho, como el goto para citar un ejemplo famoso de la generación anterior, pero eso no significa que haya que usarlos.

Si tenés un rato probá este ORM para PHP bajo Licencia GPL: verás que cuando escribas las Clases que soporten al Modelo, no habrá ni una línea de código que tenga que ver con la persistencia: será Modelo puro.

Saludos cordiales!

0voto

GusGarsaky comentado

Sé bien como trabaja un ORM y el problema de los DAOs. Con Java he trabajado con Hibernate que por cierto me parece el más avanzado ORM en la actualidad.

Volviendo al tema, en PHP soy nuevo, y aún no entiendo bien el lenguaje. No entendía en su totalidad como funcionaba this, si funcionaba igual como en Java. Después de un par de vistas a unos ejemplos en StackOverFlow, ya me quedó claro.

En resumen, como this en PHP al igual que en Java hace referencia a la clase u objeto mismo, simplemente se haría:

<?php

public function save()
{
    $caller = $this; // guarda la referencia
    $callerClass = get_class($caller); // 'Customer' o 'User'

    // obtiene el servicio dinámicamente, de acuerdo al objeto llamante
    $service = ServiceFactory::create($callerClass);

    // le envía el objeto llamante al servicio Customer para que lo guarde en la BD
    $service->save($caller);
}

Y eso sería todo. Gracias de todos modos por responder.

PD: Gracias por el link del ORM. Estaba evaluando algunas opciones.

0voto

Leonardo-Tadei comentado

Hola Gus,

tal cual: $this funciona igual en PHP y en Java, y ambos son una copia de _self de Smalltalk ;-)

Volviendo a tu método, es innecesario hacer

$caller = $this;

ya que

$callerClass = get_class($this);

es lo mismo, porque la primer asignación es una referencia y no una copia.

Luego, te reitero que no creo que te sirva:

 $service->save($caller);

porque el service para trabajar, necesita el Objeto entero y no solo el nombre de la clase (de dónde sacaría los valores de los atributos si no?)

El código de arriba podría ser:

public function save(){
    $callerClass = get_class($this); //  Customer' o 'User'
    // obtiene el servicio dinámicamente, de acuerdo al objeto llamante
    $service = ServiceFactory::create($callerClass);
    // le envía el objeto llamante al servicio Customer para que lo guarde en la BD
    $service->save($this);
}

Respecto al ORM, lo estamos usando en producción acá desde hace un par de años y estoy muy conetnto con el desempeño... si bien ya me muero de ganas por diseñar la versión 2!
Si hacés algún ejemplo de uso, con gusto lo puedo añadir al código como complemento de la documentación.

Saludos cordiales!

0voto

GusGarsaky comentado

Genial Leo. Eso mismo estaba pensando. Además como normalizo mi BD no necesito comprobar que todas las propiedades estén seteadas (con get_object_vars($this)), ya que no hay ningún campo que pueda ser null.

Respecto al ORM, de hecho me parece un ORM bastante flexible. Lo usaré en mi próximo proyecto ;)

Un saludo y gracias nuevamente.

Por favor, accede o regístrate para responder a esta pregunta.

Otras Preguntas y Respuestas


...

Bienvenido a entre Desarrolladores, donde puedes realizar preguntas y recibir respuestas de otros miembros de la comunidad.

Conecta