Completed
Branch master (024767)
by Henry Stivens
01:51
created

KumbiaActiveRecord   D

Complexity

Total Complexity 484

Size/Duplication

Total Lines 2457
Duplicated Lines 27.15 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
dl 667
loc 2457
rs 4.4102
c 0
b 0
f 0
wmc 484
lcom 1
cbo 8

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like KumbiaActiveRecord often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use KumbiaActiveRecord, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * KumbiaPHP web & app Framework
4
 *
5
 * LICENSE
6
 *
7
 * This source file is subject to the new BSD license that is bundled
8
 * with this package in the file LICENSE.txt.
9
 * It is also available through the world-wide-web at this URL:
10
 * http://wiki.kumbiaphp.com/Licencia
11
 * If you did not receive a copy of the license and are unable to
12
 * obtain it through the world-wide-web, please send an email
13
 * to [email protected] so we can send you a copy immediately.
14
 *
15
 * @category   Kumbia
16
 * @package    Db
17
 * @subpackage ActiveRecord
18
 * @copyright  Copyright (c) 2005 - 2017 Kumbia Team (http://www.kumbiaphp.com)
19
 * @license    http://wiki.kumbiaphp.com/Licencia     New BSD License
20
 */
21
/**
22
 * @see Db
23
 */
24
require CORE_PATH . 'libs/db/db.php';
25
26
/**
27
 * ActiveRecordBase Clase para el Mapeo Objeto Relacional
28
 *
29
 * Active Record es un enfoque al problema de acceder a los datos de una
30
 * base de datos en forma orientada a objetos. Una fila en la
31
 * tabla de la base de datos (o vista) se envuelve en una clase,
32
 * de manera que se asocian filas &uacute;nicas de la base de datos
33
 * con objetos del lenguaje de programaci&oacute;n usado.
34
 * Cuando se crea uno de estos objetos, se a&ntilde;de una fila a
35
 * la tabla de la base de datos. Cuando se modifican los atributos del
36
 * objeto, se actualiza la fila de la base de datos.
37
 *
38
 * Propiedades Soportadas:
39
 * $db = Conexion al Motor de Base de datos
40
 * $database = Base de datos a la que se conecta, especificada en databases.ini
41
 * $source = Tabla que contiene la tabla que esta siendo mapeada
42
 * $fields = Listado de Campos de la tabla que han sido mapeados
43
 * $count = Conteo del ultimo Resultado de un Select
44
 * $primary_key = Listado de columnas que conforman la llave primaria
45
 * $non_primary = Listado de columnas que no son llave primaria
46
 * $not_null = Listado de campos que son not_null
47
 * $attributes_names = nombres de todos los campos que han sido mapeados
48
 * $debug = Indica si se deben mostrar los SQL enviados al RDBM en pantalla
49
 * $logger = Si es diferente de false crea un log utilizando la clase Logger
50
 * en library/kumbia/logger/logger.php, esta crea un archivo .txt en logs/ con todas las
51
 * operaciones realizadas en ActiveRecord, si $logger = "nombre", crea un
52
 * archivo con ese nombre
53
 *
54
 * Propiedades sin Soportar:
55
 * $dynamic_update : La idea es que en un futuro ActiveRecord solo
56
 * actualize los campos que han cambiado.  (En Desarrollo)
57
 * $dynamic_insert : Indica si los valores del insert son solo aquellos
58
 * que sean no nulos. (En Desarrollo)
59
 * $select_before_update: Exige realizar una sentencia SELECT anterior
60
 * a la actualizacion UPDATE para comprobar que los datos no hayan sido
61
 * cambiados (En Desarrollo)
62
 * $subselect : Permitira crear una entidad ActiveRecord de solo lectura que
63
 * mapearia los resultados de un select directamente a un Objeto (En Desarrollo)
64
 *
65
 * @category   Kumbia
66
 * @package    Db
67
 * @subpackage ActiveRecord
68
 */
69
class KumbiaActiveRecord
70
{
71
    //Soportados
72
73
    /**
74
     * Resource de conexion a la base de datos
75
     *
76
     * @var DbBase
77
     */
78
    protected $db;
79
    /**
80
     * Base de datos a la que se conecta
81
     *
82
     * @var string
83
     */
84
    protected $database;
85
    /**
86
     * Schema donde esta la tabla
87
     *
88
     * @var string
89
     */
90
    protected $schema;
91
    /**
92
     * Tabla utilizada para realizar el mapeo
93
     *
94
     * @var string
95
     */
96
    protected $source;
97
    /**
98
     * Numero de resultados generados en la ultima consulta
99
     *
100
     * @var integer
101
     */
102
    protected $count;
103
    /**
104
     * Nombres de los atributos de la entidad
105
     *
106
     * @var array
107
     */
108
    protected $fields = array();
109
    /**
110
     * LLaves primarias de la entidad
111
     *
112
     * @var array
113
     */
114
    protected $primary_key = array();
115
    /**
116
     * Campos que no son llave primaria
117
     *
118
     * @var array
119
     */
120
    protected $non_primary = array();
121
    /**
122
     * Campos que no permiten nulos
123
     *
124
     * @var array
125
     */
126
    protected $not_null = array();
127
    /**
128
     * Campos que tienen valor por defecto
129
     *
130
     * @var array
131
     */
132
    protected $_with_default = array();
133
    /**
134
     * Nombres de atributos, es lo mismo que fields
135
     *
136
     * @var array
137
     */
138
    protected $alias = array();
139
    /**
140
     * Indica si la clase corresponde a un mapeo de una vista
141
     * en la base de datos
142
     *
143
     * @var boolean
144
     */
145
    protected $is_view = false;
146
    /**
147
     * Indica si el modelo esta en modo debug
148
     *
149
     * @var boolean
150
     */
151
    protected $debug = false;
152
    /**
153
     * Indica si se logearan los mensajes generados por la clase
154
     *
155
     * @var mixed
156
     */
157
    protected $logger = false;
158
    /**
159
     * Indica si los datos del modelo deben ser persistidos
160
     *
161
     * @var boolean
162
     */
163
    protected $persistent = false;
164
    /**
165
     * Validaciones
166
     *
167
     * inclusion_in: el campo pertenece a un conjunto de elementos
168
     * exclusion_of: el campo no pertenece a un conjunto de elementos
169
     * numericality_of: el campo debe ser numerico
170
     * format_of: el campo debe coincidir con la expresion regular compatible con perl
171
     * date_in: el campo debe ser una fecha valida
172
     * email_in: el campo debe ser un correo electronico
173
     * uniqueness_of: el campo debe ser unico
174
     *
175
     * @var array
176
     * */
177
    protected $_validates = array('inclusion_in' => array(), 'exclusion_of' => array(), 'numericality_of' => array(),
178
        'format_of' => array(), 'date_in' => array(), 'email_in' => array(), 'uniqueness_of' => array());
179
    /**
180
     * Campos que terminan en _in
181
     *
182
     * @var array
183
     */
184
    protected $_in = array();
185
    /**
186
     * Campos que terminan en _at
187
     *
188
     * @var array
189
     */
190
    protected $_at = array();
191
    /**
192
     * Variable para crear una condicion basada en los
193
     * valores del where
194
     *
195
     * @var string
196
     */
197
    protected $_where_pk;
198
    /**
199
     * Indica si ya se han obtenido los metadatos del Modelo
200
     *
201
     * @var boolean
202
     */
203
    protected $_dumped = false;
204
    /**
205
     * Indica si hay bloqueo sobre los warnings cuando una propiedad
206
     * del modelo no esta definida-
207
     *
208
     * @var boolean
209
     */
210
    protected $_dump_lock = false;
211
    /**
212
     * Tipos de datos de los campos del modelo
213
     *
214
     * @var array
215
     */
216
    protected $_data_type = array();
217
    /**
218
     * Relaciones a las cuales tiene una cardinalidad 1-1
219
     *
220
     * @var array
221
     */
222
    protected $_has_one = array();
223
    /**
224
     * Relaciones a las cuales tiene una cardinalidad 1-n
225
     *
226
     * @var array
227
     */
228
    protected $_has_many = array();
229
    /**
230
     * Relaciones a las cuales tiene una cardinalidad 1-1
231
     *
232
     * @var array
233
     */
234
    protected $_belongs_to = array();
235
    /**
236
     * Relaciones a las cuales tiene una cardinalidad n-n (muchos a muchos) o 1-n inversa
237
     *
238
     * @var array
239
     */
240
    protected $_has_and_belongs_to_many = array();
241
    /**
242
     * Clases de las cuales es padre la clase actual
243
     *
244
     * @var array
245
     */
246
    protected $parent_of = array();
247
    /**
248
     * Persistance Models Meta-data
249
     */
250
    protected static $_models = array();
251
    /**
252
     * Persistance Models Meta-data
253
     */
254
    protected static $models = array();
255
256
    /**
257
     * Constructor del Modelo
258
     *
259
     * @param array $data
260
     */
261
    function __construct($data=null)
262
    {
263
        if (!$this->source) {
264
            $this->_model_name();
265
        }
266
267
        /**
268
         * Inicializa el modelo en caso de que exista initialize
269
         */
270
        if (method_exists($this, 'initialize')) {
271
            $this->initialize();
272
        }
273
274
        /**
275
         * Conecta a la bd
276
         * */
277
        $this->_connect();
278
279
        if ($data) {
280
            if (!is_array($data)) $data = Util::getParams(func_get_args());
281
            foreach($this->fields as $field) {
282
                if (isset($data[$field])) {
283
                    $this->$field = $data[$field];
284
                }
285
            }
286
        }
287
    }
288
289
    /**
290
     * Obtiene el nombre de la relacion en el RDBM a partir del nombre de la clase
291
     *
292
     */
293
    protected function _model_name()
294
    {
295
        if (!$this->source) {
296
            $this->source = Util::smallcase(get_class($this));
297
        }
298
    }
299
300
    /**
301
     * Establece publicamente el $source de la tabla
302
     *
303
     * @param string $source
304
     */
305
    public function set_source($source)
306
    {
307
        $this->source = $source;
308
    }
309
310
    /**
311
     * Devuelve el source actual
312
     *
313
     * @return string
314
     */
315
    public function get_source()
316
    {
317
        return $this->source;
318
    }
319
320
    /**
321
     * Establece la base datos a utilizar
322
     *
323
     * @param string $database
324
     */
325
    public function set_database($database)
326
    {
327
        $this->database = $database;
328
    }
329
330
    /**
331
     * Devuelve la base de datos
332
     *
333
     * @return string
334
     */
335
    public function get_database()
336
    {
337
        if ($this->database) {
338
            return $this->database;
339
        } else {
340
            $core = Config::read('config');
341
            return $core['application']['database'];
342
        }
343
    }
344
345
    /**
346
     * Pregunta si el ActiveRecord ya ha consultado la informacion de metadatos
347
     * de la base de datos o del registro persistente
348
     *
349
     * @return boolean
350
     */
351
    public function is_dumped()
352
    {
353
        return $this->_dumped;
354
    }
355
356
    /**
357
     * Devuelve los registros del modelo al que se está asociado.
358
     *
359
     * @param string $mmodel nombre del modelo asociado
360
     * @return array|NULL|FALSE si existen datos devolverá un array,
361
     * NULL si no hay datos asociados aun, y false si no existe ninguna asociación.
362
     */
363
    protected function _get_relation_data($mmodel)
364
    {
365
        if (array_key_exists($mmodel, $this->_belongs_to)) {
366
            $relation = $this->_belongs_to[$mmodel];
367
            return self::get($relation->model)->find_first($this->{$relation->fk});
368
        } elseif (array_key_exists($mmodel, $this->_has_one)) {
369
            $relation = $this->_has_one[$mmodel];
370
            if ($this->{$this->primary_key[0]}) {
371
                return self::get($relation->model)->find_first("{$relation->fk}={$this->db->add_quotes($this->{$this->primary_key[0]}) }");
372
            } else {
373
                return;
374
            }
375
        } elseif (array_key_exists($mmodel, $this->_has_many)) {
376
            $relation = $this->_has_many[$mmodel];
377
            if ($this->{$this->primary_key[0]}) {
378
                return self::get($relation->model)->find("{$relation->fk}={$this->db->add_quotes($this->{$this->primary_key[0]}) }");
379
            } else {
380
                return array();
381
            }
382
        } elseif (array_key_exists($mmodel, $this->_has_and_belongs_to_many)) {
383
            $relation = $this->_has_and_belongs_to_many[$mmodel];
384
            $relation_model = self::get($relation->model);
385
            $source = ($this->schema ? "{$this->schema}." : NULL ) . $this->source;
386
            $relation_source = ($relation_model->schema ? "{$relation_model->schema}." : NULL ) . $relation_model->source;
387
            /**
388
             * Cargo atraves de que tabla se efectuara la relacion
389
             *
390
             */
391
            if (!isset($relation->through)) {
392
                if ($source > $relation_source) {
393
                    $relation->through = "{$this->source}_{$relation_source}";
394
                } else {
395
                    $relation->through = "{$relation_source}_{$this->source}";
396
                }
397
            }else{
398
                $through = explode('/', $relation->through);
399
                $relation->through = end($through);
400
            }
401
            if ($this->{$this->primary_key[0]}) {
402
                return $relation_model->find_all_by_sql("SELECT $relation_source.* FROM $relation_source, {$relation->through}, $source
403
                    WHERE {$relation->through}.{$relation->key} = {$this->db->add_quotes($this->{$this->primary_key[0]}) }
404
                    AND {$relation->through}.{$relation->fk} = $relation_source.{$relation_model->primary_key[0]}
405
                    AND {$relation->through}.{$relation->key} = $source.{$this->primary_key[0]}
406
                    ORDER BY $relation_source.{$relation_model->primary_key[0]}");
407
            } else {
408
                return array();
409
            }
410
        } else {
411
            return FALSE; //si no existe ninguna asociación devuelve false.
412
        }
413
    }
414
415
    /**
416
     * Valida que los valores que sean leidos del objeto ActiveRecord esten definidos
417
     * previamente o sean atributos de la entidad
418
     *
419
     * @param string $property
420
     */
421
    function __get($property)
422
    {
423
        if (!$this->_dump_lock) {
424
            if (!isset($this->$property)) {
425
                return $this->_get_relation_data($property);
426
            }
427
        }
428
        return $this->$property;
429
    }
430
431
    /**
432
     * Valida que los valores que sean asignados al objeto ActiveRecord esten definidos
433
     * o sean atributos de la entidad
434
     *
435
     * @param string $property
436
     * @param mixed $value
437
     */
438
    function __set($property, $value)
439
    {
440
        if (!$this->_dump_lock) {
441
            if (is_object($value) && is_subclass_of($value, 'KumbiaActiveRecord')) {
442
                if (array_key_exists($property, $this->_belongs_to)) {
443
                    $relation = $this->_belongs_to[$property];
444
                    $value->dump_model();
445
                    $this->{$relation->fk} = $value->{$value->primary_key[0]};
446
                    return;
447
                } elseif (array_key_exists($property, $this->_has_one)) {
448
                    $relation = $this->_has_one[$property];
449
                    $value->{$relation->fk} = $this->{$this->primary_key[0]};
450
                    return;
451
                }
452
            } elseif ($property == "source") {
453
                $value = self::sql_item_sanitize($value);
454
            }
455
        }
456
        $this->$property = $value;
457
    }
458
459
    /**
460
     * Devuelve un valor o un listado dependiendo del tipo de Relación
461
     *
462
     */
463
    public function __call($method, $args = array())
464
    {
465
        if (substr($method, 0, 8) == "find_by_") {
466
            $field = substr($method, 8);
467
            self::sql_item_sanitize($field);
468
            if (isset($args[0])) {
469
                $arg = array("conditions: $field = {$this->db->add_quotes($args[0])}");
470
                unset($args[0]);
471
            } else {
472
                $arg = array();
473
            }
474
            return call_user_func_array(array($this, "find_first"), array_merge($arg, $args));
475
        }
476
        if (substr($method, 0, 9) == "count_by_") {
477
            $field = substr($method, 9);
478
            self::sql_item_sanitize($field);
479
            if (isset($args[0])) {
480
                $arg = array("conditions: $field = {$this->db->add_quotes($args[0])}");
481
                unset($args[0]);
482
            } else {
483
                $arg = array();
484
            }
485
            return call_user_func_array(array($this, "count"), array_merge($arg, $args));
486
        }
487
        if (substr($method, 0, 12) == "find_all_by_") {
488
            $field = substr($method, 12);
489
            self::sql_item_sanitize($field);
490
            if (isset($args[0])) {
491
                $arg = array("conditions: $field = {$this->db->add_quotes($args[0])}");
492
                unset($args[0]);
493
            } else {
494
                $arg = array();
495
            }
496
            return call_user_func_array(array($this, "find"), array_merge($arg, $args));
497
        }
498
        $model = preg_replace('/^get/', '', $method);
499
        $mmodel = Util::smallcase($model);
500
        if (($data = $this->_get_relation_data($mmodel)) !== FALSE) {
501
            return $data;
502
        }
503
504
        if (method_exists($this, $method)) {
505
            call_user_func_array(array($this, $method), $args);
506
        } else {
507
            throw new KumbiaException("No existe el método '$method' en ActiveRecord::" . get_class($this));
508
        }
509
510
        return $this->$method($args);
511
    }
512
513
    /**
514
     * Se conecta a la base de datos y descarga los meta-datos si es necesario
515
     */
516
    protected function _connect()
517
    {
518
        if (!is_object($this->db)) {
519
            $this->db = Db::factory($this->database);
520
        }
521
        $this->db->debug = $this->debug;
522
        $this->db->logger = $this->logger;
523
        $this->dump();
524
    }
525
526
    /**
527
     * Cargar los metadatos de la tabla
528
     *
529
     */
530
    public function dump_model()
531
    {
532
        $this->_connect();
533
    }
534
535
    /**
536
     * Verifica si la tabla definida en $this->source existe
537
     * en la base de datos y la vuelca en dump_info
538
     *
539
     * @return boolean
540
     */
541
    protected function dump()
542
    {
543
        if ($this->_dumped) {
544
            return false;
545
        }
546
        //$a = array();
547
        if ($this->source) {
548
            $this->source = str_replace(";", '', strtolower($this->source));
549
        } else {
550
            $this->_model_name();
551
            if (!$this->source) {
552
                return false;
553
            }
554
        }
555
        $table = $this->source;
556
        $schema = $this->schema;
557
        if (!count(self::get_meta_data($this->source))) {
558
            $this->_dumped = true;
559
            $this->_dump_info($table, $schema);
560
            if (!count($this->primary_key)) {
561
                if (!$this->is_view) {
562
                    throw new KumbiaException("No se ha definido una llave primaria para la tabla '$table' esto imposibilita crear el ActiveRecord para esta entidad");
563
                }
564
            }
565
        } else {
566
            if (!$this->is_dumped()) {
567
                $this->_dumped = true;
568
                $this->_dump_info($table, $schema);
569
            }
570
        }
571
        return true;
572
    }
573
574
    /**
575
     * Vuelca la informaci&oacute;n de la tabla $table en la base de datos
576
     * para armar los atributos y meta-data del ActiveRecord
577
     *
578
     * @param string $table
579
     * @return boolean
580
     */
581
    protected function _dump_info($table, $schema = '')
582
    {
583
        $this->_dump_lock = true;
584
        if (!count(self::get_meta_data($table))) {
585
            $meta_data = $this->db->describe_table($table, $schema);
586
            if ($meta_data) {
587
                self::set_meta_data($table, $meta_data);
588
            }
589
        }
590
        foreach (self::get_meta_data($table) as $field) {
591
            $this->fields[] = $field['Field'];
592
            $aliasAux = $field['Field'];
593
            if ($field['Key'] == 'PRI') {
594
                $this->primary_key[] = $field['Field'];
595
                $this->alias[$field['Field']] = 'Código';
596
            } else
597
                $this->non_primary[] = $field['Field'];
598
            /**
599
             * Si se indica que no puede ser nulo, pero se indica un
600
             * valor por defecto, entonces no se incluye en la lista, ya que
601
             * al colocar un valor por defecto, el campo nunca sera nulo
602
             *
603
             */
604
            if ($field['Null'] == 'NO' && !(isset($field['Default']) && $field['Default'])) {
605
                $this->not_null[] = $field['Field'];
606
            }
607
            if (isset($field['Default']) && $field['Default']) {
608
                $this->_with_default[] = $field['Field'];
609
            }
610
            if ($field['Type']) {
611
                $this->_data_type[$field['Field']] = strtolower($field['Type']);
612
            }
613
            if (substr($field['Field'], strlen($field['Field']) - 3, 3) == '_at') {
614
                $this->_at[] = $field['Field'];
615
                $aliasAux = substr($field['Field'], 0, -3);
616
            }
617
            if (substr($field['Field'], strlen($field['Field']) - 3, 3) == '_in') {
618
                $this->_in[] = $field['Field'];
619
                $aliasAux = substr($field['Field'], 0, -3);
620
            }
621
            if (substr($field['Field'], strlen($field['Field']) - 3, 3) == '_id') {
622
                $aliasAux = substr($field['Field'], 0, -3);
623
            }
624
            //humanizando el alias
625
            $this->alias[$field['Field']] = ucwords(strtr($aliasAux, '_-', '  '));
626
        }
627
        $this->_dump_lock = false;
628
        return true;
629
    }
630
631
    /**
632
     * Retorna un array de los campos (fields) de una tabla Humanizados
633
     *
634
     * @param $key
635
     * @return array
636
     */
637
    public function get_alias($key=null)
638
    {
639
        if ($key && array_key_exists($key, $this->alias)) {
640
            return $this->alias[$key];
641
        } else {
642
            throw new KumbiaException("No se pudo obtener el Alias, porque el key: \"$key\" no existe.");
643
        }
644
    }
645
646
    /**
647
     * Asigna un nuevo valor al alias dado un key
648
     *
649
     * @param string $key
650
     * @param string $value
651
     */
652
    public function set_alias($key=null, $value=null)
653
    {
654
        if ($key && array_key_exists($key, $this->alias)) {
655
            $this->alias[$key] = $value;
656
        } else {
657
            throw new KumbiaException("No se pudo asignar el nuevo valor al Alias, porque el key: \"$key\" no existe.");
658
        }
659
    }
660
661
    /**
662
     * Commit a Transaction
663
     *
664
     * @return success
665
     */
666
    public function commit()
667
    {
668
        return $this->db->commit();
669
    }
670
671
    /**
672
     * Rollback a Transaction
673
     *
674
     * @return success
675
     */
676
    public function rollback()
677
    {
678
        return $this->db->rollback();
679
    }
680
681
    /**
682
     * Start a transaction in RDBM
683
     *
684
     * @return success
685
     */
686
    public function begin()
687
    {
688
        $this->_connect();//(true);
689
        return $this->db->begin();
690
    }
691
692
    /**
693
     * Find all records in this table using a SQL Statement
694
     *
695
     * @param string $sqlQuery
696
     * @return ActiveRecord Cursor
697
     */
698
    public function find_all_by_sql($sqlQuery)
699
    {
700
        $results = array();
701
        foreach ($this->db->fetch_all($sqlQuery) as $result) {
702
            $results[] = $this->dump_result($result);
703
        }
704
        return $results;
705
    }
706
707
    /**
708
     * Find a record in this table using a SQL Statement
709
     *
710
     * @param string $sqlQuery
711
     * @return ActiveRecord Cursor
712
     */
713
    public function find_by_sql($sqlQuery)
714
    {
715
        $row = $this->db->fetch_one($sqlQuery);
716
        if ($row !== false) {
717
            $this->dump_result_self($row);
718
            return $this->dump_result($row);
719
        } else {
720
            return false;
721
        }
722
    }
723
724
    /**
725
     * Execute a SQL Statement directly
726
     *
727
     * @param string $sqlQuery
728
     * @return int affected
729
     */
730
    public function sql($sqlQuery)
731
    {
732
        return $this->db->query($sqlQuery);
733
    }
734
735
    /**
736
     * Return Fist Record
737
     *
738
     * Recibe los mismos parametros que find
739
     *
740
     * @param mixed $what
741
     * @return ActiveRecord Cursor
742
     */
743
    public function find_first($what = '')
744
    {
745
        $what = Util::getParams(func_get_args());
746
        $select = "SELECT ";
747
        if (isset($what['columns'])) {
748
            $select.= self::sql_sanitize($what['columns']);
749
        } elseif (isset($what['distinct'])) {
750
            $select.= 'DISTINCT ';
751
            $select.= $what['distinct'] ? self::sql_sanitize($what['distinct']) : join(",", $this->fields);
752
        } else {
753
            $select.= join(",", $this->fields);
754
        }
755
        if ($this->schema) {
756
            $select.= " FROM {$this->schema}.{$this->source}";
757
        } else {
758
            $select.= " FROM {$this->source}";
759
        }
760
        $what['limit'] = 1;
761
        $select.= $this->convert_params_to_sql($what);
762
        $resp = false;
763
764
        $result = $this->db->fetch_one($select);
765
        if ($result) {
766
            $this->dump_result_self($result);
767
            $resp = $this->dump_result($result);
768
        }
769
770
        return $resp;
771
    }
772
773
    /**
774
     * Find data on Relational Map table
775
     *
776
     * @param string $what
777
     * @return ActiveRecord Cursor
778
     *
779
     * columns: columnas a utilizar
780
     * conditions : condiciones de busqueda en WHERE
781
     * join: inclusion inner join o outer join
782
     * group : campo para grupo en GROUP BY
783
     * having : condicion para el grupo
784
     * order : campo para criterio de ordenamiento ORDER BY
785
     * distinct: campos para hacer select distinct
786
     */
787
    public function find($what = '')
788
    {
789
        $what = Util::getParams(func_get_args());
790
        $select = "SELECT ";
791
        if (isset($what['columns'])) {
792
            $select.= $what['columns'] ? self::sql_sanitize($what['columns']) : join(",", $this->fields);
793
        } elseif (isset($what['distinct'])) {
794
            $select.= 'DISTINCT ';
795
            $select.= $what['distinct'] ? self::sql_sanitize($what['distinct']) : join(",", $this->fields);
796
        } else {
797
            $select.= join(",", $this->fields);
798
        }
799
        if ($this->schema) {
800
            $select.= " FROM {$this->schema}.{$this->source}";
801
        } else {
802
            $select.= " FROM {$this->source}";
803
        }
804
        $select.= $this->convert_params_to_sql($what);
805
        $results = array();
806
        $all_results = $this->db->in_query($select);
807
        foreach ($all_results AS $result) {
808
            $results[] = $this->dump_result($result);
809
        }
810
811
        $this->count = count($results, COUNT_NORMAL);
812
        if (isset($what[0]) && is_numeric($what[0])) {
813
            if (!isset($results[0])) {
814
                $this->count = 0;
815
                return false;
816
            } else {
817
                $this->dump_result_self($all_results[0]);
818
                $this->count = 1;
819
                return $results[0];
820
            }
821
        } else {
822
            $this->count = count($results, COUNT_NORMAL);
823
            return $results;
824
        }
825
    }
826
827
    /*
828
     * Arma una consulta SQL con el parametro $what, así:
829
     *  $what = Util::getParams(func_get_args());
830
     *  $select = "SELECT * FROM Clientes";
831
     *  $select.= $this->convert_params_to_sql($what);
832
     *
833
     * @param string|array $what
834
     * @return string
835
     */
836
837
    public function convert_params_to_sql($what = '')
838
    {
839
        $select = '';
840
        if (is_array($what)) {
841
            if (!isset($what['conditions'])) {
842
                if (!isset($this->primary_key[0]) && (isset($this->id) || $this->is_view)) {
843
                    $this->primary_key[0] = "id";
844
                }
845
                self::sql_item_sanitize($this->primary_key[0]);
846
                if (isset($what[0])) {
847
                    if (is_numeric($what[0])) {
848
                        $what['conditions'] = "{$this->primary_key[0]} = ".(int)$what[0] ;
849
                    } else {
850
                        if ($what[0] == '') {
851
                            $what['conditions'] = "{$this->primary_key[0]} = ''";
852
                        } else {
853
                            $what['conditions'] = $what[0];
854
                        }
855
                    }
856
                }
857
            }
858
            if (isset($what['join'])) {
859
                $select.= " {$what['join']}";
860
            }
861
            if (isset($what['conditions'])) {
862
                $select.= " WHERE {$what['conditions']}";
863
            }
864
            if (isset($what['group'])) {
865
                $select.= " GROUP BY {$what['group']}";
866
            }
867
            if (isset($what['having'])) {
868
                $select.= " HAVING {$what['having']}";
869
            }
870
            if (isset($what['order'])) {
871
                self::sql_sanitize($what['order']);
872
                $select.= " ORDER BY {$what['order']}";
873
            }
874
            $limit_args = array($select);
875
            if (isset($what['limit'])) {
876
                array_push($limit_args, "limit: ".(int)$what['limit']);
877
            }
878
            if (isset($what['offset'])) {
879
                array_push($limit_args, "offset: ".(int)$what['offset']);
880
            }
881
            if (count($limit_args) > 1) {
882
                $select = call_user_func_array(array($this, 'limit'), $limit_args);
883
            }
884
        } else {
885
            if (strlen($what)) {
886
                if (is_numeric($what)) {
887
                    $select.= "WHERE {$this->primary_key[0]} = ".(int)$what[0] ;
888
                } else {
889
                    $select.= "WHERE $what";
890
                }
891
            }
892
        }
893
        return $select;
894
    }
895
896
    /*
897
     * Devuelve una clausula LIMIT adecuada al RDBMS empleado
898
     *
899
     * limit: maxima cantidad de elementos a mostrar
900
     * offset: desde que elemento se comienza a mostrar
901
     *
902
     * @param string $sql consulta select
903
     * @return String clausula LIMIT adecuada al RDBMS empleado
904
     */
905
906
    public function limit($sql)
907
    {
908
        $args = func_get_args();
909
        return call_user_func_array(array($this->db, 'limit'), $args);
910
    }
911
912
    /**
913
     * Ejecuta un SELECT DISTINCT
914
     * @param string $what
915
     * @return array
916
     *
917
     * Soporta parametros iguales a find
918
     *
919
     */
920
    public function distinct($what = '')
921
    {
922
        $what = Util::getParams(func_get_args());
923
        if ($this->schema) {
924
            $table = $this->schema . "." . $this->source;
925
        } else {
926
            $table = $this->source;
927
        }
928
        if (!isset($what['columns'])) {
929
            $what['columns'] = $what['0'];
930
        } else {
931
            if (!$what['columns']) {
932
                $what['columns'] = $what['0'];
933
            }
934
        }
935
        $what['columns'] = self::sql_sanitize($what['columns']);
936
        $select = "SELECT DISTINCT {$what['columns']} FROM $table ";
937
        /**
938
         * Se elimina el de indice cero ya que por defecto convert_params_to_sql lo considera como una condicion en WHERE
939
         */
940
        unset($what[0]);
941
        $select.= $this->convert_params_to_sql($what);
942
        $results = array();
943
        foreach ($this->db->fetch_all($select) as $result) {
944
            $results[] = $result[0];
945
        }
946
        return $results;
947
    }
948
949
    /**
950
     * Ejecuta una consulta en el RDBM directamente
951
     *
952
     * @param string $sql
953
     * @return resource
954
     */
955
    static public function static_select_one($sql)
956
    {
957
        $db = Db::factory();
958
        if (substr(ltrim($sql), 0, 7) != "SELECT") {
959
            $sql = "SELECT " . $sql;
960
        }
961
        $num = $db->fetch_one($sql);
962
        return $num[0];
963
    }
964
965
    /**
966
     * Realiza un conteo de filas
967
     *
968
     * @param string $what
969
     * @return integer
970
     */
971
    public function count($what = '')
972
    {
973
        $what = Util::getParams(func_get_args());
974
        if ($this->schema) {
975
            $table = "{$this->schema}.{$this->source}";
976
        } else {
977
            $table = $this->source;
978
        }
979
        unset($what['order']);
980
        if (isset($what['distinct']) && $what['distinct']) {
981
            if (isset($what['group'])) {
982
                $select = "SELECT COUNT(*) FROM (SELECT DISTINCT {$what['distinct']} FROM $table ";
983
                $select.= $this->convert_params_to_sql($what);
984
                $select.= ') AS t ';
985
            } else {
986
                $select = "SELECT COUNT(DISTINCT {$what['distinct']}) FROM $table ";
987
                $select.= $this->convert_params_to_sql($what);
988
            }
989
        } else {
990
            $select = "SELECT COUNT(*) FROM $table ";
991
            $select.= $this->convert_params_to_sql($what);
992
        }
993
        $num = $this->db->fetch_one($select);
994
        return $num[0];
995
    }
996
997
    /**
998
     * Realiza un promedio sobre el campo $what
999
     *
1000
     * @param string $what
1001
     * @return array
1002
     */
1003
    public function average($what = '')
1004
    {
1005
        $what = Util::getParams(func_get_args());
1006
        if (isset($what['column'])) {
1007
            if (!$what['column']) {
1008
                $what['column'] = $what[0];
1009
            }
1010
        } else {
1011
            $what['column'] = $what[0];
1012
        }
1013
        unset($what[0]);
1014
        self::sql_item_sanitize($what['column']);
1015
        if ($this->schema) {
1016
            $table = "{$this->schema}.{$this->source}";
1017
        } else {
1018
            $table = $this->source;
1019
        }
1020
        $select = "SELECT AVG({$what['column']}) FROM $table ";
1021
        $select.= $this->convert_params_to_sql($what);
1022
        $num = $this->db->fetch_one($select);
1023
        return $num[0];
1024
    }
1025
1026
    public function sum($what = '')
1027
    {
1028
        $what = Util::getParams(func_get_args());
1029
        if (isset($what['column'])) {
1030
            if (!$what['column']) {
1031
                $what['column'] = $what[0];
1032
            }
1033
        } else {
1034
            $what['column'] = $what[0];
1035
        }
1036
        unset($what[0]);
1037
        self::sql_item_sanitize($what['column']);
1038
        if ($this->schema) {
1039
            $table = "{$this->schema}.{$this->source}";
1040
        } else {
1041
            $table = $this->source;
1042
        }
1043
        $select = "SELECT SUM({$what['column']}) FROM $table ";
1044
        $select.= $this->convert_params_to_sql($what);
1045
        $num = $this->db->fetch_one($select);
1046
        return $num[0];
1047
    }
1048
1049
    /**
1050
     * Busca el valor maximo para el campo $what
1051
     *
1052
     * @param string $what
1053
     * @return mixed
1054
     */
1055
    public function maximum($what = '')
1056
    {
1057
        $what = Util::getParams(func_get_args());
1058
        if (isset($what['column'])) {
1059
            if (!$what['column']) {
1060
                $what['column'] = $what[0];
1061
            }
1062
        } else {
1063
            $what['column'] = $what[0];
1064
        }
1065
        unset($what[0]);
1066
        self::sql_item_sanitize($what['column']);
1067
        if ($this->schema) {
1068
            $table = "{$this->schema}.{$this->source}";
1069
        } else {
1070
            $table = $this->source;
1071
        }
1072
        $select = "SELECT MAX({$what['column']}) FROM $table ";
1073
        $select.= $this->convert_params_to_sql($what);
1074
        $num = $this->db->fetch_one($select);
1075
        return $num[0];
1076
    }
1077
1078
    /**
1079
     * Busca el valor minimo para el campo $what
1080
     *
1081
     * @param string $what
1082
     * @return mixed
1083
     */
1084
    public function minimum($what = '')
1085
    {
1086
        $what = Util::getParams(func_get_args());
1087
        if (isset($what['column'])) {
1088
            if (!$what['column']) {
1089
                $what['column'] = $what[0];
1090
            }
1091
        } else {
1092
            $what['column'] = $what[0];
1093
        }
1094
        unset($what[0]);
1095
        self::sql_item_sanitize($what['column']);
1096
        if ($this->schema) {
1097
            $table = "{$this->schema}.{$this->source}";
1098
        } else {
1099
            $table = $this->source;
1100
        }
1101
        $select = "SELECT MIN({$what['column']}) FROM $table ";
1102
        $select.= $this->convert_params_to_sql($what);
1103
        $num = $this->db->fetch_one($select);
1104
        return $num[0];
1105
    }
1106
1107
    /**
1108
     * Realiza un conteo directo mediante $sql
1109
     *
1110
     * @param string $sqlQuery
1111
     * @return mixed
1112
     */
1113
    public function count_by_sql($sqlQuery)
1114
    {
1115
        $num = $this->db->fetch_one($sqlQuery);
1116
        return $num[0];
1117
    }
1118
1119
    /**
1120
     * Iguala los valores de un resultado de la base de datos
1121
     * en un nuevo objeto con sus correspondientes
1122
     * atributos de la clase
1123
     *
1124
     * @param array $result
1125
     * @return ActiveRecord
1126
     */
1127
    public function dump_result($result)
1128
    {
1129
        $obj = clone $this;
1130
        /**
1131
         * Consulta si la clase es padre de otra y crea el tipo de dato correcto
1132
         */
1133
        if (isset($result['type'])) {
1134
            if (in_array($result['type'], $this->parent_of)) {
1135
                if (class_exists($result['type'])) {
1136
                    $obj = new $result['type'];
1137
                    unset($result['type']);
1138
                }
1139
            }
1140
        }
1141
        $this->_dump_lock = true;
1142
        if (is_array($result)) {
1143
            foreach ($result as $k => $r) {
1144
                if (!is_numeric($k)) {
1145
                    $obj->$k = stripslashes($r);
1146
                }
1147
            }
1148
        }
1149
        $this->_dump_lock = false;
1150
        return $obj;
1151
    }
1152
1153
    /**
1154
     * Iguala los valores de un resultado de la base de datos
1155
     * con sus correspondientes atributos de la clase
1156
     *
1157
     * @param array $result
1158
     * @return ActiveRecord
1159
     */
1160
    public function dump_result_self($result)
1161
    {
1162
        $this->_dump_lock = true;
1163
        if (is_array($result)) {
1164
            foreach ($result as $k => $r) {
1165
                if (!is_numeric($k)) {
1166
                    $this->$k = stripslashes($r);
1167
                }
1168
            }
1169
        }
1170
        $this->_dump_lock = false;
1171
    }
1172
1173
    /**
1174
     * Crea un nuevo registro utilizando los datos del $_REQUEST
1175
     *
1176
     * @param string $form, equivalente a $_REQUEST[$form]
1177
     * @return boolean success
1178
     * @deprecated No es seguro
1179
     */
1180
    public function create_from_request($form = null)
1181
    {
1182
        if (!$form) {
1183
            $form = $this->source;
1184
        }
1185
        return $this->create($_REQUEST[$form]);
1186
    }
1187
1188
    /**
1189
     * Saves a new Row using values from $_REQUEST
1190
     *
1191
     * @param string $form form name for request, equivalent to $_REQUEST[$form]
1192
     * @return boolean success
1193
     * @deprecated No es seguro
1194
     */
1195
    public function save_from_request($form = null)
1196
    {
1197
        if (!$form) {
1198
            $form = $this->source;
1199
        }
1200
        return $this->save($_REQUEST[$form]);
1201
    }
1202
1203
    /**
1204
     * Updates a Row using values from $_REQUEST
1205
     *
1206
     * @param string $form form name for request, equivalent to $_REQUEST[$form]
1207
     * @return boolean|null success
1208
     */
1209
    public function update_from_request($form = null)
1210
    {
1211
        if (!$form) {
1212
            $form = $this->source;
1213
        }
1214
        return $this->update($_REQUEST[$form]);
1215
    }
1216
1217
    /**
1218
     * Creates a new Row in map table
1219
     *
1220
     * @param mixed $values
1221
     * @return boolean success
1222
     */
1223
    public function create()
1224
    {
1225
        if (func_num_args() > 0) {
1226
            $params = Util::getParams(func_get_args());
1227
            $values = (isset($params[0]) && is_array($params[0])) ? $params[0] : $params;
1228
            foreach ($this->fields as $field) {
1229
                if (isset($values[$field])) {
1230
                    $this->$field = $values[$field];
1231
                }
1232
            }
1233
        }
1234
        if ($this->primary_key[0] == 'id') {
1235
            $this->id = null;
1236
        }
1237
        return $this->save();
1238
    }
1239
1240
    /**
1241
     * Consulta si un determinado registro existe o no
1242
     * en la entidad de la base de datos
1243
     *
1244
     * @return boolean
1245
     */
1246
    function exists($where_pk = '')
1247
    {
1248
        if ($this->schema) {
1249
            $table = "{$this->schema}.{$this->source}";
1250
        } else {
1251
            $table = $this->source;
1252
        }
1253
        if (!$where_pk) {
1254
            $where_pk = array();
1255
            foreach ($this->primary_key as $key) {
1256
                if ($this->$key) {
1257
                    $where_pk[] = " $key = '{$this->$key}'";
1258
                }
1259
            }
1260
            if (count($where_pk)) {
1261
                $this->_where_pk = join(" AND ", $where_pk);
1262
            } else {
1263
                return 0;
1264
            }
1265
            $query = "SELECT COUNT(*) FROM $table WHERE {$this->_where_pk}";
1266
        } else {
1267
            if (is_numeric($where_pk)) {
1268
                $query = "SELECT COUNT(*) FROM $table WHERE {$this->primary_key[0]} = '$where_pk'";
1269
            } else {
1270
                $query = "SELECT COUNT(*) FROM $table WHERE $where_pk";
1271
            }
1272
        }
1273
        $num = $this->db->fetch_one($query);
1274
        return $num[0];
1275
    }
1276
1277
    /**
1278
     * Saves Information on the ActiveRecord Properties
1279
     * @param array $values array de valores a cargar
1280
     * @return boolean success
1281
     */
1282
    public function save($values=null)
1283
    {
1284
        if ($values) {
1285
            if (!is_array($values))
1286
                $values = Util::getParams(func_get_args());
1287
            foreach ($this->fields as $field) {
1288
                if (isset($values[$field])) {
1289
                    $this->$field = $values[$field];
1290
                }
1291
            }
1292
        }
1293
        $ex = $this->exists();
1294
        if ($this->schema) {
1295
            $table = $this->schema . "." . $this->source;
1296
        } else {
1297
            $table = $this->source;
1298
        }
1299
        #Run Validation Callbacks Before
1300
        if (method_exists($this, 'before_validation')) {
1301
            if ($this->before_validation() == 'cancel') {
1302
                return false;
1303
            }
1304
        } else {
1305
            if (isset($this->before_validation)) {
1306
                $method = $this->before_validation;
1307
                if ($this->$method() == 'cancel') {
1308
                    return false;
1309
                }
1310
            }
1311
        }
1312
        if (!$ex) {
1313
            if (method_exists($this, "before_validation_on_create")) {
1314
                if ($this->before_validation_on_create() == 'cancel') {
1315
                    return false;
1316
                }
1317
            } else {
1318
                if (isset($this->before_validation_on_create)) {
1319
                    $method = $this->before_validation_on_create;
1320
                    if ($this->$method() == 'cancel') {
1321
                        return false;
1322
                    }
1323
                }
1324
            }
1325
        }
1326
        if ($ex) {
1327
            if (method_exists($this, "before_validation_on_update")) {
1328
                if ($this->before_validation_on_update() == 'cancel') {
1329
                    return false;
1330
                }
1331
            } else {
1332
                if (isset($this->before_validation_on_update)) {
1333
                    $method = $this->before_validation_on_update;
1334
                    if ($this->$method() == 'cancel') {
1335
                        return false;
1336
                    }
1337
                }
1338
            }
1339
        }
1340
1341
        /**
1342
         * Validacion validates_presence
1343
         *
1344
         */
1345
        if (isset($this->_validates['presence_of'])) {
1346
            foreach ($this->_validates['presence_of'] as $f => $opt) {
1347
                if (isset($this->$f) && (is_null($this->$f) || $this->$f == '')) {
1348
                    if (!$ex && $f == $this->primary_key[0])
1349
                        continue;
1350
                    if (isset($opt['message'])) {
1351
                        Flash::error($opt['message']);
1352
                        return false;
1353
                    } else {
1354
                        $field = isset($opt['field']) ? $opt['field'] : $f;
1355
                        Flash::error("Error: El campo $field no puede ser nulo");
1356
                        return false;
1357
                    }
1358
                }
1359
            }
1360
        }
1361
1362
        /**
1363
         * Recordamos que aqui no aparecen los que tienen valores por defecto,
1364
         * pero sin embargo se debe estar pendiente de validar en las otras verificaciones
1365
         * los campos nulos, ya que en estas si el campo es nulo, realmente se refiere a un campo que
1366
         * debe tomar el valor por defecto
1367
         *
1368
         */
1369
        foreach ($this->not_null as $f) {
1370
            if (in_array($f, $this->_with_default)) {
1371
                continue;
1372
            }
1373
1374
            if (!isset($this->$f) || is_null($this->$f) || $this->$f == '') {
1375
                if (!$ex && $f == $this->primary_key[0]) {
1376
                    continue;
1377
                }
1378
                if (!$ex && in_array($f, $this->_at)) {
1379
                    continue;
1380
                }
1381
                if ($ex && in_array($f, $this->_in)) {
1382
                    continue;
1383
                }
1384
                Flash::error("Error: El campo $f no puede ser nulo");
1385
                return false;
1386
            }
1387
        }
1388
1389
        /**
1390
         * Validacion validates_length
1391
         *
1392
         */
1393
        if (isset($this->_validates['length_of'])) {
1394
            foreach ($this->_validates['length_of'] as $f => $opt) {
1395
                if (isset($this->$f) && !is_null($this->$f) && $this->$f != '') {
1396
                    $field = isset($opt['field']) ? $opt['field'] : $f;
1397
1398
                    if (strlen($this->$f) < $opt['min']) {
1399
                        if (isset($opt['too_short']))
1400
                            Flash::error($opt['too_short']);
1401
                        else
1402
                            Flash::error("Error: El campo $field debe tener como mínimo $opt[min] caracteres");
1403
                        return false;
1404
                    }
1405
1406
                    if (strlen($this->$f) > $opt['max']) {
1407
                        if (isset($opt['too_long']))
1408
                            Flash::error($opt['too_long']);
1409
                        else
1410
                            Flash::error("Error: El campo $field debe tener como máximo $opt[max] caracteres");
1411
                        return false;
1412
                    }
1413
                }
1414
            }
1415
        }
1416
1417
        /**
1418
         * Validacion validates_inclusion
1419
         *
1420
         */
1421
        foreach ($this->_validates['inclusion_in'] as $f => $opt) {
1422
            if (isset($this->$f) && !is_null($this->$f) && $this->$f != '') {
1423
                if (!in_array($this->$f, $opt['list'])) {
1424
                    if (isset($opt['message'])) {
1425
                        Flash::error($opt['message']);
1426
                    } else {
1427
                        $field = isset($opt['field']) ? $opt['field'] : $f;
1428
                        Flash::error("$field debe tener un valor entre (" . join(",", $opt['list']) . ")");
1429
                    }
1430
                    return false;
1431
                }
1432
            }
1433
        }
1434
1435
        /**
1436
         * Validacion validates_exclusion
1437
         *
1438
         */
1439
        foreach ($this->_validates['exclusion_of'] as $f => $opt) {
1440
            if (isset($this->$f) && !is_null($this->$f) && $this->$f != '') {
1441
                if (in_array($this->$f, $opt['list'])) {
1442
                    if (isset($opt['message'])) {
1443
                        Flash::error($opt['message']);
1444
                    } else {
1445
                        $field = isset($opt['field']) ? $opt['field'] : $f;
1446
                        Flash::error("$field no debe tener un valor entre (" . join(",", $opt['list']) . ")");
1447
                    }
1448
                    return false;
1449
                }
1450
            }
1451
        }
1452
1453
        /**
1454
         * Validacion validates_numericality
1455
         *
1456
         */
1457
        foreach ($this->_validates['numericality_of'] as $f => $opt) {
1458
            if (isset($this->$f) && !is_null($this->$f) && $this->$f != '') {
1459
                if (!is_numeric($this->$f)) {
1460
                    if (isset($opt['message'])) {
1461
                        Flash::error($opt['message']);
1462
                    } else {
1463
                        $field = isset($opt['field']) ? $opt['field'] : $f;
1464
                        Flash::error("$field debe tener un valor numérico");
1465
                    }
1466
                    return false;
1467
                }
1468
            }
1469
        }
1470
1471
        /**
1472
         * Validacion validates_format
1473
         *
1474
         */
1475
        foreach ($this->_validates['format_of'] as $f => $opt) {
1476
            if (isset($this->$f) && !is_null($this->$f) && $this->$f != '') {
1477
                if (!filter_var($this->$f, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => $opt['pattern'])))) {
1478
                    if (isset($opt['message'])) {
1479
                        Flash::error($opt['message']);
1480
                    } else {
1481
                        $field = isset($opt['field']) ? $opt['field'] : $f;
1482
                        Flash::error("Formato erroneo para $field");
1483
                    }
1484
                    return false;
1485
                }
1486
            }
1487
        }
1488
1489
        /**
1490
         * Validacion validates_date
1491
         *
1492
         */
1493
        foreach ($this->_validates['date_in'] as $f => $opt) {
1494
            if (isset($this->$f) && !is_null($this->$f) && $this->$f != '') {
1495
                if (!filter_var($this->$f, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => "/^\d{4}[-\/](0[1-9]|1[012])[-\/](0[1-9]|[12][0-9]|3[01])$/")))) {
1496
                    if (isset($opt['message'])) {
1497
                        Flash::error($opt['message']);
1498
                    } else {
1499
                        $field = isset($opt['field']) ? $opt['field'] : $f;
1500
                        Flash::error("Formato de fecha erroneo para $field");
1501
                    }
1502
                    return false;
1503
                }
1504
            }
1505
        }
1506
1507
        /**
1508
         * Validacion validates_email
1509
         *
1510
         */
1511
        foreach ($this->_validates['email_in'] as $f => $opt) {
1512
            if (isset($this->$f) && !is_null($this->$f) && $this->$f != '') {
1513
                if (!filter_var($this->$f, FILTER_VALIDATE_EMAIL)) {
1514
                    if (isset($opt['message'])) {
1515
                        Flash::error($opt['message']);
1516
                    } else {
1517
                        $field = isset($opt['field']) ? $opt['field'] : $f;
1518
                        Flash::error("Formato de e-mail erroneo en el campo $field");
1519
                    }
1520
                    return false;
1521
                }
1522
            }
1523
        }
1524
1525
        /**
1526
         * Validacion validates_uniqueness
1527
         *
1528
         */
1529
        // parche para que no tome encuenta el propio registro
1530
        // al validar campos unicos, ya que si lo toma en cuenta
1531
        // lanzará error de validacion porque ya existe un registro
1532
        // con igual valor en el campo unico.
1533
        $and_condition = $ex ? " AND {$this->primary_key[0]} != '{$this->{$this->primary_key[0]}}'" : '';
1534
        foreach ($this->_validates['uniqueness_of'] as $f => $opt) {
1535
            if (isset($this->$f) && !is_null($this->$f) && $this->$f != '') {
1536
                $result = $this->db->fetch_one("SELECT COUNT(*) FROM $table WHERE $f = {$this->db->add_quotes($this->$f)} $and_condition");
1537
                if ($result[0]) {
1538
                    if (isset($opt['message'])) {
1539
                        Flash::error($opt['message']);
1540
                    } else {
1541
                        $field = isset($opt['field']) ? $opt['field'] : $f;
1542
                        Flash::error("El valor '{$this->$f}' ya existe para el campo $field");
1543
                    }
1544
                    return false;
1545
                }
1546
            }
1547
        }
1548
1549
        #Run Validation Callbacks After
1550
        if (!$ex) {
1551
            if (method_exists($this, "after_validation_on_create")) {
1552
                if ($this->after_validation_on_create() == 'cancel') {
1553
                    return false;
1554
                }
1555
            } else {
1556
                if (isset($this->after_validation_on_create)) {
1557
                    $method = $this->after_validation_on_create;
1558
                    if ($this->$method() == 'cancel') {
1559
                        return false;
1560
                    }
1561
                }
1562
            }
1563
        }
1564
        if ($ex) {
1565
            if (method_exists($this, "after_validation_on_update")) {
1566
                if ($this->after_validation_on_update() == 'cancel') {
1567
                    return false;
1568
                }
1569
            } else {
1570
                if (isset($this->after_validation_on_update)) {
1571
                    $method = $this->after_validation_on_update;
1572
                    if ($this->$method() == 'cancel')
1573
                        return false;
1574
                }
1575
            }
1576
        }
1577
1578
        if (method_exists($this, 'after_validation')) {
1579
            if ($this->after_validation() == 'cancel') {
1580
                return false;
1581
            }
1582
        } else {
1583
            if (isset($this->after_validation)) {
1584
                $method = $this->after_validation;
1585
                if ($this->$method() == 'cancel') {
1586
                    return false;
1587
                }
1588
            }
1589
        }
1590
        # Run Before Callbacks
1591
        if (method_exists($this, "before_save")) {
1592
            if ($this->before_save() == 'cancel') {
1593
                return false;
1594
            }
1595
        } else {
1596
            if (isset($this->before_save)) {
1597
                $method = $this->before_save;
1598
                if ($this->$method() == 'cancel') {
1599
                    return false;
1600
                }
1601
            }
1602
        }
1603
        if ($ex) {
1604
            if (method_exists($this, "before_update")) {
1605
                if ($this->before_update() == 'cancel') {
1606
                    return false;
1607
                }
1608
            } else {
1609
                if (isset($this->before_update)) {
1610
                    $method = $this->before_update;
1611
                    if ($this->$method() == 'cancel') {
1612
                        return false;
1613
                    }
1614
                }
1615
            }
1616
        }
1617
        if (!$ex) {
1618
            if (method_exists($this, "before_create")) {
1619
                if ($this->before_create() == 'cancel') {
1620
                    return false;
1621
                }
1622
            } else {
1623
                if (isset($this->before_create)) {
1624
                    $method = $this->before_create;
1625
                    if ($this->$method() == 'cancel') {
1626
                        return false;
1627
                    }
1628
                }
1629
            }
1630
        }
1631
        $environment = Config::read('databases');
1632
        $config = $environment[$this->get_database()];
1633
        if ($ex) {
1634
            $fields = array();
1635
            $values = array();
1636
            foreach ($this->non_primary as $np) {
1637
                $np = self::sql_item_sanitize($np);
1638
                if (in_array($np, $this->_in)) {
1639
                    if ($config['type'] == 'oracle') {
1640
                        $this->$np = date("Y-m-d");
1641
                    } else {
1642
                        $this->$np = date("Y-m-d G:i:s");
1643
                    }
1644
                }
1645
                if (isset($this->$np)) {
1646
                    $fields[] = $np;
1647
                    if (is_null($this->$np) || $this->$np == '' && $this->$np!='0') {
1648
                        $values[] = 'NULL';
1649
                    } else {
1650
                        /**
1651
                         * Se debe especificar el formato de fecha en Oracle
1652
                         */
1653
                        if ($this->_data_type[$np] == 'date' && $config['type'] == 'oracle') {
1654
                            $values[] = "TO_DATE(" . $this->db->add_quotes($this->$np) . ", 'YYYY-MM-DD')";
1655
                        } else {
1656
                            $values[] = $this->db->add_quotes($this->$np);
1657
                        }
1658
                    }
1659
                }
1660
            }
1661
            $val = $this->db->update($table, $fields, $values, $this->_where_pk);
1662
        } else {
1663
            $fields = array();
1664
            $values = array();
1665
            foreach ($this->fields as $field) {
1666
                if ($field != $this->primary_key[0] || $this->{$this->primary_key[0]}) {
1667
                    if (in_array($field, $this->_at)) {
1668
                        if ($config['type'] == 'oracle') {
1669
                            $this->$field = date("Y-m-d");
1670
                        } else {
1671
                            $this->$field = date("Y-m-d G:i:s");
1672
                        }
1673
                    }
1674
                    if (in_array($field, $this->_in)) {
1675
                        unset($this->$field);
1676
                    }
1677
1678
                    if (isset($this->$field) && $this->$field !== '' && $this->$field !== NULL) {
1679
                        $fields[] = self::sql_sanitize($field);
1680
1681
                        if (($this->_data_type[$field] == 'datetime' || $this->_data_type[$field] == 'date') && ($config['type'] == 'mysql' || $config['type'] == 'mysqli')) {
1682
                            $values[] = $this->db->add_quotes(date("Y-m-d G:i:s", strtotime($this->$field)));
1683
                        } elseif ($this->_data_type[$field] == 'date' && $config['type'] == 'oracle') {
1684
                            //Se debe especificar el formato de fecha en Oracle
1685
                            $values[] = "TO_DATE(" . $this->db->add_quotes($this->$field) . ", 'YYYY-MM-DD')";
1686
                        } else {
1687
                            $values[] = $this->db->add_quotes($this->$field);
1688
                        }
1689
                    } elseif (in_array($field, $this->_with_default)) {
1690
                        $fields[] = self::sql_sanitize($field);
1691
                        $values[] = 'DEFAULT';
1692
                    } else {
1693
                        $fields[] = self::sql_sanitize($field);
1694
                        $values[] = 'NULL';
1695
                    }
1696
                } else {
1697
                    /**
1698
                     * Campos autonumericos en Oracle deben utilizar una sequencia auxiliar
1699
                     */
1700
                    if ($config['type'] == 'oracle') {
1701
                        if (!$this->id) {
1702
                            $fields[] = "id";
1703
                            $values[] = $this->source . "_id_seq.NEXTVAL";
1704
                        }
1705
                    }
1706
                    if ($config['type'] == 'informix') {
1707
                        if (!$this->id) {
1708
                            $fields[] = "id";
1709
                            $values[] = 0;
1710
                        }
1711
                    }
1712
                }
1713
            }
1714
1715
            $val = $this->db->insert($table, $values, $fields);
1716
        }
1717
        if (!isset($config['pdo']) && $config['type'] == 'oracle') {
1718
            $this->commit();
1719
        }
1720
        if (!$ex) {
1721
            //$this->db->logger = true;
1722
            $m = $this->db->last_insert_id($table, $this->primary_key[0]);
1723
            $this->find_first($m);
1724
        }
1725
        if ($val) {
1726
            if ($ex) {
1727
                if (method_exists($this, "after_update")) {
1728
                    if ($this->after_update() == 'cancel') {
1729
                        return false;
1730
                    }
1731
                } else {
1732
                    if (isset($this->after_update)) {
1733
                        $method = $this->after_update;
1734
                        if ($this->$method() == 'cancel') {
1735
                            return false;
1736
                        }
1737
                    }
1738
                }
1739
            }
1740
            if (!$ex) {
1741
                if (method_exists($this, "after_create")) {
1742
                    if ($this->after_create() == 'cancel') {
1743
                        return false;
1744
                    }
1745
                } else {
1746
                    if (isset($this->after_create)) {
1747
                        $method = $this->after_create;
1748
                        if ($this->$method() == 'cancel') {
1749
                            return false;
1750
                        }
1751
                    }
1752
                }
1753
            }
1754
            if (method_exists($this, "after_save")) {
1755
                if ($this->after_save() == 'cancel') {
1756
                    return false;
1757
                }
1758
            } else {
1759
                if (isset($this->after_save)) {
1760
                    $method = $this->after_save;
1761
                    if ($this->$method() == 'cancel') {
1762
                        return false;
1763
                    }
1764
                }
1765
            }
1766
            return $val;
1767
        } else {
1768
            return false;
1769
        }
1770
    }
1771
1772
    /**
1773
     * Find All data in the Relational Table
1774
     *
1775
     * @param string $field
1776
     * @param string $value
1777
     * @return ActiveRecord Cursor
1778
     */
1779
    function find_all_by($field, $value)
1780
    {
1781
        self::sql_item_sanitize($field);
1782
        return $this->find("conditions: $field = {$this->db->add_quotes($value) }");
1783
    }
1784
1785
    /**
1786
     * Updates Data in the Relational Table
1787
     *
1788
     * @param mixed $values
1789
     * @return boolean|null sucess
1790
     */
1791
    function update()
1792
    {
1793
        if (func_num_args() > 0) {
1794
            $params = Util::getParams(func_get_args());
1795
            $values = (isset($params[0]) && is_array($params[0])) ? $params[0] : $params;
1796
            foreach ($this->fields as $field) {
1797
                if (isset($values[$field])) {
1798
                    $this->$field = $values[$field];
1799
                }
1800
            }
1801
        }
1802
        if ($this->exists()) {
1803
            if (method_exists($this, 'before_change')) {
1804
                $obj = clone $this;
1805
                if ($this->before_change($obj->find($this->{$this->primary_key[0]})) == 'cancel') {
1806
                    return false;
1807
                }
1808
                unset($obj);
1809
            }
1810
            if ($this->save()) {
1811
                if (method_exists($this, 'after_change')) {
1812
                    if ($this->after_change($this) == 'cancel') {
1813
                        return false;
1814
                    }
1815
                }
1816
                return true;
1817
            }
1818
        } else {
1819
            Flash::error('No se puede actualizar porque el registro no existe');
1820
            return false;
1821
        }
1822
    }
1823
1824
    /**
1825
     * Deletes data from Relational Map Table
1826
     *
1827
     * @param mixed $what
1828
     */
1829
    public function delete($what = '')
1830
    {
1831
        if (func_num_args() > 1) {
1832
            $what = Util::getParams(func_get_args());
1833
        }
1834
        if ($this->schema) {
1835
            $table = $this->schema . "." . $this->source;
1836
        } else {
1837
            $table = $this->source;
1838
        }
1839
        $conditions = '';
1840
        if (is_array($what)) {
1841
            if ($what["conditions"]) {
1842
                $conditions = $what["conditions"];
1843
            }
1844
        } else {
1845
            if (is_numeric($what)) {
1846
                self::sql_sanitize($this->primary_key[0]);
1847
                $conditions = "{$this->primary_key[0]} = '$what'";
1848
            } else {
1849
                if ($what) {
1850
                    $conditions = $what;
1851
                } else {
1852
                    self::sql_sanitize($this->primary_key[0]);
1853
                    $conditions = "{$this->primary_key[0]} = '{$this->{$this->primary_key[0]}}'";
1854
                }
1855
            }
1856
        }
1857
        if (method_exists($this, "before_delete")) {
1858
            if ($this->{$this->primary_key[0]}) {
1859
                $this->find($this->{$this->primary_key[0]});
1860
            }
1861
            if ($this->before_delete() == 'cancel') {
1862
                return false;
1863
            }
1864
        } else {
1865
            if (isset($this->before_delete)) {
1866
                if ($this->{$this->primary_key[0]}) {
1867
                    $this->find($this->{$this->primary_key[0]});
1868
                }
1869
                $method = $this->before_delete;
1870
                if ($this->$method() == 'cancel') {
1871
                    return false;
1872
                }
1873
            }
1874
        }
1875
        $val = $this->db->delete($table, $conditions);
1876
        if ($val) {
1877
            if (method_exists($this, "after_delete")) {
1878
                if ($this->after_delete() == 'cancel') {
1879
                    return false;
1880
                }
1881
            } else {
1882
                if (isset($this->after_delete)) {
1883
                    $method = $this->after_delete;
1884
                    if ($this->$method() == 'cancel') {
1885
                        return false;
1886
                    }
1887
                }
1888
            }
1889
        }
1890
        return $val;
1891
    }
1892
1893
    /**
1894
     * Actualiza todos los atributos de la entidad
1895
     * $Clientes->update_all("estado='A', fecha='2005-02-02'", "id>100");
1896
     * $Clientes->update_all("estado='A', fecha='2005-02-02'", "id>100", "limit: 10");
1897
     *
1898
     * @param string $values
1899
     */
1900
    public function update_all($values)
1901
    {
1902
        $params = array();
1903
        if ($this->schema) {
1904
            $table = $this->schema . "." . $this->source;
1905
        } else {
1906
            $table = $this->source;
1907
        }
1908
        if (func_num_args() > 1) {
1909
            $params = Util::getParams(func_get_args());
1910
        }
1911
        if (!isset($params['conditions']) || !$params['conditions']) {
1912
            if (isset($params[1])) {
1913
                $params['conditions'] = $params[1];
1914
            } else {
1915
                $params['conditions'] = '';
1916
            }
1917
        }
1918
        if ($params['conditions']) {
1919
            $params['conditions'] = " WHERE " . $params['conditions'];
1920
        }
1921
        $sql = "UPDATE $table SET $values {$params['conditions']}";
1922
        $limit_args = array($sql);
1923
        if (isset($params['limit'])) {
1924
            array_push($limit_args, "limit: $params[limit]");
1925
        }
1926
        if (isset($params['offset'])) {
1927
            array_push($limit_args, "offset: $params[offset]");
1928
        }
1929
        if (count($limit_args) > 1) {
1930
            $sql = call_user_func_array(array($this, 'limit'), $limit_args);
1931
        }
1932
        $environment = Config::read('databases');
1933
        $config = $environment[$this->get_database()];
1934
        if (!isset($config->pdo) || !$config->pdo) {
1935
            if ($config['type'] == "informix") {
1936
                $this->db->set_return_rows(false);
1937
            }
1938
        }
1939
        return $this->db->query($sql);
1940
    }
1941
1942
    /**
1943
     * Delete All data from Relational Map Table
1944
     *
1945
     * @param string $conditions
1946
     * @return boolean
1947
     */
1948
    public function delete_all($conditions = '')
1949
    {
1950
        //$limit = '';
1951
        if ($this->schema) {
1952
            $table = $this->schema . "." . $this->source;
1953
        } else {
1954
            $table = $this->source;
1955
        }
1956
        if (func_num_args() > 1) {
1957
            $params = Util::getParams(func_get_args());
1958
            $limit_args = array($select);
1959
            if (isset($params['limit'])) {
1960
                array_push($limit_args, "limit: $params[limit]");
1961
            }
1962
            if (isset($params['offset'])) {
1963
                array_push($limit_args, "offset: $params[offset]");
1964
            }
1965
            if (count($limit_args) > 1) {
1966
                $select = call_user_func_array(array($this, 'limit'), $limit_args);
1967
            }
1968
        }
1969
        return $this->db->delete($table, $conditions);
1970
    }
1971
1972
    /**
1973
     * *********************************************************************************
1974
     * Metodos de Debug
1975
     * *********************************************************************************
1976
     */
1977
1978
    /**
1979
     * Imprime una version humana de los valores de los campos
1980
     * del modelo en una sola linea
1981
     *
1982
     */
1983
    public function inspect()
1984
    {
1985
        $inspect = array();
1986
        foreach ($this->fields as $field) {
1987
            if (!is_array($field)) {
1988
                $inspect[] = "$field: {$this->$field}";
1989
            }
1990
        }
1991
        return join(", ", $inspect);
1992
    }
1993
1994
    /**
1995
     * *********************************************************************************
1996
     * Metodos de Validacion
1997
     * *********************************************************************************
1998
     */
1999
2000
    /**
2001
     * Valida que el campo no sea nulo
2002
     *
2003
     * @param string $field campo a validar
2004
     * @param array $params parametros adicionales
2005
     *
2006
     * message: mensaje a mostrar
2007
     * field: nombre de campo a mostrar en el mensaje
2008
     */
2009
    protected function validates_presence_of($field, $params=array())
2010
    {
2011
        if (is_string($params))
2012
            $params = Util::getParams(func_get_args());
2013
2014
        $this->_validates['presence_of'][$field] = $params;
2015
    }
2016
2017
    /**
2018
     * Valida el tamañoo de ciertos campos antes de insertar
2019
     * o actualizar
2020
     *
2021
     * @params string $field campo a validar
2022
     * @param int $max valor maximo
2023
     * @param int $min valor minimo
2024
     * @param array $params parametros adicionales
2025
     *
2026
     * too_short: mensaje a mostrar cuando se muy corto
2027
     * too_long: mensaje a mostrar cuando sea muy largo
2028
     * field: nombre de campo a mostrar en el mensaje
2029
     */
2030
    protected function validates_length_of($field, $max, $min=0, $params=array())
2031
    {
2032
        if (is_string($params))
2033
            $params = Util::getParams(func_get_args());
2034
2035
        $this->_validates['length_of'][$field] = $params;
2036
        $this->_validates['length_of'][$field]['min'] = $min;
2037
        $this->_validates['length_of'][$field]['max'] = $max;
2038
    }
2039
2040
    /**
2041
     * Valida que el campo se encuentre entre los valores de una lista
2042
     * antes de insertar o actualizar
2043
     *
2044
     * @param string $field campo a validar
2045
     * @param array $list
2046
     *
2047
     * message: mensaje a mostrar
2048
     * field: nombre del campo a mostrar en el mensaje
2049
     */
2050
    protected function validates_inclusion_in($field, $list, $params=array())
2051
    {
2052
        if (is_string($params))
2053
            $params = Util::getParams(func_get_args());
2054
2055
        $this->_validates['inclusion_in'][$field] = $params;
2056
        $this->_validates['inclusion_in'][$field]['list'] = $list;
2057
    }
2058
2059
    /**
2060
     * Valida que el campo no se encuentre entre los valores de una lista
2061
     * antes de insertar o actualizar
2062
     *
2063
     * @param string $field campo a validar
2064
     * @param array $list
2065
     *
2066
     * message: mensaje a mostrar
2067
     * field: nombre del campo a mostrar en el mensaje
2068
     */
2069
    protected function validates_exclusion_of($field, $list, $params=array())
2070
    {
2071
        if (is_string($params))
2072
            $params = Util::getParams(func_get_args());
2073
2074
        $this->_validates['exclusion_of'][$field] = $params;
2075
        $this->_validates['exclusion_of'][$field]['list'] = $list;
2076
    }
2077
2078
    /**
2079
     * Valida que el campo tenga determinado formato segun una expresion regular
2080
     * antes de insertar o actualizar
2081
     *
2082
     * @param string $field campo a validar
2083
     * @param string $pattern expresion regular para preg_match
2084
     *
2085
     * message: mensaje a mostrar
2086
     * field: nombre del campo a mostrar en el mensaje
2087
     */
2088
    protected function validates_format_of($field, $pattern, $params=array())
2089
    {
2090
        if (is_string($params))
2091
            $params = Util::getParams(func_get_args());
2092
2093
        $this->_validates['format_of'][$field] = $params;
2094
        $this->_validates['format_of'][$field]['pattern'] = $pattern;
2095
    }
2096
2097
    /**
2098
     * Valida que ciertos atributos tengan un valor numerico
2099
     * antes de insertar o actualizar
2100
     *
2101
     * @param string $field campo a validar
2102
     *
2103
     * message: mensaje a mostrar
2104
     * field: nombre del campo a mostrar en el mensaje
2105
     */
2106
    protected function validates_numericality_of($field, $params=array())
2107
    {
2108
        if (is_string($params))
2109
            $params = Util::getParams(func_get_args());
2110
2111
        $this->_validates['numericality_of'][$field] = $params;
2112
    }
2113
2114
    /**
2115
     * Valida que ciertos atributos tengan un formato de e-mail correcto
2116
     * antes de insertar o actualizar
2117
     *
2118
     * @param string $field campo a validar
2119
     *
2120
     * message: mensaje a mostrar
2121
     * field: nombre del campo a mostrar en el mensaje
2122
     */
2123
    protected function validates_email_in($field, $params=array())
2124
    {
2125
        if (is_string($params))
2126
            $params = Util::getParams(func_get_args());
2127
2128
        $this->_validates['email_in'][$field] = $params;
2129
    }
2130
2131
    /**
2132
     * Valida que ciertos atributos tengan un valor unico antes
2133
     * de insertar o actualizar
2134
     *
2135
     * @param string $field campo a validar
2136
     *
2137
     * message: mensaje a mostrar
2138
     * field: nombre del campo a mostrar en el mensaje
2139
     */
2140
    protected function validates_uniqueness_of($field, $params=array())
2141
    {
2142
        if (is_string($params))
2143
            $params = Util::getParams(func_get_args());
2144
2145
        $this->_validates['uniqueness_of'][$field] = $params;
2146
    }
2147
2148
    /**
2149
     * Valida que ciertos atributos tengan un formato de fecha acorde al indicado en
2150
     * config/config.ini antes de insertar o actualizar
2151
     *
2152
     * @param string $field campo a validar
2153
     *
2154
     * message: mensaje a mostrar
2155
     * field: nombre del campo a mostrar en el mensaje
2156
     */
2157
    protected function validates_date_in($field, $params=array())
2158
    {
2159
        if (is_string($params))
2160
            $params = Util::getParams(func_get_args());
2161
2162
        $this->_validates['date_in'][$field] = $params;
2163
    }
2164
2165
    /**
2166
     * Verifica si un campo es de tipo de dato numerico o no
2167
     *
2168
     * @param string $field
2169
     * @return boolean
2170
     */
2171
    public function is_a_numeric_type($field)
2172
    {
2173
        if (strpos(" " . $this->_data_type[$field], "int") || strpos(" " . $this->_data_type[$field], "decimal") || strpos(" " . $this->_data_type[$field], "number")) {
2174
            return true;
2175
        } else {
2176
            return false;
2177
        }
2178
    }
2179
2180
    /**
2181
     * Obtiene los datos de los metadatos generados por Primera vez en la Sesi&oacute;n
2182
     *
2183
     * @param string $table
2184
     * @return array
2185
     */
2186
    static function get_meta_data($table)
2187
    {
2188
        if (isset(self::$models[$table])) {
2189
            return self::$models[$table];
2190
        } elseif (PRODUCTION) {
2191
2192
            $metadata = Cache::driver()->get($table, 'kumbia.models');
2193
            if ($metadata) {
2194
                return self::$models[$table] = unserialize($metadata);
2195
            }
2196
        }
2197
        return array();
2198
    }
2199
2200
    /**
2201
     * Crea un registro de meta datos para la tabla especificada
2202
     *
2203
     * @param string $table
2204
     * @param array $meta_data
2205
     */
2206
    static function set_meta_data($table, $meta_data)
2207
    {
2208
        if (PRODUCTION) {
2209
            Cache::driver()->save(serialize($meta_data), Config::get('config.application.metadata_lifetime'), $table, 'kumbia.models');
2210
        }
2211
        self::$models[$table] = $meta_data;
2212
        return true;
2213
    }
2214
2215
    /*     * *****************************************************************************************
2216
     * Metodos para generacion de relaciones
2217
     * ***************************************************************************************** */
2218
2219
    /**
2220
     * Crea una relacion 1-1 entre dos modelos
2221
     *
2222
     * @param string $relation
2223
     *
2224
     * model : nombre del modelo al que se refiere
2225
     * fk : campo por el cual se relaciona (llave foranea)
2226
     */
2227
    protected function has_one($relation)
2228
    {
2229
        $params = Util::getParams(func_get_args());
2230
        for ($i = 0; isset($params[$i]); $i++) {
2231
            $relation = Util::smallcase($params[$i]);
2232
            $index = explode('/', $relation);
2233
            $index = end($index);
2234
            if (!array_key_exists($index, $this->_has_one)) {
2235
                $this->_has_one[$index] = new stdClass();
2236
                $this->_has_one[$index]->model = isset($params['model']) ? $params['model'] : $relation;
2237
                $this->_has_one[$index]->fk = isset($params['fk']) ? $params['fk'] : Util::smallcase(get_class($this)) . '_id';
2238
            }
2239
        }
2240
    }
2241
2242
    /**
2243
     * Crea una relacion 1-1 inversa entre dos modelos
2244
     *
2245
     * @param string $relation
2246
     *
2247
     * model : nombre del modelo al que se refiere
2248
     * fk : campo por el cual se relaciona (llave foranea)
2249
     */
2250
    protected function belongs_to($relation)
2251
    {
2252
        $params = Util::getParams(func_get_args());
2253
        for ($i = 0; isset($params[$i]); $i++) {
2254
            $relation = Util::smallcase($params[$i]);
2255
            $index = explode('/', $relation);
2256
            $index = end($index);
2257
            if (!array_key_exists($index, $this->_belongs_to)) {
2258
                $this->_belongs_to[$index] = new stdClass();
2259
                $this->_belongs_to[$index]->model = isset($params['model']) ? $params['model'] : $relation;
2260
                $this->_belongs_to[$index]->fk = isset($params['fk']) ? $params['fk'] : "{$relation}_id";
2261
            }
2262
        }
2263
    }
2264
2265
    /**
2266
     * Crea una relacion 1-n entre dos modelos
2267
     *
2268
     * @param string $relation
2269
     *
2270
     * model : nombre del modelo al que se refiere
2271
     * fk : campo por el cual se relaciona (llave foranea)
2272
     */
2273
    protected function has_many($relation)
2274
    {
2275
        $params = Util::getParams(func_get_args());
2276
        for ($i = 0; isset($params[$i]); $i++) {
2277
            $relation = Util::smallcase($params[$i]);
2278
            $index = explode('/', $relation);
2279
            $index = end($index);
2280
            if (!array_key_exists($index, $this->_has_many)) {
2281
                $this->_has_many[$index] = new stdClass();
2282
                $this->_has_many[$index]->model = isset($params['model']) ? $params['model'] : $relation;
2283
                $this->_has_many[$index]->fk = isset($params['fk']) ? $params['fk'] : Util::smallcase(get_class($this)) . '_id';
2284
            }
2285
        }
2286
    }
2287
2288
    /**
2289
     * Crea una relacion n-n o 1-n inversa entre dos modelos
2290
     *
2291
     * @param string $relation
2292
     *
2293
     * model : nombre del modelo al que se refiere
2294
     * fk : campo por el cual se relaciona (llave foranea)
2295
     * key: campo llave que identifica al propio modelo
2296
     * through : atrav�s de que tabla
2297
     */
2298
    protected function has_and_belongs_to_many($relation)
2299
    {
2300
        $params = Util::getParams(func_get_args());
2301
        for ($i = 0; isset($params[$i]); $i++) {
2302
            $relation = Util::smallcase($params[$i]);
2303
            if (!array_key_exists($relation, $this->_has_and_belongs_to_many)) {
2304
                $this->_has_and_belongs_to_many[$relation] = new stdClass();
2305
                $this->_has_and_belongs_to_many[$relation]->model = isset($params['model']) ? $params['model'] : $relation;
2306
                $this->_has_and_belongs_to_many[$relation]->fk = isset($params['fk']) ? $params['fk'] : "{$relation}_id";
2307
                $this->_has_and_belongs_to_many[$relation]->key = isset($params['key']) ? $params['key'] : Util::smallcase(get_class($this)) . '_id';
2308
                if (isset($params['through'])) {
2309
                    $this->_has_and_belongs_to_many[$relation]->through = $params['through'];
2310
                }
2311
            }
2312
        }
2313
    }
2314
2315
    /**
2316
     * Herencia Simple
2317
     */
2318
2319
    /**
2320
     * Especifica que la clase es padre de otra
2321
     *
2322
     * @param string $parent
2323
     */
2324
    public function parent_of($parent)
2325
    {
2326
        $parents = func_get_args();
2327
        foreach ($parents as $parent) {
2328
            if (!in_array($parent, $this->parent_of)) {
2329
                $this->parent_of[] = $parent;
2330
            }
2331
        }
2332
    }
2333
2334
    /**
2335
     * Elimina caracteres que podrian ayudar a ejecutar
2336
     * un ataque de Inyeccion SQL
2337
     *
2338
     * @param string $sql_item
2339
     */
2340
    public static function sql_item_sanitize($sql_item)
2341
    {
2342
        $sql_item = trim($sql_item);
2343
        if ($sql_item !== '' && $sql_item !== null) {
2344
            $sql_temp = preg_replace('/\s+/', '', $sql_item);
2345
            if (!preg_match('/^[a-zA-Z0-9_\.]+$/', $sql_temp)) {
2346
                throw new KumbiaException("Se esta tratando de ejecutar una operacion maliciosa!");
2347
            }
2348
        }
2349
        return $sql_item;
2350
    }
2351
2352
    /**
2353
     * Elimina caracteres que podrian ayudar a ejecutar
2354
     * un ataque de Inyeccion SQL
2355
     *
2356
     * @param string $sql_item
2357
     */
2358
    public static function sql_sanitize($sql_item)
2359
    {
2360
        $sql_item = trim($sql_item);
2361
        if ($sql_item !== '' && $sql_item !== null) {
2362
            $sql_temp = preg_replace('/\s+/', '', $sql_item);
2363
            if (!preg_match('/^[a-zA-Z_0-9\,\(\)\.\*]+$/', $sql_temp)) {
2364
                throw new KumbiaException("Se esta tratando de ejecutar una operacion maliciosa!");
2365
            }
2366
        }
2367
        return $sql_item;
2368
    }
2369
2370
    /**
2371
     * Al sobreescribir este metodo se puede controlar las excepciones de un modelo
2372
     *
2373
     * @param unknown_type $e
2374
     */
2375
    protected function exceptions($e)
2376
    {
2377
        throw $e;
2378
    }
2379
2380
    /**
2381
     * Implementacion de __toString Standard
2382
     *
2383
     */
2384
    public function __toString()
2385
    {
2386
        return "<" . get_class() . " Object>";
2387
    }
2388
2389
    /**
2390
     * Paginador para el modelo
2391
     *
2392
     * conditions: condiciones para paginacion
2393
     * page: numero de pagina a mostrar (por defecto la pagina 1)
2394
     * per_page: cantidad de elementos por pagina (por defecto 10 items por pagina)
2395
     *
2396
     * @return un objeto Page identico al que se regresa con el util paginate
2397
     *
2398
     */
2399
    public function paginate()
2400
    {
2401
        $args = func_get_args();
2402
        array_unshift($args, $this);
2403
        //if(!class_exists('Paginator')){
2404
        require_once CORE_PATH . 'libs/kumbia_active_record/behaviors/paginate.php';
2405
        //}
2406
        return call_user_func_array(array('Paginator', 'paginate'), $args);
2407
    }
2408
2409
    /**
2410
     * Paginador para el modelo atraves de consulta sql
2411
     *
2412
     * @param string $sql consulta sql
2413
     *
2414
     * page: numero de pagina a mostrar (por defecto la pagina 1)
2415
     * per_page: cantidad de elementos por pagina (por defecto 10 items por pagina)
2416
     *
2417
     * @return un objeto Page identico al que se regresa con el util paginate_by_sql
2418
     *
2419
     */
2420
    public function paginate_by_sql($sql)
2421
    {
2422
        $args = func_get_args();
2423
        array_unshift($args, $this);
2424
        //if(!class_exists('Paginator')){
2425
        require_once CORE_PATH . 'libs/kumbia_active_record/behaviors/paginate.php';
2426
        //}
2427
        return call_user_func_array(array('Paginator', 'paginate_by_sql'), $args);
2428
    }
2429
2430
    /**
2431
     * Operaciones al serializar
2432
     *
2433
     * */
2434
    public function __sleep()
2435
    {
2436
        /**
2437
         * Anulando conexion a bd en el modelo
2438
         * */
2439
        $this->db = null;
2440
2441
        return array_keys(get_object_vars($this));
2442
    }
2443
2444
    /**
2445
     * Operaciones al deserializar
2446
     *
2447
     * */
2448
    public function __wakeup()
2449
    {
2450
        /**
2451
         * Restableciendo conexion a la bd
2452
         * */
2453
        $this->_connect();
2454
    }
2455
2456
    /**
2457
     * Obtiene la instacia de un modelo
2458
     *
2459
     * @param string $model
2460
     * @return ActiveRecord
2461
     * @throw KumbiaException
2462
     * */
2463
    public static function get($model)
2464
    {
2465
        if (isset(self::$_models[$model])) {
2466
            return self::$_models[$model];
2467
        }
2468
2469
        /**
2470
         * Nombre de la clase
2471
         * */
2472
        $Model = Util::camelcase(basename($model));
2473
2474
        /**
2475
         * Verifico si esta cargada la clase
2476
         * */
2477
        if (!class_exists($Model, false)) {
2478
            /**
2479
             * Carga la clase
2480
             * */
2481
			$model = Util::smallcase($Model);
2482
            $file = APP_PATH . "models/$model.php";
2483
            if (is_file($file)) {
2484
                include $file;
2485
            } else {
2486
                throw new KumbiaException(null, 'no_model');
2487
            }
2488
        }
2489
2490
        self::$_models[$model] = $obj = new $Model();
2491
        return $obj;
2492
    }
2493
2494
    /**
2495
     * Devuelve un JSON de este modelo
2496
     *
2497
     * @return string JSON del modelo
2498
     */
2499
    public function to_json()
2500
    {
2501
        return json_encode($this);
2502
    }
2503
2504
    /**
2505
     * Devuelve un array de este modelo
2506
     *
2507
     * @return array Array del modelo
2508
     */
2509
    public function to_array()
2510
    {
2511
        return ((array) $this);
2512
    }
2513
2514
    /**
2515
     * Devuelve un PHP serial de este modelo
2516
     * Usarlo con cuidado, ya que pasa todos los atributos, no sólo los publicos
2517
     *
2518
     * @return string  Serial de PHP del modelo
2519
     */
2520
    public function serialize()
2521
    {
2522
        return serialize($this);
2523
    }
2524
2525
}
2526