php WordPress

Cómo refactorizar el anidamiento masivo con cláusulas de guarda

Antes de ver el cómo, deberíamos ver el porqué. Y antes de eso, ver un ejemplo de anidamiento masivo para entender el problema.

Vaya por delante que esto es algo que todos hemos hecho en alguna ocasión, el motivo de este post no es criticar, simplemente tratar de mostrar una forma mejor de desarrollar código, más mantenible y escalable.

De hecho, circula un meme por las redes que, si eres desarrollador/a, habrás visto en alguna ocasión:

En este ejemplo, con fines didácticos, tenemos un método de una clase que comprueba que el formulario se ha enviado. A continuación verifica en nonce. Después comprueba que recibimos un email válido. Con ese email comprobamos si tenemos al usuario registrado, y por último si es administrador.

<?php

class MyForm {
    public function check_form() {
        try {
            if ( isset( $_POST['btn'] ) ) {
                $nonce = $_POST['_wpnonce'] ?? '';
                if ( wp_verify_nonce( $nonce, 'my-nonce' ) ) {
                    $email = $_POST['_email'] ?? '';
                    if ( is_email( $email ) ) {
                        if ( email_exists( $email ) ) {
                            $user = get_userdata( email_exists( $email ) );
                            if ( in_array( 'administrator', (array) $user->roles ) ) {
                                //do something
                            } else {
                                throw new Exception( 'User is not an administrator' );
                            }
                        } else {
                            throw new Exception( 'User not found' );
                        }
                    } else {
                        throw new Exception( 'Not valid email' );
                    }
                } else {
                    throw new Exception( 'An error occurred while checking Nonce' );
                }
            } else {
                throw new Exception( 'Form not submitted' );
            }
        } catch ( Exception $e ) {
            wp_die( 'Error: ' . $e->getMessage(), 'Error' );
        }
    }
}

Como podemos ver, tenemos un montón de condiciones anidadas. Esto se puede complicar en función de las necesidades, y crecer y crecer hasta un punto que sea incluso difícil de leer.

Lo ideal sería encapsular cada comprobación en un método independiente, con un nombre que le de sentido y semántica al código. Además de leerse mejor el código. A esto se le conoce como cláusulas de guarda.

Las cláusulas de guarda son un método que nos permite hacer nuestro código más legible, semántico, escalable, testeable y con menor nivel de indentación.

De este modo, en lugar de anidamientos, tendremos una lista «plana» de condicionales, una tras otra, con early returns (si no se cumple una condición, no seguimos).

Algo como esto:

<?php

class MyForm {
    public function validate() {
        try {
            $this->check_form();

            // do something
        } catch ( Exception $e ) {
            wp_die( 'Error: ' . $e->getMessage(), 'Error' );
        }
    }

    public function check_form() {
        if ( ! $this->is_form_submitted() ) {
            throw new Exception( 'Form not submitted' );
        }

        if ( ! $this->is_nonce_ok() ) {
            throw new Exception( 'An error occurred while checking Nonce' );
        }

        $email = $_POST['_email'] ?? '';
        if ( ! is_email( $email ) ) {
            throw new Exception( 'Not valid email' );
        }

        if ( ! email_exists( $email ) ) {
            throw new Exception( 'User not found' );
        }

        if ( $this->is_user_an_administrator($email) ) {
            throw new Exception( 'User is not an administrator' );
        }        
    }

    private function is_form_submitted(): bool {
        return isset( $_POST['btn'] );
    }

    private function is_nonce_ok(): bool {
        $nonce = $_POST['_wpnonce'] ?? '';

        return (bool) wp_verify_nonce( $nonce, 'my-nonce' );
    }

    private function is_user_an_administrator( string $email ): bool {
        $user = get_userdata( email_exists( $email ) );
        
        return in_array( 'administrator', (array) $user->roles );
    }
}

Llamando a los método con nombres descriptivos, nos ayudará a leer mejor el código y a que sea más fácil generar tests unitarios.

No es lo mismo tener en código un isset($_POST[‘btn’]) (donde no sabes qué es btn) que un método que se llame is_form_submitted() (donde estás preguntando si el formulario ha sido enviado).

¿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.

Comentarios

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *:

  • El fin del tratamiento es únicamente la moderación de comentarios para evitar spam
  • La legitimación es tu consentimiento al comentar
  • No se comunicará ningún dato a terceros salvo por obligación legal
  • Tienes derecho al acceso, rectificación y eliminación de los comentarios