2010-04-21 16 views
5

Estoy escribiendo una aplicación web (PHP) para mi amigo y he decidido usar mi entrenamiento OOP limitado de Java.Excepciones de PHP en las clases

Mi pregunta es ¿cuál es la mejor manera de señalar en mi clase/aplicación que cosas críticas específicas fallaron sin realmente romper mi página.

Mi problema es que tengo un Object "SummerCamper" que toma un camper_id como argumento para cargar todos los datos necesarios en el objeto de la base de datos. Supongamos que alguien especifica un camper_id en la cadena de consulta que no existe, lo paso al constructor de objetos y la carga falla. Actualmente no veo una forma de devolver un mensaje falso del constructor.

He leído lo que podía hacer esto con excepciones, lanzando una excepción si no hay registros se encuentran en la base de datos o si algún tipo de error en la validación en la entrada de la camper_id de la aplicación, etc.

Sin embargo, realmente no he encontrado una buena manera de alertar a mi programa de que la carga de objetos ha fallado. Traté de devolver falso desde dentro de la captura, pero el objeto aún persiste en mi página php. Entiendo que podría poner una variable $ is_valid = false si la carga falla y luego verificar el Objeto usando un método get, pero creo que puede haber formas mejores.

¿Cuál es la mejor manera de lograr la terminación esencial de un objeto si falla una carga? ¿Debería cargar datos en el objeto desde fuera del constructor? ¿Hay algún tipo de patrón de diseño que debería estudiar?

Cualquier ayuda sería apreciada.

function __construct($camper_id){ 
     try{ 
      $query = "SELECT * FROM campers WHERE camper_id = $camper_id"; 
      $getResults = mysql_query($query); 

      $records = mysql_num_rows($getResults); 

      if ($records != 1) { 
       throw new Exception('Camper ID not Found.'); 
      } 

      while($row = mysql_fetch_array($getResults)) 
      { 
       $this->camper_id = $row['camper_id']; 
       $this->first_name = $row['first_name']; 
       $this->last_name = $row['last_name']; 
       $this->grade = $row['grade']; 
       $this->camper_age = $row['camper_age']; 
       $this->camper_gender = $row['gender']; 
       $this->return_camper = $row['return_camper']; 
      } 
     } 
     catch(Exception $e){ 
      return false; 
     } 



    } 

Respuesta

11

A constructor in PHP siempre devolverá void. Este

public function __construct() 
{ 
    return FALSE; 
} 

no funcionará. Throwing an Exception en el constructor

public function __construct($camperId) 
{ 
    if($camperId === 1) { 
     throw new Exception('ID 1 is not in database'); 
    } 
} 

sería terminar la ejecución de instrucciones a menos que se captura en algún lugar

try { 
    $camper = new SummerCamper(1); 
} catch(Exception $e) { 
    $camper = FALSE; 
} 

Se podía mover el código anterior en un static method de SummerCamper para crear instancias de ella en lugar de utilizar la palabra clave new (que es común en Java oí)

class SummerCamper 
{ 
    protected function __construct($camperId) 
    { 
     if($camperId === 1) { 
      throw new Exception('ID 1 is not in database'); 
     } 
    } 
    public static function create($camperId) 
    { 
     $camper = FALSE; 
     try { 
      $camper = new self($camperId); 
     } catch(Exception $e) { 
      // uncomment if you want PHP to raise a Notice about it 
      // trigger_error($e->getMessage(), E_USER_NOTICE); 
     } 
     return $camper; 
    } 
} 

de esta manera se podría hacer

$camper = SummerCamper::create(1); 

y obtener FALSE en $camper cuando el $camper_id no existe. Desde statics are considered harmful, es posible que desee utilizar una fábrica en su lugar.

Otra opción sería desacoplar por completo el acceso a la base de datos del SummerCamper. Básicamente, SummerCamper es una entidad que solo debe preocuparse por cosas SummerCamper.Si le das a saber cómo persistir, estás creando efectivamente un ActiveRecord o RowDataGateway. Usted puede ir con un enfoque DataMapper:

class SummerCamperMapper 
{ 
    public function findById($id) 
    { 
     $camper = FALSE; 
     $data = $this->dbAdapter->query('SELECT id, name FROM campers where ?', $id); 
     if($data) { 
      $camper = new SummerCamper($data); 
     } 
     return $camper; 
    } 
} 

y su Entidad

class SummerCamper 
{ 
    protected $id; 
    public function __construct(array $data) 
    { 
     $this->id = data['id']; 
     // other assignments 
    } 
} 

DataMapper es algo más complicado, pero le da desacoplado código que es más fácil de mantener y flexible en el extremo. Eche un vistazo alrededor de SO, hay una serie de preguntas sobre estos temas.

1

Lanzar una excepción desde el constructor es probablemente el enfoque correcto. Puede ver esto en un lugar apropiado y tomar las medidas necesarias (por ejemplo, mostrar una página de error). Como no mostró ningún código, no está claro dónde estaba capturando su excepción o por qué eso no pareció funcionar.

1
try { 
    $camper = new SummerCamper($id); 
    $camper->display(); 
} catch (NonexistentCamper $ex) { 
    handleFailure($ex); 
} 
+0

Gordon, que desea seguir este ejemplo para crear y usar su SummerCamper (este es el código del cliente para su clase), y QUITAR el try {} catch {} de su constructor SummerCamper. La forma en que lo tiene escrito ahora, está tragándose la excepción antes de que el código del cliente pueda verla. – grossvogel

+0

@grossvogel no hay try/catch en el constructor. Supongo que está refiriéndose a la función estática. Ahí está a propósito. Por lo que yo entendí del OP, él no está tan preocupado por la verdadera excepción como por no obtener una instancia. Además, si el bloque try/catch está fuera del método estático, tendría que repetir el try/catch cada vez que necesite crear un SummerCamper. Eso no es SECO. – Gordon

4

Para añadir a las respuestas de los otros, tener en cuenta que usted puede lanzar diferentes tipos de excepciones desde un único método y los maneja cada uno de manera diferente:

try { 
    $camper = new SummerCamper($camper_id); 
} catch (NoRecordsException $e) { 
    // handle no records 
} catch (InvalidDataException $e) { 
    // handle invalid data 
} 
+2

Sí. Buena sugerencia. – Gordon

Cuestiones relacionadas